設計 設計 実装 完成

MAESTRO 全機能カタログ & テストカバレッジ

8 領域・502 機能のインベントリと既存テストのカバレッジ一覧(TDD 機能テスト計画)

サマリ

502
機能総数 (Total features)
352
FULL カバー済み
124
PARTIAL 一部のみ
26
NONE 未カバー
領域 (Area)機能数FULLPARTIALNONE
Engine core655294
Tools5632222
Pieces3218131
Reflection6151100
Bridge core503992
Bridge auth & spaces6648171
Services & DB8769135
UI85433111
合計50235212426

本ページの数値は 8 つのインベントリ表をパースして算出した実数(502 件)。設計仕様 (design spec) の概算目標値は 483 件 (FULL 318 / PARTIAL 137 / NONE 29)。差分はインベントリ確定時の項目追加・分類更新による。

機能一覧 (Master feature table)

ID領域機能挙動ソース既存テストカバレッジギャップ
ENG-001Engine coreParallel read-tool executionConsecutive PARALLEL_SAFE tools run via Promise.all; side-effecting tools act as a sequential barrieragent-loop.ts (`canExecuteInParallel`, `PARALLEL_SAFE_TOOL_NAMES`, executeMovement)agent-loop.test.ts "runs consecutive safe tool calls in parallel"; "keeps side-effecting tools sequential as a barrier"; "executes regular tools before transition even if transition appears mid-batch"FULL
ENG-002Engine coretransition tool (non-terminal)buildTransitionTool enum = only rules[].next; validateTransition rejects targets outside rules; processTransitionCalls records lessonsagent-loop.ts (`buildTransitionTool`, `validateTransition`, `processTransitionCalls`)agent-loop.test.ts "non-terminal transition (movement-to-movement) still works"; "transition({next_step:…})"; "lets transition/complete win even if it shares a batch with a repeated call" (tool-loop)PARTIALbuildTransitionTool / validateTransition exercised only indirectly via executeMovement; no unit test that an out-of-rules next is rejected in isolation
ENG-003Engine corecomplete tool — status routingsuccess→COMPLETE, aborted→ABORT, needs_user_input→ASK via COMPLETE_STATUS_TO_NEXT / buildMovementResultFromCompleteagent-loop.ts (`buildCompleteTool`, `parseCompleteArgs`, `buildMovementResultFromComplete`)agent-loop.test.ts §7.1 "success status…becomes movement output"; "aborted status routes via ABORT next"; "needs_user_input routes via ASK next"FULL
ENG-004Engine corecomplete arg validationsuccess requires non-empty result; aborted requires abort_reason; needs_user_input requires missing_info; invalid → forced retry (no accumulatedText fallback)agent-loop.ts (`parseCompleteArgs`, `validateCompleteArgs`)agent-loop.test.ts §7.1 "rejects success with empty result"; "rejects aborted without abort_reason"; (needs_user_input/missing_info path covered via routing test)PARTIALNo explicit test that needs_user_input with empty missing_info is rejected (validateCompleteArgs branch); covered for success/aborted only
ENG-005Engine coreMultiple complete precedence0 completes → continue; >1 conflicting args → retry; >1 identical → first usedagent-loop.ts (complete dedup logic ~L1503-1535)agent-loop.test.ts §7.2 "invalid native complete forces retry"; "two native completes with conflicting args → retry"; "two native completes with identical args → first one used"FULL
ENG-006Engine coreConversation-history integrity on retryEvery tool_use id gets a matching tool_result even when complete is rejectedagent-loop.ts (retry path)agent-loop.test.ts §7.7 "all tool_use ids get a tool_result on retry"FULL
ENG-007Engine coreTool-call loop detection (intra-movement)Identical tool-call batch repeated ≥ maxToolLoopRepeats (default 5) → ABORT; counter resets on arg changeagent-loop.ts (`DEFAULT_MAX_TOOL_LOOP_REPEATS`, `buildToolLoopAbortMessage`, consecutiveToolRepeats)agent-loop.tool-loop.test.ts (5 tests: limit abort, custom limit, under-limit, arg-change reset, transition wins)FULL
ENG-008Engine coreLoop-watchdog one-shot warningAt maxToolLoopRepeats-2 (min 2) inject a single [loop watchdog] reminder before abortingagent-loop.ts (`warnAt`, `toolLoopWarned`, `injectLoopWarning`)(implicit in tool-loop tests' under-limit path)PARTIALNo assertion that the watchdog warning message is actually injected exactly once before abort
ENG-009Engine coreMax-iterations abortMovement exceeding safety.maxIterations aborts with buildMaxIterationsAbortMessageagent-loop.ts (`buildMaxIterationsAbortMessage`)agent-loop.test.ts "aborts when maxIterations is exceeded"FULL
ENG-010Engine coreText-only-response handlingNON-terminal movement aborts after N text-only turns; STRICTLY-terminal (defaultNext COMPLETE, no rules) salvages prose as completion; counter resets on tool useagent-loop.ts (`TEXT_ONLY_REMIND_EMPTY`, textOnlyOutcome)agent-loop.test.ts "aborts after text-only responses in a NON-terminal movement"; "salvages text-only prose…STRICTLY-terminal"; "does NOT salvage…still has rules"; "resets text-only counter when tool calls happen"FULL
ENG-011Engine coreContext-overflow forced transition (runtime usage)ContextManager threshold 95% → force_transition builds result via movement.defaultNext; falls to ABORT when defaultNext is terminal/absentagent-loop.ts (`buildContextOverflowResult`, `buildForceTransitionResult`)agent-loop.test.ts "triggers force_transition when context manager signals exhaustion"; "fires onContextAction callback when context threshold crossed"FULL
ENG-012Engine coreOversized initial prompt guardPrompt over budget pre-send → dedup→compact→summarize; aborts/force-transitions when still oversizedagent-loop.ts + context/prompt-guard.ts (`guardPromptBeforeSend`)agent-loop.test.ts "aborts when initial prompt is oversized and defaultNext is terminal"; "falls back to ABORT when oversized prompt has no defaultNext"; prompt-guard.test.ts (full stage matrix)FULL
ENG-013Engine coreCancel signal handlingAlready-aborted cancelSignal returns ABORT immediately; mid-movement cancel returns ABORT "cancelled"agent-loop.ts (cancelSignal checks)agent-loop.test.ts "returns ABORT immediately when cancelSignal is already aborted"PARTIALMid-iteration cancel (after work started) not asserted at agent-loop level; only the pre-loop case
ENG-014Engine coreContextManager threshold ladder70%→warn, 85%→prompt, 95%→force_transition; each fires once; isExhausted ≥0.99context-manager.ts (`ContextManager`)context-manager.test.ts (13 tests: each threshold, fire-once, defaults, limitTokens, isExhausted, hasUsageData, char fallback)FULL
ENG-015Engine coreOllama context-limit autodetectfetchOllamaContextLimit prefers num_ctx > model_info.context_length; llama.cpp /props fallback; default on failurecontext-manager.tscontext-manager.test.ts (5 tests incl. num_ctx precedence, /props fallback, n_ctx absent)FULL
ENG-016Engine coreTool catalog injection into system promptbuildSystemPrompt / buildGlobalPreamble auto-inject available-tools list + 1-line summaries; script section gated on Bash presenceagent-loop.ts (`buildGlobalPreamble`, `buildSystemPrompt`)agent-loop.test.ts "injects the script section when Bash is among the presented tools"; "omits…when Bash is not presented"; preamble byte-stability testsFULL
ENG-017Engine corePreamble/guidance split (job-stable vs movement)buildGlobalPreamble byte-identical across movements; buildMovementGuidance carries persona/instruction/transitionsagent-loop.tsagent-loop.test.ts "preamble is byte-identical across two different movements"; "guidance carries movement-specific instruction, persona, transitions"; "buildSystemPrompt wrapper still contains both halves"FULL
ENG-018Engine coreProgressive pressure (intra-movement revisit)visitCount==2 → caution; >=3 → warning injected into guidanceagent-loop.ts (`buildMovementGuidance` L672)agent-loop.test.ts "guidance injects progressive-pressure warning on revisit"PARTIALOnly the revisit-warning presence is asserted; the visitCount==2 (caution) vs >=3 (warning) distinction is not separately tested
ENG-019Engine coreChecklist watchdogOne-shot reminder after 5 iterations with no checklist tool; suppressed if CreateChecklist used earlyagent-loop.tsagent-loop.test.ts "injects a one-shot reminder after 5 iterations"; "does NOT fire when CreateChecklist is called early"; traceability "watchdog_fire"FULL
ENG-020Engine coreCross-movement Read cacheCacheable read served to later movement; errors not cached; non-allowlisted tools (Bash) skippedagent-loop.ts + context/tool-result-cache.tsagent-loop.test.ts "returns a cached Read result…"; "does not cache error results"; "skips caching tools outside the cacheable allowlist"FULL
ENG-021Engine coreCache invalidationEdit/Write invalidate affected path; Grep entries all-evicted on edit; Bash conservatively invalidates all file entries; no invalidate on tool erroragent-loop.ts + context/invalidation.ts + tool-result-cache.tsagent-loop.test.ts Phase 2/4 (7 tests); invalidation.test.ts (5); tool-result-cache.test.ts (28)FULL
ENG-022Engine coreCache key constructionDeterministic v1-prefixed keys for Read/Grep/Glob/WebFetch/Office; URL scheme/host normalization; workspace isolationcontext/cache-key.tscache-key.test.ts (25); tool-result-cache.test.ts key testsFULL
ENG-023Engine coreFile-read dedupOlder Reads of same file replaced with placeholder, most-recent kept; ignores non-Read tools; idempotentcontext/file-read-dedup.tsfile-read-dedup.test.ts (10)FULL
ENG-024Engine coreHistory summarizationsummarizeHistory replaces middle turns with summary, preserves tail; LLM-failure → summarized=false; update-mode with prior summarycontext/history-compactor.tshistory-compactor.test.ts (21: splitIntoTurns, buildSummaryPrompt, selectTailTurnStartIndex, summarizeHistory, summarizeForceTransition)FULL
ENG-025Engine coreToken estimationchar→token by script class (ASCII/CJK/other), per-message overhead, image budget, tool serializationcontext/token-estimate.tstoken-estimate.test.ts (23)FULL
ENG-026Engine corePrompt-guard staged compactiondedup→compact→summarize ladder; reserve-cap headroom; stage-3 skipped when disabled/no isolated LLM; ok:false when all failcontext/prompt-guard.tsprompt-guard.test.ts (27)FULL
ENG-027Engine coreConversation class (seed/enter/persist)seed order (preamble+guidance+task); enterMovement appends guidance w/o reset; flush/loadFrom round-trip; rewrite truncatescontext/conversation.tsconversation.test.ts (in-memory + persistence, 8)FULL
ENG-028Engine coreConversation.replayableTurnsStrips system + control-flow tool_calls (complete/transition) + dangling/orphan tool messages; keeps valid pairscontext/conversation.tsconversation.test.ts (7 replayableTurns cases)FULL
ENG-029Engine coreConversation.seedContinuation / hasReplayableTranscriptBuilds [preamble,guidance,...priorTurns,user] & rewrites transcript; hasReplayableTranscript guards undefined/missing/system-onlycontext/conversation.tsconversation.test.ts (seedContinuation + 4 hasReplayableTranscript)FULL
ENG-030Engine coreContinuation seed in executeMovementpriorTurns replayed & handoff block suppressed; plain seed when absentagent-loop.tsagent-loop.test.ts "replays priorTurns and suppresses handoff block"; "without priorTurns, uses plain seed"; "with empty priorTurns array, behaves like plain seed"FULL
ENG-031Engine coreAtomic JSON persistencewriteAtomicJson (tmp+rename, mkdir parents); readSafeJson returns missing/corrupt/wrong-version; quarantineCorruptFilecontext/atomic-json.tsatomic-json.test.ts (11)FULL
ENG-032Engine coreWorkspace path normalizationRejects absolute/backslash/drive/UNC/NUL/empty; canonicalizes ../.; prefixWorkspacePath join with traversal guardcontext/path-normalize.tspath-normalize.test.ts (15)FULL
ENG-033Engine corePiece loop guards (cross-movement)enforceLoopGuards: consecutive revisits > max_consecutive_revisits (default 4) → abort; counters reset on movement changepiece-runner.ts (`enforceLoopGuards`)piece-runner.test.ts "aborts when loop detection fires due to consecutive revisits"PARTIALPer-movement max_consecutive_revisits override and the counter-reset-on-movement-change branch not separately asserted
ENG-034Engine coreASK limit fallbackASK result resolves default_next or first non-self/non-ASK/non-ABORT rule; aborts when no fallback; resolves COMPLETE default_next via ASK-limit fallbackpiece-runner.ts (result.next==='ASK' branch)piece-runner.test.ts "falls back to default_next when ASK limit is reached"; "aborts when ASK limit reached and no fallback"; "resolves a COMPLETE default_next reached via the ASK-limit fallback"FULL
ENG-035Engine coreWAIT_SUBTASKS pauseresult.next==='WAIT_SUBTASKS' pauses piece run (waiting_subtasks)piece-runner.ts (L1144)piece-runner.test.ts "does NOT write snapshot on waiting_subtasks (transient pause)"PARTIALThe pause/resume return contract is only asserted via the snapshot-suppression side effect; no direct test of the waiting_subtasks PieceRunResult shape
ENG-036Engine coreSpawnSubTask depth-limit skipMovement requiring SpawnSubTask skipped at depth limit (incl. via shared_tools); resolves COMPLETE default_next on skippiece-runner.ts (`pieceUsesSpawn`, spawn skip)piece-runner.test.ts "resolves a COMPLETE default_next when a SpawnSubTask movement is skipped at the depth limit"; "skips a SpawnSubTask movement at the depth limit even when…shared_tools"FULL
ENG-037Engine coreWAITING_HUMAN_BROWSER / _TOOL_REQUEST pausesNon-ASK human-wait sentinels pause the runpiece-runner.ts (L1159, L1176)(none found)NONENo test exercises the WAITING_HUMAN_BROWSER or WAITING_HUMAN_TOOL_REQUEST branches
ENG-038Engine coreReview-feedback carry-forwardverify movement output carried into later execute/process/plan/analyze; truncated per/combined caps; git status+diff appended after verifypiece-runner.ts (`buildInstructionWithFeedback`, `shouldCarryReviewFeedback`, truncate/trim)piece-runner.test.ts "carries cumulative verify feedback into later execute/analyze movements"; "appends safe git status and diff context after verify loops"FULL
ENG-039Engine coreLessons accumulation & injectiontransition/complete lessons accumulated, capped (MAX_LESSONS_LENGTH 2000), injected into next movement; written to lessons.jsonlpiece-runner.ts (`buildLessonsContext`, `writeLessonLog`, lessonsAccumulator)(no direct test of buildLessonsContext/writeLessonLog; snapshot tests reference lessons)PARTIALLessons capture appears in cancel-snapshot tests but the injection-into-next-movement and 2000-char trim logic is not directly asserted; writeLessonLog untested
ENG-040Engine coreloadPiece terminal-rule validationrejects rules[].next ∈ {COMPLETE,ABORT,ASK}; accepts default_next COMPLETE & WAIT_SUBTASKS sentinel; all bundled pieces validpiece-runner.ts (`validatePieceDef`, `loadPiece`)piece-runner.test.ts (Phase 6b: 6 tests incl. all bundled pieces load)FULL
ENG-041Engine coreloadPiece multi-dir resolutioncustom dir(s) win over builtin; first dir wins on dup name; string|string[] formspiece-runner.ts (`loadPiece`)piece-runner.test.ts "resolves from a list of custom dirs"; "first dir wins"; "string form still works"FULL
ENG-042Engine corenormalizeRequiredMcpretains valid slugs, drops invalid, undefined when absent, coerces non-array to []piece-runner.tspiece-runner.test.ts (4 required_mcp tests)FULL
ENG-043Engine corenormalizeSharedTools / mergeToolNamesshared_tools sanitized; mergeToolNames unions shared+movement first-seen order, drops dups/non-stringspiece-runner.tspiece-runner.test.ts (shared_tools 4 + mergeToolNames 3)FULL
ENG-044Engine corevalidateAllowedSshConnectionsvalidates id format / wildcard / empty-deny; tolerates missing allowlist (A4 workspace-policy gating); reports offenderspiece-runner.tspiece-runner.test.ts (allowed_ssh_connections 15 + loader-tolerance 1)FULL
ENG-045Engine coreWorkspace tool-policy resolution & injectionresolveWorkspaceTools (safe default, Bash/browser opt-in); runPiece injects workspaceTools overriding movement.allowed_tools; grantedTools unionedpiece-runner.ts (runPiece) + workspace-tool-policy.tspiece-runner.test.ts (resolveWorkspaceTools 5 + runPiece injection 4); workspace-tool-policy.test.tsFULL
ENG-046Engine corerunPiece max_movements defaultiterates when max_movements missing / 0 / negativepiece-runner.ts (runPiece)piece-runner.test.ts "still iterates when piece.max_movements is missing"; "…is 0 or negative"FULL
ENG-047Engine corebuildFollowupNoticedetects follow-up when output/ or subtasks/ has non-hidden files; ignores dotfiles; empty for fresh wspiece-runner.tspiece-runner.test.ts (5 buildFollowupNotice tests)FULL
ENG-048Engine coreCancel/terminal memory snapshotssnapshot+meta-event on cancelled (pre/mid movement) and aborted; NOT on success or waiting_subtasks; v2 captures finalOutput/history/lessons w/ truncationpiece-runner.tspiece-runner.test.ts (7 snapshot tests)FULL
ENG-049Engine coreConversation continuity end-to-endmovement 2 sees movement 1 history; transcript.jsonl written; continuation job replays prior turns & suppresses LIMIT-1 handoffpiece-runner.ts + conversation.tspiece-runner.conversation.test.ts (2)FULL
ENG-050Engine corePiece classification promptbuildClassificationPrompt includes task+descriptions+files; biases toward chat; routes workspace-apppiece-classifier.tspiece-classifier.test.ts (buildClassificationPrompt 4)FULL
ENG-051Engine coreClassification response parsingparseClassificationResponse extracts valid name, handles noise, prefers longest match, null on invalid/emptypiece-classifier.tspiece-classifier.test.ts (parseClassificationResponse 5)FULL
ENG-052Engine coreclassifyPiece (async LLM orchestration)Builds prompt, calls LLM, parses + falls back to default piecepiece-classifier.ts (`classifyPiece`)(none)NONEThe end-to-end classify path (LLM call wiring, fallback when parse returns null, error handling) is untested; only its two pure helpers are
ENG-053Engine coreloadAllPieceTriggersLoads triggers/keywords from all pieces across dirs; custom wins on dup name; skips invalid piecespiece-runner.ts (`loadAllPieceTriggers`)(none)NONENo test; dir-precedence and skip-on-load-error branches uncovered
ENG-054Engine corebuildChecklistContextBuilds checklist context block from logs rootpiece-runner.ts(referenced in piece-runner.test.ts import only; no describe block)PARTIALSymbol imported in test file but no behavior assertion targets it specifically
ENG-055Engine corebuildLocalConversationContextRenders current instruction + recent-conversation + workspace files; interjection precedence; truncation; omitRecentConversation flaglocal-context.tslocal-context.test.ts (7)FULL
ENG-056Engine corePieceCatalog caching/layeringBuilt-ins + user pieces (custom wins); TTL cache; invalidate(userId); per-folder cache isolationpiece-catalog.tspiece-catalog.test.ts (7)FULL
ENG-057Engine corestripThinkingTokensStrips <think>/<|thinking|>/gemma channel blocks; leaves unclosed intact; preserves unicodestrip-thinking.tsstrip-thinking.test.ts (11) + agent-loop.test.ts (6)FULL
ENG-058Engine coreprompt-coach scoringbuildCoachMessages (source inclusion + truncation); normalizeCoachResult (clamp/defaults); runPromptCoach (normalize + error propagation)prompt-coach.tsprompt-coach.test.ts (9)FULL
ENG-059Engine coreConsole screen injectionInjects SSH console screen each iteration when SshConsole* allowed + session exists; guards on missing taskId / no session / non-console piece; tail truncationagent-loop.ts (buildSystemPrompt)agent-loop.test.ts (5 console) + agent-loop-console.test.ts (4)FULL
ENG-060Engine coreHandoff block constructionStatic Continue block always; dynamic block only with handoffContext; null prevResult handled; long prevResult truncated head+tailagent-loop.ts (buildSystemPrompt)agent-loop.test.ts (5 handoff tests)FULL
ENG-061Engine corePer-user AGENTS.md / memory / working-dir injectionInjects AGENTS.md + persistence protocol + Working Directory + approach/error-recovery sections, gated on userId/workspacePathagent-loop.ts (buildSystemPrompt)agent-loop.user-agents.test.ts (9)FULL
ENG-062Engine coreExisting-workspace-files injectionInjects existing input-files section when present, omits when emptyagent-loop.ts (buildSystemPrompt)agent-loop.test.ts (2)FULL
ENG-063Engine coreTraceability event emissionmovement_start/tool_call/result/movement_complete; cache_set/hit/invalidate; watchdog_fire; run_start/complete; followup_detected; shared runIdagent-loop.ts + piece-runner.tsagent-loop.test.ts (T-1, 5) + piece-runner.test.ts (T-2, 2)FULL
ENG-064Engine coreownerId/userId defaultingDefaults to 'local'/synthetic when unowned; preserves real owner in auth modepiece-runner.ts (runPiece)piece-runner.test.ts "defaults ownerId and userId to…"; "preserves a real ownerId/userId"FULL
ENG-065Engine coreMission char-budget trimmingbuildGlobalPreamble trims mission text when total exceeds MISSION_TOTAL_CHAR_BUDGETagent-loop.ts (L428 overflow trim)(none)NONEThe mission/preamble char-budget overflow trim is not asserted by any test
TOOL-001ToolsReadRead file slice (offset/limit); delegates binary detection; rejects directories (EISDIR), enforces readonly/core.test.ts (88 it: binary detector, readonly/ enforce, symlink escape)FULLEISDIR/dir-reject path (MEMORY: prior crash) not explicitly named in describes — verify a dir-reject test exists
TOOL-002ToolsWriteWrite to workspace; assertWritable blocks readonly/ + outside-workspacecore.test.ts (readonly/ enforcement, resolveAndGuard symlink escape)FULL
TOOL-003ToolsEditExact-string replace; same write-guard path as Writecore.test.tsPARTIALEdit-specific match-fail / replace_all behavior not clearly enumerated in describes
TOOL-004ToolsBashShell exec; AbortSignal propagation, path-scope guard, install-pattern blocking, unrestricted mode, env scrub, history logcore.test.ts (AbortSignal, checkBashPathScope, install rejection, bashUnrestricted, env scrub, history)FULLStrong coverage incl. cancel-traceability
TOOL-005ToolsGlobPattern file matchcore.test.ts (core tools)PARTIALFew dedicated Glob assertions; edge cases (no-match, ignore) unclear
TOOL-006ToolsGrepContent searchcore.test.tsPARTIALSame — limited Grep-specific cases
TOOL-007ToolsWebSearchSearXNG search w/ fallback + history; query sanitizeweb.test.ts (sanitizeQuery, searchViaSearxng, fallback history, parseSearchResultsFromText)FULL
TOOL-008ToolsWebFetchFetch URL → readability text; SSRF guard; persistent context mgmtweb.test.ts (web tools, persistent context), shared/ssrf.test.ts, shared/readability.test.ts, web.binary.test.tsFULLSSRF tested at shared layer; binary handling covered
TOOL-009ToolsDownloadFileDownload to input/ or source/; source-library index.jsonl; raw log-onlyweb.test.ts (source-library routing 2 it), raw-savePARTIALSize-limit / large-file / redirect handling on download not asserted
TOOL-010ToolsReadImageRead image → vision; usage trackingimage.test.ts (13 it), image.vision-usage.test.tsFULL
TOOL-011ToolsAnnotateImageDraw annotations on imageimage.test.tsPARTIALAnnotation rendering correctness lightly covered
TOOL-012ToolsReadExcelParse xlsx/xls/xlsm/xlsb; size limit 10MB; format-mismatch + CFB/HTML/CSV disguise rejection; optional stylesoffice.test.ts (format rejection 8 it, styles), excel-styles.test.tsPARTIAL**Size-limit (>10MB) enforcement path not tested** — only format mismatch
TOOL-013ToolsReadDocxParse docx/docm; size limit 10MBoffice.test.tsPARTIALSize-limit path untested; happy-path light
TOOL-014ToolsReadPdfExtract PDF text; query/grep mode (regex, case-insensitive, no-match, empty-query); size limit 10MB; OOXML-as-pdf rejectionoffice.test.ts (ReadPdf query mode 5 it, format rejection)PARTIALSize-limit path untested
TOOL-015ToolsReadPPTXParse pptx/pptm; slide order resolution; notes; **ZIP-bomb detection (uncompressed >200MB)**; size limit 50MBoffice.test.tsNONE**ZIP-bomb detection AND PPTX size-limit have NO test** — highest-risk office gap
TOOL-016ToolsReadMsgParse Outlook .msgmsg.test.ts (41 it)FULL
TOOL-017ToolsSplitExcelSheetsSplit workbook into per-sheet filesoffice.test.tsPARTIALSplit correctness lightly covered
TOOL-018ToolsSplitDocxSectionsSplit docx into section filesoffice.test.tsPARTIALSame
TOOL-019ToolsPdfToImagesRender PDF pages to images; edit-not-allowed error, missing-file error, invalid page_range erroroffice.test.ts (3 error-path it)FULLError paths covered
TOOL-020ToolsSQLiteRun SQL on workspace .db; SELECT happy path, write guards (editAllowed), always-blocked DDL, PRAGMA, errors, path-traversal guard, large result setsdata.test.ts (25 it across 9 describe)FULLStrong — guards + traversal + DDL block
TOOL-021ToolsBatchReviewTextWithLLMPer-file isolated LLM review; output-path prefix enforcementreview.test.ts (3 it)PARTIALOnly 3 it; LLM-failure / partial-file handling not covered
TOOL-022ToolsMergeReviewedResultsMerge reviewed JSON → markdown; rejects outputs outside prefixesreview.test.tsPARTIALMerge edge cases (malformed JSON) untested
TOOL-023ToolsBrowseWebPlaywright browse + actions mode; URL validation, SSRF/localhost block, file-URL workspace containment, traversal reject, auth-expiry, download filename sanitize, unique-path, recording, source persistbrowser.test.ts (36 it), browser.runpageactions.test.ts, browser-frame-chain.e2e.test.tsFULLValidation/SSRF/recording very strong; live page-action correctness is e2e-gated (env-dependent)
TOOL-024ToolsSpawnSubTaskSpawn parallel subtask joborchestration.test.ts (8 it)FULL
TOOL-025ToolsXSearchX/Twitter searchx.test.ts (18 it)PARTIALLive transaction-id breakage (MEMORY) means runtime failures not covered by unit tests
TOOL-026ToolsXUserPostsX user timeline postsx.test.tsPARTIALSame upstream breakage caveat
TOOL-027ToolsXPostDetailSingle X post detailx.test.tsPARTIALSame
TOOL-028ToolsXFetchCardMediaFetch X card mediax.test.tsPARTIALLikely thin coverage
TOOL-029ToolsXTimelineX home/timelinex.test.tsPARTIALLikely thin coverage
TOOL-030ToolsSearchPlacesMaps place searchmaps.test.ts (27 it)FULL
TOOL-031ToolsGetDirectionsMaps routingmaps.test.tsFULL
TOOL-032ToolsReverseGeocodeCoords → addressmaps.test.tsFULL
TOOL-033ToolsGetYouTubeTranscriptFetch YouTube transcriptyoutube.test.ts (20 it)FULL
TOOL-034ToolsSearchYouTubeYouTube searchyoutube.test.tsFULL
TOOL-035ToolsSearchAmazonAmazon product searchamazon.test.ts (13 it)FULL
TOOL-036ToolsTranscribeAudioSpeech-to-textspeech.test.ts (14 it)FULL
TOOL-037ToolsCreateChecklistCreate task checklist (META)checklist.test.ts (24 it incl. META_TOOL describe)FULL
TOOL-038ToolsCheckItemMark checklist item (META)checklist.test.tsFULL
TOOL-039ToolsGetChecklistRead checklist (META)checklist.test.tsFULL
TOOL-040ToolsListPiecesList available piecespieces.test.ts (11 it)FULL
TOOL-041ToolsGetPieceRead a piece YAMLpieces.test.tsFULL
TOOL-042ToolsCreatePieceCreate custom piece (per-user fork)pieces.test.tsPARTIALValidation/lint reject paths (COMPLETE/ABORT in next) coverage unclear here
TOOL-043ToolsUpdatePieceUpdate custom piecepieces.test.tsPARTIALSame
TOOL-044ToolsInstallSkillInstall skill from registry (META)skills.test.ts (26 it)FULL
TOOL-045ToolsReadSkillRead skill content (META, always available)skills.test.tsFULL
TOOL-046ToolsListSkillsList skills (META)skills.test.tsFULL
TOOL-047ToolsReadToolDocRead tool doc incl. MCP tools via cache (META, always available); name-required error, bad-MCP-name errordocs.test.ts (10 it)FULL
XCUT-001ToolsDynamic tool loading + dispatch orderindex.ts tryLoadModule lazy-imports each module; executeToolInner dispatches in fixed order (mcp → web → image → data → office → review → x → orchestration → browser → maps → … → skills); failed loads silently skippedindex.test.ts (1 it: SshConsole catalog)NONE**Dispatch order, first-match-wins, and silent-skip-on-load-failure are essentially untested** (single catalog test)
XCUT-002ToolsMETA_TOOLS always-injected at dispatchindex.ts keeps a LARGER META_TOOLS list (incl. RequestTool, Mission, UserFolder, AppDocs, Skills) injected into getToolDefs regardless of movement allowed_tools(indirect via tool-categories.test.ts subset)PARTIALindex.ts's runtime META injection (20 names) not directly asserted; only tool-categories' SUBSET set is tested
XCUT-003Toolstool-categories: name→category mapfirst-write-wins map built from ALL_TOOL_DEFS('core') + MODULE_SPECS; memoised; listToolCategories; SENSITIVE_CATEGORIES={ssh,browser}; SENSITIVE_TOOLS={Bash}; META_TOOLS subsettool-categories.test.ts (12 it: META_TOOLS entries, sensitive sets, map)FULL
XCUT-004ToolsRequestTool flowMETA tool; classifies request as already_available / available_not_allowed(requested) / unknown(hard-error); name+reason required; interactive→pending approval, non-interactive→auto_denied; records via ctx.recordToolRequesttool-request.test.ts (9 it), db/repository.tool-requests.test.tsFULLAll 3 classification branches + interactive/auto-deny covered
XCUT-005Toolsworkspace-tool-policy resolutionresolveWorkspaceTools: safe categories default-on (minus disabledSafe), sensitive categories+tools opt-in only (enabledSensitive), META always unioned, Bash special-cased, mcp__* always carried; parseToolPolicy tolerant JSONworkspace-tool-policy.test.ts, workspace-tool-policy.regression.test.ts, db/repository.tool-policy.test.ts, bridge/space-api.tool-policy.test.tsFULLStrong; regression test guards Bash toggle (MEMORY PR #653)
XCUT-006Toolsraw-save result loggingsaveRawData writes {logsRoot}/raw/<tool>-<ts>.txt + rawdata-history.jsonl for RAW_SAVE_TOOLS (web/x/youtube/amazon/speech/ms-learn); logRawDownload records DownloadFile path-only; errors swallowed to logger.warnraw-save.test.ts (5 it)PARTIALHelper, not a tool. Per-tool wiring (that each RAW_SAVE_TOOL actually triggers save) is not asserted end-to-end
XCUT-007Toolsstructured-blocksRich-UI structured-data save helper (no TOOL_DEFS)structured-blocks.test.ts (4 it)PARTIALHelper; basic coverage
XCUT-008Toolsbinary-detect shared helperMagic-byte/binary detection used by Read + WebFetchbinary-detect.test.ts (26 it)FULLWell covered; underpins Read/WebFetch binary handling
XCUT-009Toolsconflict-guard (persistent workspace)Detects concurrent-edit conflicts in shared spaceconflict-guard.test.ts (9 it), core.test.ts (conflict detection)FULL
PIECE-001Pieceschatpiece-classifier.test (as default-bias target); reflection/silent-fork & applier.fuzz use 'chat'; piece-runner all-load sweepPARTIALNo test asserts the single-movement respond contract or its 43-tool allowlist; only used as a name fixture + load-sweep
PIECE-002Piecesgeneralpiece-runner.test (loadPiece('general') line 401, used in review-prompt/shared_tools tests); pieces-api uses 'general' fixture; load sweepPARTIALLoaded & used as a fixture for unrelated assertions (review prompts, SpawnSubTask skip), but its 4-movement decompose/verify loop is not asserted as a contract
PIECE-003Piecesresearchpiece-runner.test loadPiece('research') (line 403, review-prompt structure assertion); load sweepPARTIALReview-prompt/plan-aware structure asserted for it, but the dig/analyze/verify transition logic is not exercised
PIECE-004Piecesresearch-subload sweep onlyPARTIALNo direct test; only schema-load smoke. Spawn-from-parent linkage untested
PIECE-005Piecesdata-processpiece-classifier.test (longer-match-wins: data-process over general); load sweepPARTIALClassifier disambiguation asserted; movement contract not
PIECE-006Piecesoffice-processpiece-runner.test loadPiece('office-process') (line 402); piece-classifier fixture; load sweepPARTIALLoaded for review-prompt assertion; 2-movement contract not exercised
PIECE-007Piecesslidepiece-classifier.test (chat vs slide routing fixture); load sweepPARTIALUsed as classifier fixture name; movement/tool contract untested
PIECE-008Piecesbrainstormingload sweep onlyPARTIALNo direct test; schema-load smoke only
PIECE-009Piecessns-researchload sweep onlyPARTIALNo direct test
PIECE-010Piecesx-ai-digestload sweep onlyPARTIALNo direct test
PIECE-011Piecespiece-builderpiece-classifier.test (workspace-app routed AWAY from piece-builder); load sweepPARTIALOnly the negative-routing case touches it; its own contract untested
PIECE-012Piecesworkspace-apppiece-runner.test dedicated movement-structure assertions (build→verify, default_next checks, lines 542-557); piece-classifier routing test (line 41-52); workspace-app E2E harness (separate subsystem)FULLMovement structure + default_next + classifier routing all asserted; strongest-covered piece
PIECE-013Piecesssh-consolepiece-runner.test dedicated assertion (interact default_next: COMPLETE, line 563-572); tools/ssh-console.test; ssh allowed_ssh_connections validation testsFULLMovement + ssh-connection allowlist contract asserted
PIECE-014Piecesssh-opspiece-runner.test dedicated assertion (line 576+); ssh allowlist validation tests; load sweepFULLLoaded + structurally asserted (execute/verify); ssh allowlist format validation covered. Lighter than ssh-console but real assertions exist
PIECE-015Pieceshelpload sweep onlyPARTIALNo direct test
PINFRA-001PiecesloadPiece (custom-dir priority + builtin fallback)Resolves piece by name across custom dirs (string|string[]), custom wins, fallback to builtinpiece-runner.test "loadPiece multi-dir" (string|string[], per-user wins, first-dir wins, backward-compat); worker.test loadPiece('local-piece')FULLMulti-dir resolution well covered
PINFRA-002PiecesvalidatePieceDef (Phase 6b terminal-next rejection)Throws if any rules[].next ∈ {COMPLETE,ABORT,ASK}; default_next exemptpiece-runner.test "loadPiece terminal-rule validation (Phase 6b)": rejects COMPLETE/ABORT/ASK in rules[].next, accepts default_next: COMPLETE, accepts movement-to-movement + WAIT_SUBTASKSFULLCore invariant directly asserted with error-message matching
PINFRA-003Piecesdefault_next sentinel (engine-internal fallback)Used for context-overflow / ASK-limit / SpawnSubTask-skip; allows COMPLETE/ABORT/ASKpiece-runner.test "falls back to default_next when ASK limit is reached", "resolves a COMPLETE default_next reached via ASK-limit fallback", SpawnSubTask-skip→default_next testsFULLAll three sentinel paths exercised
PINFRA-004Pieceslint-pieces.mjs (CI lint, terminal-next ban)Standalone script: hard-fails any piece with COMPLETE/ABORT/ASK in rules[].next**none** (no *.test for the script; runtime equivalent in validatePieceDef IS tested)NONEScript itself has no test; logic duplicated/covered by PINFRA-002 but the CLI (file collection, exit codes, parse-error path) is untested
PINFRA-005PiecesCreatePiece tool validationRejects missing/0 max_movements, non-array shared_tools, bad rules[].next; writes to dirs[0] (per-user)tools/pieces.test "CreatePiece" block (missing max_movements, max_movements:0, valid write, shared_tools array/non-array)FULLValidation + write-target asserted
PINFRA-006PiecesUpdatePiece tool (built-in protection)Refuses overwriting built-in (isBuiltinOnly); allows update when custom override existstools/pieces.test "UpdatePiece" block (refuses built-in overwrite regression, allows custom override)FULLBuilt-in immutability regression covered
PINFRA-007Piecesrules[].next / default_next domain validation (tool path)validRuleNexts = movements ∪ WAIT_SUBTASKS; validDefaultNexts adds COMPLETE/ABORT/ASKCovered via CreatePiece tests + pieces-api "rejects rules[].next: COMPLETE" / "accepts default_next: COMPLETE"FULLBoth accept & reject branches asserted at API + tool level
PINFRA-008PiecesListPieces / GetPiece (custom+builtin merge, priority)Lists from per-user + global-custom + builtin; GetPiece prefers dirs[0]tools/pieces.test "customPiecesDir as array — P1-a regression" (ListPieces both dirs, GetPiece second-dir, GetPiece prefers per-user)FULLMulti-dir merge + priority covered
PINFRA-009Piecespiece classifier — buildClassificationPromptBuilds LLM prompt from task + all piece descriptions + keyword hints; biases to chat; routes workspace-apppiece-classifier.test "buildClassificationPrompt" (includes text/descriptions, file names, chat bias, workspace-app routing)FULLPrompt construction + routing bias asserted
PINFRA-010Piecespiece classifier — parseClassificationResponseExtracts valid piece name from noisy LLM output; longer-match-wins; null on invalid/emptypiece-classifier.test "parseClassificationResponse" (valid, noisy, longer-match data-process>general, invalid→null, empty→null)FULLParse edge cases covered
PINFRA-011Piecespiece catalog (loadAllPieceTriggers / catalog merge)Enumerates all pieces' triggers; custom overrides builtin by namepiece-catalog.test (builtin+custom merge, custom override wins, triggers exposed)FULLCatalog merge & override asserted
PINFRA-012Piecespieces-api CRUD + per-user/builtin authzGET/PUT/POST/DELETE with per-user custom dirs, builtin immutability, cross-user authz, ?source=pieces-api.test — extensive: no-auth legacy + auth-aware (built-in 403, own-custom 200, cross-user 403, admin edit-but-not-delete builtin, duplicate-name reject, source resolution)FULLOne of the most thoroughly tested infra areas
PINFRA-013PiecesSSH allowlist validation (allowed_ssh_connections)Validates UUID/wildcard/empty allowlist; rejects SshExec without allowlist & bad formatspiece-runner.test (id-format reject, wildcard, multi-movement offenders) + pieces-api.test (SshExec w/o allowlist, UUID, wildcard, empty, bad format, non-array)FULLBoth engine + API layers covered
PINFRA-014Piecesshared_tools / required_mcp normalizationDrops invalid entries, warns on SSH shared_tools w/o connectionspiece-runner.test imports normalizeRequiredMcp/normalizeSharedTools/mergeToolNames; tools/pieces + pieces-api shared_tools array/non-array testsFULLNormalization + merge covered
PINFRA-015Piecescustom/default piece namespace separation (reflection silent fork)Reflection forks built-in into data/users/{u}/pieces with forked_from_commit instead of mutating builtinreflection/silent-fork.test (silentFork chat/data-process into per-user dir)FULLSilent-fork path covered (reflection subsystem)
PINFRA-016Piecesall-built-in-pieces load sweepLoops every pieces/*.yaml asserting loadPiece().not.toThrow()piece-runner.test (~line 531-537)FULLThis is what gives every piece PARTIAL; it validates schema but NOT each piece's movement/rule semantics
PINFRA-017Piecesper-user piece resolution in worker/scheduler/continue pathscustomPiecesDir threaded through worker, continue-job, classifier, pieces-apiworker.test loadPiece('local-piece', piecesDir, customPieceDirs); partly via pieces-api auth testsPARTIALWorker path has one test; full propagation across scheduler/continue paths (per the known "no-auth local fallback" checklist) not comprehensively asserted
REFL-001ReflectionEnqueue: enabled gate (default OFF)maybeEnqueueReflection returns early when cfg.enabled=falsesrc/worker.ts (363)worker.reflection-enqueue.test.ts ("does nothing when enabled=false")FULLDEFAULT_REFLECTION default value (enabled:false) itself not asserted in a config test
REFL-002ReflectionEnqueue: no-recursion on reflection jobsreturns early when job.taskKind==='reflection'src/worker.ts (371)worker.reflection-enqueue.test.ts ("does NOT recurse")FULL
REFL-003ReflectionEnqueue: no-auth owner fallback to 'local'ownerId ?? 'local' for namespace + budget keysrc/worker.ts (377)enqueue.test.ts (2 cases: fallback + budget keyed to 'local')FULL
REFL-004ReflectionEnqueue: worker_required gateskip when workerRequired and no worker has role 'reflection'src/worker.ts (379-388)enqueue.test.ts (3 cases: true+none/true+exists/false)FULL
REFL-005ReflectionEnqueue: per-user daily token budget capskip when spent >= cap; cap=0 = unlimited; UTC-day windowsrc/worker.ts (392-405)enqueue.test.ts (at/under cap, cap=0, UTC 25h, independent users)FULL
REFL-006ReflectionEnqueue: space_id propagation (Space-as-Principal)carries job.spaceId into payload (null for personal)src/worker.ts (407-420)enqueue.test.ts (carries space_id / null personal)FULL
REFL-007ReflectionDispatch: reflection job routes to runReflectionJobworker picks task_kind='reflection', imports + runs runner, role-keyed routingsrc/worker.ts (1129-1130, 2044-2073)worker.reflection-dispatch.test.ts (2 cases)PARTIALdispatch test is small (~2 cases); gateway routing-key + 401-without-key path not asserted end-to-end
REFL-008ReflectionRunner: loadReflectionInputs (memory/activity/comments/feedback + observedRevisions)assembles inputs, builds bodyRevision CAS mapload-inputs.tsload-inputs.test.ts (~8 cases, CAS)FULL
REFL-009ReflectionRunner: forced submit_reflection tool_call (LLM)callReflectionLlm forces the tool, parses result, tokensllm-client.tsllm-client.test.ts (~10 cases)FULL
REFL-010ReflectionRunner: prompt constructionbuildSystemPrompt / buildUserPromptreflection-prompt.tsreflection-prompt.test.ts (~18 cases)FULL
REFL-011ReflectionRunner: schema validation of LLM outputreflection-schema parse of memory_changes/piece_changesreflection-schema.tsreflection-schema.test.ts (~10 cases, all ops)FULL
REFL-012ReflectionRunner: end-to-end happy path + outcome + metric rowrunReflectionJob applies, snapshots, records metricreflection-runner.tsreflection-runner.test.ts (~12 cases: applied/abstained/failed, snapshot, lock, space, budget)FULL
REFL-013ReflectionRunner: failure fallbacks record outcome='failed'loadInputs/LLM/apply throw → metric 'failed', return 'failed'reflection-runner.tsreflection-runner.test.ts (failed)PARTIALonly some of the 3 throw-sites (loadInputs/LLM/apply+snapshot) explicitly covered; writeSnapshot-throws-but-continue branch not clearly asserted
REFL-014ReflectionRunner: Space-as-Principal storage resolutionspace job → data/spaces/{id}; personal → data/users/{userId}; attribution stays per-userreflection-runner.tsreflection-runner.test.ts (space-principal), revisions.test.tsFULL
REFL-015ReflectionValidator: rejected_unknown_typetype not in {user,feedback,project,reference}semantic-validator.tssemantic-validator.test.ts; applier.fuzz.test.tsFULL
REFL-016ReflectionValidator: rejected_bad_namename fails isValidMemoryNamesemantic-validator.tssemantic-validator.test.ts; applier.test.ts; fuzzFULL
REFL-017ReflectionValidator: rejected_body_too_largebody > maxBodyBytes (UTF-8)semantic-validator.tssemantic-validator.test.ts; fuzzFULL
REFL-018ReflectionValidator: rejected_injected_directiveprompt-injection heuristics in body/descriptionsemantic-validator.tssemantic-validator.test.tsFULLonly validator test hits it; not exercised through applier (low risk, static check)
REFL-019ReflectionValidator: rejected_missing_targetnon-add op missing merge_target OR target not in indexsemantic-validator.tssemantic-validator.test.ts; applier.test.ts; fuzzFULL
REFL-020ReflectionValidator: rejected_name_collisionadd with already-existing namesemantic-validator.tssemantic-validator.test.ts; fuzzFULL
REFL-021ReflectionValidator: rejected_target_piece_mismatchpiece_changes.target_piece ≠ input.pieceNamesemantic-validator.tssemantic-validator.test.ts; applier.test.ts; fuzzFULL
REFL-022ReflectionValidator: rejected_invalid_yamlpiece new_yaml fails YAML parsesemantic-validator.tssemantic-validator.test.ts; fuzzFULL
REFL-023ReflectionValidator: rejected_invalid_piecenew_yaml parses but fails piece-lintsemantic-validator.tssemantic-validator.test.ts; fuzzFULL
REFL-024ReflectionValidator: rejected_dangerous_pieceCOMPLETE/ABORT/ASK in rules[].nextsemantic-validator.tssemantic-validator.test.ts; fuzzFULL
REFL-025ReflectionApply CAS: rejected_stale_targetmerge_target body revision != snapshot → reject at write timeapplier.tsapplier.test.ts; fuzzFULLraised by applier (not static validator); covered
REFL-026ReflectionApply op: addupsertMemoryEntry for new nameapplier.ts (applyOne)applier.test.ts; schema; runnerFULL
REFL-027ReflectionApply op: update (+CAS)upsert replacing body/desc/type, requires CAS passapplier.tsapplier.test.ts (ops + CAS)FULL
REFL-028ReflectionApply op: merge_into (+CAS)read existing, append timestamped section, upsert mergedapplier.tsapplier.test.tsFULL
REFL-029ReflectionApply op: remove (+CAS)removeMemoryEntry (trash + index), requires CASapplier.tsapplier.test.tsFULL
REFL-030ReflectionApply: missing-target-on-disk at write timenon-add op, target gone from disk → rejected_missing_targetapplier.tsapplier.test.ts (rejected_missing_target)FULL
REFL-031ReflectionApply: 3-change hard capmemory_changes truncated to 3 with WARNapplier.tsapplier.test.ts / fuzzPARTIALcap value 3 truncation likely covered by fuzz inputs but a dedicated ">3 dropped with WARN" assertion not confirmed
REFL-032ReflectionApply: applyOne write error → code='failed' on decisionper-change try/catch records failedapplier.tsPARTIALthe per-change applyOne throw path (decision.code='failed') not obviously asserted (distinct from runner-level 'failed')
REFL-033ReflectionOutcome decision: abstainedabstain_reason + no applied + no rejectedapplier.ts (decideOutcome)applier.test.ts (abstained); runnerFULL
REFL-034ReflectionOutcome decision: partialany applied AND any rejected/droppedapplier.tsapplier.test.ts (partial)FULL
REFL-035ReflectionOutcome decision: appliedany applied, none rejectedapplier.tsapplier.test.ts (applied); runnerFULL
REFL-036ReflectionOutcome decision: rejectedany rejected/dropped, none appliedapplier.tsapplier.test.ts (rejected); fuzzFULL
REFL-037ReflectionOutcome decision: fallback abstainedempty input (0 changes, no piece)applier.tsapplier.test.tsPARTIALthe empty-input fallback branch (vs explicit abstain_reason) not clearly distinguished in tests
REFL-038ReflectionPiece write: cooldown gate (2 edits/24h)>=2 edits in window → {written:false, reason:'cooldown'} → pieceCooldownDroppedpiece-writer.ts; applier.tspiece-writer.test.ts (cooldown); applier.test.ts (cooldown)FULL
REFL-039ReflectionPiece write: cooldown drop vs semantic reject distinctionpieceCooldownDropped separate from pieceRejectCode in outcomeapplier.tsapplier.test.ts; piece-writer.test.tsFULL
REFL-040ReflectionPiece write: atomic temp+rename + catalog invalidate + recordPieceEditwritePiece tmp.{pid} → rename, repo.recordPieceEdit, catalog.invalidatepiece-writer.tspiece-writer.test.ts (~8 cases)FULLatomic-rename crash-safety (partial-write) not stress-tested
REFL-041ReflectionSilent fork of built-in piecebuiltin source → copy to data/users/{userId}/pieces with forked_from_commit/forked_at; idempotent; throws if source missingsilent-fork.tssilent-fork.test.ts (~9 cases)FULL
REFL-042ReflectionDrift detection (forked_from_commit vs latest builtin commit)detectDrift returns drifted flag; graceful no-op without gitdrift-detect.tsdrift-detect.test.ts (~7 cases)FULL
REFL-043ReflectionSnapshot write (.reflection-history/{ts}-{jobId} + index.jsonl append)writeSnapshot persists before/after files, appends index.jsonl atomically under locksnapshot.ts; reflection-runner.tssnapshot.test.ts (~34 cases)FULL
REFL-044ReflectionSnapshot: before/after memory file capturerunner builds beforeFiles/afterFiles from accepted decisions (remove uses merge_target)reflection-runner.tsreflection-runner.test.ts; snapshot.test.tsPARTIALthe remove-op before-file naming branch (merge_target vs name) in runner not specifically asserted
REFL-045ReflectionSnapshot: piece before/after YAML capturereads custom piece path pre/post for snapshotreflection-runner.tsreflection-runner.test.tsPARTIALpiece YAML before/after capture lightly covered; existsSync(customPath) edge not asserted
REFL-046ReflectionbodyRevision (SHA-1 of parsed body) consistencysame helper for loader observedRevisions and applier CASrevisions.tsrevisions.test.ts (~8 cases)FULL
REFL-047ReflectionPer-user file lock (withUserLock)serializes apply+snapshot critical section (lockfile)user-lock.ts; applier.ts; reflection-runner.tsuser-lock.test.ts; applier.test.ts (lock); runnerFULLconcurrent contention/torn-write scenario only indirectly assured
REFL-048ReflectionActivity log summarizersummarizes activity.log to bounded size for promptactivity-summarizer.tsactivity-summarizer.test.ts (~5 cases)FULL
REFL-049ReflectionDB: reflection_metrics row (outcome applied/partial/abstained/rejected/failed)recordReflectionMetric inserts one row per reflection jobsrc/db/repository.ts (~4093-4127)reflection-runner.test.ts; migrate.reflection-columns.test.tsFULL
REFL-050ReflectionDB: reflection_piece_edits cooldown trackingrecordPieceEdit insert + count-in-window querysrc/db/repository.ts (~4068-4090)piece-writer.test.ts; migrate testFULL
REFL-051ReflectionDB: aggregateReflectionMetrics (budget)sums tokens since UTC-day start for budget gatesrc/db/repository.tsenqueue.test.ts (budget)FULL
REFL-052ReflectionDB: task_kind / payload columns + reflection job creationmigrate adds task_kind, payload; reflection job created with taskKind='reflection'src/db/repository.ts; migrate.tsmigrate.reflection-columns.test.ts (~23 cases)FULL
REFL-053ReflectionAPI: GET /history (paged, owner-scoped, nextCursor)lists snapshot index entries, most-recent-first, cursor paginationbridge/reflection-api.tsreflection-api.test.ts (~29 cases)FULL
REFL-054ReflectionAPI: GET /history/:snapshotId (404 no-leak for non-owner/missing)detail or 404 (no existence leak)bridge/reflection-api.tsreflection-api.test.tsFULL
REFL-055ReflectionAPI: POST /history/:snapshotId/revert (idempotent)revertSnapshotForUser; {reverted:true} first, {reverted:false} afterbridge/reflection-api.ts; snapshot.tsreflection-api.test.ts (revert); snapshot.test.ts (revert)FULL
REFL-056ReflectionAPI: space-aware store resolution + canEditInSpace authzresolves per-user vs per-space store; space history needs edit rights; 404 on mismatchbridge/reflection-api.tsreflection-api.test.ts (space-principal)PARTIALcanEditInSpace negative path (member-without-edit-rights → 404) not clearly asserted as a distinct multiuser case
REFL-057ReflectionAPI: GET /metricsaggregated reflection metrics endpointbridge/reflection-api.tsreflection-api.test.tsPARTIALendpoint exists; depth of assertion (per-outcome breakdown) unverified
REFL-058ReflectionAPI: GET /latest-for-task/:taskId (admin)latest snapshot for a task; null (not 404) when none; admin-gatedbridge/reflection-api.tsreflection-api.test.tsPARTIALadmin-gating + null-vs-404 distinction not confirmed asserted
REFL-059ReflectionRetention: pruneOldSnapshots (age)delete snapshot dirs older than retentionDays; index.jsonl untouchedretention.tsretention.test.ts (~17 cases)FULL
REFL-060ReflectionRetention: enforceDiskCap (oldest-first)prune oldest until under capBytesretention.tsretention.test.tsFULL
REFL-061ReflectionRetention: runReflectionRetentionSweep (per-user + per-space)sweeps both data/users and data/spaces treesretention.tsretention.test.ts (space-principal)FULL
APIC-001Bridge coreGET /api/local/tasksList tasks filtered by buildVisibilityWhere (owner/admin/org/public/space members)local-tasks-api.ts:123local-tasks-api.test.ts (visibility list: owner/admin/same-org/diff-org bystander)FULL
APIC-002Bridge corePOST /api/local/tasksCreate task+job; visibility enum validation, org-scope check, spaceId forces private, owner from req.user (or local no-auth)local-tasks-api.ts:134local-tasks-api.test.ts (visibility, no-auth owner, runtime_dir, attachments, spaceId), spaces.test.tsFULL
APIC-003Bridge coreGET /api/local/tasks/:taskIdGet one task; canViewTask visibility gatelocal-tasks-api.ts:342space-view.test.ts (membership read gate), spaces.test.tsFULL
APIC-004Bridge coreGET /api/local/tasks/:taskId/tool-requestsList pending tool requests; canViewTask gatelocal-tasks-api.ts:361local-tasks-api.test.ts:175 (lists pending)PARTIALview-gate denial path not directly asserted on this route
APIC-005Bridge corePOST /api/local/tasks/:taskId/tool-requests/:reqId/decideApprove/deny a parked tool request; write-gate (403 view-only / 404 non-member), re-queue parked job, 409 already-decidedlocal-tasks-api.ts:380local-tasks-api.test.ts:143 (approve/deny/409/invalid value/park-contract guard)FULL
APIC-006Bridge corePUT /api/local/tasks/:taskId/feedbackSubmit rating/feedback; owner-or-admin (404 to others even if public)local-tasks-api.ts:418local-tasks-api.test.ts:740 (owner/admin/non-owner 404), spaces.test.ts:268FULL
APIC-007Bridge corePUT /api/local/tasks/:taskId/missionSet/update task mission textlocal-tasks-api.ts:442none found (only auth.test.ts matches the word "mission")NONENo test exercises this route — authz + validation unverified
APIC-008Bridge coreGET /api/local/tasks/:taskId/commentsList comments; canViewTask gatelocal-tasks-api.ts:474covered indirectly via comment-write-gate.test.ts setup; no dedicated read-gate assertionPARTIALGET read-gate not directly asserted
APIC-009Bridge corePOST /api/local/tasks/:taskId/commentsPost comment (user message → spawns/reuses job); **comment write gate** (owner/admin/editor 201, viewer 403, non-member 404), attachment save, tool_request gate atomicity, idle-job reuselocal-tasks-api.ts:492comment-write-gate.test.ts (owner/admin/editor/viewer-403/non-member-404/personal-404), local-tasks-api.test.ts:816 (ownership, attachments, job reuse, terminal→fresh job)FULL
APIC-010Bridge corePATCH /api/local/tasks/:taskIdUpdate task (visibility change, etc.); owner/admin, org-scope validationlocal-tasks-api.ts:623local-tasks-api.test.ts:451 (visibility change, invalid enum, org scope), spaces.test.ts:255 (non-member 404)FULL
APIC-011Bridge corePOST /api/local/tasks/:taskId/regenerate-titleRegenerate AI title; owner/admin onlylocal-tasks-api.ts:681spaces.test.ts:296 (owner regenerate, +line 318 case)PARTIALOnly exercised in space context; non-owner-denied / no-generator paths not asserted
APIC-012Bridge corePOST /api/local/tasks/evaluate-promptPrompt-coach: evaluate an instruction via LLMlocal-tasks-api.ts:717local-tasks-api.test.ts:1370 (no-generator, blank, happy paths)FULL
APIC-013Bridge coreDELETE /api/local/tasks/:taskIdDelete task; owner-or-admin (404 to others even public)local-tasks-api.ts:756local-tasks-api.test.ts:336 (owner/admin/non-owner-404), spaces.test.ts:69FULL
APIC-014Bridge corePOST /api/local/tasks/:taskId/cancelCancel running job; write-gate (editor 200, viewer 403, non-member 404)local-tasks-api.ts:778comment-write-gate.test.ts:117 (editor/viewer-403/non-member-404), spaces.test.ts:98FULL
APIC-015Bridge corePOST /api/local/tasks/:taskId/continueSpawn child job with new piece; 409 job_in_progress / no_previous_job, handoff comment, instruction persistlocal-tasks-api.ts:818local-tasks-api.test.ts:1072 (happy/handoff/persist/409 in-progress/409 no-prev), spaces.test.ts:122 (space_id carry, non-member 404)FULL
APIC-016Bridge coreGET /api/local/tasks/:taskId/streamSSE stream of job events (tool calls, deltas)local-tasks-api.ts:914none (/stream matches only in app-share/share/space-api tests)NONESSE event mapping, visibility gate, reconnection all untested
APIC-017Bridge coreGET /api/local/tasks/:taskId/filesList files in a section (input/output/logs/workspace); canViewTask gate; traversal 400local-files-api.ts:41local-files-api.test.ts:74 (default section, subdir, unknown section 400, private-hidden, traversal 400, logs from runtime_dir + workspace_path fallback)FULL
APIC-018Bridge coreGET /api/local/tasks/:taskId/files/contentServe file as text/plain; traversal 400, dir 400, missing 404, leading-slash striplocal-files-api.ts:86local-files-api.test.ts:152 (text, requires-path, 404 not 500, dir 400, traversal 400, abs-path strip)FULL
APIC-019Bridge coreGET /api/local/tasks/:taskId/files/rawServe raw bytes by extension; trusted=1 drops CSP sandbox (owner-trust model), setUntrustedFileResponseHeaders, ensurePathWithin traversal guard (400)local-files-api.ts:136local-files-api.test.ts:197 (type-by-ext, sandbox default, trusted for owner/admin/non-owner-shared-viewer/ownerless, no-user-still-sandboxed, no-auth, non-HTML stays sandboxed, traversal 400)FULLStrong coverage incl. CSP trust matrix
APIC-020Bridge coreGET /api/local/tasks/:taskId/files/office-previewExcel→cells JSON / PPTX→slide PNGs; section/path validation 400, soffice missing → 503local-files-api.ts:203office-preview.test.ts (file-type detect, spreadsheet preview incl. formula, soffice-missing throws) tests the *module*; the HTTP route itself not directly hitPARTIALModule logic FULL; HTTP route (section enum 400, canViewTask gate, 503 mapping) not exercised end-to-end
APIC-021Bridge corePUT /api/local/tasks/:taskId/files/contentWrite/overwrite output file; owner/admin only, output-section-only, refuse while running, traversal 400, string-content validationlocal-files-api.ts:240local-files-api.test.ts:290 (owner write, non-owner 404, admin, refuse-running, output-only, requires-string, traversal 400)FULL
APIC-022Bridge corePOST /api/local/tasks/:taskId/files/uploadUpload files to input/ (≤60mb); owner/admin, O_EXCL rename-on-collision, name sanitize, refuse-running, section/empty/traversal 400local-files-api.ts:324local-files-api.test.ts:366 (input upload, subdir, collision rename, name sanitize, non-owner 404, admin, refuse-running, logs/workspace 400, empty 400, traversal 400)FULL
APIC-023Bridge corePOST /api/local/tasks/:taskId/files/deleteDelete files; owner/admin, idempotent skip-missing, skip-dirs, refuse-running, validate-all-first (no partial), traversal 400local-files-api.ts:392local-files-api.test.ts:470 (delete, single path, skip-missing/dirs, non-owner 404, refuse-running, logs 400, empty 400, traversal 400, validate-all-first)FULL
APIC-024Bridge corePOST /api/local/tasks/:taskId/files/download-zipZip selected files; read-gated (non-owner of public OK), all sections, safeZipEntryName zip-slip protection, traversal 400, empty 400local-files-api.ts:447local-files-api.test.ts:547 (owner zip, logs section, non-owner-public read, private 404, 404-nothing-matches, traversal 400, empty 400, zip-slip backslash, ..-strip)FULL
APIC-025Bridge coreGET /api/piecesList pieces (builtin + global-custom + caller's user-custom, priority-merged, no hiding)pieces-api.ts:251pieces-api.test.ts (no-auth list, auth-aware merge, same-name no-hide, source tagging)FULL
APIC-026Bridge coreGET /api/pieces/:nameGet one piece; ?source= selector, invalid source → 400, priority resolutionpieces-api.ts:325pieces-api.test.ts (404 unknown, ?source=builtin, priority resolution, invalid-source typo 400)FULL
APIC-027Bridge corePUT /api/pieces/:nameUpdate; builtin by non-admin 403, admin can edit builtin, own user-custom 200, others' 403, lint (rules[].next COMPLETE reject, SshExec allowlist, shared_tools)pieces-api.ts:370pieces-api.test.ts (builtin-non-admin 403, own 200, other-user 403, admin builtin, ?source typo 400, lint rules)FULL
APIC-028Bridge corePOST /api/piecesCreate; dup-name reject, builtin-name reject, no-auth→piecesDir vs user-custom, 503 when userPiecesRootDir unset for authed non-admin/admin, returns actual sourcepieces-api.ts:430pieces-api.test.ts (create, dup reject, builtin-name reject, 503 fallback hole, source return, no-auth local user-custom)FULL
APIC-029Bridge coreDELETE /api/pieces/:nameDelete; builtin non-deletable 403 (even admin), own user-custom 200, ?source typo 400 no-destructive-fallbackpieces-api.ts:490pieces-api.test.ts (builtin 403, own 200, admin-cannot-delete-builtin, ?source=user-custom target, typo 400)FULL
APIC-030Bridge coreGET /api/configReturn v2 config + ETag; omits legacy provider/flat-storage keys; admin-only mountconfig-api.ts:87config-api.test.ts (v2 shape+etag, omits legacy, exposes storage.*)FULLNote: sensitive-masking observed in tests as worker api_key exclusion (APIC-033), not on this raw GET
APIC-031Bridge corePUT /api/configUpdate with If-Match ETag optimistic lock → **409 on stale etag**; legacy-key reject 400; force config_version=2; YAML round-tripconfig-api.ts:93config-api.test.ts (round-trip, force-version, legacy-key 400s ×6, storage round-trip, **409 stale etag**)FULL
APIC-032Bridge corePOST /api/config/reloadReload config from file (500 on parse error)config-api.ts:117config-api.test.ts:208 (reload from file)PARTIAL500 error path not asserted
APIC-033Bridge coreGET /api/workersList workers, allowlisted fields only (**no api_key leak**), synthesized default, proxy/proxyType exposed; 401 unauthconfig-api.ts:126config-api.test.ts (default synth, allowlist fields, proxy no-key-leak, 401 unauth, 200 authed)FULLThis is the de-facto sensitive-masking test
APIC-034Bridge coreGET /api/workers/:workerId/backendsFetch /v1/models from proxy worker; 404 unknown, direct→empty, 60s cache, scheme/URL validation (no apiKey leak to fetch), 502 upstream fail, 401 unauthconfig-api.ts:149config-api.test.ts (404, direct empty, fetch list, cache, file://+data: reject, malformed reject, 502, 401)FULL
APIC-035Bridge coreGET /api/toolsRuntime tool catalog (builtin+meta+MCP+ssh w/ availability flags); ?legacy=1 flat array; 401 unauthtools-api.ts:242tools-api.test.ts (catalog, source/category tags, meta scope, ssh avail/unavail, MCP authed/offline/no-user, 401, legacy flat, placeholder, TestWorkspaceApp)FULL
APIC-036Bridge coreGET /api/scheduled-tasksList; visibility filter, ?spaceId= scopescheduled-tasks-api.ts:105scheduled-tasks-api.test.ts (list, visibility filter owner/non-owner/admin, ?spaceId scope)FULL
APIC-037Bridge coreGET /api/scheduled-tasks/:idGet one scheduled taskscheduled-tasks-api.ts:120scheduled-tasks-api.test.ts (covered via list/manage setup)PARTIALNo dedicated GET-by-id view-gate assertion
APIC-038Bridge corePOST /api/scheduled-tasksCreate; visibility enum+org-scope validation, spaceId forces private + edit-rights fallback to NULL, no-auth synthetic local owner, requires bodyscheduled-tasks-api.ts:133scheduled-tasks-api.test.ts (visibility, org reject, spaceId force-private/edit-fallback/see-not-edit-fallback, no-auth local, browserSessionProfile owner check, daily, require-body)FULL
APIC-039Bridge corePATCH /api/scheduled-tasks/:idUpdate; canManageSchedule (owner/admin/space-editor); **non-owner may manage LIFECYCLE only (pause/resume/reschedule/visibility) — content edits owner-only (P3 confused-deputy guard)**; keeps space schedule privatescheduled-tasks-api.ts:267scheduled-tasks-api.test.ts (pause/resume, owner-or-admin 404 paths, admin any, space-editor manage, lost-edit-rights, keep-private)FULLConfused-deputy split (lifecycle vs content) is the known P3 design caveat
APIC-040Bridge coreDELETE /api/scheduled-tasks/:idDelete; owner/admin/space-editor (non-owner 404)scheduled-tasks-api.ts:461scheduled-tasks-api.test.ts (owner, admin any, non-owner 404, space-editor survives-creator-leaving)FULL
APIC-041Bridge corePOST /api/scheduled-tasks/:id/triggerManually trigger a run; canManageSchedule gatescheduled-tasks-api.ts:480scheduled-tasks-api.test.ts:203 (creator who lost edit rights can no longer trigger)PARTIALHappy-path trigger spawning a run not directly asserted; only the denial path
APIC-042Bridge coreGET /api/local/tasks/:id/subtasks/activitiesAggregate subtask activity tree; canViewTask gate (404 hidden, public allowed)subtask-activity-api.ts:28subtask-activity-api.test.ts (nested when waiting_subtasks, 404 not-found, 404 cannot-see, public allowed)FULL
APIC-043Bridge coreGET /api/local/tasks/:id/subtasks/:jobId/activitySingle subtask activity; 404 subtask-not-foundsubtask-activity-api.ts:68subtask-activity-api.test.ts:211 (404 subtask not found)PARTIALview-gate denial on this specific route not separately asserted
APIC-044Bridge coreGET /api/local/tasks/:id/subtasks/:jobId/filesList subtask worktree files; canViewTask, isJobWithinWorkspace containment 404, 404 no-worktreesubtask-files-api.ts:12subtask-files-api.test.ts:67 (grouped listing, 400 bad id, 404 missing/private/no-worktree/outside-workspace)FULL
APIC-045Bridge coreGET /api/local/tasks/:id/subtasks/:jobId/files/*Serve subtask file; resolve+startsWith(base+sep) traversal → 403, 404 missing, canViewTask, org-member allowed, sendFilesubtask-files-api.ts:55subtask-files-api.test.ts:121 (serve inside worktree, 404 missing, 404 cannot-see, org-member allowed)PARTIALThe 403 traversal branch (resolved≠base path-escape) is not directly asserted on this route
APIC-046Bridge coreisJobWithinWorkspaceSeparator-bounded containment (rejects /12 vs /123 prefix sibling)local-api-helpers.tslocal-api-helpers.test.ts:8 (descendants, sibling-prefix reject, null/empty)FULL
APIC-047Bridge coreensurePathWithin / isPathEscapeErrorPath-traversal + **symlink-escape** hardening, sibling-prefix bypass guardlocal-api-helpers.tslocal-api-helpers.test.ts:92,106 (inside ok, traversal reject, sibling-prefix, symlink-escape, in-root resolve)FULL
APIC-048Bridge coresafeZipEntryNameNeutralizes .., backslash, Windows drive-letter in zip entry names (zip-slip)local-api-helpers.tslocal-api-helpers.test.ts:25 (plain path, never-absolute, drive-letter→C_)FULL
APIC-049Bridge coresetUntrustedFileResponseHeadersnosniff + CSP sandbox on untrusted file responseslocal-api-helpers.tslocal-api-helpers.test.ts:78FULL
APIC-050Bridge coreoffice-preview module (getSpreadsheetPreview/getPresentationPreview/SofficeUnavailableError)Excel cells incl. formula results, PPTX via soffice, throws when soffice absent (→503)office-preview.tsoffice-preview.test.ts (file detect, spreadsheet+formula, soffice-missing throws)FULLModule-level only; see APIC-020 for the HTTP wiring gap
APIS-001Bridge auth & spacesOAuth2 Google login (GET /auth/google, callback)Passport Google strategy, status gate (active→app, pending→/auth/pending)auth.tsauth.test.tsFULL
APIS-002Bridge auth & spacesOAuth2 Gitea login (GET /auth/gitea, callback)Passport OAuth2 Gitea; pending→admin auto-promote if adminEmailsauth.tsauth.test.tsPARTIALcallback strategy logic tested indirectly; live OAuth handshake not E2E
APIS-003Bridge auth & spacesLocal auth login (POST /auth/local)username/password login w/ rate limiterauth.ts, login-rate-limit.tsauth.local.test.ts, login-rate-limit.test.tsFULL
APIS-004Bridge auth & spacesLocal signup (POST /auth/local/signup)self-register → pending status awaiting approvalauth.tsauth.local.test.tsFULL
APIS-005Bridge auth & spacesLogin rate limitingLoginRateLimiter + per-IP throttle scopelogin-rate-limit.ts, auth.tslogin-rate-limit.test.tsFULL
APIS-006Bridge auth & spacesrequireAuth middlewareauthenticated AND status==='active' else 401/redirectauth.tsauth.test.tsFULL
APIS-007Bridge auth & spacesrequireAdmin middlewarerole==='admin' else 401/403auth.tsauth.test.tsPARTIALexercised via admin routers; few direct unit cases
APIS-008Bridge auth & spacesGET /auth/status / /login / /pending / /logoutsession state pages + logoutauth.tsauth.test.tsPARTIALlogout/pending HTML paths lightly covered
APIS-009Bridge auth & spacesSession store (persistent secret, survive restart)auto-gen session secret, short-secret warningauth.tsauth.session-store.test.tsFULL
APIS-010Bridge auth & spacesGitea orgs fetch (/api/v1/user/orgs)populates session.orgIds for 'org' visibilityauth.ts (`resolveOrgIds`)auth.test.ts, local-orgs.integration.test.tsPARTIALfetch failure/network-error path not asserted
APIS-011Bridge auth & spacesChange password (requireAuth + current-password)requires current pw, 204 on successauth.tsauth.test.tsPARTIALwrong-current-pw rejection coverage thin
APIS-012Bridge auth & spacesAdmin users CRUD (GET/POST/PATCH/DELETE /api/admin/users)**admin-only** (guard); create/approve/disable/set-passwordadmin-api.tsadmin-api.test.ts, admin-api.local.test.tsFULL
APIS-013Bridge auth & spacesAdmin orgs CRUD + members (/api/admin/orgs*)**admin-only**; org create/edit/delete, add/remove memberadmin-api.tsadmin-api.test.ts, local-orgs.integration.test.tsFULL
APIS-014Bridge auth & spaces**No-auth admin passthrough**when authActive=false, admin guard → passthroughadmin-api.ts, branding-api.tsadmin-api.local.test.tsPARTIALbranding adminGuard no-auth path not asserted
APIS-015Bridge auth & spacesTask share create (POST /api/local/tasks/:id/share)**owner-or-admin only** (checkTaskOwnership) → tokenshare-api.tsshare-api.test.tsFULL
APIS-016Bridge auth & spacesTask unshare (DELETE .../share)**owner-or-admin only**share-api.tsshare-api.test.tsFULL
APIS-017Bridge auth & spacesPublic shared task read (GET /api/shared/:token)token-only (no auth); invalid/revoked token → 404share-api.tsshare-api.test.tsFULL
APIS-018Bridge auth & spacesShared task comments/files/raw (/api/shared/:token/...)token-scoped read of files/comments/raw contentshare-api.tsshare-api.test.tsPARTIALpath-escape on /files/content lightly tested
APIS-019Bridge auth & spacesShared subtask activities/files (/api/shared/:token/subtasks/*)token-scoped subtask containment (isJobWithinWorkspace prefix guard)share-api.tsshare-api.test.tsPARTIALwildcard /subtasks/:jobId/files/* traversal not directly asserted
APIS-020Bridge auth & spacesPublic app-share resolve (GET /api/app-share/:token)token→(spaceId,appName); revoked/invalid→404app-share-api.tsapp-share-api.test.tsFULL
APIS-021Bridge auth & spacesApp-share file read (content/raw/list)**server-side path containment**: outside allowed route→403; escape→403app-share-api.tsapp-share-api.test.tsFULLstrong path-escape/forbidden coverage
APIS-022Bridge auth & spacesApp-share is GET-onlyno write/delete endpoints (read-only public share)app-share-api.tsapp-share-api.test.tsFULL
APIS-023Bridge auth & spacesSpace list (GET /spaces/)visibility-scoped list via repo viewer gatespace-api.tsspace-api.test.tsFULL
APIS-024Bridge auth & spacesSpace get (GET /spaces/:id)getSpace({viewer}) null→404 (private/org/public gate)space-api.tsspace-api.test.tsFULL
APIS-025Bridge auth & spacesSpace create (POST /spaces/)authenticated user creates space (owner)space-api.tsspace-api.test.tsFULL
APIS-026Bridge auth & spacesSpace update/archive (PATCH /:id, POST /:id/archive)**canManageSpace** (owner/admin/member-owner) else 403space-api.tsspace-api.test.ts, space-api.members.test.tsFULL
APIS-027Bridge auth & spacesSpace file list/content/raw/office-preview (GET /:id/files*)read gated by getSpace({viewer}) visibility; not-found→404space-api.ts, office-preview.tsspace-api.test.ts, office-preview.test.ts, local-files-api.test.tsPARTIALnon-member-on-public read path not asserted per-endpoint
APIS-028Bridge auth & spacesSpace file upload/delete (POST /:id/files/upload,/delete)**canEditInSpace** (owner/admin/editor); viewer→403space-api.tsspace-api.write-gates.test.tsFULLstrong write-gate authz coverage (20 authz assertions)
APIS-029Bridge auth & spacesSpace file mkdir/move/download-zip (POST /:id/files/...)**canEditInSpace**; protected top-dirs blockedspace-api.tsspace-api.write-gates.test.ts, space-api.write-endpoint.test.tsFULL
APIS-030Bridge auth & spacesSpace file write (POST /:id/files/write)**canEditInSpace** write gatespace-api.tsspace-api.write-endpoint.test.ts, space-api.write-gates.test.tsFULL
APIS-031Bridge auth & spacesSpace calendar read (GET /:id/calendar, /calendar/day)visibility-gated readspace-api.ts, cross-calendar-api.tsspace-api.calendar.test.ts, cross-calendar-api.test.tsFULL
APIS-032Bridge auth & spacesSpace calendar event CRUD (POST/PATCH/DELETE /:id/calendar/events*)edit gate; multi-day mirrorspace-api.tsspace-api.calendar.test.tsPARTIALnon-editor write rejection coverage lighter than file gates
APIS-033Bridge auth & spacesSpace members list (GET /:id/members)members visible; **email masked** unless admin/owner/self-memberspace-api.tsspace-api.members.test.tsFULLPII masking explicitly tested
APIS-034Bridge auth & spacesSpace member add/role/remove (POST/PATCH/DELETE /:id/members*)**canManageSpace**; owner-add blocked(400); self-removal allowed; non-member→404space-api.tsspace-api.members.test.tsFULL27 authz assertions
APIS-035Bridge auth & spacesSpace invite issue/get/revoke (GET/POST/DELETE /:id/invite)**canManageSpace**; no-auth→404space-api.tsspace-api.invite.test.tsFULL
APIS-036Bridge auth & spacesInvite preview + accept (GET /invite/:token, POST /invite/:token/accept)authed user joins via valid token; invalid/expired→404; no-auth→404space-api.tsspace-api.invite.test.tsFULLmaxUses/expiry validity via isSpaceInviteValid
APIS-037Bridge auth & spacesApp share per-space (GET/POST/DELETE /:id/apps/:app/share)manage-gated issue/revoke of public app linkspace-api.tsspace-api.app-share.test.tsPARTIAL6 authz assertions; non-manager revoke path thin
APIS-038Bridge auth & spacesSpace tool-policy (GET /:id/tool-policy, PUT)GET visibility-gated; **PUT canManageSpace**; sensitive (Bash/SSH/browser/MCP) opt-inspace-api.tsspace-api.tool-policy.test.tsPARTIALonly 9 authz assertions; Bash/sensitive write-path round-trip (PR #653 regression area) under-covered
APIS-039Bridge auth & spacesBrowser captcha-pool (GET/DELETE /captcha-pool)captcha session pool read/clearbrowser-api.tsbrowser-api.test.tsFULL
APIS-040Bridge auth & spacesBrowser task-session (GET/POST /task-session/:taskId*)per-task browser session acquire/releasebrowser-api.tsbrowser-api.test.tsFULL
APIS-041Bridge auth & spacesBrowser profile list (GET / )space-aware list; **decryptableByViewer** flag (DEK owner-bound)browser-session-api.tsbrowser-session-api.test.tsFULLnon-owner sees but cannot decrypt — asserted
APIS-042Bridge auth & spacesBrowser profile create (POST)space ctx → **canEditInSpace**; personal → owner-scoped; spaceId ignored when no spaceAccessbrowser-session-api.tsbrowser-session-api.test.tsFULL
APIS-043Bridge auth & spacesBrowser profile delete (DELETE /:id)space editor/owner OR personal owner-onlybrowser-session-api.tsbrowser-session-api.test.tsFULL
APIS-044Bridge auth & spacesBrowser interactive login session (POST /profiles/:id/login)spawn noVNC session for owner; DEK owner-boundbrowser-session-api.ts, novnc-proxy.tsbrowser-session-api.test.ts, novnc-proxy.test.tsPARTIALnoVNC proxy authz/upgrade path lightly covered
APIS-045Bridge auth & spacesConsole WS attach (attachConsoleWs upgrade)decideAccess: visibility gate + canWrite (owner OR admin)console-ws-api.tsconsole-ws-api.test.tsFULLdecideAccess unit-tested
APIS-046Bridge auth & spacesConsole status router (createConsoleStatusRouter)requireAuth; status poll for owner/adminconsole-ws-api.tsconsole-ws-api.test.ts, console-session-api.test.tsFULL
APIS-047Bridge auth & spacesConsole session router (createConsoleSessionRouter)requireAuth task-scoped session resolutionconsole-ws-api.tsconsole-session-api.test.tsFULL
APIS-048Bridge auth & spacesConsole admin (GET /ssh/console-sessions, POST .../:taskId/kill)**requireAdmin** list/kill console sessionsconsole-admin-api.tsconsole-admin-api.test.tsFULL
APIS-049Bridge auth & spacesGateway admin CRUD (POST/PATCH/DELETE /:id, rotate/revoke keys)**requireAdmin** backend + API-key mgmtadmin-gateway-api.tsadmin-gateway-api.test.ts, .budget-rate.test.ts, .metric-labels.test.tsFULL18 authz assertions; budget/rate/labels covered
APIS-050Bridge auth & spacesGateway usage (GET /:id/usage)**requireAdmin** per-backend usageadmin-gateway-api.tsadmin-gateway-api.test.tsFULL
APIS-051Bridge auth & spacesGateway status (GET / status)non-admin status snapshotadmin-gateway-status-api.tsadmin-gateway-status-api.test.tsFULL
APIS-052Bridge auth & spacesGateway mount/proxy (mountGateway, classifyGatewayPath)LLM gateway request routing/classification middlewaregateway-mount.tsgateway-mount.test.tsPARTIALpath classification + config-equiv heavily tested; auth on proxied LLM calls (Bearer/key) not asserted in this file
APIS-053Bridge auth & spacesNotifications VAPID key (GET /api/notifications/vapid-public-key)requireAuthnotifications-api.tsnotifications-api.test.tsFULL
APIS-054Bridge auth & spacesNotifications subscriptions (GET/POST/DELETE /subscriptions)requireAuth; web-push subscribe/unsubscribenotifications-api.tsnotifications-api.test.tsFULL
APIS-055Bridge auth & spacesNotifications preferences (GET/PUT /preferences)requireAuth pref read/writenotifications-api.tsnotifications-api.test.tsFULL
APIS-056Bridge auth & spacesNotifications test/send (POST send endpoints)requireAuth push triggernotifications-api.tsnotifications-api.test.tsPARTIALsend/test push delivery path lightly asserted
APIS-057Bridge auth & spacesBranding read (GET /api/branding)public branding config readbranding-api.tsbranding-api.test.tsFULL
APIS-058Bridge auth & spacesBranding static serve (/branding/*)static asset servingbranding-api.tsbranding-api.test.tsPARTIALstatic traversal not asserted
APIS-059Bridge auth & spacesBranding upload/delete (POST/DELETE /api/branding/upload)**adminGuard** (passthrough in no-auth)branding-api.tsbranding-api.test.tsFULL
APIS-060Bridge auth & spacesSecurity headers / HSTS opt-inHSTS default off (max-age=0 clear), opt-in enable; 180d max-agesecurity-headers.tssecurity-headers.test.tsFULL15 expects (no authz; correct)
APIS-061Bridge auth & spacesServer TLS listenerHTTPS listener creation; cert handlingserver-tls-listener.tsserver-tls-listener.test.ts, server.tls.test.tsFULL
APIS-062Bridge auth & spacesDashboard API (createDashboardApi)worker/node (GPU) status tabsdashboard-api.tsdashboard-api.test.tsPARTIALno explicit auth guard visible in router; visibility of dashboard data not gated/asserted
APIS-063Bridge auth & spacesDashboard workersworker status aggregationdashboard-workers.tsdashboard-workers.test.tsFULL
APIS-064Bridge auth & spacesbuildVisibilityWhere / buildSpaceVisibilityWhereSQL WHERE for private/org/publicvisibility.tsvisibility.test.tsFULL53 expects
APIS-065Bridge auth & spacescanManageSpace / canEditInSpace / canEditEntity / canUserSeeTaskrole/owner/admin authz predicatesvisibility.tsvisibility.test.tsFULLcentral predicates well unit-tested
APIS-066Bridge auth & spacesCross-space credential transfer (G3/#006)copy MCP/SSH creds between spaces, per-user token non-copy(PR #642 — NOT in main)(n/a in main)NONE**Not merged to main** — no endpoint present; out of scope for current tree
SVC-001Services & DBWorker poll/claim loopprocessNext/acquireJobOrRequeue claim a queued job or requeue when issue lock heldsrc/worker.tsworker.test.ts ("requeues a claimed job when the issue lock is already held")PARTIALpoll cadence, pokePoll, empty-queue path not directly asserted
SVC-002Services & DBRole / task_class matchingcanClaimRole/supportsRole/getSupportedRoles gate which jobs a worker claimssrc/worker.ts, src/db/repository.ts (claimNextJob)repository.test.ts ("claims only jobs matching a healthy worker role set"; "does not let unhealthy workers claim queued jobs")FULLrole-set matching + healthy filter covered at repo layer
SVC-003Services & DBTitle-only / reflection role filtersroles:['title'] skips agent jobs; roles:['reflection'] runs reflection handlersrc/worker.ts (handleReflectionJob, supportsRole)worker.reflection-dispatch.test.ts; worker.reflection-enqueue.test.ts (16)PARTIALreflection enqueue + dispatch well covered; title-only worker skip path has no direct test
SVC-004Services & DBJob lifecycle transitionsqueued→dispatching→running→succeeded/failed/waiting_human/waiting_subtasks via updateJobsrc/worker.ts, src/db/repository.tsworker.test.ts; repository.test.ts; spaces-task-ops (cancel flips running→cancelled)PARTIALterminal mapping covered (metrics test); waiting_human/waiting_subtasks transitions only indirectly exercised
SVC-005Services & DBRetry / maxAttempts policyterminal vs transient classification; one recovery retry for max_iterations; give up on repeatsrc/worker.ts (executeJob retry logic)worker.test.ts (5 retry cases incl. self-abort, max_iterations 1st/2nd hit, llm_error budget)FULLstrong branch coverage of terminal vs transient + attempt budget
SVC-006Services & DBRetry handoff summarybuildRetryHandoffSummary/writeRetryHandoffSummary carry lessons+diagnostics into retrysrc/worker.tsworker.test.ts ("writes retry handoff summary"; "builds retry handoff summary from diagnostics and lessons")FULL
SVC-007Services & DBDeadline / hard-abort guardshouldDeadlineAbort aborts past deadline; job-guard timer force-abortssrc/worker.tsworker.job-guard.test.ts (8: before/after deadline, disabled=0, no re-abort, cancel mid-run, hard deadline)FULL
SVC-008Services & DBCancellation checkcancelCheck returns true when DB job status is cancelledsrc/worker.ts, repository.requestJobCancelworker.test.ts ("cancelCheck returns true when cancelled"); repository.test.ts (requestJobCancel 3 cases)FULLrunning→cancelled, queued no-op, unknown id all covered
SVC-009Services & DBmax_concurrency slot schedulingruns up to N concurrent, refills freed slot, single-flight when unsetsrc/worker.tsworker.concurrency.test.ts (5: concurrent, single-flight, waitForCompletion, decrement-on-throw)FULL
SVC-010Services & DBInitialize / health probeinitialize probes /api/tags + /v1/models, forwards Bearer, stays healthy on llama-server compatsrc/worker.tsworker.test.ts (3 initialize cases)FULL
SVC-011Services & DBUnhealthy on LLM conn errorrequeues jobs + marks worker unhealthy on connection errorssrc/worker.tsworker.test.ts ("requeues jobs and marks the worker unhealthy on LLM connection errors")FULL
SVC-012Services & DBresolveToolSpaceId (per-space MCP/SSH)NULL space + owner → personal space; keeps explicit; fails CLOSED on resolver errorsrc/worker.tsworker.test.ts (5 resolveToolSpaceId cases)FULLfail-closed sentinel asserted
SVC-013Services & DBPiece resolution for null-owner jobno-auth job loads piece from data/users/local/pieces consistentlysrc/worker.tsworker.test.ts (2 null-ownerId piece cases)FULL
SVC-014Services & DBprepareJobWorkspace resolutionuses stored workspacePath or falls back to legacy local/{taskId} + backfillssrc/worker.ts, src/spaces/workspace-resolver.tsworker.test.ts (2 cases); workspace-resolver.test.ts (6)FULLpersistent/ephemeral/personal + backfill covered
SVC-015Services & DBmaybeEnqueueReflection budget gateenqueue/skip by enabled flag, daily UTC budget cap, worker-required, per-user keyingsrc/worker.tsworker.reflection-enqueue.test.ts (16)FULLstrong: cap=0, 25h window, no-recursion, independent budgets
SVC-016Services & DBresolveModel / role→model mappingselects backend model for the job rolesrc/worker.ts (resolveModel)worker/sticky-backend.test.ts (resolver follow-current); scheduling.test.ts (normalizeJobRole/parseUiRole)PARTIALrole parsing well covered; resolveModel's own fallback branches not directly tested
SVC-017Services & DBSticky backend resolverpersists first backend, advances on CHANGE, retries on persist failuresrc/worker.ts (createStickyBackendResolver)worker/sticky-backend.test.ts (6)FULL
SVC-018Services & DBIdle-routing yieldpickIdlerIndex yields to strictly-idler serving sibling, ignores unhealthy/non-servingsrc/worker.ts (findIdlerCompetitor)worker/idle-routing.test.ts (9)FULL
SVC-019Services & DBanswerSubtaskAsk / subtask resumeanswers a subtask ASK; parent requeue when all subtasks donesrc/worker.ts, repository.requeueParentJobIfAllSubtasksDone(none direct in scope)NONErepo helper requeueParentJobIfAllSubtasksDone + worker answerSubtaskAsk untested
SVC-020Services & DBWorker shutdown hooksinstalls + drains shutdown hooks in order, tolerates throwing hooksrc/worker.ts / worker-bootstrapworker-bootstrap.test.ts (4)FULL
SVC-021Services & DBWorker metricscounters increment on labelled inc; terminal-status mapping completesrc/worker.tsworker.metrics.test.ts (3)FULL
SVC-022Services & DBWorkerManager differential rebuildrebuild on config change: keep busy/unchanged worker, retire changed/removed without requeue, prune retiredsrc/worker-manager.tsworker-manager.test.ts (8)FULLlive-job protection + def-signature diff covered
SVC-023Services & DBWorkerManager shutdown requeuerequeues running jobs only on shutdown when worker fails to drain; not on clean shutdownsrc/worker-manager.tsworker-manager.test.ts (2 shutdown cases)FULL
SVC-024Services & DBWorker dep injection setterssetMcpDeps/setWorkerMetrics/setSkillCatalog/setPushService wire shared depssrc/worker-manager.ts(none direct)NONElow-risk wiring; uncovered
SVC-025Services & DBScheduler cron conversionconvertToCron maps daily/weekly/monthly→cron; passes cron through; once special-casedsrc/scheduler.tsscheduler.test.ts (convertToCron 5 cases)FULL
SVC-026Services & DBcalcNextRun / toSqliteDatetimenext-run from cron; SQLite-compatible datetime; once→nullsrc/scheduler.tsscheduler.test.ts (calcNextRun 3 + toSqliteDatetime 2)FULL
SVC-027Services & DBScheduler skip-on-in-progressskips when prior job running/waiting_subtasks; starts new on waiting_humansrc/scheduler.ts (IN_PROGRESS_STATUSES)scheduler.test.ts (4 skip cases)FULLwaiting_human-no-longer-blocks asserted
SVC-028Services & DBSchedule ownership inheritancepropagates ownerId/visibility/scopeOrg + space_id to spawned local_task; NULL owner for systemsrc/scheduler.ts (executeScheduledTask)scheduler.test.ts (multiple ownership + space-bound vs personal cases)FULL
SVC-029Services & DBScheduled pieceName / classifierfalls back to classifier when pieceName unset; preserves explicit pieceNamesrc/scheduler.tsscheduler.test.ts (pieceName cases)FULL
SVC-030Services & DBScheduled script (task_kind=script)runs browser-macro/script, writes logs to runtimeDir + output to space tree; failure paths; gate+allowlistsrc/scheduler.ts (executeScriptScheduledTask)scheduler.test.ts (script cases incl. gate disabled, not-in-allowlist, audit row, missing script, null scriptName guard)FULL
SVC-031Services & DBexecuteById manual runruns a scheduled task on demandsrc/scheduler.ts(none direct)NONEmanual-trigger path untested
CFG-001Services & DBtransformKeys snake↔cameltoCamel/toSnake/toSnakeKeys round-trip incl. nested/arrayssrc/config.tsconfig.test.ts (toSnakeKeys 5 cases)FULL
CFG-002Services & DBloadConfig provider.retryloads from YAML or default; deprecated profiles→roles shim; proxy/proxyType defaultssrc/config.tsconfig.test.ts (provider.retry + roles/proxy cases)FULL
CFG-003Services & DBvalidateConfigrejects bad concurrency/maxMovements/ask/maxStreamMinutes/maxJobMinutes/maxDepth/retry/worker fields/proxy typesrc/config.tsconfig.test.ts (~35 validateConfig cases)FULLexhaustive boundary coverage
CFG-004Services & DBEnv overrides (config.ts)OLLAMA_BASE_URL/OLLAMA_MODEL/CONCURRENCY/WORKTREE_DIR/AAO_* override loaded configsrc/config.tsconfig.audit-regression.test.ts (OLLAMA_BASE_URL targets executed worker only, leaves persisted llm + extra workers)PARTIALonly OLLAMA_BASE_URL asserted; CONCURRENCY/WORKTREE_DIR/OLLAMA_MODEL/AAO_* overrides untested
CFG-005Services & DBnormalizeConfig version handlingv2 pass-through; missing→v1; unsupported version throws; null input→emptysrc/config-normalize.tsconfig-normalize.test.ts (version handling, ~6)FULL
CFG-006Services & DBnormalizeConfig v1→v2 llmproxy→connection_type, worker.model inheritance, single default worker, profiles→roles, retry/metrics mapsrc/config-normalize.tsconfig-normalize.test.ts (v1→v2 block, ~10)FULL
CFG-007Services & DBconfig-normalizer storage mirrortop-level flat keys ↔ storage.* both directions; storage.* precedence (#369); worktreeDir survivalsrc/config-normalize.ts, src/config.tsconfig-normalize.test.ts (storage migration + backwards-compat, ~8); config.audit-regression.test.ts (trash_retention_days takes effect)FULLprecedence trap (default-merge-before-normalize) regression-guarded
CFG-008Services & DBEnv-ref preservation${VAR} / env: prefix preserved verbatim through normalizesrc/config-normalize.tsconfig-normalize.test.ts (3 env-ref cases)FULL
CFG-009Services & DBbrowser.display_mode migrationlegacy captchaSolve=novnc → displayMode=novnc, key removedsrc/config-normalize.ts (migrateBrowserDisplayMode)config-normalize.test.ts (display_mode case)FULL
CFG-010Services & DBnormalize fixtures (real configs)v1-single-ollama / multi-worker-proxy / gateway-with-keys / mcp-and-ssh normalize correctlysrc/config-normalize.tsconfig-normalize.test.ts (4 fixture cases)FULL
CFG-011Services & DBAuth config loadingparses auth section snake_case + primary_provider; undefined when absentsrc/config.tsconfig-auth.test.ts (4)PARTIALparsing covered; provider validation/exclusivity not asserted here
CFG-012Services & DBConfigManager read + ETaggetConfig/getConfigForApi; etag from mtime; reloadFromFilesrc/config-manager.tsconfig-manager.test.ts (load, etag, reload, masked-for-API)FULL
CFG-013Services & DBConfigManager write + CASupdateConfig writes YAML, rejects stale etag, creates file on fresh install, rejects unparseablesrc/config-manager.tsconfig-manager.test.ts (update, stale-etag reject, fresh-install create, invalid YAML reject)FULL
CFG-014Services & DBConfigManager masking preservationmasks apiKey (********); preserves masked field on update; matches workers/backends by id; drops mask when no prior keysrc/config-manager.ts (mergeWithMaskPreservation)config-manager.test.ts (mask + preserve by-id for llm.workers + gateway.backends, drop-mask path)FULL
CFG-015Services & DBConfigManager change eventsemits config-changed on updatesrc/config-manager.tsconfig-manager.test.ts ("emits config-changed on update")FULL
CFG-016Services & DBconfig-manager env overridesOLLAMA_*/WORKTREE_DIR/CONCURRENCY/DB_PATH applied at runtime readsrc/config-manager.ts(none direct in config-manager.test.ts)NONEDB_PATH + runtime env override path untested at config-manager layer
CFG-017Services & DBServer TLS / listen-port configmergeServerConfig TLS defaults, http_redirect port normalize/collision, resolveListenPort + PORT envsrc/server/config.tsserver/config.test.ts (24)FULL(PORT/LOG_LEVEL env live here, not config.ts)
CFG-018Services & DBmigrate-config CLI--help/unknown flag/missing file/dry-run/in-place+backup/already-v2/version-99src/scripts/migrate-config.tsscripts/migrate-config.test.ts (8)FULL
DB-001Services & DBcreateJob / getJob round-trippersists job incl. space_id, runtime_dir, continued_from; subtask inherits parent space_idsrc/db/repository.tsspaces-resolution.test.ts; runtime-dir.test.ts; migrate.test.tsFULL
DB-002Services & DBclaimNextJob / retry / peekrole+health-aware claim; claimNextRetryJob; peekNextClaimablesrc/db/repository.tsrepository.test.ts (worker-aware scheduling 2)PARTIALclaimNextRetryJob + peekNextClaimable not individually asserted
DB-003Services & DBrequeueRunningJobs / recoveryrequeueRunningJobs, recoverOrphanedJobs, recoverStuckRunningJobs, resumeMcpWaitingJobssrc/db/repository.ts(worker-manager shutdown exercises requeue indirectly)PARTIALrecoverOrphaned/recoverStuck/resumeMcp* have no direct unit test
DB-004Services & DBrequestJobCancelrunning→cancelled; queued no-op; unknown id falsesrc/db/repository.tsrepository.test.ts (3)FULL
DB-005Services & DBcreateLocalTask / getLocalTaskpersists owner/visibility/space_id/runtime_dir; reads backsrc/db/repository.tsrepository.test.ts; repository-auth.test.ts; spaces-resolution.test.tsFULL
DB-006Services & DBlistLocalTasks + latestJob/subtasksjoins latest job, subtask info, ownerName/org name; ownerId filtersrc/db/repository.tsrepository.test.ts (listLocalTasks suite); repository-auth.test.ts (ownerId filter)FULL
DB-007Services & DBdeleteLocalTask whitelistdeletes task+jobs; refuses when running; whitelists only ephemeral/local per-task dirs; protects shared space treesrc/db/repository.tsrepository.test.ts (4); spaces-task-ops.test.ts (~9 delete cases incl. symlink/trailing-slash/null-space)FULLdata-loss regression strongly guarded
DB-008Services & DBlocal_task_commentsaddLocalTaskComment, listLocalTaskComments, getUninjected/markInjected, getLatestResultComment, attachmentssrc/db/repository.tsrepository.test.ts (Feedback/Share); migrate.test.ts (attachments column)PARTIALcomment inject/uninject + getLatestResultComment lifecycle not directly asserted
DB-009Services & DBbuildVisibilityWhereadmin 1=1; owner/public; same-org branch; spaceColumn membership+owner OR; admin ignores spaceColumnsrc/db/repository.tsbridge/visibility.test.ts (33 incl. param-order, byte-identical back-compat)FULLparam ordering + admin-ignores-membership asserted
DB-010Services & DBcanManageSpace / canEditInSpacerole-gated write checks (admin/owner/member roles, viewer read-only)src/db/repository.tsbridge/visibility.test.ts (canManage + canEdit suites)FULL
DB-011Services & DBSpaces CRUDcreateSpace/getSpace/listSpaces (visibility-filtered)/updateSpace/archiveSpacesrc/db/repository.tsspaces-repository.test.ts (12)FULL
DB-012Services & DBensurePersonalSpace invariantexactly one private personal space/user, idempotent; never hard-deletablesrc/db/repository.tsspaces-repository.test.ts (ensurePersonalSpace + hardDelete invariant)FULL
DB-013Services & DBhardDeleteSpace crypto-shredshreds case space DEK on delete; never personalsrc/db/repository.tsspaces-repository.test.ts (crypto-shred case)FULL
DB-014Services & DBSpace members CRUD + visibilityadd/list/getRole/updateRole/remove; membership-based read; non-member leak prevention; revocationsrc/db/repository.tsrepository.shared-space.test.ts (23)FULLcollaboration + leak + revocation all asserted
DB-015Services & DBPersonal space owner-only visibilityeven admin cannot see another user's personal space (list+get); no-auth synthetic owner pathsrc/db/repository.tsrepository.personal-space-visibility.test.ts (8)FULL
DB-016Services & DBPer-space resource isolationspace-A task sees only space-A MCP/SSH (not space-B/global); personal sees no case-space resourcessrc/db/repository.tsrepository.spaces-resource-isolation.test.ts (4)FULL
DB-017Services & DBSpace tool_policy persistencedefault null; round-trips JSON; clear via nullsrc/db/repository.tsrepository.tool-policy.test.ts (5)FULL
DB-018Services & DBSpace invitescreate (no-expiry default), regenerate replaces, expiry validity, revoke, FK cascadesrc/db/repository.tsrepository.space-invites.test.ts (7)FULL
DB-019Services & DBApp share linkscreate/get/revoke/resolveAppShareTokensrc/db/repository.tsrepository.app-share.test.ts (out-of-listed but present)PARTIALpresent file not fully read; assume happy-path coverage
DB-020Services & DBTool requests + grant overlayrecord/list newest-first, de-dup by job/task/piece, idempotent decide, granted-tools round-trip, per-piece aggregatesrc/db/repository.tsrepository.tool-requests.test.ts (7)FULL
DB-021Services & DBUser CRUD + OAuth linkcreateUser/getById/getByEmail/findOrCreateUserByOAuth (link by email)/listUsers/updateUser/deleteUser cascadesrc/db/repository.tsrepository-auth.test.ts (17)FULL
DB-022Services & DBLocal-auth credentialssetLocalPassword/verify round-trip, per-user salt, createLocalUser email-takeover reject, upsertLocalSystemAdmin idempotent, delete refuses local usersrc/db/repository.tsrepository-local-auth.test.ts (12)FULL
DB-023Services & DBLocal orgscreateLocalOrg (lorg: id), rename, member add/remove idempotent, listUserLocalOrgs, delete cascade + downgrade scoped resourcessrc/db/repository.tsrepository-local-orgs.test.ts (10)FULL
DB-024Services & DBensureLocalUser DEK fixcreates per-user row so DEK/SSH create succeeds; idempotentsrc/db/repository.tsensure-local-user.test.ts (4)FULL
DB-025Services & DBScheduledTasks CRUDcreate/get/list/getDue/update/delete; space_id persisted (not dead-wired)src/db/repository.tsrepository.test.ts (ScheduledTasks 5); spaces-resolution.test.ts (space_id round-trip)FULL
DB-026Services & DBShare token (local task)shareLocalTask token, idempotent, unshare clears, getByShareToken null on unknown/after-unsharesrc/db/repository.tsrepository.test.ts (Share feature 6)FULL
DB-027Services & DBaddAuditLogsingle audit_log insert helpersrc/db/repository.tsscheduler.test.ts (user_script_run audit row asserted via path)PARTIALaddAuditLog itself not unit-tested; only one consumer asserted
DB-028Services & DBGateway virtual keysfind/list/revoke/delete/touchLastUsed + usagesrc/db/repository.tsrepository.gateway-keys.test.ts; gateway-usage.test.tsFULL(gateway-adjacent; in repo file)
DB-029Services & DBLLM usage aggregationincrementLlmUsage (daily) + hourly + queryLlmUsageDaily + org mapsrc/db/repository.tsrepository.llm-usage.test.ts; llm-usage-hourly.test.tsFULL
DB-030Services & DBPush subscriptions / prefsupsert/list/get/delete/markSuccess/markFailure; notification prefssrc/db/repository.ts(push-service.test.ts at higher layer)PARTIALrepo-level push CRUD not directly asserted
DB-031Services & DBCalendar eventsgetCalendarEvent/deleteCalendarEventsrc/db/repository.tsrepository.calendar.test.ts; cross-calendar.test.tsFULL
DB-032Services & DBresumeToolRequestJobresumes a job waiting on a tool grantsrc/db/repository.ts(none direct)NONEresume-after-grant path untested at repo layer
DB-033Services & DBSchema migrations (idempotent ALTER)runMigrations adds owner_id/continued_from/last_backend_id/attachments; idempotentsrc/db/migrate.ts, schema.sqlmigrate.test.ts (16); migrate.space-id/ssh-space/mcp-space/reflection-columns/gateway-2b/privatize/rename testsFULLeach major migration has its own test file
DB-034Services & DBMCP table migrationscreates mcp_servers/tokens/tools/oauth_pending idempotent; BLOB cols; auth_kind default; owner indexsrc/db/migrate.tsmigrate.test.ts (MCP suite); migrate.mcp-auth-columns.test.tsFULL
DB-035Services & DBSpace backfill migrationsbackfillMcpServer/Ssh/BrowserSession space_ids; privatize space rows; rename personal titlesrc/db/migrate.tsmigrate.mcp-space/ssh-space/privatize-space-rows/rename-personal-space-title.test.tsFULL
DB-036Services & DBresolveTaskWorkspaceDir / dirspersistent→space files tree; ephemeral→throwaway; personal-space; ensureWorkspaceDirs creates subdirssrc/spaces/workspace-resolver.tsworkspace-resolver.test.ts (6); spaces-resolution.test.ts (resolveTaskFolderContext)FULL
DB-037Services & DBCredential DEK encryptionencrypt under SPACE DEK for space profile, OWNER DEK for personal; migration fallback; cross-DEK isolation; throws with no materialsrc/crypto/profile-dek.tscrypto/profile-dek.test.ts (6)FULLspace-vs-owner DEK separation + non-decryptability asserted
DB-038Services & DBBranding storagebranding config/assets read-write (gitignored data/branding)src/bridge/branding-api.tsbridge/branding-api.test.tsPARTIALAPI-layer test exists; storage-layer branch coverage unverified
UI-001UICalendar month grid + barsmonth grid, week split, range/time fmt, multi-day bar layout`lib/calendar.ts`lib/calendar.test.tsFULL
UI-002UICommand palettebuild/filter/group commands, hotkey gate`lib/command-palette.ts`lib/command-palette.test.tsFULL
UI-003UICron → form stateparse cron string into schedule fields`lib/cronForm.ts`lib/cronForm.test.tsPARTIALonly cronToFormState exported/tested; reverse (form→cron) lives in ScheduleFields.tsx untested
UI-004UIFile move resolutionbasename/parentDir, resolve drag-move plan (conflicts)`lib/fileMove.ts`lib/fileMove.test.tsFULL
UI-005UIFile type categorizationsplit name, category, color class`lib/fileType.ts`lib/fileType.test.tsFULL
UI-006UIFile view sort/formatsort entries, toggle sort, fmt timestamp/size`lib/fileView.ts`lib/fileView.test.tsFULL
UI-007UIHelp docs frontmatter/rendersplit/validate frontmatter, parse, slugify, render HTML, filter`lib/help.ts`lib/help.test.tsFULL
UI-008UILive-workspace auto-focusdecide auto-focus on new live task`lib/live-workspace.ts`lib/live-workspace.test.tsFULL
UI-009UIMemory summary/filtersummarize + filter memory entries by type`lib/memorySummary.ts`lib/memorySummary.test.tsFULL
UI-010UINotifications logicstatus→event map, shouldNotify, debounce, build options`lib/notifications.ts`lib/notifications.test.tsFULLcreateNotification/permission are browser-API thin wrappers (not unit-covered, acceptable)
UI-011UIOutput-path detection/linkifyregex split text by output paths, linkify escaped HTML`lib/output-path-detect.ts`lib/output-path-detect.test.tsFULL
UI-012UIOwner display namederive owner label`lib/owner.ts`lib/owner.test.tsFULL
UI-013UIPublic route matchingmatch login/join/shared public routes`lib/publicRoute.ts`lib/publicRoute.test.tsFULL
UI-014UIPush subscribe helpersisPushSupported/iOS/PWA, base64→Uint8Array`lib/push-subscribe.ts`lib/push-subscribe.test.tsFULL
UI-015UIShared view URLsshared image base + raw url builders`lib/sharedView.ts`lib/sharedView.test.tsFULL
UI-016UISharing scope previewvisibility preview text + tooltip`lib/sharingScope.ts`lib/sharingScope.test.tsFULL
UI-017UISpace branding varsparse color, derive brand CSS vars`lib/spaceBranding.ts`lib/spaceBranding.test.tsFULL
UI-018UISpace rail sortorder spaces for rail`lib/spaceSort.ts`lib/spaceSort.test.tsFULL
UI-019UISpace task filter/countfilter tasks for space, count running`lib/spaceTasks.ts`lib/spaceTasks.test.tsFULL
UI-020UISplit pieces (custom/default)split + resolve piece options`lib/splitPieces.ts`lib/splitPieces.test.tsFULL
UI-021UIStreaming field extractextract a field from partial SSE stream`lib/streamFieldExtract.ts`lib/streamFieldExtract.test.tsFULL
UI-022UITab swipe physicsaxis lock, resist, commit, neighbor index`lib/tab-swipe.ts`lib/tab-swipe.test.tsFULL
UI-023UITask list filter/sort/groupquery match, group-by-status, counts, filter+sort`lib/taskFilter.ts`lib/taskFilter.test.tsFULL
UI-024UITask scope filterfilter tasks by scope tab`lib/taskScope.ts`lib/taskScope.test.tsFULL
UI-025UITheme pref + applyresolve/store/apply theme, system-dark, events`lib/theme.ts` (+ `theme-css-parity.test.ts`)lib/theme.test.ts, lib/theme-css-parity.test.tsFULL
UI-026UITool policy split/patchsplit categories, build policy patch, count enabled`lib/toolPolicy.ts`lib/toolPolicy.test.tsPARTIALknown Bash/sensitive-tools write-path gap (MEMORY: PR #653) — verify both category + sensitiveTools systems covered
UI-027UIUnsaved-changes guarddetect unsaved, confirm discard, hook`lib/unsavedGuard.ts`lib/unsavedGuard.test.tsPARTIALuseUnsavedGuard hook portion needs jsdom; pure parts covered
UI-028UIURL state (useUrlState)read/build search params, legacy ?page=tasks redirect`lib/urlState.ts`lib/urlState.test.tsFULLhook wrapper useUrlState.ts itself untested (jsdom) but pure core covered
UI-029UIMisc utils + activity log parserelativeTime, status tones, previewable-by-type, parseActivityLog`lib/utils.ts`lib/utils.test.tsFULL
UI-030UIWorkspace dir roleclassify input/output/logs/subtasks dir`lib/workspaceDirs.ts`lib/workspaceDirs.test.tsFULL
UI-031UIPet state machinepet mood/animation state transitions`lib/pets/petState.ts`lib/pets/petState.test.tsFULLlib/pets/toolIconMap.ts is a static map (no test, low value)
UI-032UIi18n workspace terminologylocale keys use "ワークスペース" consistently`i18n/index.ts`, `i18n/locales`i18n/layout-workspace.test.tsPARTIALonly layout/workspace terms checked; full key-parity across locales not asserted
UI-033UIService worker push handlerSW push event → notification payload`sw/push-handler.ts`sw/push-handler.test.tsFULL
UI-034UIApp share URL builderbuild shareable app URL/token`components/spaces/appShareUrl.ts`components/spaces/appShareUrl.test.tsFULL
UI-035UIApp↔iframe postMessage bridgevalidate/route postMessage between app iframe + host`components/spaces/app-bridge.ts`components/spaces/app-bridge.test.ts (31 cases)FULLheaviest pure suite; strong
UI-036UIApp file gateway (shared/auth)route file reads through gateway, prevent 401 drift`components/spaces/app-file-gateway.ts`components/spaces/app-file-gateway.test.tsFULL
UI-037UIChat detail split logicsplit chat vs detail pane state`components/spaces/ChatDetailSplit.tsx` (logic)components/spaces/ChatDetailSplit.test.tsPARTIALpure split logic tested; component render not
UI-038UISpace detail keyingstable key/identity for space detail`components/spaces/SpaceDetail.tsx` (logic)components/spaces/spaceDetailKeying.test.tsPARTIALkeying logic only
UI-039UIApp auto-approve gatedecide auto-approve of app file ops`components/spaces/AppRunner.tsx` (logic)components/spaces/AppRunner.autoapprove.test.tsxPARTIALonly auto-approve branch; AppRunner iframe lifecycle untested
UI-040UIApp-share API clientapp-share fetch client behavior`src/api.ts` (app-share)api.app-share.test.tsPARTIALonly app-share endpoints; rest of api.ts untested
UI-041UIHarness gatewaytest-harness gateway (mounts real AppRunner)`harness/`harness/harness-gateway.test.tsFULL
UI-042UIShared tabs (public view)which tabs show in SharedView/SharedAppView`pages/SharedView.tsx`/`SharedAppView.tsx` (logic)pages/sharedTabs.test.tsPARTIALtab-selection logic only
UI-043UIDetail tab selectiondetail tab list / readonly gating`components/detail/*` (logic)detail/detailTabs.test.ts, detail/detail-readonly.test.tsPARTIALtab + readonly logic; not full panels
UI-044UITask data source resolutionchoose live vs shared data source for task`components/detail/task-data-source.tsx` (logic)detail/task-data-source.test.tsFULL
UI-045UIConsole key handlingterminal key → escape sequences`detail/tabs/console/*`detail/tabs/console/keys.test.tsPARTIALkey mapping only; TerminalView render/websocket untested
UI-046UITopBar collapse logicwhich tabs collapse into overflow`components/layout/TopBar.tsx` (logic)layout/topbar-collapse.test.tsPARTIALcollapse math only
UI-047UISettings sidebar sectionssection list/visibility logic`components/settings/SettingsSidebar.tsx` (logic)settings/settingsSidebar.test.tsPARTIALsection logic; per-form rendering untested
UI-048UIRotating tipspick/rotate chat tip strings`components/chat/RotatingTips.tsx` (logic)chat/RotatingTips.tips.test.tsFULL
UI-049UIFile drag-n-drop transferbuild/read DataTransfer drag sources for files`lib/fileDnd.ts` (`dragSources`, `readDragSources`)noneNONEpure, easy to test now; DnD bugs are user-visible
UI-050UIFiles → base64convert File[] to base64 upload payload`lib/fileBase64.ts`noneNONEuses FileReader (jsdom/node-FileReader needed); logic small but worth a test
UI-051UILocal date helperslocalToday, tz offset, shiftDay`lib/localDate.ts`noneNONEpure date math — high regression risk (tz), trivially testable
UI-052UISpace colorderive deterministic space color`lib/spaceColor.ts`noneNONEpure; deterministic-output test trivial
UI-053UIPolling/stale constantsPOLLING + STALE_TIME tables`lib/constants.ts`noneNONElow value (constants) — skip unless invariants needed
UI-054UIHelp content loaderload+assemble help sections from raw md`lib/help-content.ts` (`loadHelpSections`)nonePARTIALhelp.ts parsing covered, but the loader/aggregation step is untested
UI-055UIFilePreview render (md/csv/jsonl/image/pdf)render each preview type; **relative-image rewrite** to /api/local/tasks/:id/files/raw`components/files/FilePreview.tsx` (`resolvedHref`, line ~320)none (only lib/utils isPreviewable flags)PARTIALthe relative-image rewrite is core, user-visible, untested. Could extract resolveImageHref to lib → sandbox-testable
UI-056UIFileBrowser (grid/list, sort, select, move)tile/detail views, multi-select, drag-move, breadcrumb`components/files/FileBrowser.tsx` + `FileTileGrid/FileDetailList/FileBreadcrumb/MoveTargetDialog`e2e spaces.spec.ts (icon grid/upload/delete/multiselect/download)PARTIALcore flows covered by e2e; jsdom unit tests absent (sort/select logic is in tested libs UI-006/UI-004)
UI-056bUIuseFileBrowser/useFileView/useFilePreview hooksdata fetching + view state for files`hooks/useFileBrowser.ts`, `useFileView.ts`, `useFilePreview.ts`noneNONEjsdom
UI-057UIChatPane / ChatMessage / MovementGroupmessage stream, movement grouping, tool-calls section`components/chat/*`e2e (inline chat create/open, cancel)PARTIALrendering untested in jsdom; movement-group grouping logic not extracted to lib
UI-058UITool-request approval cardinline Approve/Deny grants + resolves request`components/chat/ToolRequestApproval.tsx`e2e tool-request.spec.ts (approve, deny)FULLjsdom unit absent; e2e covers both branches
UI-059UISettings ConfigForm + per-section formsedit each config.yaml section (Tools/LLM/Context/Safety/SSH/MCP/Gateway/Branding/Pets/Reflection/...)`components/settings/*Form.tsx` (~45 files)settingsSidebar (sections), toolPolicy (UI-026)PARTIALhuge surface; only sidebar + tool-policy logic tested. Per-form validation/round-trip largely untested
UI-060UIMemory & Learning tabedit memory entries, reflection history + revert`components/settings/MemoryLearningForm.tsx`, `ReflectionForm.tsx`e2e spaces.spec.ts (memory persists A vs B), memorySummary lib (UI-009)PARTIALpersistence covered by e2e; revert button + reflection-history list render untested
UI-061UIUsage dashboardper-user daily LLM usage charts`components/usage/UsagePage.tsx`, `settings/GatewayKeyUsagePanel.tsx`noneNONEjsdom; aggregation/format logic not extracted to lib
UI-062UIWorker/Node status widgetsrail GPU panel, per-backend rows, worker pills`components/dashboard/*Widget.tsx`, `hooks/useWorkerStatus/useNodeStatus`e2e spaces.spec.ts (GPU panel toggle, proxy per-backend rows)PARTIALe2e smoke; widget render/empty-state untested in unit
UI-063UIBrowser session panel + PiPlive browser session view, picture-in-picture`components/browser/*`, `hooks/usePictureInPicture.ts`e2e spaces.spec.ts (BROWSER panel groups render)PARTIALPiP controller untested; panel render only via e2e
UI-064UISSH config/console UIconsole terminal, grants, audit, key rotation`components/settings/Ssh*.tsx`, `userfolder/Ssh*.tsx`, `useConsoleSession`none (types only ssh-console-types.ts)NONElarge untested surface; console session hook + websocket untested
UI-065UIMobile edge-swipe / swipeable tabsedge swipe nav, tab swipe gestures`hooks/useEdgeSwipe.ts`, `components/mobile/SwipeableTabs.tsx`hooks/useEdgeSwipe.test.ts (DOM — **fails in sandbox**), lib/tab-swipe.ts (UI-022)PARTIALphysics covered as lib; the DOM hook test exists but can't run here
UI-066UIPets overlay + frame analysispet sprite overlay, tool-spark, frame analysis`components/pets/*`, `hooks/usePetFrameAnalysis/useActivePet`lib/pets/petState.test.ts (UI-031)PARTIALstate machine covered; overlay render + frame analysis untested
UI-067UICreateTaskDialog + PromptCoach + ScheduleFieldsnew chat/task dialog, prompt coaching, schedule builder`components/create/*`e2e (+新規 opens dialog; ephemeral warning), cronForm lib (UI-003)PARTIALdialog open + ephemeral warning via e2e; prompt-coach + form→cron untested
UI-068UIMonaco file editor (userfolder)edit AGENTS.md/MCP/skills/memory files`components/userfolder/MonacoFileEditor.tsx` + panelse2e (AGENTS.md persistence, panels render)PARTIALpersistence via e2e; editor component untested in unit
UI-069UICommand palette UIopen palette, fuzzy filter, execute command`components/command/CommandPalette.tsx`lib/command-palette.test.ts (UI-002)PARTIALall logic in lib (FULL); only the render/keyboard wiring is jsdom
UI-070UIEmbed cards (X/Maps/YouTube/Amazon)render structured-block embeds + detail modals`components/embed/*`noneNONEjsdom; parsing of block data could be extracted to lib
UI-071UISetup wizardfirst-run setup steps`components/setup/SetupWizard.tsx`, `hooks/useSetupState`noneNONEjsdom
UI-072UIToast / notifications hookstoast queue, task notifications dispatch`hooks/useToast.ts`, `useTaskNotifications.ts`lib/notifications.test.ts (UI-010)PARTIALdecision logic in lib; hook dispatch/debounce wiring untested
UI-073UISpaces foundation + rail + detail tabscreate case space, open detail, mobile rail↔detail`components/spaces/*`e2e/spaces.spec.ts (37 cases)FULLmost thorough e2e suite (chat CRUD, files, calendar, settings, apps, members, MCP isolation)
UI-074UICross-space calendartop-bar tab, grid, month nav, per-space dots`components/spaces/CrossSpaceCalendar.tsx`e2e/calendar.spec.tsFULL
UI-075UITasks→workspace migrationTasks tab removed, personal WS auto-select, legacy redirect`App.tsx`, `lib/urlState.ts`e2e/tasks.spec.tsFULLalso lib-covered (UI-028)
UI-076UISharing scope (multi-user)manager/member/stranger visibility (200/404), invite pickerspace visibilitye2e-auth/sharing-scope.auth.spec.tsFULLrequires auth seed
UI-077UIShared space membershipinvite→member row+avatars, role change, removal`components/spaces/SpaceMembersPanel.tsx`e2e-auth/shared-space.auth.spec.tsFULL
UI-078UISpace invite linksjoin via token, invalid/revoked states`components/spaces/JoinSpace.tsx`e2e-auth/space-invite.auth.spec.tsFULL
UI-079UIWorkspace file input flowinput/output folders show, next-action guidance, ephemeral warningFiles tab + create dialoge2e-auth/workspace-file-input.auth.spec.tsFULL
UI-080UIApp-share public viewlogin-free read-only shared app view`pages/SharedAppView.tsx`, `SharedView.tsx`none (only lib appShareUrl/sharedTabs/sharedView)PARTIALpublic viewer flow has no e2e; logic libs covered
UI-081UIHelp pagerender help sections by category, search`pages/HelpPage.tsx`none (lib help.ts FULL)PARTIALrendering + search wiring not e2e/jsdom tested
UI-082UIPieces page (CRUD)list/edit custom vs default pieces, movement editor`pages/PiecesPage.tsx`, `settings/PieceEditor.tsx`, `MovementForm.tsx`, `RulesTable.tsx`none (lib splitPieces FULL)PARTIALpiece editor / movement rules UI untested
UI-083UISchedules pagelist/create scheduled tasks (5 types)`pages/SchedulesPage.tsx`none (lib cronForm partial)PARTIALschedule CRUD UI untested
UI-084UIAdmin / users pageuser CRUD, captcha, org form`pages/UsersPage.tsx`, `AdminCaptchaPage.tsx`, `admin/*`, `settings/OrgsForm.tsx`noneNONEno UI tests for admin user management

重要ギャップ (Top gaps)

各領域インベントリの「Top gaps」を集約。最もリスクの高い未テスト挙動。

Engine core

Highest-risk untested behaviors, ranked:

  1. ENG-052 classifyPiece async path (NONE). Routing every task to a piece runs through this LLM call + fallback. Only the two pure helpers are tested; the wiring, the null-parse→default-piece fallback, and LLM-error handling have zero coverage. A regression here silently misroutes all tasks.
  2. ENG-037 WAITING_HUMAN_BROWSER / WAITING_HUMAN_TOOL_REQUEST pauses (NONE). Two human-wait sentinel branches in piece-runner have no test. A break leaves jobs hung or, worse, falsely completed — same failure class as the documented context-overflow default_next trap.
  3. ENG-039 Lessons injection + cap + lessons.jsonl (PARTIAL). Lessons appear in snapshot tests, but the core promise — accumulated lessons injected into the next movement's prompt and trimmed at 2000 chars — is not directly asserted, nor is writeLessonLog. Silent loss of cross-movement learning would be invisible.
  4. ENG-053 loadAllPieceTriggers (NONE). Trigger/keyword loading feeds the classifier hint layer; the custom-dir precedence and skip-on-invalid-piece branches are uncovered.
  5. ENG-033 cross-movement loop guard per-movement override + reset (PARTIAL) and ENG-013 mid-iteration cancel (PARTIAL). The default-revisit abort is tested, but per-movement max_consecutive_revisits and the counter-reset-on-movement-change branch are not — the exact knobs that prevent runaway loops. Likewise mid-work cancellation (vs pre-loop) is only covered via snapshot side effects.

Tools

  1. ReadPPTX ZIP-bomb detection & PPTX size limit (TOOL-015) — NONE. office.ts:2124-2130 rejects total-uncompressed > 200MB and validateOfficeFile enforces the 50MB file cap, but office.test.ts contains no test that crafts an over-limit / high-compression-ratio PPTX. This is the only declared anti-DoS guard in the tools layer and it is completely unverified. A malformed/regressed check would silently allow a decompression bomb.
  2. Office file-size-limit enforcement across ReadExcel/ReadDocx/ReadPdf (TOOL-012/013/014) — PARTIAL→NONE for the limit path. Tests thoroughly cover *format-mismatch* rejection (CFB/HTML/CSV/OOXML disguises) but never exercise stats.size > maxSize → warning emission. The resolveMaxSize config override (tools.office_*_max_size_mb) is also untested.
  3. index.ts dispatch order, first-match-wins, and silent-skip-on-load-failure (XCUT-001) — NONE. A single test checks the SshConsole catalog. The core invariant — that a tool name resolves to exactly one module in a fixed precedence order, and that a module failing to import is skipped without crashing dispatch — has no test. A duplicate tool name or an import regression would pass CI.
  4. index.ts runtime META_TOOLS injection (XCUT-002) — PARTIAL. The 20-name runtime META list in index.ts:411 (RequestTool, MissionUpdate, all UserFolder/AppDocs tools, etc.) is what actually makes these always-available regardless of a movement's allowed_tools, yet only the smaller tool-categories.ts SUBSET is asserted. The two lists can drift (they already differ by design); nothing tests that the runtime injection list matches expectations.
  5. DownloadFile robustness (TOOL-009) — PARTIAL. Only source-vs-input routing is tested. No coverage for download size limits, redirect following, content-type handling, or failure modes — the same blast radius class as WebFetch but without WebFetch's SSRF/binary test depth.

Pieces

  1. 8 of 15 pieces have ZERO direct test beyond the schema-load sweep (research-sub, brainstorming, sns-research, x-ai-digest, help) and several more are only name-fixtures (chat, slide, data-process, piece-builder). No test asserts their movement/rule/transition contract or their allowed_tools allowlist. A piece could be silently broken (wrong tool name, dropped movement, bad transition graph that still parses) and only loadPiece().not.toThrow() would catch it — which it won't, since those are semantic not schema errors. Highest-value targets: research-sub (spawned, complex 3-movement loop), brainstorming, sns-research, x-ai-digest.
  2. No allowed_tools-vs-real-tool-registry consistency test. Pieces list tool names in allowed_tools as plain strings; nothing asserts every name in every piece resolves to an actual registered tool. A typo (or a renamed/removed tool) leaves a dead allowlist entry that silently never gets offered to the LLM — exactly the class of drift the maintenance-checklist warns about. This would be a cheap, high-leverage sweep test across all 15 pieces.
  3. lint-pieces.mjs (PINFRA-004) has no test. The CI gate script (file collection, exit codes 0/1/2, YAML parse-error path) is entirely untested. Its core rule is mirrored by validatePieceDef (tested), but the script's own CLI behavior — including its parse-error exit(2) and multi-file argument handling — could regress without notice.
  4. No transition-graph reachability / dangling-next validation for pieces. Validation only bans terminal values in rules[].next and (in the tool path) checks next ∈ movements∪WAIT_SUBTASKS. But file-backed loadPiece/validatePieceDef does NOT verify that every rules[].next points to an existing movement, nor that initial_movement exists, nor that every movement is reachable. A built-in YAML with a typo'd next: would load fine and only fail at runtime ("Movement not found"). No test guards this for the shipped pieces.
  5. Per-user piece resolution propagation (PINFRA-017) is only partially tested. The known "no-auth 'local' fallback" / "custom-default separation" checklists call out worker, continue-job, scheduler, classifier, and pieces-api as all needing custom-dir threading. Only the worker load path and pieces-api authz have tests; the scheduler-spawn and continue-job (resume after ASK/subtasks) paths resolving the correct per-user custom piece are not directly asserted — a regression here would silently fall back to built-in pieces for custom-piece users.

Reflection

  1. REFL-032 — applyOne per-change write-failure path (decision.code='failed'): when a memory write throws mid-loop, the applier records that single change as failed and continues. This distinct per-change failure branch (vs runner-level outcome='failed') is not clearly asserted. It is the only place a memory mutation can partially fail under the lock, so it directly affects partial/rejected outcome correctness.
  2. REFL-056 — space authz negative path (canEditInSpace → 404): the reflection-api space-store resolution depends on canEditInSpace. Tests cover the space-principal happy path, but a space *member without edit rights* hitting history/revert (must 404, no leak) is not asserted as a distinct multiuser case — exactly the kind of cross-principal authz gap that whole-branch reviews catch but per-unit tests miss.
  3. REFL-013 / REFL-044 / REFL-045 — runner snapshot-capture + non-fatal writeSnapshot failure: the runner's writeSnapshot-throws-but-record-metric-anyway branch, plus the before/after capture edge cases (remove-op uses merge_target for the before-file name; piece-YAML pre/post capture when customPath doesn't yet exist), are only lightly exercised. A torn snapshot here silently degrades the revert feature.
  4. REFL-007 — reflection dispatch / gateway routing: worker.reflection-dispatch.test.ts is small (~2 cases). The gateway role-keyed routing (reflectionRoutingKey, roleFallback:'reflection') and the "send worker API key or gateway 401s" requirement are documented in code comments but not asserted end-to-end — a deploy-shaped risk (401 storms) that unit tests don't catch.
  5. REFL-031 / REFL-037 — boundary branches in applier: the ">3 memory_changes truncated with WARN" hard cap and the decideOutcome empty-input fallback-to-abstained (distinct from an explicit abstain_reason) lack dedicated assertions. Both are silent-correctness branches: a regression would mis-drop changes or mislabel outcomes without any test going red.

Bridge core

  1. APIC-016 GET /api/local/tasks/:taskId/stream (NONE) — The SSE event stream (tool-call/delta mapping, visibility gate, the toolBuf/event-name reconstruction logic around line 976) has zero direct tests. This is the live job-progress feed the UI depends on; a regression here is invisible to CI.
  2. APIC-007 PUT /api/local/tasks/:taskId/mission (NONE) — Mission update route is completely untested: no authz (owner/admin vs viewer), no validation, no persistence assertion. Only superficial string match in an unrelated auth test.
  3. APIC-020 office-preview HTTP route (PARTIAL) — The module is well tested, but the /files/office-preview endpoint wiring is not: section-enum 400, canViewTask gate, and the SofficeUnavailableError→503 mapping are unexercised end-to-end (only handleOfficePreviewError would do the 503).
  4. APIC-045 subtask file 403 traversal branch (PARTIAL)GET /subtasks/:jobId/files/* has the explicit resolved !== base && !startsWith(base+sep) → 403 escape guard, but no test drives a traversal payload to assert the 403. The containment helper is unit-tested (APIC-046) but the route-level enforcement is not.
  5. APIC-011 regenerate-title (PARTIAL) — Only exercised inside the spaces test in an owner-happy context; the owner/admin-only authz denial path and the missing-generator branch are not asserted. (Secondary: APIC-041 trigger happy-path, APIC-008 comments read-gate, and APIC-037/043 GET-by-id view gates are similar PARTIAL thin spots.)

Bridge auth & spaces

Highest risk first (authorization-weighted):

  1. APIS-038 Space tool-policy write under-tested (HIGH). PUT /:id/tool-policy gates by canManageSpace, but only 9 authz assertions and — per MEMORY (PR #653) — the Bash/sensitive-tool write path (buildPolicyPatch only walked categories, missing the SENSITIVE_TOOLS separate channel) was a real save-loss bug. The read/validate/render/write round-trip for sensitive tools across both delivery systems is not asserted end-to-end. A regression here silently grants/denies Bash/SSH/browser/MCP. Add an integration test that PUTs a sensitive-tool policy as a non-manager (expect 403) and as owner (expect persisted both-channels).
  2. APIS-052 Gateway proxy auth boundary (HIGH). mountGateway proxies LLM traffic; classification and config-equivalence are well tested, but the authentication of proxied requests (Bearer/API-key acceptance/rejection, 401 on bad key) is not asserted in gateway-mount.test.ts. An unauthenticated path slipping through the gateway mount would expose backend LLM spend. Deploy-order note in MEMORY (gateway→client) hints this boundary is operationally fragile.
  3. APIS-062 Dashboard API has no visible auth guard (MEDIUM-HIGH). createDashboardApi router shows no requireAuth/requireAdmin in its route definitions and the test asserts 0 authz cases. Worker/node (GPU) status may leak infra topology to unauthenticated/non-admin users. Confirm guard is applied at mount site (server.ts) and add an authz assertion.
  4. APIS-019 Shared-subtask wildcard traversal (MEDIUM). GET /api/shared/:token/subtasks/:jobId/files/* is a public token route with a wildcard path segment; containment relies on isJobWithinWorkspace prefix guard. No direct test asserts path-traversal/prefix-collision rejection on this wildcard. This is exactly the class fixed previously (startsWith prefix collision) — add a traversal/escape test.
  5. APIS-014 No-auth admin passthrough breadth (MEDIUM). When authActive=false, admin guards downgrade to passthrough (admin-api covered; branding adminGuard no-auth path not asserted). If a deployment runs no-auth on a shared network, branding upload/delete is fully open. Document/assert the no-auth posture for every admin route, not just user CRUD.

Lower-priority partials worth noting: APIS-018/APIS-027 (per-endpoint path-escape / non-member-on-public read not asserted), APIS-032 (calendar non-editor write rejection lighter than file gates), APIS-044 (noVNC proxy upgrade authz), APIS-056 (push send delivery).

Services & DB

  1. Job recovery & resume helpers untested (DB-003, DB-032, SVC-019). recoverOrphanedJobs, recoverStuckRunningJobs, resumeMcpWaitingJobs, resumeToolRequestJob, and requeueParentJobIfAllSubtasksDone have no direct unit tests. These run on worker restart / subtask completion and directly affect whether jobs get stuck or double-run — exactly the class of bug seen in the config-rebuild double-execution incident. Highest-risk gap because failures are silent (stuck or duplicated jobs in prod).
  2. Env override matrix only partially covered (CFG-004, CFG-016). Only OLLAMA_BASE_URL is asserted (in config.audit-regression). CONCURRENCY, WORKTREE_DIR, OLLAMA_MODEL, DB_PATH, and the AAO_* metrics vars have no test. Env overrides are a documented public interface and a known footgun (storage mirror precedence already bit the project in #368/#369).
  3. answerSubtaskAsk / subtask ASK plumbing (SVC-019). The path where a parent worker answers a child subtask's ASK and the parent requeues on all-subtasks-done is untested end-to-end. This is core to the waiting_subtasks/waiting_human lifecycle and has tangled state.
  4. addAuditLog not unit-tested (DB-027). Only one consumer (script run) is asserted indirectly. Audit log is a security/compliance surface; no test guards the row shape or that mutating ops actually write audit rows.
  5. Comment injection lifecycle (DB-008). getUninjectedComments / markCommentsInjected / getLatestResultComment drive what the agent sees from user comments and what the user sees as the final result — no direct assertion of the inject→mark→re-query cycle, despite being on the hot path for continuation jobs.

UI

  1. UI-055 FilePreview relative-image rewrite (resolveImageHref/resolvedHref, FilePreview.tsx ~L320) — core, user-visible (broken images in markdown), and currently untested. *Sandbox-testable IF the href-rewrite function is extracted to lib/ (pure string transform); today it's inline in a component so as-is it needs jsdom.*
  2. UI-059 Settings forms (~45 *Form.tsx) — largest untested surface; only SettingsSidebar section logic + toolPolicy are covered. Per-form validation/round-trip is jsdom/e2e. Mitigation that IS sandbox-testable: extract each form's validation/serialization into pure helpers (the established pattern in this repo) and unit-test those.
  3. UI-064 SSH UI (config/console/grants/audit/rotation) — large surface, only TS types tested, no behavior tests. Console session + websocket needs e2e; grant/audit list filtering + form validation are extractable to lib (sandbox-testable).
  4. UI-049/UI-051/UI-052 untested pure libs (fileDnd, localDate, spaceColor) — immediately sandbox-testable, no extraction needed. localDate (tz math) is the highest regression risk of the three.
  5. UI-026 toolPolicy Bash/sensitive-tools write-path — known real bug class (MEMORY: PR #653 fixed a save gap where buildPolicyPatch only walked categories and missed SENSITIVE_TOOLS/Bash). Test exists but should assert both the category system and the separate sensitiveTools array across read/validate/render/write. Sandbox-testable now (pure).