MicroFish/.kiro/specs/graph-build-empty-fix/tasks.md

10 KiB

Implementation Plan

  • 1. Foundation: reproduce the empty-graph symptom on main defaults

  • 1.1 Reproduce the empty-graph failure mode on the pre-fix main configuration

    • Stand up a local Neo4j (per docker compose up neo4j or an existing host instance) and an unmodified backend on the current main branch with the documented default .env (LLM via Dashscope/Qwen, no EMBEDDING_* overrides).
    • Upload a small representative seed file, kick off a graph build, and observe the worker until it terminates.
    • Capture (a) the resulting Task envelope (status, error), (b) the underlying mirofish.* logs (ERROR/WARNING lines from graphiti_adapter and graph_builder), and (c) the result of MATCH (n:Entity {group_id: $gid}) RETURN count(n) in Neo4j.
    • Observable completion: the captured failure-mode notes (Task envelope + log excerpt + node count) are appended under a new "Reproduction Log" section in .kiro/specs/graph-build-empty-fix/research.md, identifying which call site surfaces the failure (or which call site silently swallows it).
    • Requirements: 1.1, 1.2, 1.3
  • 1.2 Reconcile reproduction findings with the design hypothesis

    • Compare the Reproduction Log against the dim-mismatch / Dashscope-can't-serve-OpenAI-embeddings hypothesis recorded in research.md.
    • If findings match, mark the hypothesis "confirmed" in research.md and proceed to Task 2.
    • If findings diverge (root cause is elsewhere — e.g., a different silent path in graphiti-core or graphiti_adapter), append a one-paragraph design revision to design.md under "Overview" and add or rescope tasks before proceeding to Task 2.
    • Observable completion: research.md has an explicit "confirmed" or "diverged" verdict with one-line rationale; if diverged, design.md has the matching revision paragraph dated to the implementation run.
    • Requirements: 1.2, 1.4
  • 2. Core: flip embedding defaults to local Ollama

  • 2.1 (P) Update embedding defaults in backend configuration

    • In backend/app/config.py, change the three EMBEDDING_* defaults so that, with no .env override, the embedder resolves to a local Ollama instance with mxbai-embed-large: model mxbai-embed-large, base URL http://localhost:11434/v1, API key ollama.
    • Do not introduce a new env var, rename any existing one, or change GRAPHITI_LLM_PROVIDER (which stays openai).
    • Observable completion: importing Config in a fresh Python shell with an empty .env returns the three new default values; setting any of the three in .env continues to override them (override semantics unchanged).
    • Requirements: 2.1, 5.4
    • Boundary: Config
  • 2.2 (P) Add progress.emptyGraphFailure locale key in English and Chinese

    • Add a new entry under the existing progress.* namespace in locales/en.json and locales/zh.json whose value names the failure (graph build produced 0 entities for the project's group_id).
    • Wording must remain readable in both locale switches via utils.locale.t and vue-i18n without templated arguments (no placeholders).
    • Observable completion: t('progress.emptyGraphFailure') resolves to a non-empty, locale-appropriate string under set_locale('en') and set_locale('zh').
    • Requirements: 4.4, 4.5
    • Boundary: locales
  • 3. Core: gate graph-build completion on a non-zero entity-node count

  • 3.1 Insert non-zero-count gate into the graph-build worker

    • In backend/app/services/graph_builder.py, immediately after _get_graph_info(graph_id) returns and before complete_task(...) is called inside _build_graph_worker, branch on graph_info.node_count == 0.
    • On zero: log at ERROR level via mirofish.graph_builder's existing logger naming graph_id, then call TaskManager().fail_task(task_id, t('progress.emptyGraphFailure')) and return without invoking complete_task.
    • On non-zero: proceed with the existing complete_task path unchanged.
    • Do not weaken or touch the existing except Exception branch in the worker — the gate is additional.
    • Observable completion: a graph build whose add_batch returned cleanly but produced 0 (:Entity {group_id}) rows in Neo4j surfaces in the UI as a FAILED task with Task.error == t('progress.emptyGraphFailure'), and the corresponding ERROR log line is present in the backend logs; Project.status no longer rests in GRAPH_BUILDING for the affected project.
    • Requirements: 1.4, 3.3, 4.2, 4.4, 4.5
    • Depends: 2.2
  • 4. Core: documentation updates for the new default

  • 4.1 (P) Flip the README env block to Ollama-active, OpenAI/Gemini commented

    • In README.md, edit the env-block code fence (around the existing embedding section) so the three Ollama lines are uncommented and the OpenAI/Gemini examples become commented-out fallback blocks beneath, with one-line guidance on when to use each.
    • In the surrounding setup prose, list ollama pull mxbai-embed-large as a prerequisite alongside Neo4j; keep the existing one-line curl smoke test that confirms embedding length == 1024.
    • Observable completion: a reader following the README's setup section in order ends up with EMBEDDING_* configured for local Ollama (no manual uncomment step) and with the ollama pull step queued before the first graph build.
    • Requirements: 2.2, 6.2, 6.3, 6.4
    • Boundary: README.md
  • 4.2 (P) Update CLAUDE.md to reflect Ollama as the default embedder

    • In CLAUDE.md "Required Environment Variables", state that the default EMBEDDING_MODEL is mxbai-embed-large via Ollama at http://localhost:11434/v1, demote OpenAI and Gemini to "Other supported configurations", and retain the 1024-dim invariant plus the explicit rejection of 768-dim nomic-embed-text.
    • Observable completion: the "Required Environment Variables" block names Ollama as the active default and CLAUDE.md no longer implies that text-embedding-3-small is the default.
    • Requirements: 3.2, 6.1
    • Boundary: CLAUDE.md
  • 4.3 (P) Tighten docker-compose.yml comment to point at the active .env.example block

    • In docker-compose.yml, update the comment at the mirofish service (around lines 31-33) so it documents host.docker.internal:11434 as the way the container reaches the host Ollama daemon, and references the .env.example Ollama block as the active default rather than an optional override.
    • Do not change service definitions, networks, or env_file wiring.
    • Observable completion: docker-compose.yml reads as documentation that aligns with the new defaults; running docker compose config still produces a valid configuration (no syntax regression).
    • Requirements: 6.3
    • Boundary: docker-compose.yml
  • 4.4 Coordinate the .env.example diff (hook-protected file)

    • The Claude harness cannot write .env.example directly. Produce the exact diff (Ollama block uncommented as active, OpenAI/Gemini blocks present but commented) and record it in .kiro/specs/graph-build-empty-fix/HANDOFF.md so the developer can apply it manually before merge.
    • Confirm that the diff matches Config's new defaults from Task 2.1 (model, base URL, key strings) so operator-visible defaults align with Config defaults.
    • Observable completion: HANDOFF.md contains the literal block to paste into .env.example, with a one-line "apply manually before merging" note; the diff is internally consistent with Config's defaults from Task 2.1.
    • Requirements: 2.2
    • Depends: 2.1
  • 5. Validation: end-to-end and backwards compatibility

  • 5.1 End-to-end smoke: graph build → profile generation → report agent on the new defaults

    • With a running local Neo4j and a running local Ollama (with mxbai-embed-large pulled), run npm run dev, create a new project, upload a representative seed file, and exercise the pipeline through Step 4 (Report).
    • Confirm: the graph build Task terminates with status=COMPLETED and a non-zero node_count; the env-setup step produces a non-empty list of OASIS profiles; the report agent's SearchResult / InsightForge / Panorama / Interview tool calls return non-empty results.
    • If a representative seed file is not available locally, document this explicitly (no silent skip) and stop after the graph-build verification.
    • Observable completion: a short "Smoke Run" section is appended to research.md recording the project's group_id, the captured node_count / edge_count, and a one-line confirmation per downstream step; the PR description summarises this run.
    • Requirements: 1.5, 2.3, 7.1, 7.2, 7.3, 7.4
    • Depends: 3.1, 4.4
  • 5.2 Backwards-compatibility check for explicit OpenAI/Gemini overrides

    • With .env containing explicit OpenAI- or Gemini-compatible EMBEDDING_* values, restart the backend and confirm that _build_llm_and_embedder constructs the same embedder as the pre-change implementation (OpenAI branch when the operator sets OpenAI values; Gemini branch under GRAPHITI_LLM_PROVIDER=gemini).
    • Confirm the graph build completes against the override without engaging the new gate's failure path on the happy case.
    • Observable completion: the captured Task.result.graph_info shows a non-zero node_count under the override; no change in observed behaviour vs. the pre-change implementation for these providers; record outcome in the same research.md "Smoke Run" section under a "Backwards-compat" sub-heading.
    • Requirements: 5.1, 5.2, 5.3
    • Depends: 3.1
  • 5.3 Negative-path check: empty-graph gate fires and surfaces in the UI

    • Force the new gate to fire — either by pointing EMBEDDING_* at an unreachable Ollama, or by stubbing _get_graph_info to return node_count=0 for one run — and confirm the resulting Task envelope.
    • Confirm: Task.status == FAILED, Task.error is the localised progress.emptyGraphFailure string in the active locale, the backend ERROR log entry from Task 3.1 is present, and the surrounding project's status moves out of GRAPH_BUILDING (not stuck).
    • Observable completion: the captured Task envelope and log excerpt are recorded in research.md (or the PR description) as the gate's negative-path evidence.
    • Requirements: 2.4, 4.4, 4.5
    • Depends: 3.1