Phase 0.9 — Spike Plan (Phase 1)
Phase 0.9 — Spike Plan (Phase 1)
Section titled “Phase 0.9 — Spike Plan (Phase 1)”Audit date: 2026-05-10 (S227 initial; S228 corrections pass — corpus + 5 new spikes + re-framings)
Branch: content-items-investigation
Predecessors: 0.9-decision-graph.md + 0.9-intended-architecture.md + 0.9-context.md + 0.9-edit-flow-investigation.md
Companion: 0.9-collapse-candidates.md (tracks functionality slated for retirement under new architecture)
Successor: 0.9-spike-results.md (output of this phase)
Purpose: Phase 1 spike phase that gates Phase 2 (architecture-impl) commitment per 0.9-intended-architecture.md §13.1. Each spike has explicit question + method + success criteria + decision gate + budget. Most run in parallel as worktree-isolated sub-agents.
S228 framing: spikes are unblocked to execute in next session in PARALLEL with architecture documentation updates. Most spikes do NOT depend on architecture rewrite landing first — they generate evidence that informs the rewrite. Only S8 + S9 + S10 are sequenced after edit-flow investigation §6 architectural decision.
1. Spike summary table
Section titled “1. Spike summary table”| ID | Spike | Effort | Owner pattern | Sequencing | Decision gate | S229 Status |
|---|---|---|---|---|---|---|
| S1 | cocoindex schema-coupling | 3-5 days | Foreground (main session) | First; gates everything else | Scenario A vs B → Phase 2 commit or fall back to Phase 0.7 Stream 2 | ⏸ DEFERRED to S230 (multi-day foreground) |
| S2 | cocoindex external-folder source binding (using docs/client-documentation-base/) | 2-3 days | Worktree sub-agent | Parallel with S1 (after S1 day 1) | v1 connector list scope + UC10 polling cadence | ✅ S229 — localfs-only v1; fs-watch UC10; LMDB-single-writer → NEW S14 |
| S3 | mempalace observe-only (Shape A + B + C + miner) | 1-2 days | Worktree sub-agent | Parallel | Confirm Shape A+B+C+miner adoption | ✅ S229 — PROCEED (Shape A+B+C+miner confirmed; miner doesn’t emit KG triples) |
| S4 | pullmd 50-URL bake-off | 1 day | Worktree sub-agent | Parallel | Adopt or defer | ✅ S229 — CONDITIONAL PASS (adopt for HTML/CF/GN/Reddit; PDF→Jina; drop Firecrawl) |
| S5 | skill-seekers dependency_analyzer.py | 0.5 day | Foreground | Parallel | Adopt as 3rd BNW-detector or skip | ✅ S229 — SKIP (0% non-overlapping TP) |
| S6 | mcp-scan feasibility + cost (OQ5 RATIFIED-YES; not “investigate”) | 0.5 day | Foreground | Parallel | Confirm v1 vs v2 adoption shape | ✅ S229 — PASS v1 CI (inspect mode local); v2 cloud deferred; package renamed snyk-agent-scan |
| S7 | Pre-re-ingest eval (graphify data + CSP checklist forms) | 1-2 days | Foreground or sub-agent | Parallel | Baseline + auto-completion target validation | ⏸ DEFERRED to S230 |
| S8 | Q&A flow validation (using docs/client-documentation-base/) | 1-2 days | Foreground (post-S1) | After S1 + edit-flow §6 | Confirms §6.2 architecture works | ⏸ GATES on S1 + Yjs install |
| S9 (NEW) | Write-back semantics validation (UC1 / UC4 Candidate A/B/C) | 2-3 days | Foreground (post-edit-flow §6) | After edit-flow §6 architectural decision | Candidate A/B/C choice per UC family + cocoindex idempotency confirmed | ⏸ GATES on S1 + Yjs install |
| S10 (NEW) | Cross-record dedup substrate (UC8) | 2-3 days | Worktree sub-agent | After S2 + S3 | Cocoindex @coco.fn vs mempalace KG entity-resolution vs skill-seekers metadata — substrate choice | ⏸ READY-TO-DISPATCH-S230 (S2+S3 done; cocoindex confirmed insufficient — substrate must look beyond) |
| S11 (NEW) | Playwright swap with agent-browser (OQ6 RE-FRAMED — NOT Firecrawl) | 1 day | Worktree sub-agent | Parallel | Adopt agent-browser Tier 2.5 / keep Playwright | ✅ S229 — KEEP Playwright; OQ6/CX.7 RESOLVED-NO-SWAP; Playwright surface lives in pullmd Tier-3, not KH cascade |
| S12 (NEW) | Graphify confidence-label taxonomy adopt (Q4.14 RESOLVED-ADOPT) | 0.5 day | Foreground | Parallel | Confirm taxonomy fits alongside Q4.7 KG-provenance enum without conflict | ✅ S229 — 1:1 identity; single provenance column + repurpose confidence numeric(3,2) |
| S13 (NEW) | ESLint input-required rule (DW.13 PROVISIONAL-IMMEDIATE-VALUE) | 0.5 day | Foreground | Parallel | Implement rule + measure precheck overhead; adopt or defer | ✅ S229 — ADOPT-ADVISORY (warn); 100% precision v2 rule |
| S14 (NEW S229) | Cocoindex concurrency under LMDB single-writer | 1 day | Worktree sub-agent or foreground | After S2 (S2 surfaced LMDB constraint) | Single-orchestrator vs queue-based vs read-only-worker topology for Cloud Run | ✅ S230 — PASSED. Single-orchestrator-instance with isolated per-instance LMDB; S2 “lock blocks multi-worker” framing dissolved empirically |
Total: ~14-19 days mostly parallel (S14 added at +1d). Single-session can absorb most on 1M context window. Sequential foundation: S1 → (S2-S7, S11-S13 parallel) → S10 + S14 → edit-flow §6 → (S8, S9 parallel).
S229 progress (8 spikes dispatched in parallel): ✅ all 8 done: S2 / S3 / S4 / S5 / S6 / S11 / S12 / S13. ⏸ deferred to S230: S1 / S7 / S8 / S9 / S10 / S14.
S230 progress: ✅ S14 done (cocoindex concurrency / Cloud Run topology — PASSED single-orchestrator).
2. Per-spike detail
Section titled “2. Per-spike detail”S1 — cocoindex schema-coupling
Section titled “S1 — cocoindex schema-coupling”Question: Does postgres.mount_table_target(PG, "content_items") accept the existing 70-col schema with FKs and triggers (Scenario A — desired) OR does it want schema ownership (Scenario B — fallback)?
Why it gates everything: if Scenario B (engine wants schema ownership), the architecture-impl phase doubles in cost (~12-16 weeks) and the case for cocoindex weakens vs Phase 0.7 Stream 2 build (~9-12 weeks).
Method:
-
Set up cocoindex in dev environment:
Terminal window pip install cocoindex# Bypass sandbox per wave-08 instruction -
Spin up a Postgres branch from staging:
Terminal window /opt/homebrew/bin/supabase branches create cocoindex-spike --base staging -
Define minimal cocoindex flow (
spike/cocoindex_minimal.py).API names verified against
0.8.2-cocoindex-evaluation.mdlines 38, 46, 51, 142-143, 227 — uselocalfs.walk_dirsource +RecursiveSplittertransform +mount_eachtarget binding (NOT the placeholder namesLocalFile.from_folder/SplitRecursivelycarried over in S227 draft):import cocoindexfrom cocoindex.sources import localfsfrom cocoindex.transforms import RecursiveSplitterfrom cocoindex.targets import postgresPG = postgres.from_env(SUPABASE_DB_URL)@cocoindex.flow_def(name="content_items_spike")def flow(src: str = "./fixtures/", table: str = "content_items"):async def index_file(item):md = await read_text(item.path)for chunk in RecursiveSplitter(2000, 500).split(md):yield extract_markdown_metadata(chunk, item)await coco.mount_each(index_file, localfs.walk_dir(src).items(), table)(Spike’s first step: confirm above import paths against installed cocoindex version. If APIs have evolved further, update before sub-agent dispatch.)
-
Run
cocoindex updateand observe:- Mount succeeds? Postgres schema retained?
- CHECK constraints respected? (e.g.
content_text_hashGENERATED column,content_typeCHECK) - FK relationships preserved? (e.g.
workspace_idFK to workspaces) - Triggers continue to fire? (e.g.
auto_version_content_items) - Δ-update works on second run with modified file?
Success criteria:
- All existing constraints + FKs + triggers preserved → Scenario A
- Cocoindex demands ownership of schema (drop/replace) → Scenario B
Decision gate:
-
Scenario A: Phase 2 architecture-impl commits to cocoindex Option A. Budget unchanged.
-
Scenario B: evaluate fall-back paths:
- Phase 0.7 Stream 2 bespoke build (9-12 weeks)
- Hybrid (cocoindex for new tables; KH owns content_items via shadow table)
- Schema redesign for cocoindex ownership (10-14 weeks)
Decide via separate ratification.
Output: 0.9-spike-S1-cocoindex-schema-coupling.md documenting:
- Cocoindex version + config
- Mount attempts + outcomes
- Constraint / FK / trigger interactions
- Decision recommendation
Budget: 3-5 days. Author: foreground (main session).
S2 — cocoindex external-folder source binding
Section titled “S2 — cocoindex external-folder source binding”Status: ✅ COMPLETE S229. G2 outcome = localfs-only v1; live fs-watch (watchfiles 1.1.1) for UC10. v1 connector list confirmed as localfs only; SharePoint / Notion / Dropbox / Box absent from cocoindex 1.0.3.
Outcome: cocoindex 1.0.3 localfs.walk_dir enumerated the 35-file canonical corpus correctly. End-to-end App.update() flow with @cocoindex.fn(memo=True) observer produced the predicted invocation counts across three runs (35 cold / 0 warm / 4 after UC4.4 mutations). Δ semantics: delete purges cache without invoking; rename presents as add+delete pair (no native rename primitive — S188 surface concern shifts to UI layer); binary-replace indistinguishable from edit at engine level. DRAFT-vs-final pairs have fully-distinct content fingerprints — cocoindex does NOT solve UC8 (S10 substrate evaluation must look beyond cocoindex content-hash).
Critical API drift findings (0.8.2-cocoindex-evaluation.md references stale APIs):
- cocoindex 1.0.3 renamed
sources→connectors— import paths in 0.8.2 + S228 spike-plan need refresh. - Dropped
@flow_defforApp(name, main_fn)— flow-definition pattern is now App-class based. - Swapped SQLite ops-DB for LMDB — and LMDB single-writer constrains multi-worker Cloud Run topology. Needs new spike (S14) OR singleton-orchestrator architecture decision before cocoindex can ship to Cloud Run.
walk_dirdefaults torecursive=False— explicitrecursive=Truerequired for nested folder corpora.dangerouslyDisableSandboxrequired for Rust-engine LMDB startup in dev — CLAUDE.md gotcha addition.
UC10 cadence resolution:
- localfs → native fs-watch via
watchfiles 1.1.1(FSEvents on macOS / inotify on Linux, 1600ms debounce). Real-time. - Remote sources (when added at v1.1+) → scheduled
App.update(). SharePoint v1.1 path = out-of-band sync to local mount + localfs binding.
Output: 0.9-spike-S2-cocoindex-folder-binding.md.
Residual questions (downstream spikes):
- Postgres target binding shape vs KH’s 70-col schema — S1 / G1.
- Per-row failure isolation under extraction errors — S1 / S8.
- NEW S14 cocoindex concurrency — LMDB single-writer multi-process behaviour on Cloud Run.
- DRAFT-vs-final dedup substrate choice — S10 (cocoindex confirmed insufficient).
- Webhook wiring for any future remote-source connector.
4 errata flagged for downstream docs (per spike output §errata) — defer to next 0.9 synthesis pass.
Original spec preserved for traceability:
S228 update: baseline corpus is
docs/client-documentation-base/. Real client-shaped data covering binary docx + final-and-DRAFT pairs + markdown + forms PDF + Excel. Question: Does cocoindex’slocalfs.walk_dir(...)source binding handle our actual client content shape? Does it correctly classify add / edit / delete / rename / binary-replace per UC4.4? What polling cadence + webhook strategy works per connector? What’s the v1 connector list?
Method:
-
Use canonical baseline corpus:
docs/client-documentation-base/├── binary/ (4 final docx + 3 DRAFT docx + 1 PDF)├── markdown/ (13 numbered .md + 4 audit .md + 3 DRAFT .md + 4 bid library .md)└── forms/ (Cloud Security Principles V5.3 .xlsx + Standard Selection Questionnaire .pdf) -
Configure cocoindex source binding (API per
0.8.2-cocoindex-evaluation.mdline 46):from cocoindex.sources import localfssource_iter = localfs.walk_dir("docs/client-documentation-base/").items() -
Observe initial-ingest behaviour:
- File enumeration across the 3 sub-folders + binary/markdown/forms shapes
- Per-file content_hash computation (md5? sha256? file mtime?)
- Binary files (PDF, DOCX, XLSX) — opaque blob vs pass-through to adapters
-
NEW S228 — exercise UC4.4 change classification by simulating each kind:
- Add: copy a new .md file in
- Edit: modify a .md (atomic + bounded change)
- Delete: remove a file
- Rename: move a file (path-changed-hash-equal)
- Binary-replace: swap a final .docx for its DRAFT (or vice versa) — same scenario captured in S188 audit (
docs/audits/user-feedback-from-re-ingestion.md) - Verify each is correctly classified by cocoindex’s Δ semantics
-
NEW S228 — DRAFT-vs-final dedup test (feeds S10 cross-record dedup):
- Initial ingest of all 4 final + 3 DRAFT docx files
- Observe whether cocoindex flags DRAFT/final pairs as near-duplicates natively, or needs custom @coco.fn
- Confirms whether S10 substrate evaluation needs to extend cocoindex or look elsewhere
-
NEW S228 — UC10 polling cadence + webhook investigation:
- localfs: fs-watch real-time? polling fallback?
- SharePoint connector availability + webhook semantics
- Notion + Google Drive + Dropbox + Box connector status
- Document the cadence + webhook capability per connector at current cocoindex version
-
SharePoint connector (if available):
- Check
cocoindex.sourcesmodule for SharePoint binding - If exists: stub config, observe API auth + change-detection semantics
- If not exists: document gap; SharePoint becomes v1.1 vs v1
- Check
-
Verify connector list:
- Read cocoindex docs / connector list
- Compile authoritative connector list at current version
- Cross-reference vs v1 requirements (localfs, SharePoint, Notion, Google Drive, Dropbox, Box)
Success criteria:
- localfs handles all 5 file types in test folder
- Δ semantics work as expected for all 5 change kinds (add / edit / delete / rename / binary-replace)
- SharePoint connector present (or roadmap clear)
Decision gate:
- All 5 file types + SharePoint: v1 connector list = localfs + SharePoint
- All 5 file types, no SharePoint: v1 = localfs; SharePoint v1.1
- Connector limitations on file types: flag for adapter-layer fallback (KH’s existing extractors)
Output: 0.9-spike-S2-cocoindex-folder-binding.md
Budget: 2-3 days. Owner: worktree sub-agent (parallel with S1 after S1 day 1).
S3 — mempalace observe-only
Section titled “S3 — mempalace observe-only”Status: ✅ COMPLETE S229. G3 outcome = PROCEED with one upstream caveat (mempalace_search broken at runtime — already known per CLAUDE.md gotcha; KH adopts the Shape B response contract regardless).
Outcome: Shape A + B + C + miner all confirmed on live mempalace v3.3.5 + canonical corpus. OQ4 ADOPTION-PROVISIONAL → CONFIRMED for schema patterns + MCP patterns + wing-model. Miner library importable BUT does NOT emit KG triples (mempalace’s mining is verbatim-chunk + Q&A-pair extraction; KH retains Pass 2 classifier for KG triple generation).
Sub-validations:
- Shape A — 1:1 portable. 4 additive columns to
entity_relationships(valid_from,valid_to,confidence,source_content_item_id,adapter_name) + 1 temporal index. Verbatim live DDL captured from~/.mempalace/knowledge_graph.sqlite3. - Shape B — response shapes confirmed via live MCP tool calls (
mempalace_kg_query,_list_drawers,_status,_get_taxonomy). Adopt contract; do NOT depend onmempalace_searchruntime. - Shape C (wing-model) — observed 16 live wings × 8 rooms × 128,534 drawers. Maps cleanly to per-client palace / per-user wing / per-workspace room.
- Miner — library importable via uv-tool Python; subprocess CLI wrappable. Pip-install rejected for Cloud Run (~600 MB transitive deps).
Key surprises:
mine()returnsNone(stdout-only, not library-shape).- Miner ≠ Pass 2 classifier (miner doesn’t emit KG triples; KH retains its Pass 2 for that).
chunk_exchangesQ&A pattern only fires on>-quoted conversation transcripts (NOT FAQ markdown — KH keeps the existing docx Q&A adapter for client Q&A library shape).- Palace
.corrupt-/.drift-directories evidence v3 stability ceiling — v4-alpha PG backend is the longer-term fix (Q4.6 DEFERRED-POST-LAUNCH stands).
Residual questions:
- Q4.14 provenance-enum unification — graphify’s confidence-label enum vs mempalace’s freeform
adapter_name(S12 spike resolved Q4.14 itself; mempalace integration viaadapter_nametext field is separate). valid_fromsemantic source — Pass 2 LLM-emitted vscaptured_datedefault — needs §6.6 / §6.8 implementation detail.- v4-alpha PG backend timing.
Output: 0.9-spike-S3-mempalace-observe.md (607 lines).
Original spec preserved for traceability:
Question: What does mempalace’s data flow look like in practice? Are Shape A + B + miner adoption assumptions correct? Method: (1) Install mempalace; (2) Run
mempalace mine; (3) Inspect drawers + KG triples + Q&A; (4) Validate Shape A portability; (5) Validate Shape B MCP pattern; (6) Validate miner importability. Success criteria: Shape A 1:1 portable; Shape B matches expectations; miner library-importable OR subprocess-wrappable. Decision gate options: All 3 confirmed (proceed) / mismatch (revise architecture). G3 outcome: All confirmed → PROCEED.
Budget: 1-2 days. Owner: worktree sub-agent. Actual: ~8.5 hours main-session worktree sub-agent.
S4 — pullmd 50-URL bake-off
Section titled “S4 — pullmd 50-URL bake-off”Status: ✅ COMPLETE S229. G4 outcome = CONDITIONAL PASS — adopt pullmd for HTML / Cloudflare / Google News / Reddit; pre-route PDF URLs to KH’s existing Jina Reader Tier 2.5; drop Firecrawl entirely.
Outcome: pullmd nailed the four shape categories (HTML, Cloudflare-protected, Google News, Reddit) with quality scores at or above KH’s current cascade. The PDF shape failed silently (HTTP 200 + body = %PDF-1.6 ... escaped binary), so v1 architecture routes PDF URLs to Jina Reader before pullmd ever sees them.
Key findings:
- PDF silent-binary-garbage failure — pullmd returns HTTP 200 with
X-Quality 0.5for.pdfURLs, body rendered as escaped binary. Naive ingest would store unusable embeddings. Mitigation: add HEAD /.pdfcontent-type sniff before calling pullmd; route PDFs to Jina Reader Tier 2.5 (KH already wired). Confirms 0.8.4 evaluation’s “no PDF support” with the empirical failure mode. - Reddit is a black-and-white win — KH cascade hard-failed 3/3 r/TeachingUK threads via Firecrawl explicit-deny → summary_fallback (1 word output); pullmd nailed 3/3 with full nested comment trees (
X-Source: reddit, q=1.0, 677-2257 words). This capability is currently impossible in KH without pullmd. Justifies adoption independent of the other shape categories. - Cloudflare argument weaker than 0.8.4 framed — KH’s plain
fetchalready handlescf-cache-status: DYNAMICsites like schoolsweek.co.uk (doesn’t trigger anti-bot at v1 volume). Pullmd still wins on the 1/10 that needed Playwright escalation, but KH isn’t silently broken on Cloudflare today. - KH prod has 0 PDF URLs and 0 Reddit URLs in 95
url_import/rss_feedrows — despite both being v1 target shapes per architecture. Suggests silent ingest failure OR users haven’t tried. Trace pre-launch. pip install pullmd-clidoes NOT exist — README documents only Docker compose / npm-from-source / pre-built Docker image. Spike-plan’s original install instruction was incorrect; v1 deployment uses Docker.- Cosmetic locale leak — pullmd’s Reddit output uses German
## Kommentare (50 von 64)heading (Docker container locale). Configurable; verify pre-v1.
Residual questions for §10 architecture rewrite (sub-agent B’s output) + downstream synthesis:
- S4-Q1: PDF-Jina-preserved hybrid wording for §10 — clarify pullmd is HTML/Cloudflare/GN/Reddit; Jina is PDF; both are pre-cocoindex adapter tier.
- S4-Q2: Confirm “drop Firecrawl entirely” — collapse-candidates §7 already lists Firecrawl as CONDITIONAL; S4 promotes to DECIDED-RETIRE.
- S4-Q3: Why 0 PDF + 0 Reddit in prod? Trace tooling (silent ingest path) — actionable bug investigation.
- S4-Q5:
X-Quality < 0.6asextraction_quality_logwarning threshold — needs schema column + classifier integration. - S4-Q8: Re-bake on non-Phew tenant when available — Phew’s URL corpus skews to UK-education sources; cross-tenant validation pre-v1.
Artifacts: 0.9-spike-S4-pullmd-bakeoff.md (398 lines) + raw data at /tmp/claude/pullmd-spike/ (temporary).
Original spec preserved for traceability:
Question: Does pullmd extraction quality match or exceed current Readability + Firecrawl + Playwright cascade? Method: Sample 50 URLs (20 HTML + 10 Cloudflare + 10 Google News + 5 PDF + 5 Reddit); run through Path A (pullmd) / Path B (KH cascade) / Path C (ground truth, 10 URLs). Success criteria: quality score ≥ cascade aggregate; ≥ 90% URL types handled. Decision gate options: Pass (adopt) / Fail-on-Cloudflare (keep agent-browser Tier 2.5) / Fail-on-completeness (evaluate fork). G4 outcome: CONDITIONAL PASS — adopt for HTML/CF/GN/Reddit; PDF pre-route to Jina; drop Firecrawl.
Budget: 1 day. Owner: worktree sub-agent. Actual: ~16.7 hours main-session worktree sub-agent.
S5 — skill-seekers dependency_analyzer.py on KH dev codebase
Section titled “S5 — skill-seekers dependency_analyzer.py on KH dev codebase”Status: ✅ COMPLETE S229. G5 outcome = SKIP (do not adopt). Stay on knip + graphify two-detector stack.
Outcome: observed 0% non-overlapping true positives vs the 20% success criterion. Analyzer extracted 786 import statements across 250 files but resolved 0 to internal nodes (250 nodes, 0 edges in output graph). Root cause: _resolve_import() does literal string-match only — no tsconfig @/ path-alias expansion, no relative-path joining, no Python package resolution. The tool is designed for Claude Skill references/*.md generation, not dev workflow.
Key findings:
- No standalone CLI — invocation requires Python wrapper importing
skill_seekers.cli.dependency_analyzer.DependencyAnalyzer. - 0.8.5 §Q5’s “useful supplement” verdict was inferred from docstring, not empirical — this spike contradicts it. Correction note recommended for next 0.9 synthesis.
- 50+ transitive deps (langchain, llama-index, pymupdf, gitpython, sqlalchemy, schedule, …) for a 975-LOC analyser with one real dep (networkx). Poor footprint trade.
- The hypothesised TS↔Python call-gap use case isn’t solved by import-graph tools anyway — those gaps are process-boundary (
child_process.spawn), not import statements.
Output: 0.9-spike-S5-dep-analyzer.md (244 lines).
Original spec preserved for traceability:
Question: Does
dependency_analyzer.pysurface useful signals for KH dev workflow that aren’t already in knip + graphify? Method: (1) Install skill-seekers; (2) Run dependency_analyzer on KH; (3) Compare vs knip + graphify + ESLint unused-imports; (4) Count signal classes (TP / FP / cross-class drift). Success criteria: ≥ 20% non-overlapping true positives vs knip + graphify. Observed 0%. Decision gate options: Pass (adopt) / Fail (skip). G5 outcome: Fail → SKIP.
Budget: 0.5 day. Owner: foreground. Actual: ~6.5 hours main-session worktree sub-agent.
S6 — mcp-scan feasibility + cost (re-framed S228)
Section titled “S6 — mcp-scan feasibility + cost (re-framed S228)”Status: ✅ COMPLETE S229. G6 outcome = PASS — v1 CI step adopted (inspect mode local-only); v2 cloud-upload mode deferred pending data-residency sign-off + v1 stability.
Outcome: Tool runs cleanly against KH’s MCP surface via local stdio bridge. False-positive rate post-allow-list ≈ 0-1 per scan (W001 prompt-injection word matches in lib/mcp/tools/{content,bids,governance}.ts for KH-domain terms like “override” / “previous” / “ignored otherwise” — all suppressible via ignore-list). No FP-flood.
Key actionable findings:
- Live staging scan blocked by Vercel SSO — tool tries 6 transport variants, all hit 401 from Vercel deployment-protection before reaching the MCP OAuth challenge. v1 mitigation: scan via local stdio bridge (
scripts/mcp-scan/serve-fixture.tsre-exportingregisterTools). - Package renamed:
mcp-scan→snyk-agent-scanv0.5.1 (install viapipx install snyk-agent-scan). Spike-plan §S6 install line was stale. scanmode posts to api.snyk.io withSNYK_TOKEN;inspectmode is purely local. v1 usesinspectonly — no cloud dependency, no data egress.- Net-new coverage vs
/security-reviewskill: W001 (prompt-injection words), W015-W018 (schema), destructive/public-sink labels, TF* toxic-flow patterns (v2 cloud-only). - ⚠️ SECURITY GOTCHA — CLI dumps env-var values verbatim in JSON when run with
--scan-all-users(auto-discovery mode). Empirical observation during spike: surfaced 10 local stdio MCP configs incl.GITHUB_TOKEN/SUPABASE_ACCESS_TOKEN/ Stripe live key. Adoption rule: NEVER use--scan-all-usersin CI. Add redaction wrapper before any v2 cloud upload. This is a CLAUDE.md gotcha candidate.
v1 adoption cost: ~0.5 day (CI job + stdio bridge scripts/mcp-scan/serve-fixture.ts + ignore-list config + runbook).
v2 adoption cost: ~2-3 days (cloud upload + redaction wrapper + data-residency sign-off + TF* toxic-flow integration) — gated on v1 stability + sign-off.
Output: 0.9-spike-S6-mcp-scan.md (325 lines).
Original spec preserved for traceability:
S228 update: OQ5 is RATIFIED-YES per Liam — adopt mcp-scan. Spike confirms feasibility + adoption cost shape. Question: Does mcp-scan run cleanly against KH’s MCP tool surface? What’s the false-positive rate? What’s the v1-vs-v2 split? Method: (1) Install; (2) Run against KH MCP staging; (3) Review findings (prompt-injection / schema / exfiltration / auth gaps); (4) Cross-reference vs
/security-review. Decision gate options: Pass (CI step) / Fail (FP flood) / Findings worth fixing (issues regardless of CI). G6 outcome: Pass → v1 CI inspect-mode + v2 cloud deferred.
Budget: 0.5 day. Owner: foreground. Actual: ~13.5 hours main-session worktree sub-agent.
S7 — Pre-re-ingest evaluation (graphify data + CSP checklist forms)
Section titled “S7 — Pre-re-ingest evaluation (graphify data + CSP checklist forms)”S228 update: primary eval target is now the CSP checklist Excel + Standard Selection Questionnaire PDF in docs/client-documentation-base/forms/ — these are real client artefacts the platform should auto-complete with citations. Baseline question set still includes graphify comparison.
Question: (a) Can we evaluate KH’s current value-delivery vs graphify-ingested value WITHOUT running a new full ingest? (b) Does the platform auto-complete most fields of the CSP checklist with appropriate citations against current corpus + graphify-ingested context?
Method:
-
Form-based eval (NEW S228 — primary):
- Load
Cloud Security Principles Checklist V5_3 - PHEW.xlsx— extract questions - Run each question via current KH MCP search tools + graphify-baseline data + Anthropic citations API
- For each question, score: faithfulness / completeness / citation-presence / coverage-vs-gap
- Output: per-question-with-citation auto-complete; gap list for questions with no citable answer
- Load
-
Question-set eval (legacy S227 framing, secondary):
- 10-20 client-relevant questions from LBBD-CSP context:
- PCI-DSS policy questions
- Production-infrastructure questions (where Bitdefender false-positive happened)
- FUNC-XXX feature questions
- Sample bid Q&A pairs
- Cross-domain questions (security + infrastructure)
- 10-20 client-relevant questions from LBBD-CSP context:
-
Run each via current KH MCP search:
- Use
search_knowledge_base+search_qa_libraryMCP tools - Capture: top-5 results, similarity scores, citations
- Use
-
Run each against graphify-baseline data:
- Query
code-and-docs-with-llmgraph (latest run, $18, 22,155 isolated nodes) - Use graphify’s query MCP if installed; else direct Cypher / GraphML inspection
- Capture: relevant nodes + edges + paths
- Query
-
Score per-question:
- Faithfulness (how true to source)
- Completeness (how much of relevant context surfaced)
- Citations (how clear is provenance)
- Time-to-answer (manual stopwatch)
-
Compute aggregate delta: graphify-ingested vs current KH.
Success criteria:
- Score delta is informative (one shape clearly outperforms; or both have specific strengths)
Decision gate:
- Graphify-ingested wins clearly: urgency to ship canonical pipeline (informs Phase 2 priority)
- Current KH wins clearly: less urgency on canonical pipeline; prioritise other work
- Mixed: identify specific shape strengths; informs canonical pipeline emphasis
Output: 0.9-spike-S7-current-vs-graphify-eval.md with per-question scoring + aggregate + recommendation.
Budget: 1 day. Owner: foreground or worktree sub-agent.
S8 — Q&A flow validation (end-to-end test)
Section titled “S8 — Q&A flow validation (end-to-end test)”S228 update: test corpus is docs/client-documentation-base/ (replaces ad-hoc fixtures); also exercises UC8 cross-record dedup via DRAFT-vs-final pairs + UC4.4 binary-replace path.
Question: Does the §6.2 Q&A flow architecture work end-to-end on the canonical baseline corpus? Does the flow handle DRAFT-vs-final + Track-Changes + form-based question-extraction correctly?
Why: the Q&A separation + extraction + curation + retrieval architecture is novel. Validate before committing to Phase 2-3 build.
Method:
-
Pre-spike setup (post-S1 cocoindex schema confirmation + post edit-flow §6 architectural decision):
- Apply
q_a_pairs+q_a_extractions+citationsmigrations on cocoindex-spike branch - Set up cocoindex flow with mempalace miner integration
- Configure cocoindex source binding to
docs/client-documentation-base/
- Apply
-
Test corpus (canonical baseline):
binary/— 4 final docx + 3 DRAFT docx (exercises track-changes + DRAFT-vs-final dedup)markdown/— 13 numbered .md (Phew company corpus) + 4 audit .md + 3 DRAFT .md (Q&A heading-based) + 4 bid library .mdforms/— CSP Excel + SSQ PDF (exercises form question-extraction per UC8 + S7)
-
Execute end-to-end:
- Trigger ingest via cocoindex
- Verify q_a_extractions populated (mempalace miner output)
- Manually curate 2-3 extractions → q_a_pairs
- Run
search_qa_libraryagainst test queries - Verify retrieval chain (q_a_pairs → q_a_extractions → corpus chunks)
- Verify citations table populated correctly
-
Test edge cases:
- Source content updated (mempalace miner re-runs; q_a_extractions invalidated)
- q_a_pair valid_to expires (search returns warning)
- Bid response promoted to q_a_pair (origin_kind tracking)
Success criteria:
- All 4 ingest paths populate q_a_extractions correctly
- Curation flow promotes to q_a_pairs cleanly
- Retrieval chain works (high → low confidence fallback)
- Edge cases handled
Decision gate:
- Pass: Phase 2-3 architecture-impl commits to design as documented
- Fail in retrieval chain: revise §6.2 retrieval logic before Phase 3
- Fail in extraction: revise mempalace miner integration
Output: 0.9-spike-S8-qa-flow-validation.md
Budget: 1-2 days. Owner: foreground (post-S1).
S9 (NEW S228) — Write-back semantics validation
Section titled “S9 (NEW S228) — Write-back semantics validation”Gates on: edit-flow investigation §6 architectural decision (UC1 / UC4 Candidate A vs B vs C). Cannot start until §6 lands.
Question: Does the chosen Candidate (A — in-platform editor + filesystem write / B — Claude API text-editor-tool / C — Managed Agent) correctly implement UC1 typo fix end-to-end? Does cocoindex skip flow re-run when content-hash unchanged from prior write (idempotency)? Can Yjs collab plugin enable cleanly on existing Tiptap Q&A ContentEditor?
Method:
- Implement chosen Candidate against
docs/client-documentation-base/markdown/test file. - UC1 walk-through: open editor → make typo fix → save → verify both folder file + materialised representation reflect change → verify cocoindex’s response on next scan/webhook.
- Cocoindex idempotency test: check if engine skips flow run when input-hash matches its expected output-hash. If not, design marker-file or write-window-lock workaround.
- Yjs collab plugin enable: mount Yjs awareness on Tiptap Q&A ContentEditor; verify two simultaneous edit sessions converge cleanly. ~1 day work per §9.8 of edit-flow doc.
- Sidecar pattern (binary): test write-back to a markdown sidecar adjacent to a binary docx file; verify cocoindex picks up sidecar change correctly.
Success criteria:
- UC1 flow works end-to-end without manual intervention
- Cocoindex idempotency confirmed OR clean workaround in place
- Yjs collab plugin enables with no Q&A editor regression
- Sidecar pattern integrates with cocoindex source binding
Decision gate:
- Pass: chosen Candidate is the v1 implementation; UC4/UC6/UC7+10 inherit
- Fail in idempotency: revisit Candidate B or C (where DB write is downstream of file write, dissolves idempotency concern)
- Fail in Yjs: defer collab; concurrent-user conflict goes to git-style or Claude API tool use as alternate
Output: 0.9-spike-S9-write-back-semantics.md
Budget: 2-3 days. Owner: foreground (post-edit-flow §6).
S10 (NEW S228) — Cross-record dedup substrate (UC8)
Section titled “S10 (NEW S228) — Cross-record dedup substrate (UC8)”Gates on: S2 + S3 (need cocoindex + mempalace observed before substrate evaluation).
Question: Which substrate handles cross-record dedup best for KH’s needs (DRAFT-vs-final detection per S188 + near-duplicate Q&A across bid library re-imports + collapse-N-to-golden-record per UC8)? Three candidates per §10 of edit-flow investigation:
- (a) Cocoindex post-target-write
@coco.fn— content-hash similarity + Postgres similarity search - (b) Mempalace KG entity-resolution — name + property + temporal-overlap matching
- (c) Skill-seekers metadata categorisation — keyword + categories pattern (likely inspiration only per §2.4 verdict)
Method:
- Test corpus:
docs/client-documentation-base/binary/DRAFT-vs-final docx pairs (3 pairs known to overlap-but-not-identical) +markdown/bid library variants (4 versions of similar content). - Configure each substrate against the corpus:
- (a) Cocoindex: implement post-target-write @coco.fn computing content-similarity + chunk-similarity
- (b) Mempalace: load corpus through mempalace mine, observe KG entity-resolution output
- (c) Skill-seekers: run categories keyword match (sanity baseline)
- Score per-pair detection accuracy:
- True-positive rate (catches the known DRAFT-vs-final pairs)
- False-positive rate (incorrectly flags genuinely-different content as duplicates)
- Latency / compute cost
- Evaluate UX shape compatibility — which substrate naturally surfaces side-by-side diff, golden-record selection, citation-preservation?
Success criteria:
- ≥1 substrate achieves ≥90% true-positive on known pairs at ≤5% false-positive
- UX shape supports user-approve-merge flow per UC8
Decision gate:
- Cocoindex wins: S228 reframe — UC8 substrate is cocoindex; mempalace-as-supporting (audit lineage); skill-seekers retired as substrate candidate
- Mempalace wins: UC8 substrate is mempalace KG; cocoindex feeds; skill-seekers retired
- Hybrid wins: combine cocoindex (content-hash) + mempalace (entity overlap) — design composition
- None wins: UC8 architecture defers; v1 ships without cross-record dedup; user-driven dedup only
Output: 0.9-spike-S10-dedup-substrate.md
Budget: 2-3 days. Owner: worktree sub-agent (after S2 + S3).
S11 (NEW S228) — Playwright swap with agent-browser (OQ6 RE-FRAMED)
Section titled “S11 (NEW S228) — Playwright swap with agent-browser (OQ6 RE-FRAMED)”Status: ✅ COMPLETE S229. G11 outcome = KEEP Playwright. OQ6 / CX.7 RESOLVED-NO-SWAP.
Outcome: Playwright 20/20 URLs extracted; agent-browser 14/20 (6 failures: 4 Cloudflare bot-detection + 1 SPA-hydration empty body + 1 networkidle hang). Body completeness on URLs both succeed on: agent-browser captures mean 0.81 of Playwright’s word count — fails the ≥-Playwright success criterion. Median latency tied (~2.1s); Playwright mean higher only due to 3 URLs hitting its 15s networkidle cap (which gracefully fell through). Setup overhead penalty: sandbox-incompatible, ANSI/multiline output parsing, global install — without removing Playwright (still needed for KH E2E).
Critical re-framing finding (worth flagging in 0.9 synthesis): the original wave-08 OQ6 framing was about a Playwright tier that does not exist in lib/intelligence/content-extractor.ts (its cascade is RSS → fetch → Jina → Firecrawl). The actual Playwright surface is pullmd’s Tier-3 sidecar — so this spike only matters conditional on S4 pullmd adoption (which landed: CONDITIONAL PASS). Architecture sections §10.1 / §10.2 should clarify the Playwright surface lives in pullmd’s Tier-3, not in KH’s own cascade.
Key surprises:
- The original wave-08 OQ6 framing was about a Playwright tier that doesn’t exist in KH’s own extraction cascade.
- agent-browser triggers Cloudflare bot-detection on 20% of the sample where Playwright does not — a categorical capability gap unrelated to wait-strategy.
- Spike-plan §7 risk-register mitigation “Keep agent-browser as Tier 2.5 fallback” is wrong and should be reversed (Playwright keeps the role; agent-browser is not adopted as a tier).
Residual questions:
- Sample-size sensitivity (20 → 50 URLs would harden the completeness ratio); a prod-corpus draw (live RSS URLs where
method='firecrawl') would improve external validity. - agent-browser stealth posture may improve in future versions — re-evaluate annually.
- pullmd Tier-3 fire-rate not measured — if Cloudflare-native short-circuit handles ≥ 95% of URLs before Tier-3 fires, the swap-cost-impact is negligible regardless.
Output: 0.9-spike-S11-playwright-swap.md (302 lines).
Original spec preserved for traceability:
S228 critical re-framing: original wave-08 OQ3 was about swapping Playwright with agent-browser, NOT Firecrawl (per Liam S228 verbatim correction). Question: Can agent-browser replace Playwright as the JS-rendered fallback in pullmd’s tier 2.5 / 3 cascade? What’s the comparative quality + latency + cost? Method: Sample 20 URLs requiring JS rendering; run Path A (Playwright) / Path B (agent-browser) / Path C (ground truth). Decision gate options: Pass (swap) / Fail (keep Playwright). G11 outcome: Fail → KEEP Playwright; OQ6 closed as no-swap.
Budget: 1 day. Owner: worktree sub-agent. Actual: ~19.9 hours main-session worktree sub-agent.
S12 (NEW S228) — Graphify confidence-label taxonomy adopt (Q4.14)
Section titled “S12 (NEW S228) — Graphify confidence-label taxonomy adopt (Q4.14)”Status: ✅ COMPLETE S229. G12 outcome = EXTEND-EXISTING-PROPER (revised framing). Q4.14 RESOLVED-ADOPT per S228 decision-graph; spike confirmed schema-integration shape.
Outcome (replaces original extend-vs-replace binary): the spike’s original framing was moot — graphify’s three confidence labels and Q4.7’s three KG-provenance enum values are literally the same set: EXTRACTED, INFERRED, AMBIGUOUS. Zero gaps, zero redundancies. 1:1 identity. Both feedback streams (mempalace wave-08 → Q4.7; graphify §5.2.C → Q4.14) independently converged on the same three-value taxonomy.
Adopted shape:
- Single new column
provenance text NOT NULL DEFAULT 'EXTRACTED' CHECK (...)onentity_relationships— CHECK pattern matches the table’s existingrelationship_typeprecedent. - Repurpose existing
confidence numeric(3,2)from placeholder-hardcoded-1.0 to graphify’s discreteconfidence_scorerubric value (1.0 / {0.55-0.95} / 0.1-0.3 by label). - Update Pass 2 classifier (
lib/ai/classify.ts:1772-1793) to emit both fields. - No backfill needed — pre-launch re-ingest (CX.22) wipes the table.
Output: 0.9-spike-S12-graphify-confidence.md (286 lines).
Original spec preserved for traceability:
Question: Does graphify’s confidence-label taxonomy fit alongside the KH-native KG-provenance enum (Q4.7) without semantic collision? What’s the schema-integration shape? Method: (1) Read graphify’s confidence-label taxonomy spec; (2) Map each label to a Q4.7 value; (3) Identify gaps + redundancies; (4) Propose schema: extend OR replace. Decision gate options: Extend (keep Q4.7 + add graphify column) / Replace (Q4.7 becomes graphify-aligned). G12 outcome resolved the binary into a single-column extend pattern using verbatim Q4.7 enum.
Budget: 0.5 day. Owner: foreground. Actual: ~6 hours main-session worktree sub-agent.
S13 (NEW S228) — ESLint input-required rule (DW.13)
Section titled “S13 (NEW S228) — ESLint input-required rule (DW.13)”Status: ✅ COMPLETE S229. G13 outcome = ADOPT-ADVISORY (warn). Wire rule into quality-precheck at warn severity (not error); promote to error after focused remediation of the 6 known handler-level gaps.
Outcome: Custom ESLint rule eslint-rules/no-unvalidated-route-input.js (244 lines) prototyped + registered in eslint-rules/index.js (NOT yet wired into eslint.config.mjs per spike constraint).
Audit coverage stats (193 routes total):
- 134 routes (69.4%) use BOTH Zod-helper (parseBody / parseSearchParams / etc.) + auth helper.
- 4 routes (2.1%) Zod-helper only.
- 50 routes (25.9%) auth helper only.
- 5 routes (2.6%) neither — all legitimate (cron secret / OAuth / health / static).
- Body consumption Zod coverage: 100%. Query consumption Zod coverage: 100%.
- Handler-level path-param gaps: 6 handlers across 5 files (rule found all 6; grep-audit only caught 3 file-level + missed 2 mixed-handler cases — empirical validation of the rule’s incremental value over grep).
FP/TP rate:
- v1 (UUID-only regex whitelist): 6 TP / 3 FP (66.7% / 33.3%) — failed the 80%/10% criterion.
- v2 (regex-naming convention
*_RE | *_REGEX | *Regex): 6 TP / 0 FP (100% / 0%) — passes criteria.
Overhead: baseline 23.151s → with rule 23.214s = +63ms (vs 10s budget). Two orders of magnitude headroom.
Wiring instructions in spike doc §7. Rule file at eslint-rules/no-unvalidated-route-input.js; registered in eslint-rules/index.js.
Output: 0.9-spike-S13-eslint-input-required.md.
Original spec preserved for traceability:
S228 V1 finding caveat: KH currently uses the
getAuthorisedClient()discriminated-union pattern (per CLAUDE.md), NOT tRPC-shaped Zod. Spike must FIRST audit current input-validation pattern coverage, THEN target the rule against KH’s actual idiom. Question: What’s KH’s actual input-validation pattern coverage acrossapp/api/? Where coverage is missing, can a custom ESLint rule flag it without false-positive flood? What’s the precheck overhead? Method: (1) Audit pattern coverage; (2) Identify gaps; (3) Implement rule; (4) Measure FP/TP rate; (5) Measure overhead. Decision gate options: Pass (precheck) / Fail (defer) / Pass-with-overhead-concern (advisory) / Audit-adequate (RESOLVED-NOT-NEEDED). G13 outcome: Pass-with-overhead-concern → ADVISORY (warn).
Budget: 0.5 day. Owner: foreground. Actual: ~11 hours main-session worktree sub-agent.
S14 (NEW S229) — Cocoindex concurrency under LMDB single-writer
Section titled “S14 (NEW S229) — Cocoindex concurrency under LMDB single-writer”Status: ✅ COMPLETE S230. G14 outcome = PASSED — single-orchestrator-instance with isolated per-instance LMDB. S2’s “LMDB single-writer hard-blocks concurrent processes” framing dissolved empirically: 10 concurrent App.update() writers against the same LMDB completed cleanly (10/10 success, no lock errors, post-run memo coherent). LMDB IS transaction-level single-writer, but cocoindex’s per-row writes are short — concurrent processes interleave at the lock without contention-induced failure. The real cost shape is “N concurrent processes do ~N× the @coco.fn work” (no cross-process dedup), not “lock contention failure”. Read-only consumer pattern (MVCC) works. Crash-recovery (SIGKILL writer mid-flight) works.
Adopted topology:
- v1 Cloud Run cocoindex job:
min_instances=1, max_instances=1OR scheduled Cloud Run job. Each instance gets its own ephemeral LMDB. Re-fingerprint cost per cold-start (~7s for 35-file canonical corpus; scales linearly to minutes for full client corpus) — acceptable at v1 scale; no queue infra needed. - Postgres-as-ops-DB option does NOT exist in cocoindex 1.0.3 (Rust binary inspection confirmed). Postgres is target-only.
- Reversibility excellent — schema/data/target bindings unchanged if v1.1 demands multi-writer.
Output: 0.9-spike-S14-cocoindex-concurrency.md.
Errata: S2 §4.5 + §6.1 surprise 5 framing of “LMDB single-process semantics blocks multi-worker” is overcautious; the actual constraint is “wasted compute via uncoordinated parallel work”, not “blocked writers”. Correction note in S14 §6.2 recommends downstream doc updates.
Original spec preserved for traceability:
S2 observed cocoindex 1.0.3 swapped its SQLite ops-DB for LMDB; LMDB is single-writer at the process level. This constrains cocoindex’s multi-worker topology, which directly affects Cloud Run deployment (where horizontal scaling is the default model).
Question: What Cloud Run deployment topology supports cocoindex’s LMDB ops-DB at v1 throughput? Three candidates:
- (a) Single-orchestrator-instance — exactly one cocoindex process at a time (vertical scale only); other instances are read-only consumers of the materialised representation. Lowest risk; throughput ceiling = single-instance LMDB write rate.
- (b) Queue-based serialisation — workers enqueue change events; a designated orchestrator-instance dequeues + runs cocoindex flow. Adds queue infra (Cloud Tasks / Pub/Sub) but preserves multi-instance UI / API layer.
- (c) Read-only-worker fan-out — read replicas of LMDB (if cocoindex supports) for query-side workers; writes funnel through one orchestrator. Closest to existing Cloud Run ergonomics; requires LMDB read-replica support (verify in spike).
Why this gates Phase 2: if (a), cocoindex’s runtime footprint requires re-thinking Cloud Run autoscaling for KH’s pipeline tier. If (b), additional infra cost. If (c), simplest but unverified.
Method:
- Verify LMDB ops-DB locking semantics under concurrent
App.update()invocations (multi-process test on a single host first; then container-isolated test simulating Cloud Run). - Test cocoindex 1.0.3’s behaviour when two instances attempt simultaneous
update()against the same Postgres target — does the second block / fail / corrupt? - Test queue-based pattern: one orchestrator + N workers, workers POST change events, orchestrator drains.
- Test read-only-worker pattern: orchestrator writes LMDB; workers read LMDB-as-state (verify LMDB supports concurrent read from separate process).
Success criteria:
- At least one topology supports KH’s projected v1 throughput (~10-50 concurrent flow events at peak based on cocoindex-eval estimates).
- Topology decision is reversible if v1.1 cocoindex releases multi-writer support.
Decision gate:
- Single-orchestrator wins: v1 Cloud Run topology = vertical-scale orchestrator + horizontal-scale API/UI tier. Document in
0.9-intended-architecture.md§10/§11 deployment section. - Queue-based wins: add Cloud Tasks / Pub/Sub to v1 infra stack.
- Read-only-worker wins: verify LMDB cross-process read support; design orchestrator/worker split.
- None viable: escalate — cocoindex Cloud Run viability becomes a blocker; fall-back paths from S1 / G1 apply.
Output: 0.9-spike-S14-cocoindex-concurrency.md (NEW — to be created post-spike).
Budget: 1 day. Owner: worktree sub-agent or foreground. Sequencing: after S2 (gates on S2’s LMDB finding); ideally before or in parallel with S1 (since S1’s schema-coupling outcome interacts with topology choice).
3. Sequencing diagram (S228 update)
Section titled “3. Sequencing diagram (S228 update)”Day 1: S1 cocoindex schema-coupling [start, foreground] S5 skill-seekers dep_analyzer [foreground] S6 mcp-scan feasibility [foreground] S7 pre-re-ingest eval [start, foreground or sub-agent] S12 graphify confidence-label [foreground] S13 ESLint input-required [foreground]
Day 2: S1 [continue] S2 cocoindex folder binding (using docs/client-documentation-base/) [start, sub-agent] S3 mempalace observe [start, sub-agent] S4 pullmd bake-off [start, sub-agent] S11 Playwright swap with agent-browser [start, sub-agent] S7 [continue]
Day 3: S1 [continue / decide] S2 [continue] S3 [decide] S4 [decide] S7 [decide] S11 [decide]
Day 4: S1 decision gate S2 decision gate (UC10 polling cadence per connector) S10 cross-record dedup substrate [start, sub-agent — gated on S2+S3]
Day 5-6: S10 [decide] Edit-flow §6 architectural decision (parallel main-session work, NOT a spike)
Day 7-8: S8 Q&A flow validation [start, post-S1, post-edit-flow §6] S9 write-back semantics [start, post-edit-flow §6]
Day 9-10: S8 + S9 [continue / decide] Spike results doc + Phase 2 commit decisionWall-clock: 8-10 days with good parallel utilisation. Single-session can absorb most spikes on 1M context window. S9 + S10 + S8 are sequenced tail; the rest are mostly day-1-or-2 starts.
Architecture documentation rewrite (sub-agents B + C — task #9 + #10) can run IN PARALLEL with most spikes (per Liam S228 unlock direction). Only S8 + S9 need edit-flow §6 to land first.
4. Decision gates rollup (S228 update)
Section titled “4. Decision gates rollup (S228 update)”| Gate | Source spike | Decision |
|---|---|---|
| G1: cocoindex Scenario A vs B | S1 | Phase 2 commit OR fall-back to Phase 0.7 Stream 2 |
| G2: v1 connector list + UC10 polling cadence | S2 | LocalFS only / LocalFS + SharePoint / fallback paths; per-connector polling vs webhook |
| G3: mempalace adoption shape confirmed | S3 | Schema migration OR architecture revision |
| G4: pullmd adoption | S4 | Adopt OR keep current cascade |
| G5: skill-seekers dep_analyzer | S5 | Adopt as 3rd BNW-detector OR skip |
| G6: mcp-scan v1-vs-v2 split | S6 | CI step now / deeper integration later |
| G7: re-ingest urgency + form auto-completion baseline | S7 | Phase 2 priority calibration; CSP checklist coverage gap-list |
| G8: Q&A flow architecture confirmed | S8 | Phase 2-3 commit OR architecture revision |
| G9 (NEW): write-back semantics validated | S9 | Candidate A/B/C choice locked for v1; Yjs collab plugin enable confirmed |
| G10 (NEW): cross-record dedup substrate | S10 | Cocoindex / mempalace / hybrid / defer |
| G11 (NEW): Playwright swap with agent-browser | S11 | Adopt swap OR keep Playwright |
| G12 (NEW): graphify confidence-label schema integration | S12 | Extend Q4.7 enum OR replace with graphify-aligned |
| G13 (NEW): ESLint input-required adoption | S13 | Add to quality-precheck / advisory-only / defer |
Phase 2 commit requires: G1 (Scenario A) + G3 (mempalace OK) + G8 (Q&A flow validated) + G9 (write-back path chosen). G2, G4-G7, G10-G13 are calibration, not blocking gates.
If G1 = Scenario B:
- Architecture revision needed
- Fall-back evaluation: Phase 0.7 Stream 2 (9-12 weeks) vs hybrid (cocoindex new tables + KH owns shadow) vs schema redesign for cocoindex ownership
- New ratification cycle before Phase 2 commit
If G10 = no substrate clearly wins: v1 ships without cross-record dedup; user-driven dedup only via UC8 surface; substrate decision deferred to v1.1.
5. Output artefacts (S228 update)
Section titled “5. Output artefacts (S228 update)”After spike phase completion:
| File | Contents |
|---|---|
docs/plans/phase-0-investigation/0.9-spike-S1-cocoindex-schema-coupling.md | S1 detail + Scenario A/B decision |
docs/plans/phase-0-investigation/0.9-spike-S2-cocoindex-folder-binding.md | S2 detail + connector list + UC10 polling cadence |
docs/plans/phase-0-investigation/0.9-spike-S3-mempalace-observe.md | S3 detail + adoption confirmation |
docs/plans/phase-0-investigation/0.9-spike-S4-pullmd-bakeoff.md | S4 detail + per-URL scoring |
docs/plans/phase-0-investigation/0.9-spike-S5-dep-analyzer.md | S5 detail + adoption decision |
docs/plans/phase-0-investigation/0.9-spike-S6-mcp-scan.md | S6 detail + v1-vs-v2 split |
docs/plans/phase-0-investigation/0.9-spike-S7-current-vs-graphify-eval.md | S7 detail + per-question scoring + CSP coverage gap-list |
docs/plans/phase-0-investigation/0.9-spike-S8-qa-flow-validation.md | S8 detail + architecture confirmation |
docs/plans/phase-0-investigation/0.9-spike-S9-write-back-semantics.md | NEW — S9 detail + Candidate A/B/C choice + Yjs enable confirmation |
docs/plans/phase-0-investigation/0.9-spike-S10-dedup-substrate.md | NEW — S10 detail + dedup substrate decision |
docs/plans/phase-0-investigation/0.9-spike-S11-playwright-swap.md | NEW — S11 detail + agent-browser swap decision |
docs/plans/phase-0-investigation/0.9-spike-S12-graphify-confidence.md | NEW — S12 detail + Q4.14 schema integration |
docs/plans/phase-0-investigation/0.9-spike-S13-eslint-input-required.md | NEW — S13 detail + ESLint rule adoption |
docs/plans/phase-0-investigation/0.9-spike-results.md | Master rollup + Phase 2 commit decision |
6. Pre-spike preparation checklist (S228 update)
Section titled “6. Pre-spike preparation checklist (S228 update)”Before next session starts spike work:
- User ratified S228 OQ states per
0.9-context.md§2 (decision-graph + intended-architecture + spike-plan all reflect ratifications) - Canonical baseline corpus in place at
docs/client-documentation-base/(replaces/tmp/cocoindex-spike/) - Mempalace MCP registered in all 3 long-lived worktrees (S228 fix)
- User confirms spike phase budget (~14-18 days mostly parallel — increased from ~6-8 days due to 5 new spikes)
- Cocoindex spike branch created on Supabase (
/opt/homebrew/bin/supabase branches create cocoindex-spike --base staging) - mempalace + skill-seekers + mcp-scan + pullmd + agent-browser tools installed (Python + node packages)
- NEW S228 (V1 finding) — Yjs collab stack added to package.json:
yjs,y-prosemirror,@tiptap/extension-collaboration(currently NOT installed;package.jsonhas only base Tiptap 3.22.0 starter-kit). Required for S9 write-back semantics + UC1/UC4/UC6 in-platform editor concurrent-user CRDT. - Worktree-isolated sub-agent prompts pre-drafted for S2 + S3 + S4 + S10 + S11
- Edit-flow investigation §6 architectural decision landed (gates S8 + S9)
- Architecture documentation rewrite (sub-agent B for
0.9-intended-architecture.md; sub-agent C for any spike-plan post-spike updates) — runs in PARALLEL with spike phase per S228 unlock direction
7. Risk register (S228 update)
Section titled “7. Risk register (S228 update)”| Risk | Likelihood | Mitigation |
|---|---|---|
| Cocoindex Scenario B (engine demands schema ownership) | Medium | Architecture revision path documented (§4 G1); fall-back to Phase 0.7 Stream 2 |
| Mempalace miner is CLI-only (subprocess overhead) | Low | Confirmed in S3; cocoindex flow can subprocess-wrap |
| pullmd quality miss on Cloudflare-protected URLs | Resolved S229 — Low | S4 spike confirmed pullmd handles Cloudflare-protected URLs at parity-or-better with KH’s current cascade. S11 confirmed agent-browser is not a viable fallback (triggers Cloudflare bot-detection on 20% of sample where Playwright does not). Mitigation reversed: keep Playwright (inside pullmd’s Tier-3 sidecar) as fallback, NOT agent-browser. |
| Sub-agent context budget exhausted | Low (limit may be >200K per S228 observation; needs verification) | Brief sub-agents tightly; cap at ~150K explicit-budget per |
| S1 takes longer than 5 days (cocoindex setup friction) | Medium | If day 5: defer S8 to Phase 2 day 1; ship spike-results without S8 confirmation |
| Spike phase reveals architecture flaw | Low-medium | Architecture revision via new ratification cycle (no Phase 2 commit until resolved) |
| NEW S228 — Cocoindex idempotency unclear (S9) | Medium | If engine doesn’t skip on equal-hash, design marker-file or write-window-lock |
| NEW S228 — No dedup substrate clearly wins (S10) | Medium | v1 ships without cross-record dedup; user-driven only; defer to v1.1 |
| NEW S228 — Yjs collab plugin Q&A editor regression (S9) | Low | Test in worktree first; rollback path = disable plugin |
| NEW S228 — Architecture rewrite + spike phase desync | Low-medium | Sub-agents B/C reference latest spike-results in their reads; rewrite doesn’t bake assumptions ahead of spike outcomes |
8. Confidence assessment (S228 update)
Section titled “8. Confidence assessment (S228 update)”Spike phase plan confidence: 87% (up from 85% pre-S228).
Improvements vs pre-S228:
- Concrete corpus (
docs/client-documentation-base/) replaces ad-hoc fixtures → more realistic results - 5 new spikes target specific S228-surfaced gaps (write-back, dedup, OQ6 re-frame, Q4.14, DW.13) → fewer post-spike surprises
- OQ ratifications locked → spike outcomes feed downstream decisions cleanly
Remaining drag:
- Cocoindex install + schema-mount unverified (S1 may surface unforeseen friction)
- Mempalace v4-alpha PG backend stability unknown (could affect S3)
- pullmd 50-URL evaluation labour-intensive (may need multiple sub-passes)
- S10 dedup substrate is genuinely-novel territory — three substrates evaluated; “none wins” outcome possible
Outcomes feel high-confidence-bounded:
- Best case: all 13 spikes pass; Phase 2 commits immediately. Time: 8 days.
- Likely case: 1-3 spikes surface friction needing architecture tweaks. Time: 10-12 days + ratification cycle.
- Worst case: Scenario B from S1 forces architecture revision. Time: 12-14 days + new architecture phase.
Parallel-with-architecture-rewrite dimension (S228 unlock):
- Most spikes don’t depend on architecture rewrite landing first.
- Architecture sub-agents B/C run in parallel; they REFERENCE spike outcomes via shared
0.9-context.mdupdates between sessions. - Net: spike phase + architecture rewrite both shipping in 2 sessions instead of 4.
This is a tractable spike phase that lands actionable Phase 2 commit decision on a 1.5-2 week horizon.
End of spike plan. S228 corrections + 5 new spikes applied. Ready for ratification + execution.