maestro/docs/features/index.html
oss-sync b857c33ef6
Some checks failed
CI / build-and-test (push) Has been cancelled
sync: update from private repo (f6d625db)
2026-06-26 03:35:45 +00:00

769 lines
294 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MAESTRO 全機能カタログ &amp; テストカバレッジ</title>
<style>
:root{
--bg:#f6f7f9; --card:#ffffff; --ink:#1f2733; --muted:#5b6675; --line:#e3e7ec;
--full:#1a7f37; --full-bg:#e6f4ea; --partial:#b3791a; --partial-bg:#fdf3e0;
--none:#c0362c; --none-bg:#fbe9e7; --other:#6b7280; --other-bg:#eef0f2;
--accent:#2563eb;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Hiragino Kaku Gothic ProN","Noto Sans JP",Meiryo,sans-serif;
line-height:1.6;font-size:14px;}
code,.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;font-size:.86em;}
code{background:#eef1f4;border-radius:4px;padding:1px 5px;color:#243; word-break:break-word;}
.wrap{max-width:1480px;margin:0 auto;padding:24px 20px 80px;}
header.page{margin-bottom:8px;}
.stage-badge{display:inline-block;font-weight:700;font-size:12px;letter-spacing:.04em;
padding:5px 14px;border-radius:999px;background:#dbeafe;color:#1e40af;border:1px solid #bfdbfe;}
.stage-legend{display:inline-flex;gap:6px;margin-left:10px;font-size:11px;color:var(--muted);align-items:center;}
.stage-legend span{display:inline-flex;align-items:center;gap:4px;}
.dot{width:9px;height:9px;border-radius:50%;display:inline-block;}
.dot.b{background:#3b82f6}.dot.y{background:#eab308}.dot.g{background:#22c55e}
h1{font-size:26px;margin:12px 0 4px;}
.sub{color:var(--muted);margin:0 0 6px;}
.links{margin:10px 0 22px;font-size:13px;}
.links a{color:var(--accent);text-decoration:none;margin-right:18px;}
.links a:hover{text-decoration:underline;}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:20px 22px;margin:18px 0;
box-shadow:0 1px 2px rgba(20,30,50,.04);}
h2{font-size:18px;margin:0 0 14px;border-left:4px solid var(--accent);padding-left:10px;}
h3{font-size:15px;margin:18px 0 6px;color:#27313f;}
.stat-row{display:flex;flex-wrap:wrap;gap:14px;margin-bottom:18px;}
.stat{flex:1 1 150px;border:1px solid var(--line);border-radius:10px;padding:14px 16px;background:#fbfcfd;}
.stat .n{font-size:30px;font-weight:700;line-height:1;}
.stat .l{font-size:12px;color:var(--muted);margin-top:6px;}
.stat.full .n{color:var(--full)} .stat.partial .n{color:var(--partial)} .stat.none .n{color:var(--none)}
.chip{display:inline-block;font-weight:700;font-size:11px;padding:2px 9px;border-radius:999px;letter-spacing:.02em;white-space:nowrap;}
.cov-full{background:var(--full-bg);color:var(--full);}
.cov-partial{background:var(--partial-bg);color:var(--partial);}
.cov-none{background:var(--none-bg);color:var(--none);}
.cov-other{background:var(--other-bg);color:var(--other);}
table{border-collapse:collapse;width:100%;font-size:13px;}
.area-table th,.area-table td{border:1px solid var(--line);padding:8px 10px;text-align:left;}
.area-table th{background:#f0f3f7;font-weight:600;}
.area-table td.num{text-align:center;}
.area-table tr.total-row td{font-weight:700;background:#f7f9fc;}
.controls{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-bottom:14px;}
#search{flex:1 1 280px;min-width:220px;padding:9px 12px;border:1px solid var(--line);border-radius:8px;font-size:14px;}
.filt-btns{display:flex;gap:6px;flex-wrap:wrap;}
.fbtn{cursor:pointer;border:1px solid var(--line);background:#fff;border-radius:8px;padding:7px 14px;font-size:12px;font-weight:600;color:var(--muted);}
.fbtn.active{background:var(--accent);color:#fff;border-color:var(--accent);}
.fbtn[data-cov="FULL"].active{background:var(--full);border-color:var(--full);}
.fbtn[data-cov="PARTIAL"].active{background:var(--partial);border-color:var(--partial);}
.fbtn[data-cov="NONE"].active{background:var(--none);border-color:var(--none);}
#count{font-size:12px;color:var(--muted);margin-left:auto;}
.table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:10px;}
table.master{font-size:12.5px;}
table.master th,table.master td{padding:7px 9px;border-bottom:1px solid var(--line);vertical-align:top;text-align:left;}
table.master thead th{position:sticky;top:0;background:#eef2f7;z-index:2;font-weight:600;border-bottom:2px solid #d4dbe4;}
table.master tbody tr:hover{background:#f5f8fc;}
.c-id{white-space:nowrap;} .c-area{white-space:nowrap;color:var(--muted);}
.c-feature{font-weight:600;min-width:140px;}
.c-behavior{min-width:240px;color:#33404f;}
.c-source{min-width:150px;} .c-test{min-width:200px;color:#445;}
.c-cov{white-space:nowrap;text-align:center;} .c-gap{min-width:200px;color:#6a7686;}
.gap-area{margin-bottom:6px;}
ol.gap-list{margin:4px 0 14px;padding-left:22px;}
ol.gap-list li{margin-bottom:8px;}
.muted-note{font-size:12px;color:var(--muted);margin-top:6px;}
footer{margin-top:40px;color:var(--muted);font-size:12px;text-align:center;}
@media (max-width:680px){ h1{font-size:21px;} .wrap{padding:16px 12px 60px;} }
</style>
</head>
<body>
<div class="wrap">
<header class="page">
<span class="stage-badge">設計</span>
<span class="stage-legend">
<span><i class="dot b"></i>設計</span>
<span><i class="dot y"></i>実装</span>
<span><i class="dot g"></i>完成</span>
</span>
<h1>MAESTRO 全機能カタログ &amp; テストカバレッジ</h1>
<p class="sub">8 領域・502 機能のインベントリと既存テストのカバレッジ一覧TDD 機能テスト計画)</p>
<div class="links">
<a href="../superpowers/specs/2026-06-25-tdd-functional-tests-design.md">&#128279; 設計仕様 (design spec)</a>
<a href="#summary">サマリ</a>
<a href="#master">機能一覧</a>
<a href="#gaps">重要ギャップ</a>
</div>
</header>
<section class="card" id="summary">
<h2>サマリ</h2>
<div class="stat-row">
<div class="stat"><div class="n">502</div><div class="l">機能総数 (Total features)</div></div>
<div class="stat full"><div class="n">352</div><div class="l">FULL カバー済み</div></div>
<div class="stat partial"><div class="n">124</div><div class="l">PARTIAL 一部のみ</div></div>
<div class="stat none"><div class="n">26</div><div class="l">NONE 未カバー</div></div>
</div>
<table class="area-table">
<thead><tr><th>領域 (Area)</th><th>機能数</th><th>FULL</th><th>PARTIAL</th><th>NONE</th></tr></thead>
<tbody>
<tr><td>Engine core</td><td class="num">65</td><td class="num"><span class="chip cov-full">52</span></td><td class="num"><span class="chip cov-partial">9</span></td><td class="num"><span class="chip cov-none">4</span></td></tr>
<tr><td>Tools</td><td class="num">56</td><td class="num"><span class="chip cov-full">32</span></td><td class="num"><span class="chip cov-partial">22</span></td><td class="num"><span class="chip cov-none">2</span></td></tr>
<tr><td>Pieces</td><td class="num">32</td><td class="num"><span class="chip cov-full">18</span></td><td class="num"><span class="chip cov-partial">13</span></td><td class="num"><span class="chip cov-none">1</span></td></tr>
<tr><td>Reflection</td><td class="num">61</td><td class="num"><span class="chip cov-full">51</span></td><td class="num"><span class="chip cov-partial">10</span></td><td class="num"><span class="chip cov-none">0</span></td></tr>
<tr><td>Bridge core</td><td class="num">50</td><td class="num"><span class="chip cov-full">39</span></td><td class="num"><span class="chip cov-partial">9</span></td><td class="num"><span class="chip cov-none">2</span></td></tr>
<tr><td>Bridge auth &amp; spaces</td><td class="num">66</td><td class="num"><span class="chip cov-full">48</span></td><td class="num"><span class="chip cov-partial">17</span></td><td class="num"><span class="chip cov-none">1</span></td></tr>
<tr><td>Services &amp; DB</td><td class="num">87</td><td class="num"><span class="chip cov-full">69</span></td><td class="num"><span class="chip cov-partial">13</span></td><td class="num"><span class="chip cov-none">5</span></td></tr>
<tr><td>UI</td><td class="num">85</td><td class="num"><span class="chip cov-full">43</span></td><td class="num"><span class="chip cov-partial">31</span></td><td class="num"><span class="chip cov-none">11</span></td></tr>
<tr class="total-row"><td>合計</td><td class="num">502</td><td class="num"><span class="chip cov-full">352</span></td><td class="num"><span class="chip cov-partial">124</span></td><td class="num"><span class="chip cov-none">26</span></td></tr>
</tbody>
</table>
<p class="muted-note">本ページの数値は 8 つのインベントリ表をパースして算出した実数502 件)。設計仕様 (design spec) の概算目標値は 483 件 (FULL 318 / PARTIAL 137 / NONE 29)。差分はインベントリ確定時の項目追加・分類更新による。</p>
</section>
<section class="card" id="master">
<h2>機能一覧 (Master feature table)</h2>
<div class="controls">
<input id="search" type="text" placeholder="検索: 機能名・ID・ソース・テスト… (live filter)">
<div class="filt-btns">
<button class="fbtn active" data-cov="ALL">すべて</button>
<button class="fbtn" data-cov="FULL">FULL</button>
<button class="fbtn" data-cov="PARTIAL">PARTIAL</button>
<button class="fbtn" data-cov="NONE">NONE</button>
</div>
<span id="count"></span>
</div>
<div class="table-scroll">
<table class="master" id="master-table">
<thead><tr>
<th>ID</th><th>領域</th><th>機能</th><th>挙動</th><th>ソース</th><th>既存テスト</th><th>カバレッジ</th><th>ギャップ</th>
</tr></thead>
<tbody>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-001</code></td><td class="c-area">Engine core</td><td class="c-feature">Parallel read-tool execution</td><td class="c-behavior">Consecutive PARALLEL_SAFE tools run via <code>Promise.all</code>; side-effecting tools act as a sequential barrier</td><td class="c-source"><code>agent-loop.ts (`canExecuteInParallel`, `PARALLEL_SAFE_TOOL_NAMES`, executeMovement)</code></td><td class="c-test">agent-loop.test.ts &quot;runs consecutive safe tool calls in parallel&quot;; &quot;keeps side-effecting tools sequential as a barrier&quot;; &quot;executes regular tools before transition even if transition appears mid-batch&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-002</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>transition</code> tool (non-terminal)</td><td class="c-behavior"><code>buildTransitionTool</code> enum = only <code>rules[].next</code>; <code>validateTransition</code> rejects targets outside rules; <code>processTransitionCalls</code> records lessons</td><td class="c-source"><code>agent-loop.ts (`buildTransitionTool`, `validateTransition`, `processTransitionCalls`)</code></td><td class="c-test">agent-loop.test.ts &quot;non-terminal transition (movement-to-movement) still works&quot;; &quot;transition({next_step:…})&quot;; &quot;lets transition/complete win even if it shares a batch with a repeated call&quot; (tool-loop)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>buildTransitionTool</code> / <code>validateTransition</code> exercised only indirectly via executeMovement; no unit test that an out-of-rules <code>next</code> is rejected in isolation</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-003</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>complete</code> tool — status routing</td><td class="c-behavior">success→COMPLETE, aborted→ABORT, needs_user_input→ASK via <code>COMPLETE_STATUS_TO_NEXT</code> / <code>buildMovementResultFromComplete</code></td><td class="c-source"><code>agent-loop.ts (`buildCompleteTool`, `parseCompleteArgs`, `buildMovementResultFromComplete`)</code></td><td class="c-test">agent-loop.test.ts §7.1 &quot;success status…becomes movement output&quot;; &quot;aborted status routes via ABORT next&quot;; &quot;needs_user_input routes via ASK next&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-004</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>complete</code> arg validation</td><td class="c-behavior">success requires non-empty result; aborted requires abort_reason; needs_user_input requires missing_info; invalid → forced retry (no accumulatedText fallback)</td><td class="c-source"><code>agent-loop.ts (`parseCompleteArgs`, `validateCompleteArgs`)</code></td><td class="c-test">agent-loop.test.ts §7.1 &quot;rejects success with empty result&quot;; &quot;rejects aborted without abort_reason&quot;; (needs_user_input/missing_info path covered via routing test)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No explicit test that <code>needs_user_input</code> with empty <code>missing_info</code> is rejected (validateCompleteArgs branch); covered for success/aborted only</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-005</code></td><td class="c-area">Engine core</td><td class="c-feature">Multiple <code>complete</code> precedence</td><td class="c-behavior">0 completes → continue; &gt;1 conflicting args → retry; &gt;1 identical → first used</td><td class="c-source"><code>agent-loop.ts (complete dedup logic ~L1503-1535)</code></td><td class="c-test">agent-loop.test.ts §7.2 &quot;invalid native complete forces retry&quot;; &quot;two native completes with conflicting args → retry&quot;; &quot;two native completes with identical args → first one used&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-006</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation-history integrity on retry</td><td class="c-behavior">Every <code>tool_use</code> id gets a matching <code>tool_result</code> even when complete is rejected</td><td class="c-source"><code>agent-loop.ts (retry path)</code></td><td class="c-test">agent-loop.test.ts §7.7 &quot;all tool_use ids get a tool_result on retry&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-007</code></td><td class="c-area">Engine core</td><td class="c-feature">Tool-call loop detection (intra-movement)</td><td class="c-behavior">Identical tool-call batch repeated ≥ <code>maxToolLoopRepeats</code> (default 5) → ABORT; counter resets on arg change</td><td class="c-source"><code>agent-loop.ts (`DEFAULT_MAX_TOOL_LOOP_REPEATS`, `buildToolLoopAbortMessage`, consecutiveToolRepeats)</code></td><td class="c-test">agent-loop.tool-loop.test.ts (5 tests: limit abort, custom limit, under-limit, arg-change reset, transition wins)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-008</code></td><td class="c-area">Engine core</td><td class="c-feature">Loop-watchdog one-shot warning</td><td class="c-behavior">At <code>maxToolLoopRepeats-2</code> (min 2) inject a single <code>[loop watchdog]</code> reminder before aborting</td><td class="c-source"><code>agent-loop.ts (`warnAt`, `toolLoopWarned`, `injectLoopWarning`)</code></td><td class="c-test">(implicit in tool-loop tests' under-limit path)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No assertion that the watchdog warning message is actually injected exactly once before abort</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-009</code></td><td class="c-area">Engine core</td><td class="c-feature">Max-iterations abort</td><td class="c-behavior">Movement exceeding <code>safety.maxIterations</code> aborts with <code>buildMaxIterationsAbortMessage</code></td><td class="c-source"><code>agent-loop.ts (`buildMaxIterationsAbortMessage`)</code></td><td class="c-test">agent-loop.test.ts &quot;aborts when maxIterations is exceeded&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-010</code></td><td class="c-area">Engine core</td><td class="c-feature">Text-only-response handling</td><td class="c-behavior">NON-terminal movement aborts after N text-only turns; STRICTLY-terminal (defaultNext COMPLETE, no rules) salvages prose as completion; counter resets on tool use</td><td class="c-source"><code>agent-loop.ts (`TEXT_ONLY_REMIND_EMPTY`, textOnlyOutcome)</code></td><td class="c-test">agent-loop.test.ts &quot;aborts after text-only responses in a NON-terminal movement&quot;; &quot;salvages text-only prose…STRICTLY-terminal&quot;; &quot;does NOT salvage…still has rules&quot;; &quot;resets text-only counter when tool calls happen&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-011</code></td><td class="c-area">Engine core</td><td class="c-feature">Context-overflow forced transition (runtime usage)</td><td class="c-behavior">ContextManager threshold 95% → <code>force_transition</code> builds result via <code>movement.defaultNext</code>; falls to ABORT when defaultNext is terminal/absent</td><td class="c-source"><code>agent-loop.ts (`buildContextOverflowResult`, `buildForceTransitionResult`)</code></td><td class="c-test">agent-loop.test.ts &quot;triggers force_transition when context manager signals exhaustion&quot;; &quot;fires onContextAction callback when context threshold crossed&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-012</code></td><td class="c-area">Engine core</td><td class="c-feature">Oversized initial prompt guard</td><td class="c-behavior">Prompt over budget pre-send → dedup→compact→summarize; aborts/force-transitions when still oversized</td><td class="c-source"><code>agent-loop.ts + context/prompt-guard.ts (`guardPromptBeforeSend`)</code></td><td class="c-test">agent-loop.test.ts &quot;aborts when initial prompt is oversized and defaultNext is terminal&quot;; &quot;falls back to ABORT when oversized prompt has no defaultNext&quot;; prompt-guard.test.ts (full stage matrix)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-013</code></td><td class="c-area">Engine core</td><td class="c-feature">Cancel signal handling</td><td class="c-behavior">Already-aborted cancelSignal returns ABORT immediately; mid-movement cancel returns ABORT &quot;cancelled&quot;</td><td class="c-source"><code>agent-loop.ts (cancelSignal checks)</code></td><td class="c-test">agent-loop.test.ts &quot;returns ABORT immediately when cancelSignal is already aborted&quot;</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Mid-iteration cancel (after work started) not asserted at agent-loop level; only the pre-loop case</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-014</code></td><td class="c-area">Engine core</td><td class="c-feature">ContextManager threshold ladder</td><td class="c-behavior">70%→warn, 85%→prompt, 95%→force_transition; each fires once; isExhausted ≥0.99</td><td class="c-source"><code>context-manager.ts (`ContextManager`)</code></td><td class="c-test">context-manager.test.ts (13 tests: each threshold, fire-once, defaults, limitTokens, isExhausted, hasUsageData, char fallback)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-015</code></td><td class="c-area">Engine core</td><td class="c-feature">Ollama context-limit autodetect</td><td class="c-behavior"><code>fetchOllamaContextLimit</code> prefers num_ctx &gt; model_info.context_length; llama.cpp /props fallback; default on failure</td><td class="c-source"><code>context-manager.ts</code></td><td class="c-test">context-manager.test.ts (5 tests incl. num_ctx precedence, /props fallback, n_ctx absent)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-016</code></td><td class="c-area">Engine core</td><td class="c-feature">Tool catalog injection into system prompt</td><td class="c-behavior"><code>buildSystemPrompt</code> / <code>buildGlobalPreamble</code> auto-inject available-tools list + 1-line summaries; script section gated on Bash presence</td><td class="c-source"><code>agent-loop.ts (`buildGlobalPreamble`, `buildSystemPrompt`)</code></td><td class="c-test">agent-loop.test.ts &quot;injects the script section when Bash is among the presented tools&quot;; &quot;omits…when Bash is not presented&quot;; preamble byte-stability tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-017</code></td><td class="c-area">Engine core</td><td class="c-feature">Preamble/guidance split (job-stable vs movement)</td><td class="c-behavior"><code>buildGlobalPreamble</code> byte-identical across movements; <code>buildMovementGuidance</code> carries persona/instruction/transitions</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts &quot;preamble is byte-identical across two different movements&quot;; &quot;guidance carries movement-specific instruction, persona, transitions&quot;; &quot;buildSystemPrompt wrapper still contains both halves&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-018</code></td><td class="c-area">Engine core</td><td class="c-feature">Progressive pressure (intra-movement revisit)</td><td class="c-behavior">visitCount==2 → caution; &gt;=3 → warning injected into guidance</td><td class="c-source"><code>agent-loop.ts (`buildMovementGuidance` L672)</code></td><td class="c-test">agent-loop.test.ts &quot;guidance injects progressive-pressure warning on revisit&quot;</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only the revisit-warning presence is asserted; the visitCount==2 (caution) vs &gt;=3 (warning) distinction is not separately tested</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-019</code></td><td class="c-area">Engine core</td><td class="c-feature">Checklist watchdog</td><td class="c-behavior">One-shot reminder after 5 iterations with no checklist tool; suppressed if CreateChecklist used early</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts &quot;injects a one-shot reminder after 5 iterations&quot;; &quot;does NOT fire when CreateChecklist is called early&quot;; traceability &quot;watchdog_fire&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-020</code></td><td class="c-area">Engine core</td><td class="c-feature">Cross-movement Read cache</td><td class="c-behavior">Cacheable read served to later movement; errors not cached; non-allowlisted tools (Bash) skipped</td><td class="c-source"><code>agent-loop.ts + context/tool-result-cache.ts</code></td><td class="c-test">agent-loop.test.ts &quot;returns a cached Read result…&quot;; &quot;does not cache error results&quot;; &quot;skips caching tools outside the cacheable allowlist&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-021</code></td><td class="c-area">Engine core</td><td class="c-feature">Cache invalidation</td><td class="c-behavior">Edit/Write invalidate affected path; Grep entries all-evicted on edit; Bash conservatively invalidates all file entries; no invalidate on tool error</td><td class="c-source"><code>agent-loop.ts + context/invalidation.ts + tool-result-cache.ts</code></td><td class="c-test">agent-loop.test.ts Phase 2/4 (7 tests); invalidation.test.ts (5); tool-result-cache.test.ts (28)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-022</code></td><td class="c-area">Engine core</td><td class="c-feature">Cache key construction</td><td class="c-behavior">Deterministic v1-prefixed keys for Read/Grep/Glob/WebFetch/Office; URL scheme/host normalization; workspace isolation</td><td class="c-source"><code>context/cache-key.ts</code></td><td class="c-test">cache-key.test.ts (25); tool-result-cache.test.ts key tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-023</code></td><td class="c-area">Engine core</td><td class="c-feature">File-read dedup</td><td class="c-behavior">Older Reads of same file replaced with placeholder, most-recent kept; ignores non-Read tools; idempotent</td><td class="c-source"><code>context/file-read-dedup.ts</code></td><td class="c-test">file-read-dedup.test.ts (10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-024</code></td><td class="c-area">Engine core</td><td class="c-feature">History summarization</td><td class="c-behavior"><code>summarizeHistory</code> replaces middle turns with summary, preserves tail; LLM-failure → summarized=false; update-mode with prior summary</td><td class="c-source"><code>context/history-compactor.ts</code></td><td class="c-test">history-compactor.test.ts (21: splitIntoTurns, buildSummaryPrompt, selectTailTurnStartIndex, summarizeHistory, summarizeForceTransition)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-025</code></td><td class="c-area">Engine core</td><td class="c-feature">Token estimation</td><td class="c-behavior">char→token by script class (ASCII/CJK/other), per-message overhead, image budget, tool serialization</td><td class="c-source"><code>context/token-estimate.ts</code></td><td class="c-test">token-estimate.test.ts (23)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-026</code></td><td class="c-area">Engine core</td><td class="c-feature">Prompt-guard staged compaction</td><td class="c-behavior">dedup→compact→summarize ladder; reserve-cap headroom; stage-3 skipped when disabled/no isolated LLM; ok:false when all fail</td><td class="c-source"><code>context/prompt-guard.ts</code></td><td class="c-test">prompt-guard.test.ts (27)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-027</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation class (seed/enter/persist)</td><td class="c-behavior">seed order (preamble+guidance+task); enterMovement appends guidance w/o reset; flush/loadFrom round-trip; rewrite truncates</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (in-memory + persistence, 8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-028</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation.replayableTurns</td><td class="c-behavior">Strips system + control-flow tool_calls (complete/transition) + dangling/orphan tool messages; keeps valid pairs</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (7 replayableTurns cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-029</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation.seedContinuation / hasReplayableTranscript</td><td class="c-behavior">Builds [preamble,guidance,...priorTurns,user] &amp; rewrites transcript; hasReplayableTranscript guards undefined/missing/system-only</td><td class="c-source"><code>context/conversation.ts</code></td><td class="c-test">conversation.test.ts (seedContinuation + 4 hasReplayableTranscript)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-030</code></td><td class="c-area">Engine core</td><td class="c-feature">Continuation seed in executeMovement</td><td class="c-behavior">priorTurns replayed &amp; handoff block suppressed; plain seed when absent</td><td class="c-source"><code>agent-loop.ts</code></td><td class="c-test">agent-loop.test.ts &quot;replays priorTurns and suppresses handoff block&quot;; &quot;without priorTurns, uses plain seed&quot;; &quot;with empty priorTurns array, behaves like plain seed&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-031</code></td><td class="c-area">Engine core</td><td class="c-feature">Atomic JSON persistence</td><td class="c-behavior">writeAtomicJson (tmp+rename, mkdir parents); readSafeJson returns missing/corrupt/wrong-version; quarantineCorruptFile</td><td class="c-source"><code>context/atomic-json.ts</code></td><td class="c-test">atomic-json.test.ts (11)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-032</code></td><td class="c-area">Engine core</td><td class="c-feature">Workspace path normalization</td><td class="c-behavior">Rejects absolute/backslash/drive/UNC/NUL/empty; canonicalizes <code>..</code>/<code>.</code>; prefixWorkspacePath join with traversal guard</td><td class="c-source"><code>context/path-normalize.ts</code></td><td class="c-test">path-normalize.test.ts (15)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-033</code></td><td class="c-area">Engine core</td><td class="c-feature">Piece loop guards (cross-movement)</td><td class="c-behavior"><code>enforceLoopGuards</code>: consecutive revisits &gt; max_consecutive_revisits (default 4) → abort; counters reset on movement change</td><td class="c-source"><code>piece-runner.ts (`enforceLoopGuards`)</code></td><td class="c-test">piece-runner.test.ts &quot;aborts when loop detection fires due to consecutive revisits&quot;</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Per-movement <code>max_consecutive_revisits</code> override and the counter-reset-on-movement-change branch not separately asserted</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-034</code></td><td class="c-area">Engine core</td><td class="c-feature">ASK limit fallback</td><td class="c-behavior">ASK result resolves <code>default_next</code> or first non-self/non-ASK/non-ABORT rule; aborts when no fallback; resolves COMPLETE default_next via ASK-limit fallback</td><td class="c-source"><code>piece-runner.ts (result.next==='ASK' branch)</code></td><td class="c-test">piece-runner.test.ts &quot;falls back to default_next when ASK limit is reached&quot;; &quot;aborts when ASK limit reached and no fallback&quot;; &quot;resolves a COMPLETE default_next reached via the ASK-limit fallback&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-035</code></td><td class="c-area">Engine core</td><td class="c-feature">WAIT_SUBTASKS pause</td><td class="c-behavior">result.next==='WAIT_SUBTASKS' pauses piece run (waiting_subtasks)</td><td class="c-source"><code>piece-runner.ts (L1144)</code></td><td class="c-test">piece-runner.test.ts &quot;does NOT write snapshot on waiting_subtasks (transient pause)&quot;</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">The pause/resume return contract is only asserted via the snapshot-suppression side effect; no direct test of the waiting_subtasks PieceRunResult shape</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-036</code></td><td class="c-area">Engine core</td><td class="c-feature">SpawnSubTask depth-limit skip</td><td class="c-behavior">Movement requiring SpawnSubTask skipped at depth limit (incl. via shared_tools); resolves COMPLETE default_next on skip</td><td class="c-source"><code>piece-runner.ts (`pieceUsesSpawn`, spawn skip)</code></td><td class="c-test">piece-runner.test.ts &quot;resolves a COMPLETE default_next when a SpawnSubTask movement is skipped at the depth limit&quot;; &quot;skips a SpawnSubTask movement at the depth limit even when…shared_tools&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-037</code></td><td class="c-area">Engine core</td><td class="c-feature">WAITING_HUMAN_BROWSER / _TOOL_REQUEST pauses</td><td class="c-behavior">Non-ASK human-wait sentinels pause the run</td><td class="c-source"><code>piece-runner.ts (L1159, L1176)</code></td><td class="c-test">(none found)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test exercises the WAITING_HUMAN_BROWSER or WAITING_HUMAN_TOOL_REQUEST branches</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-038</code></td><td class="c-area">Engine core</td><td class="c-feature">Review-feedback carry-forward</td><td class="c-behavior">verify movement output carried into later execute/process/plan/analyze; truncated per/combined caps; git status+diff appended after verify</td><td class="c-source"><code>piece-runner.ts (`buildInstructionWithFeedback`, `shouldCarryReviewFeedback`, truncate/trim)</code></td><td class="c-test">piece-runner.test.ts &quot;carries cumulative verify feedback into later execute/analyze movements&quot;; &quot;appends safe git status and diff context after verify loops&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-039</code></td><td class="c-area">Engine core</td><td class="c-feature">Lessons accumulation &amp; injection</td><td class="c-behavior">transition/complete lessons accumulated, capped (MAX_LESSONS_LENGTH 2000), injected into next movement; written to lessons.jsonl</td><td class="c-source"><code>piece-runner.ts (`buildLessonsContext`, `writeLessonLog`, lessonsAccumulator)</code></td><td class="c-test">(no direct test of buildLessonsContext/writeLessonLog; snapshot tests reference lessons)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Lessons capture appears in cancel-snapshot tests but the injection-into-next-movement and 2000-char trim logic is not directly asserted; writeLessonLog untested</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-040</code></td><td class="c-area">Engine core</td><td class="c-feature">loadPiece terminal-rule validation</td><td class="c-behavior">rejects rules[].next ∈ {COMPLETE,ABORT,ASK}; accepts default_next COMPLETE &amp; WAIT_SUBTASKS sentinel; all bundled pieces valid</td><td class="c-source"><code>piece-runner.ts (`validatePieceDef`, `loadPiece`)</code></td><td class="c-test">piece-runner.test.ts (Phase 6b: 6 tests incl. all bundled pieces load)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-041</code></td><td class="c-area">Engine core</td><td class="c-feature">loadPiece multi-dir resolution</td><td class="c-behavior">custom dir(s) win over builtin; first dir wins on dup name; string|string[] forms</td><td class="c-source"><code>piece-runner.ts (`loadPiece`)</code></td><td class="c-test">piece-runner.test.ts &quot;resolves from a list of custom dirs&quot;; &quot;first dir wins&quot;; &quot;string form still works&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-042</code></td><td class="c-area">Engine core</td><td class="c-feature">normalizeRequiredMcp</td><td class="c-behavior">retains valid slugs, drops invalid, undefined when absent, coerces non-array to []</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (4 required_mcp tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-043</code></td><td class="c-area">Engine core</td><td class="c-feature">normalizeSharedTools / mergeToolNames</td><td class="c-behavior">shared_tools sanitized; mergeToolNames unions shared+movement first-seen order, drops dups/non-strings</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (shared_tools 4 + mergeToolNames 3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-044</code></td><td class="c-area">Engine core</td><td class="c-feature">validateAllowedSshConnections</td><td class="c-behavior">validates id format / wildcard / empty-deny; tolerates missing allowlist (A4 workspace-policy gating); reports offenders</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (allowed_ssh_connections 15 + loader-tolerance 1)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-045</code></td><td class="c-area">Engine core</td><td class="c-feature">Workspace tool-policy resolution &amp; injection</td><td class="c-behavior">resolveWorkspaceTools (safe default, Bash/browser opt-in); runPiece injects workspaceTools overriding movement.allowed_tools; grantedTools unioned</td><td class="c-source"><code>piece-runner.ts (runPiece) + workspace-tool-policy.ts</code></td><td class="c-test">piece-runner.test.ts (resolveWorkspaceTools 5 + runPiece injection 4); workspace-tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-046</code></td><td class="c-area">Engine core</td><td class="c-feature">runPiece max_movements default</td><td class="c-behavior">iterates when max_movements missing / 0 / negative</td><td class="c-source"><code>piece-runner.ts (runPiece)</code></td><td class="c-test">piece-runner.test.ts &quot;still iterates when piece.max_movements is missing&quot;; &quot;…is 0 or negative&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-047</code></td><td class="c-area">Engine core</td><td class="c-feature">buildFollowupNotice</td><td class="c-behavior">detects follow-up when output/ or subtasks/ has non-hidden files; ignores dotfiles; empty for fresh ws</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (5 buildFollowupNotice tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-048</code></td><td class="c-area">Engine core</td><td class="c-feature">Cancel/terminal memory snapshots</td><td class="c-behavior">snapshot+meta-event on cancelled (pre/mid movement) and aborted; NOT on success or waiting_subtasks; v2 captures finalOutput/history/lessons w/ truncation</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">piece-runner.test.ts (7 snapshot tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-049</code></td><td class="c-area">Engine core</td><td class="c-feature">Conversation continuity end-to-end</td><td class="c-behavior">movement 2 sees movement 1 history; transcript.jsonl written; continuation job replays prior turns &amp; suppresses LIMIT-1 handoff</td><td class="c-source"><code>piece-runner.ts + conversation.ts</code></td><td class="c-test">piece-runner.conversation.test.ts (2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-050</code></td><td class="c-area">Engine core</td><td class="c-feature">Piece classification prompt</td><td class="c-behavior"><code>buildClassificationPrompt</code> includes task+descriptions+files; biases toward chat; routes workspace-app</td><td class="c-source"><code>piece-classifier.ts</code></td><td class="c-test">piece-classifier.test.ts (buildClassificationPrompt 4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-051</code></td><td class="c-area">Engine core</td><td class="c-feature">Classification response parsing</td><td class="c-behavior"><code>parseClassificationResponse</code> extracts valid name, handles noise, prefers longest match, null on invalid/empty</td><td class="c-source"><code>piece-classifier.ts</code></td><td class="c-test">piece-classifier.test.ts (parseClassificationResponse 5)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-052</code></td><td class="c-area">Engine core</td><td class="c-feature"><code>classifyPiece</code> (async LLM orchestration)</td><td class="c-behavior">Builds prompt, calls LLM, parses + falls back to default piece</td><td class="c-source"><code>piece-classifier.ts (`classifyPiece`)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">The end-to-end classify path (LLM call wiring, fallback when parse returns null, error handling) is untested; only its two pure helpers are</td></tr>
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-053</code></td><td class="c-area">Engine core</td><td class="c-feature">loadAllPieceTriggers</td><td class="c-behavior">Loads triggers/keywords from all pieces across dirs; custom wins on dup name; skips invalid pieces</td><td class="c-source"><code>piece-runner.ts (`loadAllPieceTriggers`)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test; dir-precedence and skip-on-load-error branches uncovered</td></tr>
<tr data-cov="PARTIAL" data-area="Engine core"><td class="c-id"><code>ENG-054</code></td><td class="c-area">Engine core</td><td class="c-feature">buildChecklistContext</td><td class="c-behavior">Builds checklist context block from logs root</td><td class="c-source"><code>piece-runner.ts</code></td><td class="c-test">(referenced in piece-runner.test.ts import only; no describe block)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Symbol imported in test file but no behavior assertion targets it specifically</td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-055</code></td><td class="c-area">Engine core</td><td class="c-feature">buildLocalConversationContext</td><td class="c-behavior">Renders current instruction + recent-conversation + workspace files; interjection precedence; truncation; omitRecentConversation flag</td><td class="c-source"><code>local-context.ts</code></td><td class="c-test">local-context.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-056</code></td><td class="c-area">Engine core</td><td class="c-feature">PieceCatalog caching/layering</td><td class="c-behavior">Built-ins + user pieces (custom wins); TTL cache; invalidate(userId); per-folder cache isolation</td><td class="c-source"><code>piece-catalog.ts</code></td><td class="c-test">piece-catalog.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-057</code></td><td class="c-area">Engine core</td><td class="c-feature">stripThinkingTokens</td><td class="c-behavior">Strips &lt;think&gt;/&lt;|thinking|&gt;/gemma channel blocks; leaves unclosed intact; preserves unicode</td><td class="c-source"><code>strip-thinking.ts</code></td><td class="c-test">strip-thinking.test.ts (11) + agent-loop.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-058</code></td><td class="c-area">Engine core</td><td class="c-feature">prompt-coach scoring</td><td class="c-behavior">buildCoachMessages (source inclusion + truncation); normalizeCoachResult (clamp/defaults); runPromptCoach (normalize + error propagation)</td><td class="c-source"><code>prompt-coach.ts</code></td><td class="c-test">prompt-coach.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-059</code></td><td class="c-area">Engine core</td><td class="c-feature">Console screen injection</td><td class="c-behavior">Injects SSH console screen each iteration when SshConsole* allowed + session exists; guards on missing taskId / no session / non-console piece; tail truncation</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (5 console) + agent-loop-console.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-060</code></td><td class="c-area">Engine core</td><td class="c-feature">Handoff block construction</td><td class="c-behavior">Static Continue block always; dynamic block only with handoffContext; null prevResult handled; long prevResult truncated head+tail</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (5 handoff tests)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-061</code></td><td class="c-area">Engine core</td><td class="c-feature">Per-user AGENTS.md / memory / working-dir injection</td><td class="c-behavior">Injects AGENTS.md + persistence protocol + Working Directory + approach/error-recovery sections, gated on userId/workspacePath</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.user-agents.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-062</code></td><td class="c-area">Engine core</td><td class="c-feature">Existing-workspace-files injection</td><td class="c-behavior">Injects existing input-files section when present, omits when empty</td><td class="c-source"><code>agent-loop.ts (buildSystemPrompt)</code></td><td class="c-test">agent-loop.test.ts (2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-063</code></td><td class="c-area">Engine core</td><td class="c-feature">Traceability event emission</td><td class="c-behavior">movement_start/tool_call/result/movement_complete; cache_set/hit/invalidate; watchdog_fire; run_start/complete; followup_detected; shared runId</td><td class="c-source"><code>agent-loop.ts + piece-runner.ts</code></td><td class="c-test">agent-loop.test.ts (T-1, 5) + piece-runner.test.ts (T-2, 2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Engine core"><td class="c-id"><code>ENG-064</code></td><td class="c-area">Engine core</td><td class="c-feature">ownerId/userId defaulting</td><td class="c-behavior">Defaults to 'local'/synthetic when unowned; preserves real owner in auth mode</td><td class="c-source"><code>piece-runner.ts (runPiece)</code></td><td class="c-test">piece-runner.test.ts &quot;defaults ownerId and userId to…&quot;; &quot;preserves a real ownerId/userId&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Engine core"><td class="c-id"><code>ENG-065</code></td><td class="c-area">Engine core</td><td class="c-feature">Mission char-budget trimming</td><td class="c-behavior"><code>buildGlobalPreamble</code> trims mission text when total exceeds MISSION_TOTAL_CHAR_BUDGET</td><td class="c-source"><code>agent-loop.ts (L428 overflow trim)</code></td><td class="c-test">(none)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">The mission/preamble char-budget overflow trim is not asserted by any test</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-001</code></td><td class="c-area">Tools</td><td class="c-feature">Read</td><td class="c-behavior">Read file slice (offset/limit); delegates binary detection; rejects directories (EISDIR), enforces readonly/</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (88 it: binary detector, readonly/ enforce, symlink escape)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">EISDIR/dir-reject path (MEMORY: prior crash) not explicitly named in describes — verify a dir-reject test exists</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-002</code></td><td class="c-area">Tools</td><td class="c-feature">Write</td><td class="c-behavior">Write to workspace; <code>assertWritable</code> blocks readonly/ + outside-workspace</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (readonly/ enforcement, resolveAndGuard symlink escape)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-003</code></td><td class="c-area">Tools</td><td class="c-feature">Edit</td><td class="c-behavior">Exact-string replace; same write-guard path as Write</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Edit-specific match-fail / replace_all behavior not clearly enumerated in describes</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-004</code></td><td class="c-area">Tools</td><td class="c-feature">Bash</td><td class="c-behavior">Shell exec; AbortSignal propagation, path-scope guard, install-pattern blocking, unrestricted mode, env scrub, history log</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (AbortSignal, checkBashPathScope, install rejection, bashUnrestricted, env scrub, history)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong coverage incl. cancel-traceability</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-005</code></td><td class="c-area">Tools</td><td class="c-feature">Glob</td><td class="c-behavior">Pattern file match</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts (core tools)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Few dedicated Glob assertions; edge cases (no-match, ignore) unclear</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-006</code></td><td class="c-area">Tools</td><td class="c-feature">Grep</td><td class="c-behavior">Content search</td><td class="c-source"><code></code></td><td class="c-test">core.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same — limited Grep-specific cases</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-007</code></td><td class="c-area">Tools</td><td class="c-feature">WebSearch</td><td class="c-behavior">SearXNG search w/ fallback + history; query sanitize</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (sanitizeQuery, searchViaSearxng, fallback history, parseSearchResultsFromText)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-008</code></td><td class="c-area">Tools</td><td class="c-feature">WebFetch</td><td class="c-behavior">Fetch URL → readability text; SSRF guard; persistent context mgmt</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (web tools, persistent context), shared/ssrf.test.ts, shared/readability.test.ts, web.binary.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">SSRF tested at shared layer; binary handling covered</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-009</code></td><td class="c-area">Tools</td><td class="c-feature">DownloadFile</td><td class="c-behavior">Download to input/ or source/; source-library index.jsonl; raw log-only</td><td class="c-source"><code></code></td><td class="c-test">web.test.ts (source-library routing 2 it), raw-save</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit / large-file / redirect handling on download not asserted</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-010</code></td><td class="c-area">Tools</td><td class="c-feature">ReadImage</td><td class="c-behavior">Read image → vision; usage tracking</td><td class="c-source"><code></code></td><td class="c-test">image.test.ts (13 it), image.vision-usage.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-011</code></td><td class="c-area">Tools</td><td class="c-feature">AnnotateImage</td><td class="c-behavior">Draw annotations on image</td><td class="c-source"><code></code></td><td class="c-test">image.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Annotation rendering correctness lightly covered</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-012</code></td><td class="c-area">Tools</td><td class="c-feature">ReadExcel</td><td class="c-behavior">Parse xlsx/xls/xlsm/xlsb; size limit 10MB; format-mismatch + CFB/HTML/CSV disguise rejection; optional styles</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (format rejection 8 it, styles), excel-styles.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">**Size-limit (&gt;10MB) enforcement path not tested** — only format mismatch</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-013</code></td><td class="c-area">Tools</td><td class="c-feature">ReadDocx</td><td class="c-behavior">Parse docx/docm; size limit 10MB</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit path untested; happy-path light</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-014</code></td><td class="c-area">Tools</td><td class="c-feature">ReadPdf</td><td class="c-behavior">Extract PDF text; query/grep mode (regex, case-insensitive, no-match, empty-query); size limit 10MB; OOXML-as-pdf rejection</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (ReadPdf query mode 5 it, format rejection)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Size-limit path untested</td></tr>
<tr data-cov="NONE" data-area="Tools"><td class="c-id"><code>TOOL-015</code></td><td class="c-area">Tools</td><td class="c-feature">ReadPPTX</td><td class="c-behavior">Parse pptx/pptm; slide order resolution; notes; **ZIP-bomb detection (uncompressed &gt;200MB)**; size limit 50MB</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**ZIP-bomb detection AND PPTX size-limit have NO test** — highest-risk office gap</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-016</code></td><td class="c-area">Tools</td><td class="c-feature">ReadMsg</td><td class="c-behavior">Parse Outlook .msg</td><td class="c-source"><code></code></td><td class="c-test">msg.test.ts (41 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-017</code></td><td class="c-area">Tools</td><td class="c-feature">SplitExcelSheets</td><td class="c-behavior">Split workbook into per-sheet files</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Split correctness lightly covered</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-018</code></td><td class="c-area">Tools</td><td class="c-feature">SplitDocxSections</td><td class="c-behavior">Split docx into section files</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-019</code></td><td class="c-area">Tools</td><td class="c-feature">PdfToImages</td><td class="c-behavior">Render PDF pages to images; edit-not-allowed error, missing-file error, invalid page_range error</td><td class="c-source"><code></code></td><td class="c-test">office.test.ts (3 error-path it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Error paths covered</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-020</code></td><td class="c-area">Tools</td><td class="c-feature">SQLite</td><td class="c-behavior">Run SQL on workspace .db; SELECT happy path, write guards (editAllowed), always-blocked DDL, PRAGMA, errors, path-traversal guard, large result sets</td><td class="c-source"><code></code></td><td class="c-test">data.test.ts (25 it across 9 describe)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong — guards + traversal + DDL block</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-021</code></td><td class="c-area">Tools</td><td class="c-feature">BatchReviewTextWithLLM</td><td class="c-behavior">Per-file isolated LLM review; output-path prefix enforcement</td><td class="c-source"><code></code></td><td class="c-test">review.test.ts (3 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only 3 it; LLM-failure / partial-file handling not covered</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-022</code></td><td class="c-area">Tools</td><td class="c-feature">MergeReviewedResults</td><td class="c-behavior">Merge reviewed JSON → markdown; rejects outputs outside prefixes</td><td class="c-source"><code></code></td><td class="c-test">review.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Merge edge cases (malformed JSON) untested</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-023</code></td><td class="c-area">Tools</td><td class="c-feature">BrowseWeb</td><td class="c-behavior">Playwright browse + actions mode; URL validation, SSRF/localhost block, file-URL workspace containment, traversal reject, auth-expiry, download filename sanitize, unique-path, recording, source persist</td><td class="c-source"><code></code></td><td class="c-test">browser.test.ts (36 it), browser.runpageactions.test.ts, browser-frame-chain.e2e.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Validation/SSRF/recording very strong; live page-action correctness is e2e-gated (env-dependent)</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-024</code></td><td class="c-area">Tools</td><td class="c-feature">SpawnSubTask</td><td class="c-behavior">Spawn parallel subtask job</td><td class="c-source"><code></code></td><td class="c-test">orchestration.test.ts (8 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-025</code></td><td class="c-area">Tools</td><td class="c-feature">XSearch</td><td class="c-behavior">X/Twitter search</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts (18 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Live transaction-id breakage (MEMORY) means runtime failures not covered by unit tests</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-026</code></td><td class="c-area">Tools</td><td class="c-feature">XUserPosts</td><td class="c-behavior">X user timeline posts</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same upstream breakage caveat</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-027</code></td><td class="c-area">Tools</td><td class="c-feature">XPostDetail</td><td class="c-behavior">Single X post detail</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-028</code></td><td class="c-area">Tools</td><td class="c-feature">XFetchCardMedia</td><td class="c-behavior">Fetch X card media</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Likely thin coverage</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-029</code></td><td class="c-area">Tools</td><td class="c-feature">XTimeline</td><td class="c-behavior">X home/timeline</td><td class="c-source"><code></code></td><td class="c-test">x.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Likely thin coverage</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-030</code></td><td class="c-area">Tools</td><td class="c-feature">SearchPlaces</td><td class="c-behavior">Maps place search</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts (27 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-031</code></td><td class="c-area">Tools</td><td class="c-feature">GetDirections</td><td class="c-behavior">Maps routing</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-032</code></td><td class="c-area">Tools</td><td class="c-feature">ReverseGeocode</td><td class="c-behavior">Coords → address</td><td class="c-source"><code></code></td><td class="c-test">maps.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-033</code></td><td class="c-area">Tools</td><td class="c-feature">GetYouTubeTranscript</td><td class="c-behavior">Fetch YouTube transcript</td><td class="c-source"><code></code></td><td class="c-test">youtube.test.ts (20 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-034</code></td><td class="c-area">Tools</td><td class="c-feature">SearchYouTube</td><td class="c-behavior">YouTube search</td><td class="c-source"><code></code></td><td class="c-test">youtube.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-035</code></td><td class="c-area">Tools</td><td class="c-feature">SearchAmazon</td><td class="c-behavior">Amazon product search</td><td class="c-source"><code></code></td><td class="c-test">amazon.test.ts (13 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-036</code></td><td class="c-area">Tools</td><td class="c-feature">TranscribeAudio</td><td class="c-behavior">Speech-to-text</td><td class="c-source"><code></code></td><td class="c-test">speech.test.ts (14 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-037</code></td><td class="c-area">Tools</td><td class="c-feature">CreateChecklist</td><td class="c-behavior">Create task checklist (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts (24 it incl. META_TOOL describe)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-038</code></td><td class="c-area">Tools</td><td class="c-feature">CheckItem</td><td class="c-behavior">Mark checklist item (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-039</code></td><td class="c-area">Tools</td><td class="c-feature">GetChecklist</td><td class="c-behavior">Read checklist (META)</td><td class="c-source"><code></code></td><td class="c-test">checklist.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-040</code></td><td class="c-area">Tools</td><td class="c-feature">ListPieces</td><td class="c-behavior">List available pieces</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts (11 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-041</code></td><td class="c-area">Tools</td><td class="c-feature">GetPiece</td><td class="c-behavior">Read a piece YAML</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-042</code></td><td class="c-area">Tools</td><td class="c-feature">CreatePiece</td><td class="c-behavior">Create custom piece (per-user fork)</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Validation/lint reject paths (COMPLETE/ABORT in next) coverage unclear here</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>TOOL-043</code></td><td class="c-area">Tools</td><td class="c-feature">UpdatePiece</td><td class="c-behavior">Update custom piece</td><td class="c-source"><code></code></td><td class="c-test">pieces.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Same</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-044</code></td><td class="c-area">Tools</td><td class="c-feature">InstallSkill</td><td class="c-behavior">Install skill from registry (META)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts (26 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-045</code></td><td class="c-area">Tools</td><td class="c-feature">ReadSkill</td><td class="c-behavior">Read skill content (META, always available)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-046</code></td><td class="c-area">Tools</td><td class="c-feature">ListSkills</td><td class="c-behavior">List skills (META)</td><td class="c-source"><code></code></td><td class="c-test">skills.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>TOOL-047</code></td><td class="c-area">Tools</td><td class="c-feature">ReadToolDoc</td><td class="c-behavior">Read tool doc incl. MCP tools via cache (META, always available); name-required error, bad-MCP-name error</td><td class="c-source"><code></code></td><td class="c-test">docs.test.ts (10 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Tools"><td class="c-id"><code>XCUT-001</code></td><td class="c-area">Tools</td><td class="c-feature">Dynamic tool loading + dispatch order</td><td class="c-behavior">index.ts <code>tryLoadModule</code> lazy-imports each module; <code>executeToolInner</code> dispatches in fixed order (mcp → web → image → data → office → review → x → orchestration → browser → maps → … → skills); failed loads silently skipped</td><td class="c-source"><code></code></td><td class="c-test">index.test.ts (1 it: SshConsole catalog)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**Dispatch order, first-match-wins, and silent-skip-on-load-failure are essentially untested** (single catalog test)</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-002</code></td><td class="c-area">Tools</td><td class="c-feature">META_TOOLS always-injected at dispatch</td><td class="c-behavior">index.ts keeps a LARGER META_TOOLS list (incl. RequestTool, Mission, UserFolder, AppDocs, Skills) injected into getToolDefs regardless of movement allowed_tools</td><td class="c-source"><code></code></td><td class="c-test">(indirect via tool-categories.test.ts subset)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">index.ts's runtime META injection (20 names) not directly asserted; only tool-categories' SUBSET set is tested</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-003</code></td><td class="c-area">Tools</td><td class="c-feature">tool-categories: name→category map</td><td class="c-behavior">first-write-wins map built from ALL_TOOL_DEFS('core') + MODULE_SPECS; memoised; listToolCategories; SENSITIVE_CATEGORIES={ssh,browser}; SENSITIVE_TOOLS={Bash}; META_TOOLS subset</td><td class="c-source"><code></code></td><td class="c-test">tool-categories.test.ts (12 it: META_TOOLS entries, sensitive sets, map)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-004</code></td><td class="c-area">Tools</td><td class="c-feature">RequestTool flow</td><td class="c-behavior">META 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.recordToolRequest</td><td class="c-source"><code></code></td><td class="c-test">tool-request.test.ts (9 it), db/repository.tool-requests.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">All 3 classification branches + interactive/auto-deny covered</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-005</code></td><td class="c-area">Tools</td><td class="c-feature">workspace-tool-policy resolution</td><td class="c-behavior">resolveWorkspaceTools: safe categories default-on (minus disabledSafe), sensitive categories+tools opt-in only (enabledSensitive), META always unioned, Bash special-cased, <code>mcp__*</code> always carried; parseToolPolicy tolerant JSON</td><td class="c-source"><code></code></td><td class="c-test">workspace-tool-policy.test.ts, workspace-tool-policy.regression.test.ts, db/repository.tool-policy.test.ts, bridge/space-api.tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong; regression test guards Bash toggle (MEMORY PR #653)</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-006</code></td><td class="c-area">Tools</td><td class="c-feature">raw-save result logging</td><td class="c-behavior">saveRawData writes {logsRoot}/raw/&lt;tool&gt;-&lt;ts&gt;.txt + rawdata-history.jsonl for RAW_SAVE_TOOLS (web/x/youtube/amazon/speech/ms-learn); logRawDownload records DownloadFile path-only; errors swallowed to logger.warn</td><td class="c-source"><code></code></td><td class="c-test">raw-save.test.ts (5 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Helper, not a tool. Per-tool wiring (that each RAW_SAVE_TOOL actually triggers save) is not asserted end-to-end</td></tr>
<tr data-cov="PARTIAL" data-area="Tools"><td class="c-id"><code>XCUT-007</code></td><td class="c-area">Tools</td><td class="c-feature">structured-blocks</td><td class="c-behavior">Rich-UI structured-data save helper (no TOOL_DEFS)</td><td class="c-source"><code></code></td><td class="c-test">structured-blocks.test.ts (4 it)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Helper; basic coverage</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-008</code></td><td class="c-area">Tools</td><td class="c-feature">binary-detect shared helper</td><td class="c-behavior">Magic-byte/binary detection used by Read + WebFetch</td><td class="c-source"><code></code></td><td class="c-test">binary-detect.test.ts (26 it)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Well covered; underpins Read/WebFetch binary handling</td></tr>
<tr data-cov="FULL" data-area="Tools"><td class="c-id"><code>XCUT-009</code></td><td class="c-area">Tools</td><td class="c-feature">conflict-guard (persistent workspace)</td><td class="c-behavior">Detects concurrent-edit conflicts in shared space</td><td class="c-source"><code></code></td><td class="c-test">conflict-guard.test.ts (9 it), core.test.ts (conflict detection)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-001</code></td><td class="c-area">Pieces</td><td class="c-feature">chat</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (as default-bias target); reflection/silent-fork &amp; applier.fuzz use 'chat'; piece-runner all-load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No test asserts the single-movement respond contract or its 43-tool allowlist; only used as a name fixture + load-sweep</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-002</code></td><td class="c-area">Pieces</td><td class="c-feature">general</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (<code>loadPiece('general')</code> line 401, used in review-prompt/shared_tools tests); pieces-api uses 'general' fixture; load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Loaded &amp; used as a fixture for unrelated assertions (review prompts, SpawnSubTask skip), but its 4-movement decompose/verify loop is not asserted as a contract</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-003</code></td><td class="c-area">Pieces</td><td class="c-feature">research</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test <code>loadPiece('research')</code> (line 403, review-prompt structure assertion); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Review-prompt/plan-aware structure asserted for it, but the dig/analyze/verify transition logic is not exercised</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-004</code></td><td class="c-area">Pieces</td><td class="c-feature">research-sub</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test; only schema-load smoke. Spawn-from-parent linkage untested</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-005</code></td><td class="c-area">Pieces</td><td class="c-feature">data-process</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (longer-match-wins: data-process over general); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Classifier disambiguation asserted; movement contract not</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-006</code></td><td class="c-area">Pieces</td><td class="c-feature">office-process</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test <code>loadPiece('office-process')</code> (line 402); piece-classifier fixture; load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Loaded for review-prompt assertion; 2-movement contract not exercised</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-007</code></td><td class="c-area">Pieces</td><td class="c-feature">slide</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (chat vs slide routing fixture); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Used as classifier fixture name; movement/tool contract untested</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-008</code></td><td class="c-area">Pieces</td><td class="c-feature">brainstorming</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test; schema-load smoke only</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-009</code></td><td class="c-area">Pieces</td><td class="c-feature">sns-research</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-010</code></td><td class="c-area">Pieces</td><td class="c-feature">x-ai-digest</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-011</code></td><td class="c-area">Pieces</td><td class="c-feature">piece-builder</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test (workspace-app routed AWAY from piece-builder); load sweep</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only the negative-routing case touches it; its own contract untested</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-012</code></td><td class="c-area">Pieces</td><td class="c-feature">workspace-app</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Movement structure + default_next + classifier routing all asserted; strongest-covered piece</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-013</code></td><td class="c-area">Pieces</td><td class="c-feature">ssh-console</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test dedicated assertion (interact default_next: COMPLETE, line 563-572); tools/ssh-console.test; ssh allowed_ssh_connections validation tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Movement + ssh-connection allowlist contract asserted</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PIECE-014</code></td><td class="c-area">Pieces</td><td class="c-feature">ssh-ops</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test dedicated assertion (line 576+); ssh allowlist validation tests; load sweep</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Loaded + structurally asserted (execute/verify); ssh allowlist format validation covered. Lighter than ssh-console but real assertions exist</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PIECE-015</code></td><td class="c-area">Pieces</td><td class="c-feature">help</td><td class="c-behavior"></td><td class="c-source"><code></code></td><td class="c-test">load sweep only</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No direct test</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-001</code></td><td class="c-area">Pieces</td><td class="c-feature">loadPiece (custom-dir priority + builtin fallback)</td><td class="c-behavior">Resolves piece by name across custom dirs (string|string[]), custom wins, fallback to builtin</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test &quot;loadPiece multi-dir&quot; (string|string[], per-user wins, first-dir wins, backward-compat); worker.test <code>loadPiece('local-piece')</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Multi-dir resolution well covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-002</code></td><td class="c-area">Pieces</td><td class="c-feature">validatePieceDef (Phase 6b terminal-next rejection)</td><td class="c-behavior">Throws if any <code>rules[].next</code> ∈ {COMPLETE,ABORT,ASK}; default_next exempt</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test &quot;loadPiece terminal-rule validation (Phase 6b)&quot;: rejects COMPLETE/ABORT/ASK in rules[].next, accepts default_next: COMPLETE, accepts movement-to-movement + WAIT_SUBTASKS</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Core invariant directly asserted with error-message matching</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-003</code></td><td class="c-area">Pieces</td><td class="c-feature">default_next sentinel (engine-internal fallback)</td><td class="c-behavior">Used for context-overflow / ASK-limit / SpawnSubTask-skip; allows COMPLETE/ABORT/ASK</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test &quot;falls back to default_next when ASK limit is reached&quot;, &quot;resolves a COMPLETE default_next reached via ASK-limit fallback&quot;, SpawnSubTask-skip→default_next tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">All three sentinel paths exercised</td></tr>
<tr data-cov="NONE" data-area="Pieces"><td class="c-id"><code>PINFRA-004</code></td><td class="c-area">Pieces</td><td class="c-feature">lint-pieces.mjs (CI lint, terminal-next ban)</td><td class="c-behavior">Standalone script: hard-fails any piece with COMPLETE/ABORT/ASK in rules[].next</td><td class="c-source"><code></code></td><td class="c-test">**none** (no *.test for the script; runtime equivalent in validatePieceDef IS tested)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">Script itself has no test; logic duplicated/covered by PINFRA-002 but the CLI (file collection, exit codes, parse-error path) is untested</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-005</code></td><td class="c-area">Pieces</td><td class="c-feature">CreatePiece tool validation</td><td class="c-behavior">Rejects missing/0 max_movements, non-array shared_tools, bad rules[].next; writes to dirs[0] (per-user)</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test &quot;CreatePiece&quot; block (missing max_movements, max_movements:0, valid write, shared_tools array/non-array)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Validation + write-target asserted</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-006</code></td><td class="c-area">Pieces</td><td class="c-feature">UpdatePiece tool (built-in protection)</td><td class="c-behavior">Refuses overwriting built-in (isBuiltinOnly); allows update when custom override exists</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test &quot;UpdatePiece&quot; block (refuses built-in overwrite regression, allows custom override)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Built-in immutability regression covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-007</code></td><td class="c-area">Pieces</td><td class="c-feature">rules[].next / default_next domain validation (tool path)</td><td class="c-behavior">validRuleNexts = movements WAIT_SUBTASKS; validDefaultNexts adds COMPLETE/ABORT/ASK</td><td class="c-source"><code></code></td><td class="c-test">Covered via CreatePiece tests + pieces-api &quot;rejects rules[].next: COMPLETE&quot; / &quot;accepts default_next: COMPLETE&quot;</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Both accept &amp; reject branches asserted at API + tool level</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-008</code></td><td class="c-area">Pieces</td><td class="c-feature">ListPieces / GetPiece (custom+builtin merge, priority)</td><td class="c-behavior">Lists from per-user + global-custom + builtin; GetPiece prefers dirs[0]</td><td class="c-source"><code></code></td><td class="c-test">tools/pieces.test &quot;customPiecesDir as array — P1-a regression&quot; (ListPieces both dirs, GetPiece second-dir, GetPiece prefers per-user)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Multi-dir merge + priority covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-009</code></td><td class="c-area">Pieces</td><td class="c-feature">piece classifier — buildClassificationPrompt</td><td class="c-behavior">Builds LLM prompt from task + all piece descriptions + keyword hints; biases to chat; routes workspace-app</td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test &quot;buildClassificationPrompt&quot; (includes text/descriptions, file names, chat bias, workspace-app routing)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Prompt construction + routing bias asserted</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-010</code></td><td class="c-area">Pieces</td><td class="c-feature">piece classifier — parseClassificationResponse</td><td class="c-behavior">Extracts valid piece name from noisy LLM output; longer-match-wins; null on invalid/empty</td><td class="c-source"><code></code></td><td class="c-test">piece-classifier.test &quot;parseClassificationResponse&quot; (valid, noisy, longer-match data-process&gt;general, invalid→null, empty→null)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Parse edge cases covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-011</code></td><td class="c-area">Pieces</td><td class="c-feature">piece catalog (loadAllPieceTriggers / catalog merge)</td><td class="c-behavior">Enumerates all pieces' triggers; custom overrides builtin by name</td><td class="c-source"><code></code></td><td class="c-test">piece-catalog.test (builtin+custom merge, custom override wins, triggers exposed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Catalog merge &amp; override asserted</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-012</code></td><td class="c-area">Pieces</td><td class="c-feature">pieces-api CRUD + per-user/builtin authz</td><td class="c-behavior">GET/PUT/POST/DELETE with per-user custom dirs, builtin immutability, cross-user authz, ?source=</td><td class="c-source"><code></code></td><td class="c-test">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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">One of the most thoroughly tested infra areas</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-013</code></td><td class="c-area">Pieces</td><td class="c-feature">SSH allowlist validation (allowed_ssh_connections)</td><td class="c-behavior">Validates UUID/wildcard/empty allowlist; rejects SshExec without allowlist &amp; bad formats</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (id-format reject, wildcard, multi-movement offenders) + pieces-api.test (SshExec w/o allowlist, UUID, wildcard, empty, bad format, non-array)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Both engine + API layers covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-014</code></td><td class="c-area">Pieces</td><td class="c-feature">shared_tools / required_mcp normalization</td><td class="c-behavior">Drops invalid entries, warns on SSH shared_tools w/o connections</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test imports normalizeRequiredMcp/normalizeSharedTools/mergeToolNames; tools/pieces + pieces-api shared_tools array/non-array tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Normalization + merge covered</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-015</code></td><td class="c-area">Pieces</td><td class="c-feature">custom/default piece namespace separation (reflection silent fork)</td><td class="c-behavior">Reflection forks built-in into data/users/{u}/pieces with forked_from_commit instead of mutating builtin</td><td class="c-source"><code></code></td><td class="c-test">reflection/silent-fork.test (silentFork chat/data-process into per-user dir)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Silent-fork path covered (reflection subsystem)</td></tr>
<tr data-cov="FULL" data-area="Pieces"><td class="c-id"><code>PINFRA-016</code></td><td class="c-area">Pieces</td><td class="c-feature">all-built-in-pieces load sweep</td><td class="c-behavior">Loops every pieces/*.yaml asserting loadPiece().not.toThrow()</td><td class="c-source"><code></code></td><td class="c-test">piece-runner.test (~line 531-537)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">This is what gives every piece PARTIAL; it validates schema but NOT each piece's movement/rule semantics</td></tr>
<tr data-cov="PARTIAL" data-area="Pieces"><td class="c-id"><code>PINFRA-017</code></td><td class="c-area">Pieces</td><td class="c-feature">per-user piece resolution in worker/scheduler/continue paths</td><td class="c-behavior">customPiecesDir threaded through worker, continue-job, classifier, pieces-api</td><td class="c-source"><code></code></td><td class="c-test">worker.test <code>loadPiece('local-piece', piecesDir, customPieceDirs)</code>; partly via pieces-api auth tests</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Worker path has one test; full propagation across scheduler/continue paths (per the known &quot;no-auth local fallback&quot; checklist) not comprehensively asserted</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-001</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: enabled gate (default OFF)</td><td class="c-behavior"><code>maybeEnqueueReflection</code> returns early when <code>cfg.enabled=false</code></td><td class="c-source"><code>src/worker.ts (363)</code></td><td class="c-test">worker.reflection-enqueue.test.ts (&quot;does nothing when enabled=false&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">DEFAULT_REFLECTION default value (enabled:false) itself not asserted in a config test</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-002</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: no-recursion on reflection jobs</td><td class="c-behavior">returns early when <code>job.taskKind==='reflection'</code></td><td class="c-source"><code>src/worker.ts (371)</code></td><td class="c-test">worker.reflection-enqueue.test.ts (&quot;does NOT recurse&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-003</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: no-auth owner fallback to 'local'</td><td class="c-behavior"><code>ownerId ?? 'local'</code> for namespace + budget key</td><td class="c-source"><code>src/worker.ts (377)</code></td><td class="c-test">enqueue.test.ts (2 cases: fallback + budget keyed to 'local')</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-004</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: worker_required gate</td><td class="c-behavior">skip when <code>workerRequired</code> and no worker has role 'reflection'</td><td class="c-source"><code>src/worker.ts (379-388)</code></td><td class="c-test">enqueue.test.ts (3 cases: true+none/true+exists/false)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-005</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: per-user daily token budget cap</td><td class="c-behavior">skip when spent &gt;= cap; cap=0 = unlimited; UTC-day window</td><td class="c-source"><code>src/worker.ts (392-405)</code></td><td class="c-test">enqueue.test.ts (at/under cap, cap=0, UTC 25h, independent users)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-006</code></td><td class="c-area">Reflection</td><td class="c-feature">Enqueue: space_id propagation (Space-as-Principal)</td><td class="c-behavior">carries job.spaceId into payload (null for personal)</td><td class="c-source"><code>src/worker.ts (407-420)</code></td><td class="c-test">enqueue.test.ts (carries space_id / null personal)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-007</code></td><td class="c-area">Reflection</td><td class="c-feature">Dispatch: reflection job routes to runReflectionJob</td><td class="c-behavior">worker picks task_kind='reflection', imports + runs runner, role-keyed routing</td><td class="c-source"><code>src/worker.ts (1129-1130, 2044-2073)</code></td><td class="c-test">worker.reflection-dispatch.test.ts (2 cases)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">dispatch test is small (~2 cases); gateway routing-key + 401-without-key path not asserted end-to-end</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-008</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: loadReflectionInputs (memory/activity/comments/feedback + observedRevisions)</td><td class="c-behavior">assembles inputs, builds bodyRevision CAS map</td><td class="c-source"><code>load-inputs.ts</code></td><td class="c-test">load-inputs.test.ts (~8 cases, CAS)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-009</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: forced submit_reflection tool_call (LLM)</td><td class="c-behavior">callReflectionLlm forces the tool, parses result, tokens</td><td class="c-source"><code>llm-client.ts</code></td><td class="c-test">llm-client.test.ts (~10 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-010</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: prompt construction</td><td class="c-behavior">buildSystemPrompt / buildUserPrompt</td><td class="c-source"><code>reflection-prompt.ts</code></td><td class="c-test">reflection-prompt.test.ts (~18 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-011</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: schema validation of LLM output</td><td class="c-behavior">reflection-schema parse of memory_changes/piece_changes</td><td class="c-source"><code>reflection-schema.ts</code></td><td class="c-test">reflection-schema.test.ts (~10 cases, all ops)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-012</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: end-to-end happy path + outcome + metric row</td><td class="c-behavior">runReflectionJob applies, snapshots, records metric</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (~12 cases: applied/abstained/failed, snapshot, lock, space, budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-013</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: failure fallbacks record outcome='failed'</td><td class="c-behavior">loadInputs/LLM/apply throw → metric 'failed', return 'failed'</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (failed)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only some of the 3 throw-sites (loadInputs/LLM/apply+snapshot) explicitly covered; writeSnapshot-throws-but-continue branch not clearly asserted</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-014</code></td><td class="c-area">Reflection</td><td class="c-feature">Runner: Space-as-Principal storage resolution</td><td class="c-behavior">space job → data/spaces/{id}; personal → data/users/{userId}; attribution stays per-user</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts (space-principal), revisions.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-015</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_unknown_type</td><td class="c-behavior">type not in {user,feedback,project,reference}</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.fuzz.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-016</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_bad_name</td><td class="c-behavior">name fails isValidMemoryName</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-017</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_body_too_large</td><td class="c-behavior">body &gt; maxBodyBytes (UTF-8)</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-018</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_injected_directive</td><td class="c-behavior">prompt-injection heuristics in body/description</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">only validator test hits it; not exercised through applier (low risk, static check)</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-019</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_missing_target</td><td class="c-behavior">non-add op missing merge_target OR target not in index</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-020</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_name_collision</td><td class="c-behavior">add with already-existing name</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-021</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_target_piece_mismatch</td><td class="c-behavior">piece_changes.target_piece ≠ input.pieceName</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-022</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_invalid_yaml</td><td class="c-behavior">piece new_yaml fails YAML parse</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-023</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_invalid_piece</td><td class="c-behavior">new_yaml parses but fails piece-lint</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-024</code></td><td class="c-area">Reflection</td><td class="c-feature">Validator: rejected_dangerous_piece</td><td class="c-behavior">COMPLETE/ABORT/ASK in rules[].next</td><td class="c-source"><code>semantic-validator.ts</code></td><td class="c-test">semantic-validator.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-025</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply CAS: rejected_stale_target</td><td class="c-behavior">merge_target body revision != snapshot → reject at write time</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts; fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">raised by applier (not static validator); covered</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-026</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: add</td><td class="c-behavior">upsertMemoryEntry for new name</td><td class="c-source"><code>applier.ts (applyOne)</code></td><td class="c-test">applier.test.ts; schema; runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-027</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: update (+CAS)</td><td class="c-behavior">upsert replacing body/desc/type, requires CAS pass</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (ops + CAS)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-028</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: merge_into (+CAS)</td><td class="c-behavior">read existing, append timestamped section, upsert merged</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-029</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply op: remove (+CAS)</td><td class="c-behavior">removeMemoryEntry (trash + index), requires CAS</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-030</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: missing-target-on-disk at write time</td><td class="c-behavior">non-add op, target gone from disk → rejected_missing_target</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (rejected_missing_target)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-031</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: 3-change hard cap</td><td class="c-behavior">memory_changes truncated to 3 with WARN</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts / fuzz</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">cap value 3 truncation likely covered by fuzz inputs but a dedicated &quot;&gt;3 dropped with WARN&quot; assertion not confirmed</td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-032</code></td><td class="c-area">Reflection</td><td class="c-feature">Apply: applyOne write error → code='failed' on decision</td><td class="c-behavior">per-change try/catch records failed</td><td class="c-source"><code>applier.ts</code></td><td class="c-test"></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the per-change applyOne throw path (decision.code='failed') not obviously asserted (distinct from runner-level 'failed')</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-033</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: abstained</td><td class="c-behavior">abstain_reason + no applied + no rejected</td><td class="c-source"><code>applier.ts (decideOutcome)</code></td><td class="c-test">applier.test.ts (abstained); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-034</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: partial</td><td class="c-behavior">any applied AND any rejected/dropped</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (partial)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-035</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: applied</td><td class="c-behavior">any applied, none rejected</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (applied); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-036</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: rejected</td><td class="c-behavior">any rejected/dropped, none applied</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts (rejected); fuzz</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-037</code></td><td class="c-area">Reflection</td><td class="c-feature">Outcome decision: fallback abstained</td><td class="c-behavior">empty input (0 changes, no piece)</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the empty-input fallback branch (vs explicit abstain_reason) not clearly distinguished in tests</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-038</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: cooldown gate (2 edits/24h)</td><td class="c-behavior">&gt;=2 edits in window → {written:false, reason:'cooldown'} → pieceCooldownDropped</td><td class="c-source"><code>piece-writer.ts; applier.ts</code></td><td class="c-test">piece-writer.test.ts (cooldown); applier.test.ts (cooldown)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-039</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: cooldown drop vs semantic reject distinction</td><td class="c-behavior">pieceCooldownDropped separate from pieceRejectCode in outcome</td><td class="c-source"><code>applier.ts</code></td><td class="c-test">applier.test.ts; piece-writer.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-040</code></td><td class="c-area">Reflection</td><td class="c-feature">Piece write: atomic temp+rename + catalog invalidate + recordPieceEdit</td><td class="c-behavior">writePiece tmp.{pid} → rename, repo.recordPieceEdit, catalog.invalidate</td><td class="c-source"><code>piece-writer.ts</code></td><td class="c-test">piece-writer.test.ts (~8 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">atomic-rename crash-safety (partial-write) not stress-tested</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-041</code></td><td class="c-area">Reflection</td><td class="c-feature">Silent fork of built-in piece</td><td class="c-behavior">builtin source → copy to data/users/{userId}/pieces with forked_from_commit/forked_at; idempotent; throws if source missing</td><td class="c-source"><code>silent-fork.ts</code></td><td class="c-test">silent-fork.test.ts (~9 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-042</code></td><td class="c-area">Reflection</td><td class="c-feature">Drift detection (forked_from_commit vs latest builtin commit)</td><td class="c-behavior">detectDrift returns drifted flag; graceful no-op without git</td><td class="c-source"><code>drift-detect.ts</code></td><td class="c-test">drift-detect.test.ts (~7 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-043</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot write (.reflection-history/{ts}-{jobId} + index.jsonl append)</td><td class="c-behavior">writeSnapshot persists before/after files, appends index.jsonl atomically under lock</td><td class="c-source"><code>snapshot.ts; reflection-runner.ts</code></td><td class="c-test">snapshot.test.ts (~34 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-044</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot: before/after memory file capture</td><td class="c-behavior">runner builds beforeFiles/afterFiles from accepted decisions (remove uses merge_target)</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts; snapshot.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the remove-op before-file naming branch (merge_target vs name) in runner not specifically asserted</td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-045</code></td><td class="c-area">Reflection</td><td class="c-feature">Snapshot: piece before/after YAML capture</td><td class="c-behavior">reads custom piece path pre/post for snapshot</td><td class="c-source"><code>reflection-runner.ts</code></td><td class="c-test">reflection-runner.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">piece YAML before/after capture lightly covered; existsSync(customPath) edge not asserted</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-046</code></td><td class="c-area">Reflection</td><td class="c-feature">bodyRevision (SHA-1 of parsed body) consistency</td><td class="c-behavior">same helper for loader observedRevisions and applier CAS</td><td class="c-source"><code>revisions.ts</code></td><td class="c-test">revisions.test.ts (~8 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-047</code></td><td class="c-area">Reflection</td><td class="c-feature">Per-user file lock (withUserLock)</td><td class="c-behavior">serializes apply+snapshot critical section (lockfile)</td><td class="c-source"><code>user-lock.ts; applier.ts; reflection-runner.ts</code></td><td class="c-test">user-lock.test.ts; applier.test.ts (lock); runner</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">concurrent contention/torn-write scenario only indirectly assured</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-048</code></td><td class="c-area">Reflection</td><td class="c-feature">Activity log summarizer</td><td class="c-behavior">summarizes activity.log to bounded size for prompt</td><td class="c-source"><code>activity-summarizer.ts</code></td><td class="c-test">activity-summarizer.test.ts (~5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-049</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: reflection_metrics row (outcome applied/partial/abstained/rejected/failed)</td><td class="c-behavior">recordReflectionMetric inserts one row per reflection job</td><td class="c-source"><code>src/db/repository.ts (~4093-4127)</code></td><td class="c-test">reflection-runner.test.ts; migrate.reflection-columns.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-050</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: reflection_piece_edits cooldown tracking</td><td class="c-behavior">recordPieceEdit insert + count-in-window query</td><td class="c-source"><code>src/db/repository.ts (~4068-4090)</code></td><td class="c-test">piece-writer.test.ts; migrate test</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-051</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: aggregateReflectionMetrics (budget)</td><td class="c-behavior">sums tokens since UTC-day start for budget gate</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">enqueue.test.ts (budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-052</code></td><td class="c-area">Reflection</td><td class="c-feature">DB: task_kind / payload columns + reflection job creation</td><td class="c-behavior">migrate adds task_kind, payload; reflection job created with taskKind='reflection'</td><td class="c-source"><code>src/db/repository.ts; migrate.ts</code></td><td class="c-test">migrate.reflection-columns.test.ts (~23 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-053</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /history (paged, owner-scoped, nextCursor)</td><td class="c-behavior">lists snapshot index entries, most-recent-first, cursor pagination</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts (~29 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-054</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /history/:snapshotId (404 no-leak for non-owner/missing)</td><td class="c-behavior">detail or 404 (no existence leak)</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-055</code></td><td class="c-area">Reflection</td><td class="c-feature">API: POST /history/:snapshotId/revert (idempotent)</td><td class="c-behavior">revertSnapshotForUser; {reverted:true} first, {reverted:false} after</td><td class="c-source"><code>bridge/reflection-api.ts; snapshot.ts</code></td><td class="c-test">reflection-api.test.ts (revert); snapshot.test.ts (revert)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-056</code></td><td class="c-area">Reflection</td><td class="c-feature">API: space-aware store resolution + canEditInSpace authz</td><td class="c-behavior">resolves per-user vs per-space store; space history needs edit rights; 404 on mismatch</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts (space-principal)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">canEditInSpace negative path (member-without-edit-rights → 404) not clearly asserted as a distinct multiuser case</td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-057</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /metrics</td><td class="c-behavior">aggregated reflection metrics endpoint</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">endpoint exists; depth of assertion (per-outcome breakdown) unverified</td></tr>
<tr data-cov="PARTIAL" data-area="Reflection"><td class="c-id"><code>REFL-058</code></td><td class="c-area">Reflection</td><td class="c-feature">API: GET /latest-for-task/:taskId (admin)</td><td class="c-behavior">latest snapshot for a task; null (not 404) when none; admin-gated</td><td class="c-source"><code>bridge/reflection-api.ts</code></td><td class="c-test">reflection-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">admin-gating + null-vs-404 distinction not confirmed asserted</td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-059</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: pruneOldSnapshots (age)</td><td class="c-behavior">delete snapshot dirs older than retentionDays; index.jsonl untouched</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts (~17 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-060</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: enforceDiskCap (oldest-first)</td><td class="c-behavior">prune oldest until under capBytes</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Reflection"><td class="c-id"><code>REFL-061</code></td><td class="c-area">Reflection</td><td class="c-feature">Retention: runReflectionRetentionSweep (per-user + per-space)</td><td class="c-behavior">sweeps both data/users and data/spaces trees</td><td class="c-source"><code>retention.ts</code></td><td class="c-test">retention.test.ts (space-principal)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-001</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks</td><td class="c-behavior">List tasks filtered by <code>buildVisibilityWhere</code> (owner/admin/org/public/space members)</td><td class="c-source"><code>local-tasks-api.ts:123</code></td><td class="c-test">local-tasks-api.test.ts (visibility list: owner/admin/same-org/diff-org bystander)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-002</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks</td><td class="c-behavior">Create task+job; visibility enum validation, org-scope check, spaceId forces private, owner from req.user (or <code>local</code> no-auth)</td><td class="c-source"><code>local-tasks-api.ts:134</code></td><td class="c-test">local-tasks-api.test.ts (visibility, no-auth owner, runtime_dir, attachments, spaceId), spaces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-003</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId</td><td class="c-behavior">Get one task; <code>canViewTask</code> visibility gate</td><td class="c-source"><code>local-tasks-api.ts:342</code></td><td class="c-test">space-view.test.ts (membership read gate), spaces.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-004</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/tool-requests</td><td class="c-behavior">List pending tool requests; canViewTask gate</td><td class="c-source"><code>local-tasks-api.ts:361</code></td><td class="c-test">local-tasks-api.test.ts:175 (lists pending)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">view-gate denial path not directly asserted on this route</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-005</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/tool-requests/:reqId/decide</td><td class="c-behavior">Approve/deny a parked tool request; write-gate (403 view-only / 404 non-member), re-queue parked job, 409 already-decided</td><td class="c-source"><code>local-tasks-api.ts:380</code></td><td class="c-test">local-tasks-api.test.ts:143 (approve/deny/409/invalid value/park-contract guard)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-006</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/feedback</td><td class="c-behavior">Submit rating/feedback; owner-or-admin (404 to others even if public)</td><td class="c-source"><code>local-tasks-api.ts:418</code></td><td class="c-test">local-tasks-api.test.ts:740 (owner/admin/non-owner 404), spaces.test.ts:268</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Bridge core"><td class="c-id"><code>APIC-007</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/mission</td><td class="c-behavior">Set/update task mission text</td><td class="c-source"><code>local-tasks-api.ts:442</code></td><td class="c-test">none found (only auth.test.ts matches the word &quot;mission&quot;)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">No test exercises this route — authz + validation unverified</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-008</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/comments</td><td class="c-behavior">List comments; canViewTask gate</td><td class="c-source"><code>local-tasks-api.ts:474</code></td><td class="c-test">covered indirectly via comment-write-gate.test.ts setup; no dedicated read-gate assertion</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">GET read-gate not directly asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-009</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/comments</td><td class="c-behavior">Post 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 reuse</td><td class="c-source"><code>local-tasks-api.ts:492</code></td><td class="c-test">comment-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-010</code></td><td class="c-area">Bridge core</td><td class="c-feature">PATCH /api/local/tasks/:taskId</td><td class="c-behavior">Update task (visibility change, etc.); owner/admin, org-scope validation</td><td class="c-source"><code>local-tasks-api.ts:623</code></td><td class="c-test">local-tasks-api.test.ts:451 (visibility change, invalid enum, org scope), spaces.test.ts:255 (non-member 404)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-011</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/regenerate-title</td><td class="c-behavior">Regenerate AI title; owner/admin only</td><td class="c-source"><code>local-tasks-api.ts:681</code></td><td class="c-test">spaces.test.ts:296 (owner regenerate, +line 318 case)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Only exercised in space context; non-owner-denied / no-generator paths not asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-012</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/evaluate-prompt</td><td class="c-behavior">Prompt-coach: evaluate an instruction via LLM</td><td class="c-source"><code>local-tasks-api.ts:717</code></td><td class="c-test">local-tasks-api.test.ts:1370 (no-generator, blank, happy paths)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-013</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/local/tasks/:taskId</td><td class="c-behavior">Delete task; owner-or-admin (404 to others even public)</td><td class="c-source"><code>local-tasks-api.ts:756</code></td><td class="c-test">local-tasks-api.test.ts:336 (owner/admin/non-owner-404), spaces.test.ts:69</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-014</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/cancel</td><td class="c-behavior">Cancel running job; write-gate (editor 200, viewer 403, non-member 404)</td><td class="c-source"><code>local-tasks-api.ts:778</code></td><td class="c-test">comment-write-gate.test.ts:117 (editor/viewer-403/non-member-404), spaces.test.ts:98</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-015</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/continue</td><td class="c-behavior">Spawn child job with new piece; 409 job_in_progress / no_previous_job, handoff comment, instruction persist</td><td class="c-source"><code>local-tasks-api.ts:818</code></td><td class="c-test">local-tasks-api.test.ts:1072 (happy/handoff/persist/409 in-progress/409 no-prev), spaces.test.ts:122 (space_id carry, non-member 404)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Bridge core"><td class="c-id"><code>APIC-016</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/stream</td><td class="c-behavior">SSE stream of job events (tool calls, deltas)</td><td class="c-source"><code>local-tasks-api.ts:914</code></td><td class="c-test">none (<code>/stream</code> matches only in app-share/share/space-api tests)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">SSE event mapping, visibility gate, reconnection all untested</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-017</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files</td><td class="c-behavior">List files in a section (input/output/logs/workspace); canViewTask gate; traversal 400</td><td class="c-source"><code>local-files-api.ts:41</code></td><td class="c-test">local-files-api.test.ts:74 (default section, subdir, unknown section 400, private-hidden, traversal 400, logs from runtime_dir + workspace_path fallback)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-018</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/content</td><td class="c-behavior">Serve file as text/plain; traversal 400, dir 400, missing 404, leading-slash strip</td><td class="c-source"><code>local-files-api.ts:86</code></td><td class="c-test">local-files-api.test.ts:152 (text, requires-path, 404 not 500, dir 400, traversal 400, abs-path strip)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-019</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/raw</td><td class="c-behavior">Serve raw bytes by extension; <code>trusted=1</code> drops CSP sandbox (owner-trust model), <code>setUntrustedFileResponseHeaders</code>, <code>ensurePathWithin</code> traversal guard (400)</td><td class="c-source"><code>local-files-api.ts:136</code></td><td class="c-test">local-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Strong coverage incl. CSP trust matrix</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-020</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:taskId/files/office-preview</td><td class="c-behavior">Excel→cells JSON / PPTX→slide PNGs; section/path validation 400, soffice missing → 503</td><td class="c-source"><code>local-files-api.ts:203</code></td><td class="c-test">office-preview.test.ts (file-type detect, spreadsheet preview incl. formula, soffice-missing throws) tests the *module*; the HTTP route itself not directly hit</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Module logic FULL; HTTP route (section enum 400, canViewTask gate, 503 mapping) not exercised end-to-end</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-021</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/local/tasks/:taskId/files/content</td><td class="c-behavior">Write/overwrite output file; owner/admin only, output-section-only, refuse while running, traversal 400, string-content validation</td><td class="c-source"><code>local-files-api.ts:240</code></td><td class="c-test">local-files-api.test.ts:290 (owner write, non-owner 404, admin, refuse-running, output-only, requires-string, traversal 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-022</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/upload</td><td class="c-behavior">Upload files to input/ (≤60mb); owner/admin, O_EXCL rename-on-collision, name sanitize, refuse-running, section/empty/traversal 400</td><td class="c-source"><code>local-files-api.ts:324</code></td><td class="c-test">local-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-023</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/delete</td><td class="c-behavior">Delete files; owner/admin, idempotent skip-missing, skip-dirs, refuse-running, validate-all-first (no partial), traversal 400</td><td class="c-source"><code>local-files-api.ts:392</code></td><td class="c-test">local-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-024</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/local/tasks/:taskId/files/download-zip</td><td class="c-behavior">Zip selected files; read-gated (non-owner of public OK), all sections, <code>safeZipEntryName</code> zip-slip protection, traversal 400, empty 400</td><td class="c-source"><code>local-files-api.ts:447</code></td><td class="c-test">local-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-025</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/pieces</td><td class="c-behavior">List pieces (builtin + global-custom + caller's user-custom, priority-merged, no hiding)</td><td class="c-source"><code>pieces-api.ts:251</code></td><td class="c-test">pieces-api.test.ts (no-auth list, auth-aware merge, same-name no-hide, source tagging)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-026</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/pieces/:name</td><td class="c-behavior">Get one piece; <code>?source=</code> selector, invalid source → 400, priority resolution</td><td class="c-source"><code>pieces-api.ts:325</code></td><td class="c-test">pieces-api.test.ts (404 unknown, ?source=builtin, priority resolution, invalid-source typo 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-027</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/pieces/:name</td><td class="c-behavior">Update; builtin by non-admin 403, admin can edit builtin, own user-custom 200, others' 403, lint (rules[].next COMPLETE reject, SshExec allowlist, shared_tools)</td><td class="c-source"><code>pieces-api.ts:370</code></td><td class="c-test">pieces-api.test.ts (builtin-non-admin 403, own 200, other-user 403, admin builtin, ?source typo 400, lint rules)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-028</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/pieces</td><td class="c-behavior">Create; dup-name reject, builtin-name reject, no-auth→piecesDir vs user-custom, 503 when userPiecesRootDir unset for authed non-admin/admin, returns actual source</td><td class="c-source"><code>pieces-api.ts:430</code></td><td class="c-test">pieces-api.test.ts (create, dup reject, builtin-name reject, 503 fallback hole, source return, no-auth local user-custom)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-029</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/pieces/:name</td><td class="c-behavior">Delete; builtin non-deletable 403 (even admin), own user-custom 200, ?source typo 400 no-destructive-fallback</td><td class="c-source"><code>pieces-api.ts:490</code></td><td class="c-test">pieces-api.test.ts (builtin 403, own 200, admin-cannot-delete-builtin, ?source=user-custom target, typo 400)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-030</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/config</td><td class="c-behavior">Return v2 config + ETag; omits legacy provider/flat-storage keys; admin-only mount</td><td class="c-source"><code>config-api.ts:87</code></td><td class="c-test">config-api.test.ts (v2 shape+etag, omits legacy, exposes storage.*)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Note: sensitive-masking observed in tests as worker api_key exclusion (APIC-033), not on this raw GET</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-031</code></td><td class="c-area">Bridge core</td><td class="c-feature">PUT /api/config</td><td class="c-behavior">Update with If-Match ETag optimistic lock → **409 on stale etag**; legacy-key reject 400; force config_version=2; YAML round-trip</td><td class="c-source"><code>config-api.ts:93</code></td><td class="c-test">config-api.test.ts (round-trip, force-version, legacy-key 400s ×6, storage round-trip, **409 stale etag**)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-032</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/config/reload</td><td class="c-behavior">Reload config from file (500 on parse error)</td><td class="c-source"><code>config-api.ts:117</code></td><td class="c-test">config-api.test.ts:208 (reload from file)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">500 error path not asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-033</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/workers</td><td class="c-behavior">List workers, allowlisted fields only (**no api_key leak**), synthesized default, proxy/proxyType exposed; 401 unauth</td><td class="c-source"><code>config-api.ts:126</code></td><td class="c-test">config-api.test.ts (default synth, allowlist fields, proxy no-key-leak, 401 unauth, 200 authed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">This is the de-facto sensitive-masking test</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-034</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/workers/:workerId/backends</td><td class="c-behavior">Fetch /v1/models from proxy worker; 404 unknown, direct→empty, 60s cache, scheme/URL validation (no apiKey leak to fetch), 502 upstream fail, 401 unauth</td><td class="c-source"><code>config-api.ts:149</code></td><td class="c-test">config-api.test.ts (404, direct empty, fetch list, cache, file://+data: reject, malformed reject, 502, 401)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-035</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/tools</td><td class="c-behavior">Runtime tool catalog (builtin+meta+MCP+ssh w/ availability flags); <code>?legacy=1</code> flat array; 401 unauth</td><td class="c-source"><code>tools-api.ts:242</code></td><td class="c-test">tools-api.test.ts (catalog, source/category tags, meta scope, ssh avail/unavail, MCP authed/offline/no-user, 401, legacy flat, placeholder, TestWorkspaceApp)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-036</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/scheduled-tasks</td><td class="c-behavior">List; visibility filter, <code>?spaceId=</code> scope</td><td class="c-source"><code>scheduled-tasks-api.ts:105</code></td><td class="c-test">scheduled-tasks-api.test.ts (list, visibility filter owner/non-owner/admin, ?spaceId scope)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-037</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/scheduled-tasks/:id</td><td class="c-behavior">Get one scheduled task</td><td class="c-source"><code>scheduled-tasks-api.ts:120</code></td><td class="c-test">scheduled-tasks-api.test.ts (covered via list/manage setup)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">No dedicated GET-by-id view-gate assertion</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-038</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/scheduled-tasks</td><td class="c-behavior">Create; visibility enum+org-scope validation, spaceId forces private + edit-rights fallback to NULL, no-auth synthetic <code>local</code> owner, requires body</td><td class="c-source"><code>scheduled-tasks-api.ts:133</code></td><td class="c-test">scheduled-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)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-039</code></td><td class="c-area">Bridge core</td><td class="c-feature">PATCH /api/scheduled-tasks/:id</td><td class="c-behavior">Update; <code>canManageSchedule</code> (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 private</td><td class="c-source"><code>scheduled-tasks-api.ts:267</code></td><td class="c-test">scheduled-tasks-api.test.ts (pause/resume, owner-or-admin 404 paths, admin any, space-editor manage, lost-edit-rights, keep-private)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Confused-deputy split (lifecycle vs content) is the known P3 design caveat</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-040</code></td><td class="c-area">Bridge core</td><td class="c-feature">DELETE /api/scheduled-tasks/:id</td><td class="c-behavior">Delete; owner/admin/space-editor (non-owner 404)</td><td class="c-source"><code>scheduled-tasks-api.ts:461</code></td><td class="c-test">scheduled-tasks-api.test.ts (owner, admin any, non-owner 404, space-editor survives-creator-leaving)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-041</code></td><td class="c-area">Bridge core</td><td class="c-feature">POST /api/scheduled-tasks/:id/trigger</td><td class="c-behavior">Manually trigger a run; canManageSchedule gate</td><td class="c-source"><code>scheduled-tasks-api.ts:480</code></td><td class="c-test">scheduled-tasks-api.test.ts:203 (creator who lost edit rights can no longer trigger)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">Happy-path trigger spawning a run not directly asserted; only the denial path</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-042</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/activities</td><td class="c-behavior">Aggregate subtask activity tree; canViewTask gate (404 hidden, public allowed)</td><td class="c-source"><code>subtask-activity-api.ts:28</code></td><td class="c-test">subtask-activity-api.test.ts (nested when waiting_subtasks, 404 not-found, 404 cannot-see, public allowed)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-043</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/activity</td><td class="c-behavior">Single subtask activity; 404 subtask-not-found</td><td class="c-source"><code>subtask-activity-api.ts:68</code></td><td class="c-test">subtask-activity-api.test.ts:211 (404 subtask not found)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">view-gate denial on this specific route not separately asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-044</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/files</td><td class="c-behavior">List subtask worktree files; canViewTask, <code>isJobWithinWorkspace</code> containment 404, 404 no-worktree</td><td class="c-source"><code>subtask-files-api.ts:12</code></td><td class="c-test">subtask-files-api.test.ts:67 (grouped listing, 400 bad id, 404 missing/private/no-worktree/outside-workspace)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge core"><td class="c-id"><code>APIC-045</code></td><td class="c-area">Bridge core</td><td class="c-feature">GET /api/local/tasks/:id/subtasks/:jobId/files/*</td><td class="c-behavior">Serve subtask file; <code>resolve</code>+<code>startsWith(base+sep)</code> traversal → 403, 404 missing, canViewTask, org-member allowed, sendFile</td><td class="c-source"><code>subtask-files-api.ts:55</code></td><td class="c-test">subtask-files-api.test.ts:121 (serve inside worktree, 404 missing, 404 cannot-see, org-member allowed)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">The 403 traversal branch (resolved≠base path-escape) is not directly asserted on this route</td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-046</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>isJobWithinWorkspace</code></td><td class="c-behavior">Separator-bounded containment (rejects /12 vs /123 prefix sibling)</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:8 (descendants, sibling-prefix reject, null/empty)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-047</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>ensurePathWithin</code> / <code>isPathEscapeError</code></td><td class="c-behavior">Path-traversal + **symlink-escape** hardening, sibling-prefix bypass guard</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:92,106 (inside ok, traversal reject, sibling-prefix, symlink-escape, in-root resolve)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-048</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>safeZipEntryName</code></td><td class="c-behavior">Neutralizes .., backslash, Windows drive-letter in zip entry names (zip-slip)</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:25 (plain path, never-absolute, drive-letter→C_)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-049</code></td><td class="c-area">Bridge core</td><td class="c-feature"><code>setUntrustedFileResponseHeaders</code></td><td class="c-behavior">nosniff + CSP sandbox on untrusted file responses</td><td class="c-source"><code>local-api-helpers.ts</code></td><td class="c-test">local-api-helpers.test.ts:78</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge core"><td class="c-id"><code>APIC-050</code></td><td class="c-area">Bridge core</td><td class="c-feature">office-preview module (<code>getSpreadsheetPreview</code>/<code>getPresentationPreview</code>/<code>SofficeUnavailableError</code>)</td><td class="c-behavior">Excel cells incl. formula results, PPTX via soffice, throws when soffice absent (→503)</td><td class="c-source"><code>office-preview.ts</code></td><td class="c-test">office-preview.test.ts (file detect, spreadsheet+formula, soffice-missing throws)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">Module-level only; see APIC-020 for the HTTP wiring gap</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-001</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">OAuth2 Google login (<code>GET /auth/google</code>, callback)</td><td class="c-behavior">Passport Google strategy, status gate (active→app, pending→/auth/pending)</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-002</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">OAuth2 Gitea login (<code>GET /auth/gitea</code>, callback)</td><td class="c-behavior">Passport OAuth2 Gitea; pending→admin auto-promote if adminEmails</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">callback strategy logic tested indirectly; live OAuth handshake not E2E</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-003</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Local auth login (<code>POST /auth/local</code>)</td><td class="c-behavior">username/password login w/ rate limiter</td><td class="c-source"><code>auth.ts, login-rate-limit.ts</code></td><td class="c-test">auth.local.test.ts, login-rate-limit.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-004</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Local signup (<code>POST /auth/local/signup</code>)</td><td class="c-behavior">self-register → pending status awaiting approval</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.local.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-005</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Login rate limiting</td><td class="c-behavior"><code>LoginRateLimiter</code> + per-IP throttle scope</td><td class="c-source"><code>login-rate-limit.ts, auth.ts</code></td><td class="c-test">login-rate-limit.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-006</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature"><code>requireAuth</code> middleware</td><td class="c-behavior">authenticated AND status==='active' else 401/redirect</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-007</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature"><code>requireAdmin</code> middleware</td><td class="c-behavior">role==='admin' else 401/403</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">exercised via admin routers; few direct unit cases</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-008</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature"><code>GET /auth/status</code> / <code>/login</code> / <code>/pending</code> / <code>/logout</code></td><td class="c-behavior">session state pages + logout</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">logout/pending HTML paths lightly covered</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-009</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Session store (persistent secret, survive restart)</td><td class="c-behavior">auto-gen session secret, short-secret warning</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.session-store.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-010</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Gitea orgs fetch (<code>/api/v1/user/orgs</code>)</td><td class="c-behavior">populates session.orgIds for 'org' visibility</td><td class="c-source"><code>auth.ts (`resolveOrgIds`)</code></td><td class="c-test">auth.test.ts, local-orgs.integration.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">fetch failure/network-error path not asserted</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-011</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Change password (<code>requireAuth</code> + current-password)</td><td class="c-behavior">requires current pw, 204 on success</td><td class="c-source"><code>auth.ts</code></td><td class="c-test">auth.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">wrong-current-pw rejection coverage thin</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-012</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Admin users CRUD (<code>GET/POST/PATCH/DELETE /api/admin/users</code>)</td><td class="c-behavior">**admin-only** (guard); create/approve/disable/set-password</td><td class="c-source"><code>admin-api.ts</code></td><td class="c-test">admin-api.test.ts, admin-api.local.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-013</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Admin orgs CRUD + members (<code>/api/admin/orgs*</code>)</td><td class="c-behavior">**admin-only**; org create/edit/delete, add/remove member</td><td class="c-source"><code>admin-api.ts</code></td><td class="c-test">admin-api.test.ts, local-orgs.integration.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-014</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">**No-auth admin passthrough**</td><td class="c-behavior">when authActive=false, admin guard → passthrough</td><td class="c-source"><code>admin-api.ts, branding-api.ts</code></td><td class="c-test">admin-api.local.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">branding adminGuard no-auth path not asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-015</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Task share create (<code>POST /api/local/tasks/:id/share</code>)</td><td class="c-behavior">**owner-or-admin only** (<code>checkTaskOwnership</code>) → token</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-016</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Task unshare (<code>DELETE .../share</code>)</td><td class="c-behavior">**owner-or-admin only**</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-017</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Public shared task read (<code>GET /api/shared/:token</code>)</td><td class="c-behavior">token-only (no auth); invalid/revoked token → 404</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-018</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Shared task comments/files/raw (<code>/api/shared/:token/...</code>)</td><td class="c-behavior">token-scoped read of files/comments/raw content</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">path-escape on <code>/files/content</code> lightly tested</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-019</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Shared subtask activities/files (<code>/api/shared/:token/subtasks/*</code>)</td><td class="c-behavior">token-scoped subtask containment (<code>isJobWithinWorkspace</code> prefix guard)</td><td class="c-source"><code>share-api.ts</code></td><td class="c-test">share-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">wildcard <code>/subtasks/:jobId/files/*</code> traversal not directly asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-020</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Public app-share resolve (<code>GET /api/app-share/:token</code>)</td><td class="c-behavior">token→(spaceId,appName); revoked/invalid→404</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-021</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">App-share file read (content/raw/list)</td><td class="c-behavior">**server-side path containment**: outside allowed route→403; escape→403</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong path-escape/forbidden coverage</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-022</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">App-share is GET-only</td><td class="c-behavior">no write/delete endpoints (read-only public share)</td><td class="c-source"><code>app-share-api.ts</code></td><td class="c-test">app-share-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-023</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space list (<code>GET /spaces/</code>)</td><td class="c-behavior">visibility-scoped list via repo viewer gate</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-024</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space get (<code>GET /spaces/:id</code>)</td><td class="c-behavior"><code>getSpace({viewer})</code> null→404 (private/org/public gate)</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-025</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space create (<code>POST /spaces/</code>)</td><td class="c-behavior">authenticated user creates space (owner)</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-026</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space update/archive (<code>PATCH /:id</code>, <code>POST /:id/archive</code>)</td><td class="c-behavior">**canManageSpace** (owner/admin/member-owner) else 403</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.test.ts, space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-027</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space file list/content/raw/office-preview (<code>GET /:id/files*</code>)</td><td class="c-behavior">read gated by <code>getSpace({viewer})</code> visibility; not-found→404</td><td class="c-source"><code>space-api.ts, office-preview.ts</code></td><td class="c-test">space-api.test.ts, office-preview.test.ts, local-files-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">non-member-on-public read path not asserted per-endpoint</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-028</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space file upload/delete (<code>POST /:id/files/upload</code>,<code>/delete</code>)</td><td class="c-behavior">**canEditInSpace** (owner/admin/editor); viewer→403</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-gates.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong write-gate authz coverage (20 authz assertions)</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-029</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space file mkdir/move/download-zip (<code>POST /:id/files/...</code>)</td><td class="c-behavior">**canEditInSpace**; protected top-dirs blocked</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-gates.test.ts, space-api.write-endpoint.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-030</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space file write (<code>POST /:id/files/write</code>)</td><td class="c-behavior">**canEditInSpace** write gate</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.write-endpoint.test.ts, space-api.write-gates.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-031</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space calendar read (<code>GET /:id/calendar</code>, <code>/calendar/day</code>)</td><td class="c-behavior">visibility-gated read</td><td class="c-source"><code>space-api.ts, cross-calendar-api.ts</code></td><td class="c-test">space-api.calendar.test.ts, cross-calendar-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-032</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space calendar event CRUD (<code>POST/PATCH/DELETE /:id/calendar/events*</code>)</td><td class="c-behavior">edit gate; multi-day mirror</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.calendar.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">non-editor write rejection coverage lighter than file gates</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-033</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space members list (<code>GET /:id/members</code>)</td><td class="c-behavior">members visible; **email masked** unless admin/owner/self-member</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">PII masking explicitly tested</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-034</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space member add/role/remove (<code>POST/PATCH/DELETE /:id/members*</code>)</td><td class="c-behavior">**canManageSpace**; owner-add blocked(400); self-removal allowed; non-member→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.members.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">27 authz assertions</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-035</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space invite issue/get/revoke (<code>GET/POST/DELETE /:id/invite</code>)</td><td class="c-behavior">**canManageSpace**; no-auth→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.invite.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-036</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Invite preview + accept (<code>GET /invite/:token</code>, <code>POST /invite/:token/accept</code>)</td><td class="c-behavior">authed user joins via valid token; invalid/expired→404; no-auth→404</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.invite.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">maxUses/expiry validity via <code>isSpaceInviteValid</code></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-037</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">App share per-space (<code>GET/POST/DELETE /:id/apps/:app/share</code>)</td><td class="c-behavior">manage-gated issue/revoke of public app link</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.app-share.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">6 authz assertions; non-manager revoke path thin</td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-038</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Space tool-policy (<code>GET /:id/tool-policy</code>, <code>PUT</code>)</td><td class="c-behavior">GET visibility-gated; **PUT canManageSpace**; sensitive (Bash/SSH/browser/MCP) opt-in</td><td class="c-source"><code>space-api.ts</code></td><td class="c-test">space-api.tool-policy.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only 9 authz assertions; Bash/sensitive write-path round-trip (PR #653 regression area) under-covered</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-039</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser captcha-pool (<code>GET/DELETE /captcha-pool</code>)</td><td class="c-behavior">captcha session pool read/clear</td><td class="c-source"><code>browser-api.ts</code></td><td class="c-test">browser-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-040</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser task-session (<code>GET/POST /task-session/:taskId*</code>)</td><td class="c-behavior">per-task browser session acquire/release</td><td class="c-source"><code>browser-api.ts</code></td><td class="c-test">browser-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-041</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser profile list (<code>GET /</code> )</td><td class="c-behavior">space-aware list; **decryptableByViewer** flag (DEK owner-bound)</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">non-owner sees but cannot decrypt — asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-042</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser profile create (<code>POST</code>)</td><td class="c-behavior">space ctx → **canEditInSpace**; personal → owner-scoped; spaceId ignored when no spaceAccess</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-043</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser profile delete (<code>DELETE /:id</code>)</td><td class="c-behavior">space editor/owner OR personal owner-only</td><td class="c-source"><code>browser-session-api.ts</code></td><td class="c-test">browser-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-044</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Browser interactive login session (<code>POST /profiles/:id/login</code>)</td><td class="c-behavior">spawn noVNC session for owner; DEK owner-bound</td><td class="c-source"><code>browser-session-api.ts, novnc-proxy.ts</code></td><td class="c-test">browser-session-api.test.ts, novnc-proxy.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">noVNC proxy authz/upgrade path lightly covered</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-045</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Console WS attach (<code>attachConsoleWs</code> upgrade)</td><td class="c-behavior"><code>decideAccess</code>: visibility gate + canWrite (owner OR admin)</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-ws-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">decideAccess unit-tested</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-046</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Console status router (<code>createConsoleStatusRouter</code>)</td><td class="c-behavior">requireAuth; status poll for owner/admin</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-ws-api.test.ts, console-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-047</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Console session router (<code>createConsoleSessionRouter</code>)</td><td class="c-behavior">requireAuth task-scoped session resolution</td><td class="c-source"><code>console-ws-api.ts</code></td><td class="c-test">console-session-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-048</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Console admin (<code>GET /ssh/console-sessions</code>, <code>POST .../:taskId/kill</code>)</td><td class="c-behavior">**requireAdmin** list/kill console sessions</td><td class="c-source"><code>console-admin-api.ts</code></td><td class="c-test">console-admin-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-049</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Gateway admin CRUD (<code>POST/PATCH/DELETE /:id</code>, rotate/revoke keys)</td><td class="c-behavior">**requireAdmin** backend + API-key mgmt</td><td class="c-source"><code>admin-gateway-api.ts</code></td><td class="c-test">admin-gateway-api.test.ts, .budget-rate.test.ts, .metric-labels.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">18 authz assertions; budget/rate/labels covered</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-050</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Gateway usage (<code>GET /:id/usage</code>)</td><td class="c-behavior">**requireAdmin** per-backend usage</td><td class="c-source"><code>admin-gateway-api.ts</code></td><td class="c-test">admin-gateway-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-051</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Gateway status (<code>GET /</code> status)</td><td class="c-behavior">non-admin status snapshot</td><td class="c-source"><code>admin-gateway-status-api.ts</code></td><td class="c-test">admin-gateway-status-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-052</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Gateway mount/proxy (<code>mountGateway</code>, classifyGatewayPath)</td><td class="c-behavior">LLM gateway request routing/classification middleware</td><td class="c-source"><code>gateway-mount.ts</code></td><td class="c-test">gateway-mount.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">path classification + config-equiv heavily tested; auth on proxied LLM calls (Bearer/key) not asserted in this file</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-053</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Notifications VAPID key (<code>GET /api/notifications/vapid-public-key</code>)</td><td class="c-behavior">requireAuth</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-054</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Notifications subscriptions (<code>GET/POST/DELETE /subscriptions</code>)</td><td class="c-behavior">requireAuth; web-push subscribe/unsubscribe</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-055</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Notifications preferences (<code>GET/PUT /preferences</code>)</td><td class="c-behavior">requireAuth pref read/write</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-056</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Notifications test/send (<code>POST</code> send endpoints)</td><td class="c-behavior">requireAuth push trigger</td><td class="c-source"><code>notifications-api.ts</code></td><td class="c-test">notifications-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">send/test push delivery path lightly asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-057</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Branding read (<code>GET /api/branding</code>)</td><td class="c-behavior">public branding config read</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-058</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Branding static serve (<code>/branding/*</code>)</td><td class="c-behavior">static asset serving</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">static traversal not asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-059</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Branding upload/delete (<code>POST/DELETE /api/branding/upload</code>)</td><td class="c-behavior">**adminGuard** (passthrough in no-auth)</td><td class="c-source"><code>branding-api.ts</code></td><td class="c-test">branding-api.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-060</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Security headers / HSTS opt-in</td><td class="c-behavior">HSTS default off (<code>max-age=0</code> clear), opt-in enable; 180d max-age</td><td class="c-source"><code>security-headers.ts</code></td><td class="c-test">security-headers.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">15 expects (no authz; correct)</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-061</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Server TLS listener</td><td class="c-behavior">HTTPS listener creation; cert handling</td><td class="c-source"><code>server-tls-listener.ts</code></td><td class="c-test">server-tls-listener.test.ts, server.tls.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-062</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Dashboard API (<code>createDashboardApi</code>)</td><td class="c-behavior">worker/node (GPU) status tabs</td><td class="c-source"><code>dashboard-api.ts</code></td><td class="c-test">dashboard-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">no explicit auth guard visible in router; visibility of dashboard data not gated/asserted</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-063</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Dashboard workers</td><td class="c-behavior">worker status aggregation</td><td class="c-source"><code>dashboard-workers.ts</code></td><td class="c-test">dashboard-workers.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-064</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature"><code>buildVisibilityWhere</code> / <code>buildSpaceVisibilityWhere</code></td><td class="c-behavior">SQL WHERE for private/org/public</td><td class="c-source"><code>visibility.ts</code></td><td class="c-test">visibility.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">53 expects</td></tr>
<tr data-cov="FULL" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-065</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature"><code>canManageSpace</code> / <code>canEditInSpace</code> / <code>canEditEntity</code> / <code>canUserSeeTask</code></td><td class="c-behavior">role/owner/admin authz predicates</td><td class="c-source"><code>visibility.ts</code></td><td class="c-test">visibility.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">central predicates well unit-tested</td></tr>
<tr data-cov="NONE" data-area="Bridge auth &amp; spaces"><td class="c-id"><code>APIS-066</code></td><td class="c-area">Bridge auth &amp; spaces</td><td class="c-feature">Cross-space credential transfer (G3/#006)</td><td class="c-behavior">copy MCP/SSH creds between spaces, per-user token non-copy</td><td class="c-source"><code>(PR #642 — NOT in main)</code></td><td class="c-test">(n/a in main)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">**Not merged to main** — no endpoint present; out of scope for current tree</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-001</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Worker poll/claim loop</td><td class="c-behavior"><code>processNext</code>/<code>acquireJobOrRequeue</code> claim a queued job or requeue when issue lock held</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (&quot;requeues a claimed job when the issue lock is already held&quot;)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">poll cadence, <code>pokePoll</code>, empty-queue path not directly asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-002</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Role / task_class matching</td><td class="c-behavior"><code>canClaimRole</code>/<code>supportsRole</code>/<code>getSupportedRoles</code> gate which jobs a worker claims</td><td class="c-source"><code>src/worker.ts, src/db/repository.ts (claimNextJob)</code></td><td class="c-test">repository.test.ts (&quot;claims only jobs matching a healthy worker role set&quot;; &quot;does not let unhealthy workers claim queued jobs&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">role-set matching + healthy filter covered at repo layer</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-003</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Title-only / reflection role filters</td><td class="c-behavior"><code>roles:['title']</code> skips agent jobs; <code>roles:['reflection']</code> runs reflection handler</td><td class="c-source"><code>src/worker.ts (handleReflectionJob, supportsRole)</code></td><td class="c-test">worker.reflection-dispatch.test.ts; worker.reflection-enqueue.test.ts (16)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">reflection enqueue + dispatch well covered; title-only worker skip path has no direct test</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-004</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Job lifecycle transitions</td><td class="c-behavior">queued→dispatching→running→succeeded/failed/waiting_human/waiting_subtasks via updateJob</td><td class="c-source"><code>src/worker.ts, src/db/repository.ts</code></td><td class="c-test">worker.test.ts; repository.test.ts; spaces-task-ops (cancel flips running→cancelled)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">terminal mapping covered (metrics test); waiting_human/waiting_subtasks transitions only indirectly exercised</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-005</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Retry / maxAttempts policy</td><td class="c-behavior">terminal vs transient classification; one recovery retry for max_iterations; give up on repeat</td><td class="c-source"><code>src/worker.ts (executeJob retry logic)</code></td><td class="c-test">worker.test.ts (5 retry cases incl. self-abort, max_iterations 1st/2nd hit, llm_error budget)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong branch coverage of terminal vs transient + attempt budget</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-006</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Retry handoff summary</td><td class="c-behavior"><code>buildRetryHandoffSummary</code>/<code>writeRetryHandoffSummary</code> carry lessons+diagnostics into retry</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (&quot;writes retry handoff summary&quot;; &quot;builds retry handoff summary from diagnostics and lessons&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-007</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Deadline / hard-abort guard</td><td class="c-behavior"><code>shouldDeadlineAbort</code> aborts past deadline; job-guard timer force-aborts</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.job-guard.test.ts (8: before/after deadline, disabled=0, no re-abort, cancel mid-run, hard deadline)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-008</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Cancellation check</td><td class="c-behavior"><code>cancelCheck</code> returns true when DB job status is cancelled</td><td class="c-source"><code>src/worker.ts, repository.requestJobCancel</code></td><td class="c-test">worker.test.ts (&quot;cancelCheck returns true when cancelled&quot;); repository.test.ts (requestJobCancel 3 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">running→cancelled, queued no-op, unknown id all covered</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-009</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">max_concurrency slot scheduling</td><td class="c-behavior">runs up to N concurrent, refills freed slot, single-flight when unset</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.concurrency.test.ts (5: concurrent, single-flight, waitForCompletion, decrement-on-throw)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-010</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Initialize / health probe</td><td class="c-behavior"><code>initialize</code> probes /api/tags + /v1/models, forwards Bearer, stays healthy on llama-server compat</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (3 initialize cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-011</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Unhealthy on LLM conn error</td><td class="c-behavior">requeues jobs + marks worker unhealthy on connection errors</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (&quot;requeues jobs and marks the worker unhealthy on LLM connection errors&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-012</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">resolveToolSpaceId (per-space MCP/SSH)</td><td class="c-behavior">NULL space + owner → personal space; keeps explicit; fails CLOSED on resolver error</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (5 resolveToolSpaceId cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">fail-closed sentinel asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-013</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Piece resolution for null-owner job</td><td class="c-behavior">no-auth job loads piece from data/users/local/pieces consistently</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.test.ts (2 null-ownerId piece cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-014</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">prepareJobWorkspace resolution</td><td class="c-behavior">uses stored workspacePath or falls back to legacy local/{taskId} + backfills</td><td class="c-source"><code>src/worker.ts, src/spaces/workspace-resolver.ts</code></td><td class="c-test">worker.test.ts (2 cases); workspace-resolver.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">persistent/ephemeral/personal + backfill covered</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-015</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">maybeEnqueueReflection budget gate</td><td class="c-behavior">enqueue/skip by enabled flag, daily UTC budget cap, worker-required, per-user keying</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.reflection-enqueue.test.ts (16)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">strong: cap=0, 25h window, no-recursion, independent budgets</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-016</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">resolveModel / role→model mapping</td><td class="c-behavior">selects backend model for the job role</td><td class="c-source"><code>src/worker.ts (resolveModel)</code></td><td class="c-test">worker/sticky-backend.test.ts (resolver follow-current); scheduling.test.ts (normalizeJobRole/parseUiRole)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">role parsing well covered; resolveModel's own fallback branches not directly tested</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-017</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Sticky backend resolver</td><td class="c-behavior">persists first backend, advances on CHANGE, retries on persist failure</td><td class="c-source"><code>src/worker.ts (createStickyBackendResolver)</code></td><td class="c-test">worker/sticky-backend.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-018</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Idle-routing yield</td><td class="c-behavior"><code>pickIdlerIndex</code> yields to strictly-idler serving sibling, ignores unhealthy/non-serving</td><td class="c-source"><code>src/worker.ts (findIdlerCompetitor)</code></td><td class="c-test">worker/idle-routing.test.ts (9)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Services &amp; DB"><td class="c-id"><code>SVC-019</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">answerSubtaskAsk / subtask resume</td><td class="c-behavior">answers a subtask ASK; parent requeue when all subtasks done</td><td class="c-source"><code>src/worker.ts, repository.requeueParentJobIfAllSubtasksDone</code></td><td class="c-test">(none direct in scope)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">repo helper <code>requeueParentJobIfAllSubtasksDone</code> + worker <code>answerSubtaskAsk</code> untested</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-020</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Worker shutdown hooks</td><td class="c-behavior">installs + drains shutdown hooks in order, tolerates throwing hook</td><td class="c-source"><code>src/worker.ts / worker-bootstrap</code></td><td class="c-test">worker-bootstrap.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-021</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Worker metrics</td><td class="c-behavior">counters increment on labelled inc; terminal-status mapping complete</td><td class="c-source"><code>src/worker.ts</code></td><td class="c-test">worker.metrics.test.ts (3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-022</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">WorkerManager differential rebuild</td><td class="c-behavior">rebuild on config change: keep busy/unchanged worker, retire changed/removed without requeue, prune retired</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">worker-manager.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">live-job protection + def-signature diff covered</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-023</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">WorkerManager shutdown requeue</td><td class="c-behavior">requeues running jobs only on shutdown when worker fails to drain; not on clean shutdown</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">worker-manager.test.ts (2 shutdown cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Services &amp; DB"><td class="c-id"><code>SVC-024</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Worker dep injection setters</td><td class="c-behavior">setMcpDeps/setWorkerMetrics/setSkillCatalog/setPushService wire shared deps</td><td class="c-source"><code>src/worker-manager.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">low-risk wiring; uncovered</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-025</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Scheduler cron conversion</td><td class="c-behavior"><code>convertToCron</code> maps daily/weekly/monthly→cron; passes cron through; once special-cased</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (convertToCron 5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-026</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">calcNextRun / toSqliteDatetime</td><td class="c-behavior">next-run from cron; SQLite-compatible datetime; once→null</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (calcNextRun 3 + toSqliteDatetime 2)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-027</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Scheduler skip-on-in-progress</td><td class="c-behavior">skips when prior job running/waiting_subtasks; starts new on waiting_human</td><td class="c-source"><code>src/scheduler.ts (IN_PROGRESS_STATUSES)</code></td><td class="c-test">scheduler.test.ts (4 skip cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">waiting_human-no-longer-blocks asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-028</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Schedule ownership inheritance</td><td class="c-behavior">propagates ownerId/visibility/scopeOrg + space_id to spawned local_task; NULL owner for system</td><td class="c-source"><code>src/scheduler.ts (executeScheduledTask)</code></td><td class="c-test">scheduler.test.ts (multiple ownership + space-bound vs personal cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-029</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Scheduled pieceName / classifier</td><td class="c-behavior">falls back to classifier when pieceName unset; preserves explicit pieceName</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">scheduler.test.ts (pieceName cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>SVC-030</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Scheduled script (task_kind=script)</td><td class="c-behavior">runs browser-macro/script, writes logs to runtimeDir + output to space tree; failure paths; gate+allowlist</td><td class="c-source"><code>src/scheduler.ts (executeScriptScheduledTask)</code></td><td class="c-test">scheduler.test.ts (script cases incl. gate disabled, not-in-allowlist, audit row, missing script, null scriptName guard)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Services &amp; DB"><td class="c-id"><code>SVC-031</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">executeById manual run</td><td class="c-behavior">runs a scheduled task on demand</td><td class="c-source"><code>src/scheduler.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">manual-trigger path untested</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-001</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">transformKeys snake↔camel</td><td class="c-behavior">toCamel/toSnake/toSnakeKeys round-trip incl. nested/arrays</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (toSnakeKeys 5 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-002</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">loadConfig provider.retry</td><td class="c-behavior">loads from YAML or default; deprecated profiles→roles shim; proxy/proxyType defaults</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (provider.retry + roles/proxy cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-003</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">validateConfig</td><td class="c-behavior">rejects bad concurrency/maxMovements/ask/maxStreamMinutes/maxJobMinutes/maxDepth/retry/worker fields/proxy type</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.test.ts (~35 validateConfig cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">exhaustive boundary coverage</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-004</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Env overrides (config.ts)</td><td class="c-behavior">OLLAMA_BASE_URL/OLLAMA_MODEL/CONCURRENCY/WORKTREE_DIR/AAO_* override loaded config</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config.audit-regression.test.ts (OLLAMA_BASE_URL targets executed worker only, leaves persisted llm + extra workers)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only OLLAMA_BASE_URL asserted; CONCURRENCY/WORKTREE_DIR/OLLAMA_MODEL/AAO_* overrides untested</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-005</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">normalizeConfig version handling</td><td class="c-behavior">v2 pass-through; missing→v1; unsupported version throws; null input→empty</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (version handling, ~6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-006</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">normalizeConfig v1→v2 llm</td><td class="c-behavior">proxy→connection_type, worker.model inheritance, single default worker, profiles→roles, retry/metrics map</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (v1→v2 block, ~10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-007</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">config-normalizer storage mirror</td><td class="c-behavior">top-level flat keys ↔ storage.* both directions; storage.* precedence (#369); worktreeDir survival</td><td class="c-source"><code>src/config-normalize.ts, src/config.ts</code></td><td class="c-test">config-normalize.test.ts (storage migration + backwards-compat, ~8); config.audit-regression.test.ts (trash_retention_days takes effect)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">precedence trap (default-merge-before-normalize) regression-guarded</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-008</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Env-ref preservation</td><td class="c-behavior">${VAR} / env: prefix preserved verbatim through normalize</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (3 env-ref cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-009</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">browser.display_mode migration</td><td class="c-behavior">legacy captchaSolve=novnc → displayMode=novnc, key removed</td><td class="c-source"><code>src/config-normalize.ts (migrateBrowserDisplayMode)</code></td><td class="c-test">config-normalize.test.ts (display_mode case)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-010</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">normalize fixtures (real configs)</td><td class="c-behavior">v1-single-ollama / multi-worker-proxy / gateway-with-keys / mcp-and-ssh normalize correctly</td><td class="c-source"><code>src/config-normalize.ts</code></td><td class="c-test">config-normalize.test.ts (4 fixture cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-011</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Auth config loading</td><td class="c-behavior">parses auth section snake_case + primary_provider; undefined when absent</td><td class="c-source"><code>src/config.ts</code></td><td class="c-test">config-auth.test.ts (4)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">parsing covered; provider validation/exclusivity not asserted here</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-012</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ConfigManager read + ETag</td><td class="c-behavior">getConfig/getConfigForApi; etag from mtime; reloadFromFile</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts (load, etag, reload, masked-for-API)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-013</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ConfigManager write + CAS</td><td class="c-behavior">updateConfig writes YAML, rejects stale etag, creates file on fresh install, rejects unparseable</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts (update, stale-etag reject, fresh-install create, invalid YAML reject)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-014</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ConfigManager masking preservation</td><td class="c-behavior">masks apiKey (<code>********</code>); preserves masked field on update; matches workers/backends by id; drops mask when no prior key</td><td class="c-source"><code>src/config-manager.ts (mergeWithMaskPreservation)</code></td><td class="c-test">config-manager.test.ts (mask + preserve by-id for llm.workers + gateway.backends, drop-mask path)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-015</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ConfigManager change events</td><td class="c-behavior">emits config-changed on update</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">config-manager.test.ts (&quot;emits config-changed on update&quot;)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Services &amp; DB"><td class="c-id"><code>CFG-016</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">config-manager env overrides</td><td class="c-behavior">OLLAMA_*/WORKTREE_DIR/CONCURRENCY/DB_PATH applied at runtime read</td><td class="c-source"><code>src/config-manager.ts</code></td><td class="c-test">(none direct in config-manager.test.ts)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">DB_PATH + runtime env override path untested at config-manager layer</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-017</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Server TLS / listen-port config</td><td class="c-behavior">mergeServerConfig TLS defaults, http_redirect port normalize/collision, resolveListenPort + PORT env</td><td class="c-source"><code>src/server/config.ts</code></td><td class="c-test">server/config.test.ts (24)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">(PORT/LOG_LEVEL env live here, not config.ts)</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>CFG-018</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">migrate-config CLI</td><td class="c-behavior">--help/unknown flag/missing file/dry-run/in-place+backup/already-v2/version-99</td><td class="c-source"><code>src/scripts/migrate-config.ts</code></td><td class="c-test">scripts/migrate-config.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-001</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">createJob / getJob round-trip</td><td class="c-behavior">persists job incl. space_id, runtime_dir, continued_from; subtask inherits parent space_id</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-resolution.test.ts; runtime-dir.test.ts; migrate.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-002</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">claimNextJob / retry / peek</td><td class="c-behavior">role+health-aware claim; claimNextRetryJob; peekNextClaimable</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (worker-aware scheduling 2)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">claimNextRetryJob + peekNextClaimable not individually asserted</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-003</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">requeueRunningJobs / recovery</td><td class="c-behavior">requeueRunningJobs, recoverOrphanedJobs, recoverStuckRunningJobs, resumeMcpWaitingJobs</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(worker-manager shutdown exercises requeue indirectly)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">recoverOrphaned/recoverStuck/resumeMcp* have no direct unit test</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-004</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">requestJobCancel</td><td class="c-behavior">running→cancelled; queued no-op; unknown id false</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (3)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-005</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">createLocalTask / getLocalTask</td><td class="c-behavior">persists owner/visibility/space_id/runtime_dir; reads back</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts; repository-auth.test.ts; spaces-resolution.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-006</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">listLocalTasks + latestJob/subtasks</td><td class="c-behavior">joins latest job, subtask info, ownerName/org name; ownerId filter</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (listLocalTasks suite); repository-auth.test.ts (ownerId filter)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-007</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">deleteLocalTask whitelist</td><td class="c-behavior">deletes task+jobs; refuses when running; whitelists only ephemeral/local per-task dirs; protects shared space tree</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (4); spaces-task-ops.test.ts (~9 delete cases incl. symlink/trailing-slash/null-space)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">data-loss regression strongly guarded</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-008</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">local_task_comments</td><td class="c-behavior">addLocalTaskComment, listLocalTaskComments, getUninjected/markInjected, getLatestResultComment, attachments</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (Feedback/Share); migrate.test.ts (attachments column)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">comment inject/uninject + getLatestResultComment lifecycle not directly asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-009</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">buildVisibilityWhere</td><td class="c-behavior">admin 1=1; owner/public; same-org branch; spaceColumn membership+owner OR; admin ignores spaceColumn</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">bridge/visibility.test.ts (33 incl. param-order, byte-identical back-compat)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">param ordering + admin-ignores-membership asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-010</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">canManageSpace / canEditInSpace</td><td class="c-behavior">role-gated write checks (admin/owner/member roles, viewer read-only)</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">bridge/visibility.test.ts (canManage + canEdit suites)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-011</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Spaces CRUD</td><td class="c-behavior">createSpace/getSpace/listSpaces (visibility-filtered)/updateSpace/archiveSpace</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (12)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-012</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ensurePersonalSpace invariant</td><td class="c-behavior">exactly one private personal space/user, idempotent; never hard-deletable</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (ensurePersonalSpace + hardDelete invariant)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-013</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">hardDeleteSpace crypto-shred</td><td class="c-behavior">shreds case space DEK on delete; never personal</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">spaces-repository.test.ts (crypto-shred case)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-014</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Space members CRUD + visibility</td><td class="c-behavior">add/list/getRole/updateRole/remove; membership-based read; non-member leak prevention; revocation</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.shared-space.test.ts (23)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">collaboration + leak + revocation all asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-015</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Personal space owner-only visibility</td><td class="c-behavior">even admin cannot see another user's personal space (list+get); no-auth synthetic owner path</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.personal-space-visibility.test.ts (8)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-016</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Per-space resource isolation</td><td class="c-behavior">space-A task sees only space-A MCP/SSH (not space-B/global); personal sees no case-space resources</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.spaces-resource-isolation.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-017</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Space tool_policy persistence</td><td class="c-behavior">default null; round-trips JSON; clear via null</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.tool-policy.test.ts (5)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-018</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Space invites</td><td class="c-behavior">create (no-expiry default), regenerate replaces, expiry validity, revoke, FK cascade</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.space-invites.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-019</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">App share links</td><td class="c-behavior">create/get/revoke/resolveAppShareToken</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.app-share.test.ts (out-of-listed but present)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">present file not fully read; assume happy-path coverage</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-020</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Tool requests + grant overlay</td><td class="c-behavior">record/list newest-first, de-dup by job/task/piece, idempotent decide, granted-tools round-trip, per-piece aggregate</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.tool-requests.test.ts (7)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-021</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">User CRUD + OAuth link</td><td class="c-behavior">createUser/getById/getByEmail/findOrCreateUserByOAuth (link by email)/listUsers/updateUser/deleteUser cascade</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-auth.test.ts (17)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-022</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Local-auth credentials</td><td class="c-behavior">setLocalPassword/verify round-trip, per-user salt, createLocalUser email-takeover reject, upsertLocalSystemAdmin idempotent, delete refuses local user</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-local-auth.test.ts (12)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-023</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Local orgs</td><td class="c-behavior">createLocalOrg (lorg: id), rename, member add/remove idempotent, listUserLocalOrgs, delete cascade + downgrade scoped resources</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository-local-orgs.test.ts (10)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-024</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ensureLocalUser DEK fix</td><td class="c-behavior">creates per-user row so DEK/SSH create succeeds; idempotent</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">ensure-local-user.test.ts (4)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-025</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">ScheduledTasks CRUD</td><td class="c-behavior">create/get/list/getDue/update/delete; space_id persisted (not dead-wired)</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (ScheduledTasks 5); spaces-resolution.test.ts (space_id round-trip)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-026</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Share token (local task)</td><td class="c-behavior">shareLocalTask token, idempotent, unshare clears, getByShareToken null on unknown/after-unshare</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.test.ts (Share feature 6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-027</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">addAuditLog</td><td class="c-behavior">single audit_log insert helper</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">scheduler.test.ts (user_script_run audit row asserted via path)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">addAuditLog itself not unit-tested; only one consumer asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-028</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Gateway virtual keys</td><td class="c-behavior">find/list/revoke/delete/touchLastUsed + usage</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.gateway-keys.test.ts; gateway-usage.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">(gateway-adjacent; in repo file)</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-029</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">LLM usage aggregation</td><td class="c-behavior">incrementLlmUsage (daily) + hourly + queryLlmUsageDaily + org map</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.llm-usage.test.ts; llm-usage-hourly.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-030</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Push subscriptions / prefs</td><td class="c-behavior">upsert/list/get/delete/markSuccess/markFailure; notification prefs</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(push-service.test.ts at higher layer)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">repo-level push CRUD not directly asserted</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-031</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Calendar events</td><td class="c-behavior">getCalendarEvent/deleteCalendarEvent</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">repository.calendar.test.ts; cross-calendar.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="Services &amp; DB"><td class="c-id"><code>DB-032</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">resumeToolRequestJob</td><td class="c-behavior">resumes a job waiting on a tool grant</td><td class="c-source"><code>src/db/repository.ts</code></td><td class="c-test">(none direct)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">resume-after-grant path untested at repo layer</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-033</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Schema migrations (idempotent ALTER)</td><td class="c-behavior">runMigrations adds owner_id/continued_from/last_backend_id/attachments; idempotent</td><td class="c-source"><code>src/db/migrate.ts, schema.sql</code></td><td class="c-test">migrate.test.ts (16); migrate.space-id/ssh-space/mcp-space/reflection-columns/gateway-2b/privatize/rename tests</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">each major migration has its own test file</td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-034</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">MCP table migrations</td><td class="c-behavior">creates mcp_servers/tokens/tools/oauth_pending idempotent; BLOB cols; auth_kind default; owner index</td><td class="c-source"><code>src/db/migrate.ts</code></td><td class="c-test">migrate.test.ts (MCP suite); migrate.mcp-auth-columns.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-035</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Space backfill migrations</td><td class="c-behavior">backfillMcpServer/Ssh/BrowserSession space_ids; privatize space rows; rename personal title</td><td class="c-source"><code>src/db/migrate.ts</code></td><td class="c-test">migrate.mcp-space/ssh-space/privatize-space-rows/rename-personal-space-title.test.ts</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-036</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">resolveTaskWorkspaceDir / dirs</td><td class="c-behavior">persistent→space files tree; ephemeral→throwaway; personal-space; ensureWorkspaceDirs creates subdirs</td><td class="c-source"><code>src/spaces/workspace-resolver.ts</code></td><td class="c-test">workspace-resolver.test.ts (6); spaces-resolution.test.ts (resolveTaskFolderContext)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="Services &amp; DB"><td class="c-id"><code>DB-037</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Credential DEK encryption</td><td class="c-behavior">encrypt under SPACE DEK for space profile, OWNER DEK for personal; migration fallback; cross-DEK isolation; throws with no material</td><td class="c-source"><code>src/crypto/profile-dek.ts</code></td><td class="c-test">crypto/profile-dek.test.ts (6)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">space-vs-owner DEK separation + non-decryptability asserted</td></tr>
<tr data-cov="PARTIAL" data-area="Services &amp; DB"><td class="c-id"><code>DB-038</code></td><td class="c-area">Services &amp; DB</td><td class="c-feature">Branding storage</td><td class="c-behavior">branding config/assets read-write (gitignored data/branding)</td><td class="c-source"><code>src/bridge/branding-api.ts</code></td><td class="c-test">bridge/branding-api.test.ts</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">API-layer test exists; storage-layer branch coverage unverified</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-001</code></td><td class="c-area">UI</td><td class="c-feature">Calendar month grid + bars</td><td class="c-behavior">month grid, week split, range/time fmt, multi-day bar layout</td><td class="c-source"><code>`lib/calendar.ts`</code></td><td class="c-test"><code>lib/calendar.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-002</code></td><td class="c-area">UI</td><td class="c-feature">Command palette</td><td class="c-behavior">build/filter/group commands, hotkey gate</td><td class="c-source"><code>`lib/command-palette.ts`</code></td><td class="c-test"><code>lib/command-palette.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-003</code></td><td class="c-area">UI</td><td class="c-feature">Cron → form state</td><td class="c-behavior">parse cron string into schedule fields</td><td class="c-source"><code>`lib/cronForm.ts`</code></td><td class="c-test"><code>lib/cronForm.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only <code>cronToFormState</code> exported/tested; reverse (form→cron) lives in <code>ScheduleFields.tsx</code> untested</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-004</code></td><td class="c-area">UI</td><td class="c-feature">File move resolution</td><td class="c-behavior">basename/parentDir, resolve drag-move plan (conflicts)</td><td class="c-source"><code>`lib/fileMove.ts`</code></td><td class="c-test"><code>lib/fileMove.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-005</code></td><td class="c-area">UI</td><td class="c-feature">File type categorization</td><td class="c-behavior">split name, category, color class</td><td class="c-source"><code>`lib/fileType.ts`</code></td><td class="c-test"><code>lib/fileType.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-006</code></td><td class="c-area">UI</td><td class="c-feature">File view sort/format</td><td class="c-behavior">sort entries, toggle sort, fmt timestamp/size</td><td class="c-source"><code>`lib/fileView.ts`</code></td><td class="c-test"><code>lib/fileView.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-007</code></td><td class="c-area">UI</td><td class="c-feature">Help docs frontmatter/render</td><td class="c-behavior">split/validate frontmatter, parse, slugify, render HTML, filter</td><td class="c-source"><code>`lib/help.ts`</code></td><td class="c-test"><code>lib/help.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-008</code></td><td class="c-area">UI</td><td class="c-feature">Live-workspace auto-focus</td><td class="c-behavior">decide auto-focus on new live task</td><td class="c-source"><code>`lib/live-workspace.ts`</code></td><td class="c-test"><code>lib/live-workspace.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-009</code></td><td class="c-area">UI</td><td class="c-feature">Memory summary/filter</td><td class="c-behavior">summarize + filter memory entries by type</td><td class="c-source"><code>`lib/memorySummary.ts`</code></td><td class="c-test"><code>lib/memorySummary.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-010</code></td><td class="c-area">UI</td><td class="c-feature">Notifications logic</td><td class="c-behavior">status→event map, shouldNotify, debounce, build options</td><td class="c-source"><code>`lib/notifications.ts`</code></td><td class="c-test"><code>lib/notifications.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"><code>createNotification</code>/permission are browser-API thin wrappers (not unit-covered, acceptable)</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-011</code></td><td class="c-area">UI</td><td class="c-feature">Output-path detection/linkify</td><td class="c-behavior">regex split text by output paths, linkify escaped HTML</td><td class="c-source"><code>`lib/output-path-detect.ts`</code></td><td class="c-test"><code>lib/output-path-detect.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-012</code></td><td class="c-area">UI</td><td class="c-feature">Owner display name</td><td class="c-behavior">derive owner label</td><td class="c-source"><code>`lib/owner.ts`</code></td><td class="c-test"><code>lib/owner.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-013</code></td><td class="c-area">UI</td><td class="c-feature">Public route matching</td><td class="c-behavior">match login/join/shared public routes</td><td class="c-source"><code>`lib/publicRoute.ts`</code></td><td class="c-test"><code>lib/publicRoute.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-014</code></td><td class="c-area">UI</td><td class="c-feature">Push subscribe helpers</td><td class="c-behavior">isPushSupported/iOS/PWA, base64→Uint8Array</td><td class="c-source"><code>`lib/push-subscribe.ts`</code></td><td class="c-test"><code>lib/push-subscribe.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-015</code></td><td class="c-area">UI</td><td class="c-feature">Shared view URLs</td><td class="c-behavior">shared image base + raw url builders</td><td class="c-source"><code>`lib/sharedView.ts`</code></td><td class="c-test"><code>lib/sharedView.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-016</code></td><td class="c-area">UI</td><td class="c-feature">Sharing scope preview</td><td class="c-behavior">visibility preview text + tooltip</td><td class="c-source"><code>`lib/sharingScope.ts`</code></td><td class="c-test"><code>lib/sharingScope.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-017</code></td><td class="c-area">UI</td><td class="c-feature">Space branding vars</td><td class="c-behavior">parse color, derive brand CSS vars</td><td class="c-source"><code>`lib/spaceBranding.ts`</code></td><td class="c-test"><code>lib/spaceBranding.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-018</code></td><td class="c-area">UI</td><td class="c-feature">Space rail sort</td><td class="c-behavior">order spaces for rail</td><td class="c-source"><code>`lib/spaceSort.ts`</code></td><td class="c-test"><code>lib/spaceSort.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-019</code></td><td class="c-area">UI</td><td class="c-feature">Space task filter/count</td><td class="c-behavior">filter tasks for space, count running</td><td class="c-source"><code>`lib/spaceTasks.ts`</code></td><td class="c-test"><code>lib/spaceTasks.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-020</code></td><td class="c-area">UI</td><td class="c-feature">Split pieces (custom/default)</td><td class="c-behavior">split + resolve piece options</td><td class="c-source"><code>`lib/splitPieces.ts`</code></td><td class="c-test"><code>lib/splitPieces.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-021</code></td><td class="c-area">UI</td><td class="c-feature">Streaming field extract</td><td class="c-behavior">extract a field from partial SSE stream</td><td class="c-source"><code>`lib/streamFieldExtract.ts`</code></td><td class="c-test"><code>lib/streamFieldExtract.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-022</code></td><td class="c-area">UI</td><td class="c-feature">Tab swipe physics</td><td class="c-behavior">axis lock, resist, commit, neighbor index</td><td class="c-source"><code>`lib/tab-swipe.ts`</code></td><td class="c-test"><code>lib/tab-swipe.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-023</code></td><td class="c-area">UI</td><td class="c-feature">Task list filter/sort/group</td><td class="c-behavior">query match, group-by-status, counts, filter+sort</td><td class="c-source"><code>`lib/taskFilter.ts`</code></td><td class="c-test"><code>lib/taskFilter.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-024</code></td><td class="c-area">UI</td><td class="c-feature">Task scope filter</td><td class="c-behavior">filter tasks by scope tab</td><td class="c-source"><code>`lib/taskScope.ts`</code></td><td class="c-test"><code>lib/taskScope.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-025</code></td><td class="c-area">UI</td><td class="c-feature">Theme pref + apply</td><td class="c-behavior">resolve/store/apply theme, system-dark, events</td><td class="c-source"><code>`lib/theme.ts` (+ `theme-css-parity.test.ts`)</code></td><td class="c-test"><code>lib/theme.test.ts</code>, <code>lib/theme-css-parity.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-026</code></td><td class="c-area">UI</td><td class="c-feature">Tool policy split/patch</td><td class="c-behavior">split categories, build policy patch, count enabled</td><td class="c-source"><code>`lib/toolPolicy.ts`</code></td><td class="c-test"><code>lib/toolPolicy.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">known Bash/sensitive-tools write-path gap (MEMORY: PR #653) — verify both category + sensitiveTools systems covered</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-027</code></td><td class="c-area">UI</td><td class="c-feature">Unsaved-changes guard</td><td class="c-behavior">detect unsaved, confirm discard, hook</td><td class="c-source"><code>`lib/unsavedGuard.ts`</code></td><td class="c-test"><code>lib/unsavedGuard.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>useUnsavedGuard</code> hook portion needs jsdom; pure parts covered</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-028</code></td><td class="c-area">UI</td><td class="c-feature">URL state (useUrlState)</td><td class="c-behavior">read/build search params, legacy ?page=tasks redirect</td><td class="c-source"><code>`lib/urlState.ts`</code></td><td class="c-test"><code>lib/urlState.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">hook wrapper <code>useUrlState.ts</code> itself untested (jsdom) but pure core covered</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-029</code></td><td class="c-area">UI</td><td class="c-feature">Misc utils + activity log parse</td><td class="c-behavior">relativeTime, status tones, previewable-by-type, parseActivityLog</td><td class="c-source"><code>`lib/utils.ts`</code></td><td class="c-test"><code>lib/utils.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-030</code></td><td class="c-area">UI</td><td class="c-feature">Workspace dir role</td><td class="c-behavior">classify input/output/logs/subtasks dir</td><td class="c-source"><code>`lib/workspaceDirs.ts`</code></td><td class="c-test"><code>lib/workspaceDirs.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-031</code></td><td class="c-area">UI</td><td class="c-feature">Pet state machine</td><td class="c-behavior">pet mood/animation state transitions</td><td class="c-source"><code>`lib/pets/petState.ts`</code></td><td class="c-test"><code>lib/pets/petState.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"><code>lib/pets/toolIconMap.ts</code> is a static map (no test, low value)</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-032</code></td><td class="c-area">UI</td><td class="c-feature">i18n workspace terminology</td><td class="c-behavior">locale keys use &quot;ワークスペース&quot; consistently</td><td class="c-source"><code>`i18n/index.ts`, `i18n/locales`</code></td><td class="c-test"><code>i18n/layout-workspace.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only layout/workspace terms checked; full key-parity across locales not asserted</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-033</code></td><td class="c-area">UI</td><td class="c-feature">Service worker push handler</td><td class="c-behavior">SW push event → notification payload</td><td class="c-source"><code>`sw/push-handler.ts`</code></td><td class="c-test"><code>sw/push-handler.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-034</code></td><td class="c-area">UI</td><td class="c-feature">App share URL builder</td><td class="c-behavior">build shareable app URL/token</td><td class="c-source"><code>`components/spaces/appShareUrl.ts`</code></td><td class="c-test"><code>components/spaces/appShareUrl.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-035</code></td><td class="c-area">UI</td><td class="c-feature">App↔iframe postMessage bridge</td><td class="c-behavior">validate/route postMessage between app iframe + host</td><td class="c-source"><code>`components/spaces/app-bridge.ts`</code></td><td class="c-test"><code>components/spaces/app-bridge.test.ts</code> (31 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">heaviest pure suite; strong</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-036</code></td><td class="c-area">UI</td><td class="c-feature">App file gateway (shared/auth)</td><td class="c-behavior">route file reads through gateway, prevent 401 drift</td><td class="c-source"><code>`components/spaces/app-file-gateway.ts`</code></td><td class="c-test"><code>components/spaces/app-file-gateway.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-037</code></td><td class="c-area">UI</td><td class="c-feature">Chat detail split logic</td><td class="c-behavior">split chat vs detail pane state</td><td class="c-source"><code>`components/spaces/ChatDetailSplit.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/ChatDetailSplit.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">pure split logic tested; component render not</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-038</code></td><td class="c-area">UI</td><td class="c-feature">Space detail keying</td><td class="c-behavior">stable key/identity for space detail</td><td class="c-source"><code>`components/spaces/SpaceDetail.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/spaceDetailKeying.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">keying logic only</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-039</code></td><td class="c-area">UI</td><td class="c-feature">App auto-approve gate</td><td class="c-behavior">decide auto-approve of app file ops</td><td class="c-source"><code>`components/spaces/AppRunner.tsx` (logic)</code></td><td class="c-test"><code>components/spaces/AppRunner.autoapprove.test.tsx</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only auto-approve branch; AppRunner iframe lifecycle untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-040</code></td><td class="c-area">UI</td><td class="c-feature">App-share API client</td><td class="c-behavior">app-share fetch client behavior</td><td class="c-source"><code>`src/api.ts` (app-share)</code></td><td class="c-test"><code>api.app-share.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">only app-share endpoints; rest of <code>api.ts</code> untested</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-041</code></td><td class="c-area">UI</td><td class="c-feature">Harness gateway</td><td class="c-behavior">test-harness gateway (mounts real AppRunner)</td><td class="c-source"><code>`harness/`</code></td><td class="c-test"><code>harness/harness-gateway.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-042</code></td><td class="c-area">UI</td><td class="c-feature">Shared tabs (public view)</td><td class="c-behavior">which tabs show in SharedView/SharedAppView</td><td class="c-source"><code>`pages/SharedView.tsx`/`SharedAppView.tsx` (logic)</code></td><td class="c-test"><code>pages/sharedTabs.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">tab-selection logic only</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-043</code></td><td class="c-area">UI</td><td class="c-feature">Detail tab selection</td><td class="c-behavior">detail tab list / readonly gating</td><td class="c-source"><code>`components/detail/*` (logic)</code></td><td class="c-test"><code>detail/detailTabs.test.ts</code>, <code>detail/detail-readonly.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">tab + readonly logic; not full panels</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-044</code></td><td class="c-area">UI</td><td class="c-feature">Task data source resolution</td><td class="c-behavior">choose live vs shared data source for task</td><td class="c-source"><code>`components/detail/task-data-source.tsx` (logic)</code></td><td class="c-test"><code>detail/task-data-source.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-045</code></td><td class="c-area">UI</td><td class="c-feature">Console key handling</td><td class="c-behavior">terminal key → escape sequences</td><td class="c-source"><code>`detail/tabs/console/*`</code></td><td class="c-test"><code>detail/tabs/console/keys.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">key mapping only; TerminalView render/websocket untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-046</code></td><td class="c-area">UI</td><td class="c-feature">TopBar collapse logic</td><td class="c-behavior">which tabs collapse into overflow</td><td class="c-source"><code>`components/layout/TopBar.tsx` (logic)</code></td><td class="c-test"><code>layout/topbar-collapse.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">collapse math only</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-047</code></td><td class="c-area">UI</td><td class="c-feature">Settings sidebar sections</td><td class="c-behavior">section list/visibility logic</td><td class="c-source"><code>`components/settings/SettingsSidebar.tsx` (logic)</code></td><td class="c-test"><code>settings/settingsSidebar.test.ts</code></td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">section logic; per-form rendering untested</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-048</code></td><td class="c-area">UI</td><td class="c-feature">Rotating tips</td><td class="c-behavior">pick/rotate chat tip strings</td><td class="c-source"><code>`components/chat/RotatingTips.tsx` (logic)</code></td><td class="c-test"><code>chat/RotatingTips.tips.test.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-049</code></td><td class="c-area">UI</td><td class="c-feature">File drag-n-drop transfer</td><td class="c-behavior">build/read DataTransfer drag sources for files</td><td class="c-source"><code>`lib/fileDnd.ts` (`dragSources`, `readDragSources`)</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure, easy to test now; DnD bugs are user-visible</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-050</code></td><td class="c-area">UI</td><td class="c-feature">Files → base64</td><td class="c-behavior">convert File[] to base64 upload payload</td><td class="c-source"><code>`lib/fileBase64.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">uses FileReader (jsdom/node-FileReader needed); logic small but worth a test</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-051</code></td><td class="c-area">UI</td><td class="c-feature">Local date helpers</td><td class="c-behavior">localToday, tz offset, shiftDay</td><td class="c-source"><code>`lib/localDate.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure date math — high regression risk (tz), trivially testable</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-052</code></td><td class="c-area">UI</td><td class="c-feature">Space color</td><td class="c-behavior">derive deterministic space color</td><td class="c-source"><code>`lib/spaceColor.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">pure; deterministic-output test trivial</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-053</code></td><td class="c-area">UI</td><td class="c-feature">Polling/stale constants</td><td class="c-behavior">POLLING + STALE_TIME tables</td><td class="c-source"><code>`lib/constants.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">low value (constants) — skip unless invariants needed</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-054</code></td><td class="c-area">UI</td><td class="c-feature">Help content loader</td><td class="c-behavior">load+assemble help sections from raw md</td><td class="c-source"><code>`lib/help-content.ts` (`loadHelpSections`)</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap"><code>help.ts</code> parsing covered, but the loader/aggregation step is untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-055</code></td><td class="c-area">UI</td><td class="c-feature">FilePreview render (md/csv/jsonl/image/pdf)</td><td class="c-behavior">render each preview type; **relative-image rewrite** to <code>/api/local/tasks/:id/files/raw</code></td><td class="c-source"><code>`components/files/FilePreview.tsx` (`resolvedHref`, line ~320)</code></td><td class="c-test">none (only <code>lib/utils</code> <code>isPreviewable</code> flags)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">the relative-image rewrite is core, user-visible, untested. Could extract <code>resolveImageHref</code> to lib → sandbox-testable</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-056</code></td><td class="c-area">UI</td><td class="c-feature">FileBrowser (grid/list, sort, select, move)</td><td class="c-behavior">tile/detail views, multi-select, drag-move, breadcrumb</td><td class="c-source"><code>`components/files/FileBrowser.tsx` + `FileTileGrid/FileDetailList/FileBreadcrumb/MoveTargetDialog`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (icon grid/upload/delete/multiselect/download)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">core flows covered by e2e; jsdom unit tests absent (sort/select logic is in tested libs UI-006/UI-004)</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-056b</code></td><td class="c-area">UI</td><td class="c-feature">useFileBrowser/useFileView/useFilePreview hooks</td><td class="c-behavior">data fetching + view state for files</td><td class="c-source"><code>`hooks/useFileBrowser.ts`, `useFileView.ts`, `useFilePreview.ts`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-057</code></td><td class="c-area">UI</td><td class="c-feature">ChatPane / ChatMessage / MovementGroup</td><td class="c-behavior">message stream, movement grouping, tool-calls section</td><td class="c-source"><code>`components/chat/*`</code></td><td class="c-test">e2e (inline chat create/open, cancel)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">rendering untested in jsdom; movement-group grouping logic not extracted to lib</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-058</code></td><td class="c-area">UI</td><td class="c-feature">Tool-request approval card</td><td class="c-behavior">inline Approve/Deny grants + resolves request</td><td class="c-source"><code>`components/chat/ToolRequestApproval.tsx`</code></td><td class="c-test">e2e <code>tool-request.spec.ts</code> (approve, deny)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">jsdom unit absent; e2e covers both branches</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-059</code></td><td class="c-area">UI</td><td class="c-feature">Settings ConfigForm + per-section forms</td><td class="c-behavior">edit each config.yaml section (Tools/LLM/Context/Safety/SSH/MCP/Gateway/Branding/Pets/Reflection/...)</td><td class="c-source"><code>`components/settings/*Form.tsx` (~45 files)</code></td><td class="c-test">settingsSidebar (sections), toolPolicy (UI-026)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">huge surface; only sidebar + tool-policy logic tested. Per-form validation/round-trip largely untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-060</code></td><td class="c-area">UI</td><td class="c-feature">Memory &amp; Learning tab</td><td class="c-behavior">edit memory entries, reflection history + revert</td><td class="c-source"><code>`components/settings/MemoryLearningForm.tsx`, `ReflectionForm.tsx`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (memory persists A vs B), memorySummary lib (UI-009)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">persistence covered by e2e; revert button + reflection-history list render untested</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-061</code></td><td class="c-area">UI</td><td class="c-feature">Usage dashboard</td><td class="c-behavior">per-user daily LLM usage charts</td><td class="c-source"><code>`components/usage/UsagePage.tsx`, `settings/GatewayKeyUsagePanel.tsx`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom; aggregation/format logic not extracted to lib</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-062</code></td><td class="c-area">UI</td><td class="c-feature">Worker/Node status widgets</td><td class="c-behavior">rail GPU panel, per-backend rows, worker pills</td><td class="c-source"><code>`components/dashboard/*Widget.tsx`, `hooks/useWorkerStatus/useNodeStatus`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (GPU panel toggle, proxy per-backend rows)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">e2e smoke; widget render/empty-state untested in unit</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-063</code></td><td class="c-area">UI</td><td class="c-feature">Browser session panel + PiP</td><td class="c-behavior">live browser session view, picture-in-picture</td><td class="c-source"><code>`components/browser/*`, `hooks/usePictureInPicture.ts`</code></td><td class="c-test">e2e <code>spaces.spec.ts</code> (BROWSER panel groups render)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">PiP controller untested; panel render only via e2e</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-064</code></td><td class="c-area">UI</td><td class="c-feature">SSH config/console UI</td><td class="c-behavior">console terminal, grants, audit, key rotation</td><td class="c-source"><code>`components/settings/Ssh*.tsx`, `userfolder/Ssh*.tsx`, `useConsoleSession`</code></td><td class="c-test">none (types only <code>ssh-console-types.ts</code>)</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">large untested surface; console session hook + websocket untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-065</code></td><td class="c-area">UI</td><td class="c-feature">Mobile edge-swipe / swipeable tabs</td><td class="c-behavior">edge swipe nav, tab swipe gestures</td><td class="c-source"><code>`hooks/useEdgeSwipe.ts`, `components/mobile/SwipeableTabs.tsx`</code></td><td class="c-test"><code>hooks/useEdgeSwipe.test.ts</code> (DOM — **fails in sandbox**), <code>lib/tab-swipe.ts</code> (UI-022)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">physics covered as lib; the DOM hook test exists but can't run here</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-066</code></td><td class="c-area">UI</td><td class="c-feature">Pets overlay + frame analysis</td><td class="c-behavior">pet sprite overlay, tool-spark, frame analysis</td><td class="c-source"><code>`components/pets/*`, `hooks/usePetFrameAnalysis/useActivePet`</code></td><td class="c-test"><code>lib/pets/petState.test.ts</code> (UI-031)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">state machine covered; overlay render + frame analysis untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-067</code></td><td class="c-area">UI</td><td class="c-feature">CreateTaskDialog + PromptCoach + ScheduleFields</td><td class="c-behavior">new chat/task dialog, prompt coaching, schedule builder</td><td class="c-source"><code>`components/create/*`</code></td><td class="c-test">e2e (+新規 opens dialog; ephemeral warning), cronForm lib (UI-003)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">dialog open + ephemeral warning via e2e; prompt-coach + form→cron untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-068</code></td><td class="c-area">UI</td><td class="c-feature">Monaco file editor (userfolder)</td><td class="c-behavior">edit AGENTS.md/MCP/skills/memory files</td><td class="c-source"><code>`components/userfolder/MonacoFileEditor.tsx` + panels</code></td><td class="c-test">e2e (AGENTS.md persistence, panels render)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">persistence via e2e; editor component untested in unit</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-069</code></td><td class="c-area">UI</td><td class="c-feature">Command palette UI</td><td class="c-behavior">open palette, fuzzy filter, execute command</td><td class="c-source"><code>`components/command/CommandPalette.tsx`</code></td><td class="c-test"><code>lib/command-palette.test.ts</code> (UI-002)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">all logic in lib (FULL); only the render/keyboard wiring is jsdom</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-070</code></td><td class="c-area">UI</td><td class="c-feature">Embed cards (X/Maps/YouTube/Amazon)</td><td class="c-behavior">render structured-block embeds + detail modals</td><td class="c-source"><code>`components/embed/*`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom; parsing of block data could be extracted to lib</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-071</code></td><td class="c-area">UI</td><td class="c-feature">Setup wizard</td><td class="c-behavior">first-run setup steps</td><td class="c-source"><code>`components/setup/SetupWizard.tsx`, `hooks/useSetupState`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">jsdom</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-072</code></td><td class="c-area">UI</td><td class="c-feature">Toast / notifications hooks</td><td class="c-behavior">toast queue, task notifications dispatch</td><td class="c-source"><code>`hooks/useToast.ts`, `useTaskNotifications.ts`</code></td><td class="c-test"><code>lib/notifications.test.ts</code> (UI-010)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">decision logic in lib; hook dispatch/debounce wiring untested</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-073</code></td><td class="c-area">UI</td><td class="c-feature">Spaces foundation + rail + detail tabs</td><td class="c-behavior">create case space, open detail, mobile rail↔detail</td><td class="c-source"><code>`components/spaces/*`</code></td><td class="c-test"><code>e2e/spaces.spec.ts</code> (37 cases)</td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">most thorough e2e suite (chat CRUD, files, calendar, settings, apps, members, MCP isolation)</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-074</code></td><td class="c-area">UI</td><td class="c-feature">Cross-space calendar</td><td class="c-behavior">top-bar tab, grid, month nav, per-space dots</td><td class="c-source"><code>`components/spaces/CrossSpaceCalendar.tsx`</code></td><td class="c-test"><code>e2e/calendar.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-075</code></td><td class="c-area">UI</td><td class="c-feature">Tasks→workspace migration</td><td class="c-behavior">Tasks tab removed, personal WS auto-select, legacy redirect</td><td class="c-source"><code>`App.tsx`, `lib/urlState.ts`</code></td><td class="c-test"><code>e2e/tasks.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">also lib-covered (UI-028)</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-076</code></td><td class="c-area">UI</td><td class="c-feature">Sharing scope (multi-user)</td><td class="c-behavior">manager/member/stranger visibility (200/404), invite picker</td><td class="c-source"><code>space visibility</code></td><td class="c-test"><code>e2e-auth/sharing-scope.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap">requires auth seed</td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-077</code></td><td class="c-area">UI</td><td class="c-feature">Shared space membership</td><td class="c-behavior">invite→member row+avatars, role change, removal</td><td class="c-source"><code>`components/spaces/SpaceMembersPanel.tsx`</code></td><td class="c-test"><code>e2e-auth/shared-space.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-078</code></td><td class="c-area">UI</td><td class="c-feature">Space invite links</td><td class="c-behavior">join via token, invalid/revoked states</td><td class="c-source"><code>`components/spaces/JoinSpace.tsx`</code></td><td class="c-test"><code>e2e-auth/space-invite.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="FULL" data-area="UI"><td class="c-id"><code>UI-079</code></td><td class="c-area">UI</td><td class="c-feature">Workspace file input flow</td><td class="c-behavior">input/output folders show, next-action guidance, ephemeral warning</td><td class="c-source"><code>Files tab + create dialog</code></td><td class="c-test"><code>e2e-auth/workspace-file-input.auth.spec.ts</code></td><td class="c-cov"><span class="chip cov-full">FULL</span></td><td class="c-gap"></td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-080</code></td><td class="c-area">UI</td><td class="c-feature">App-share public view</td><td class="c-behavior">login-free read-only shared app view</td><td class="c-source"><code>`pages/SharedAppView.tsx`, `SharedView.tsx`</code></td><td class="c-test">none (only lib <code>appShareUrl</code>/<code>sharedTabs</code>/<code>sharedView</code>)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">public viewer flow has no e2e; logic libs covered</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-081</code></td><td class="c-area">UI</td><td class="c-feature">Help page</td><td class="c-behavior">render help sections by category, search</td><td class="c-source"><code>`pages/HelpPage.tsx`</code></td><td class="c-test">none (lib <code>help.ts</code> FULL)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">rendering + search wiring not e2e/jsdom tested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-082</code></td><td class="c-area">UI</td><td class="c-feature">Pieces page (CRUD)</td><td class="c-behavior">list/edit custom vs default pieces, movement editor</td><td class="c-source"><code>`pages/PiecesPage.tsx`, `settings/PieceEditor.tsx`, `MovementForm.tsx`, `RulesTable.tsx`</code></td><td class="c-test">none (lib <code>splitPieces</code> FULL)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">piece editor / movement rules UI untested</td></tr>
<tr data-cov="PARTIAL" data-area="UI"><td class="c-id"><code>UI-083</code></td><td class="c-area">UI</td><td class="c-feature">Schedules page</td><td class="c-behavior">list/create scheduled tasks (5 types)</td><td class="c-source"><code>`pages/SchedulesPage.tsx`</code></td><td class="c-test">none (lib <code>cronForm</code> partial)</td><td class="c-cov"><span class="chip cov-partial">PARTIAL</span></td><td class="c-gap">schedule CRUD UI untested</td></tr>
<tr data-cov="NONE" data-area="UI"><td class="c-id"><code>UI-084</code></td><td class="c-area">UI</td><td class="c-feature">Admin / users page</td><td class="c-behavior">user CRUD, captcha, org form</td><td class="c-source"><code>`pages/UsersPage.tsx`, `AdminCaptchaPage.tsx`, `admin/*`, `settings/OrgsForm.tsx`</code></td><td class="c-test">none</td><td class="c-cov"><span class="chip cov-none">NONE</span></td><td class="c-gap">no UI tests for admin user management</td></tr>
</tbody>
</table>
</div>
</section>
<section class="card" id="gaps">
<h2>重要ギャップ (Top gaps)</h2>
<p class="muted-note">各領域インベントリの「Top gaps」を集約。最もリスクの高い未テスト挙動。</p>
<section class="gap-area"><h3>Engine core</h3>
<p>Highest-risk untested behaviors, ranked:</p>
<ol class="gap-list">
<li><strong>ENG-052 <code>classifyPiece</code> async path (NONE).</strong> 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.</li>
<li><strong>ENG-037 WAITING_HUMAN_BROWSER / WAITING_HUMAN_TOOL_REQUEST pauses (NONE).</strong> 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 <code>default_next</code> trap.</li>
<li><strong>ENG-039 Lessons injection + cap + lessons.jsonl (PARTIAL).</strong> 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 <code>writeLessonLog</code>. Silent loss of cross-movement learning would be invisible.</li>
<li><strong>ENG-053 <code>loadAllPieceTriggers</code> (NONE).</strong> Trigger/keyword loading feeds the classifier hint layer; the custom-dir precedence and skip-on-invalid-piece branches are uncovered.</li>
<li><strong>ENG-033 cross-movement loop guard per-movement override + reset (PARTIAL) and ENG-013 mid-iteration cancel (PARTIAL).</strong> The default-revisit abort is tested, but per-movement <code>max_consecutive_revisits</code> 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.</li>
</ol>
</section>
<section class="gap-area"><h3>Tools</h3>
<ol class="gap-list">
<li><strong>ReadPPTX ZIP-bomb detection &amp; PPTX size limit (TOOL-015) — NONE.</strong> <code>office.ts:2124-2130</code> rejects total-uncompressed > 200MB and <code>validateOfficeFile</code> enforces the 50MB file cap, but <code>office.test.ts</code> 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.</li>
<li><strong>Office file-size-limit enforcement across ReadExcel/ReadDocx/ReadPdf (TOOL-012/013/014) — PARTIAL→NONE for the limit path.</strong> Tests thoroughly cover *format-mismatch* rejection (CFB/HTML/CSV/OOXML disguises) but never exercise <code>stats.size &gt; maxSize</code> → warning emission. The <code>resolveMaxSize</code> config override (<code>tools.office_*_max_size_mb</code>) is also untested.</li>
<li><strong>index.ts dispatch order, first-match-wins, and silent-skip-on-load-failure (XCUT-001) — NONE.</strong> 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.</li>
<li><strong>index.ts runtime META_TOOLS injection (XCUT-002) — PARTIAL.</strong> The 20-name runtime META list in <code>index.ts:411</code> (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 <code>tool-categories.ts</code> SUBSET is asserted. The two lists can drift (they already differ by design); nothing tests that the runtime injection list matches expectations.</li>
<li><strong>DownloadFile robustness (TOOL-009) — PARTIAL.</strong> 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.</li>
</ol>
</section>
<section class="gap-area"><h3>Pieces</h3>
<ol class="gap-list">
<li><strong>8 of 15 pieces have ZERO direct test beyond the schema-load sweep</strong> (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 <code>loadPiece().not.toThrow()</code> 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.</li>
<li><strong>No allowed_tools-vs-real-tool-registry consistency test.</strong> Pieces list tool names in <code>allowed_tools</code> 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.</li>
<li><strong>lint-pieces.mjs (PINFRA-004) has no test.</strong> 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.</li>
<li><strong>No transition-graph reachability / dangling-next validation for pieces.</strong> Validation only bans terminal values in rules[].next and (in the tool path) checks next ∈ movementsWAIT_SUBTASKS. But file-backed <code>loadPiece</code>/<code>validatePieceDef</code> does NOT verify that every <code>rules[].next</code> points to an existing movement, nor that <code>initial_movement</code> exists, nor that every movement is reachable. A built-in YAML with a typo'd <code>next:</code> would load fine and only fail at runtime ("Movement not found"). No test guards this for the shipped pieces.</li>
<li><strong>Per-user piece resolution propagation (PINFRA-017) is only partially tested.</strong> 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.</li>
</ol>
</section>
<section class="gap-area"><h3>Reflection</h3>
<ol class="gap-list">
<li><strong>REFL-032 — applyOne per-change write-failure path (<code>decision.code='failed'</code>)</strong>: when a memory write throws mid-loop, the applier records that single change as <code>failed</code> and continues. This distinct per-change failure branch (vs runner-level <code>outcome='failed'</code>) is not clearly asserted. It is the only place a memory mutation can partially fail under the lock, so it directly affects <code>partial</code>/<code>rejected</code> outcome correctness.</li>
<li><strong>REFL-056 — space authz negative path (<code>canEditInSpace</code> → 404)</strong>: the reflection-api space-store resolution depends on <code>canEditInSpace</code>. 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.</li>
<li><strong>REFL-013 / REFL-044 / REFL-045 — runner snapshot-capture + non-fatal writeSnapshot failure</strong>: the runner's <code>writeSnapshot</code>-throws-but-record-metric-anyway branch, plus the before/after capture edge cases (remove-op uses <code>merge_target</code> for the before-file name; piece-YAML pre/post capture when <code>customPath</code> doesn't yet exist), are only lightly exercised. A torn snapshot here silently degrades the revert feature.</li>
<li><strong>REFL-007 — reflection dispatch / gateway routing</strong>: <code>worker.reflection-dispatch.test.ts</code> is small (~2 cases). The gateway role-keyed routing (<code>reflectionRoutingKey</code>, <code>roleFallback:'reflection'</code>) 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.</li>
<li><strong>REFL-031 / REFL-037 — boundary branches in applier</strong>: the ">3 memory_changes truncated with WARN" hard cap and the <code>decideOutcome</code> empty-input fallback-to-<code>abstained</code> (distinct from an explicit <code>abstain_reason</code>) lack dedicated assertions. Both are silent-correctness branches: a regression would mis-drop changes or mislabel outcomes without any test going red.</li>
</ol>
</section>
<section class="gap-area"><h3>Bridge core</h3>
<ol class="gap-list">
<li><strong>APIC-016 GET /api/local/tasks/:taskId/stream (NONE)</strong> — The SSE event stream (tool-call/delta mapping, visibility gate, the <code>toolBuf</code>/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.</li>
<li><strong>APIC-007 PUT /api/local/tasks/:taskId/mission (NONE)</strong> — 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.</li>
<li><strong>APIC-020 office-preview HTTP route (PARTIAL)</strong> — The module is well tested, but the <code>/files/office-preview</code> endpoint wiring is not: section-enum 400, <code>canViewTask</code> gate, and the SofficeUnavailableError→503 mapping are unexercised end-to-end (only <code>handleOfficePreviewError</code> would do the 503).</li>
<li><strong>APIC-045 subtask file 403 traversal branch (PARTIAL)</strong><code>GET /subtasks/:jobId/files/*</code> has the explicit <code>resolved !== base &amp;&amp; !startsWith(base+sep)</code> → 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.</li>
<li><strong>APIC-011 regenerate-title (PARTIAL)</strong> — 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.)</li>
</ol>
</section>
<section class="gap-area"><h3>Bridge auth &amp; spaces</h3>
<p>Highest risk first (authorization-weighted):</p>
<ol class="gap-list">
<li><strong>APIS-038 Space tool-policy write under-tested (HIGH).</strong> <code>PUT /:id/tool-policy</code> gates by <code>canManageSpace</code>, but only 9 authz assertions and — per MEMORY (PR #653) — the Bash/sensitive-tool write path (<code>buildPolicyPatch</code> only walked categories, missing the <code>SENSITIVE_TOOLS</code> 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).</li>
<li><strong>APIS-052 Gateway proxy auth boundary (HIGH).</strong> <code>mountGateway</code> proxies LLM traffic; classification and config-equivalence are well tested, but the <strong>authentication of proxied requests</strong> (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.</li>
<li><strong>APIS-062 Dashboard API has no visible auth guard (MEDIUM-HIGH).</strong> <code>createDashboardApi</code> router shows no <code>requireAuth</code>/<code>requireAdmin</code> 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.</li>
<li><strong>APIS-019 Shared-subtask wildcard traversal (MEDIUM).</strong> <code>GET /api/shared/:token/subtasks/:jobId/files/*</code> is a public token route with a wildcard path segment; containment relies on <code>isJobWithinWorkspace</code> 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.</li>
<li><strong>APIS-014 No-auth admin passthrough breadth (MEDIUM).</strong> When <code>authActive=false</code>, admin guards downgrade to passthrough (admin-api covered; <strong>branding adminGuard no-auth path not asserted</strong>). 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.</li>
</ol>
<p>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).</p>
</section>
<section class="gap-area"><h3>Services &amp; DB</h3>
<ol class="gap-list">
<li><strong>Job recovery &amp; resume helpers untested (DB-003, DB-032, SVC-019).</strong> <code>recoverOrphanedJobs</code>, <code>recoverStuckRunningJobs</code>, <code>resumeMcpWaitingJobs</code>, <code>resumeToolRequestJob</code>, and <code>requeueParentJobIfAllSubtasksDone</code> 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).</li>
<li><strong>Env override matrix only partially covered (CFG-004, CFG-016).</strong> Only <code>OLLAMA_BASE_URL</code> is asserted (in config.audit-regression). <code>CONCURRENCY</code>, <code>WORKTREE_DIR</code>, <code>OLLAMA_MODEL</code>, <code>DB_PATH</code>, and the <code>AAO_*</code> 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).</li>
<li><strong><code>answerSubtaskAsk</code> / subtask ASK plumbing (SVC-019).</strong> 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 <code>waiting_subtasks</code>/<code>waiting_human</code> lifecycle and has tangled state.</li>
<li><strong><code>addAuditLog</code> not unit-tested (DB-027).</strong> 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.</li>
<li><strong>Comment injection lifecycle (DB-008).</strong> <code>getUninjectedComments</code> / <code>markCommentsInjected</code> / <code>getLatestResultComment</code> 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.</li>
</ol>
</section>
<section class="gap-area"><h3>UI</h3>
<ol class="gap-list">
<li><strong>UI-055 FilePreview relative-image rewrite</strong> (<code>resolveImageHref</code>/<code>resolvedHref</code>, FilePreview.tsx ~L320) — core, user-visible (broken images in markdown), and <strong>currently untested</strong>. *Sandbox-testable IF the href-rewrite function is extracted to <code>lib/</code> (pure string transform); today it's inline in a component so as-is it needs jsdom.*</li>
<li><strong>UI-059 Settings forms (~45 *Form.tsx)</strong> — largest untested surface; only <code>SettingsSidebar</code> section logic + <code>toolPolicy</code> are covered. Per-form validation/round-trip is jsdom/e2e. <strong>Mitigation that IS sandbox-testable:</strong> extract each form's validation/serialization into pure helpers (the established pattern in this repo) and unit-test those.</li>
<li><strong>UI-064 SSH UI (config/console/grants/audit/rotation)</strong> — large surface, only TS types tested, no behavior tests. Console session + websocket needs e2e; <strong>grant/audit list filtering + form validation are extractable to lib (sandbox-testable).</strong></li>
<li><strong>UI-049/UI-051/UI-052 untested pure libs</strong> (<code>fileDnd</code>, <code>localDate</code>, <code>spaceColor</code>) — <strong>immediately sandbox-testable</strong>, no extraction needed. <code>localDate</code> (tz math) is the highest regression risk of the three.</li>
<li><strong>UI-026 toolPolicy Bash/sensitive-tools write-path</strong> — known real bug class (MEMORY: PR #653 fixed a save gap where <code>buildPolicyPatch</code> only walked categories and missed <code>SENSITIVE_TOOLS</code>/Bash). Test exists but should assert <strong>both</strong> the category system and the separate sensitiveTools array across read/validate/render/write. <strong>Sandbox-testable now</strong> (pure).</li>
</ol>
</section>
</section>
<footer>
MAESTRO TDD 機能テスト計画 &middot; 段階: 設計 &middot; 更新日 2026-06-25 &middot; 全 502 行
</footer>
</div>
<script>
(function(){
var search = document.getElementById('search');
var rows = Array.prototype.slice.call(document.querySelectorAll('#master-table tbody tr'));
var btns = Array.prototype.slice.call(document.querySelectorAll('.fbtn'));
var count = document.getElementById('count');
var covFilter = 'ALL';
var q = '';
// pre-cache lowercased text per row
rows.forEach(function(r){ r.__t = r.textContent.toLowerCase(); });
function apply(){
var shown = 0;
for(var i=0;i<rows.length;i++){
var r = rows[i];
var okCov = (covFilter === 'ALL') || (r.getAttribute('data-cov') === covFilter);
var okQ = !q || r.__t.indexOf(q) !== -1;
if(okCov && okQ){ r.style.display = ''; shown++; }
else { r.style.display = 'none'; }
}
count.textContent = shown + ' / ' + rows.length + ' 件表示';
}
search.addEventListener('input', function(){ q = search.value.trim().toLowerCase(); apply(); });
btns.forEach(function(b){
b.addEventListener('click', function(){
btns.forEach(function(x){ x.classList.remove('active'); });
b.classList.add('active');
covFilter = b.getAttribute('data-cov');
apply();
});
});
apply();
})();
</script>
</body>
</html>