Skip to content

fix(index): index-generation endpoints report execution wired while no executor exists #174

Closed

Description

@waterbro-8

Summary

The versioned index-generation subsystem exposes six mutating HTTP endpoints and
reports "execution_wired": true, but no code path executes a build. A build
with any work to do can never leave building, and a build for an empty
workspace can be activated while containing zero vectors.

Issue #55 (feat(index): versioned index generations for safe model migration)
is closed as completed. The path it exists to serve — re-embedding a corpus
under a new profile and switching over — cannot currently run.

Source reproduction

On main @ 7a194f1eba4167d54bd46cf84cdbe86e00532319:

  1. No executor. The target transitions exist only as definitions:
    ClaimTarget server/internal/indexgeneration/service.go:380,
    SucceedTarget :475, SkipTarget :491, FailTarget :495,
    CleanupExpired :579. Two greps from the repository root, run verbatim:
    call sites —
    grep -rn '\.\(ClaimTarget\|SucceedTarget\|FailTarget\|SkipTarget\|CleanupExpired\)(' server --include='*.go' | grep -v _test
    → no output; all mentions —
    grep -rn 'ClaimTarget\|SucceedTarget\|FailTarget\|SkipTarget\|CleanupExpired' server --include='*.go' | grep -v _test
    → eight lines, the five definitions above plus the comments at :32, :378
    and :576. The comment at :378 calls ClaimTarget "the intended Worker"
    entry point.
  2. No reader. NewSearchResolver (search_resolver.go:22) and
    SetGenerationResolver (server/internal/search/generation_routing.go:30)
    are likewise never called outside tests, so the resolver is never installed.
  3. Routing discards the generation. generation_routing.go:63 and :74
    are _ = gen followed by a call to the legacy scan, with a comment saying
    the vector table is populated by "Worker execution (a later slice)".
  4. Progress can therefore never advance. refreshProgress is called from
    exactly one place, server/internal/indexgeneration/store.go:320, inside
    completeTarget, which is reached only from the three unwired target
    transitions. state = 'ready' for a real corpus is set only at
    store.go:340,347.
  5. Consequence, both directions.
    • Non-empty workspace: the build stays building, so activate
      (store.go:419) fails its stateAllowed gate and returns
      ErrQualityGate (guard at store.go:457-460). Create → permanently stuck.
    • Empty workspace: service.go:208-223 marks the build ready when
      requiredTargets == 0, and the activation arithmetic
      SucceededTargets + SkippedTargets != RequiredTargets is 0 != 0, which
      passes. Activating a generation containing no vectors succeeds.
  6. Retention is not enforced either. CleanupExpired
    (service.go:579) has no non-test caller, so retention_until
    (0019_versioned_index_generations.sql:48) is recorded and never acted on.
  7. The documentation says the opposite of the code.
    docs/INDEX_GENERATIONS.md:120 states the endpoints "expose
    execution_wired=false" and that "the server does not expose create, …";
    server/internal/api/api.go:249,253,257,261,265,269 do expose create,
    cancel, resume, activate, rollback, and discard, and
    server/internal/api/handlers_index_generations.go:40,63,119,184 hard-code
    "execution_wired": true.

Expected vs actual

Expected: a status field describes whether execution is wired, and a mutating
endpoint either works or refuses with a reason an operator can act on.

Actual: the field is a literal true; an operator can POST a create, receive a
success, and watch a build that will never move, with no event explaining why.

Scope boundary

This issue is about the mismatch between advertised and real capability, and
about which half is correct. It is not a request to finish the worker slice.
Two mutually exclusive resolutions:

  • Smallest: make the flag honest — report execution_wired: false, align
    docs/INDEX_GENERATIONS.md, and have create/activate return an explicit
    execution_unavailable so nothing can enter the stuck or empty-activation
    states from the API.
  • Full: add the consumer (ClaimTarget → worker → SucceedTarget), install
    the resolver in cmd/memd/main.go, run CleanupExpired on a schedule, and
    require requiredTargets > 0 or an explicit empty-corpus acknowledgment before
    activation.

The first is a reviewable PR; the second is not, and probably needs its own
issue once a real model migration is on the roadmap.

Acceptance

  • One of the two resolutions above is chosen and recorded here, with the
    reason, by whoever owns index generation.
  • execution_wired cannot report a state the server does not honor: a test
    asserts the flag's value against whether a created build can reach
    active.
  • The empty-workspace activation path is either refused or documented as
    intended, with a test for the chosen behavior.
  • docs/INDEX_GENERATIONS.md matches the routes that exist.
  • If the full resolution is deferred, an issue for the worker executor is
    linked from here rather than left implied.

Evidence level

E2 — source-level and exhaustive for the caller claim (the grep above is
reproducible and its whole result set is "no non-test callers"). Nothing was
executed against a database: the stuck-build and empty-activation behaviors were
derived from the state machine, not observed.

Proposed triage

Applied on filing, per docs/maintainers/triage.md and the precedent in #135:
type:bug, area:server, severity:s2-medium, evidence:e2-source,
status:needs-triage.
Left to a maintainer: priority, and any change to the set below.

The severity is s2-medium rather than higher because no data is lost and no
wrong retrieval is served, but an administrative API reports a capability it
does not have.

Related: #55 (closed completed), #78 and #91 (the merged foundation and
routing slices), docs/INDEX_GENERATIONS.md.

Raised from the data/index audit on 2026-09-08.

Activity

  1. added
    type:bugSomething is broken or behaves incorrectly
    severity:s2-mediumMedium impact with a practical workaround
    evidence:e2-sourceSource or log evidence identifies the likely cause
    area:serverGo API, CLI, MCP, storage, or server runtime
    on Sep 8, 2026
  2. wadrzl commented on Sep 8, 2026

    @wadrzl

    认领。

    前提验证(E2 → E3 进行中):

    已确认:

    • handlers_index_generations.go:40,63,119,184 硬编码 "execution_wired": true
    • ClaimTarget/SucceedTarget/FailTarget/SkipTarget/CleanupExpired 在 server/ 下无非测试调用方(issue 中的 grep 可复现)
    • memd/main.go 未安装 generation resolver

    选择最小修复(理由:worker executor 是独立工作项,不应阻塞诚实性修复):

    1. execution_wired 改为 false
    2. create/activate 返回 execution_unavailable 错误,阻止进入 stuck 状态
    3. 空工作区激活路径拒绝或显式文档化
    4. docs/INDEX_GENERATIONS.md 对齐

    不动:worker executor 实现(应另立 issue)

    正在本地复现 stuck-build 和 empty-activation 行为,完成后提 PR。

  3. sun-970 commented on Sep 8, 2026

    @sun-970
    Contributor

    Resolution: smallest — make the flag honest, block stuck/empty-activation states, align docs.

    PR: #182

    The full worker executor (ClaimTarget → worker → SucceedTarget, resolver installation, CleanupExpired schedule) is deferred to a follow-up issue once a real model migration is on the roadmap.

  4. wadrzl commented on Sep 8, 2026

    @wadrzl

    实现完成

    选择最小修复,改动如下:

    代码变更

    1. server/internal/indexgeneration/types.go

      • 新增 ErrExecutionUnavailable = errors.New("index generation execution is not wired")
    2. server/internal/api/handlers_index_generations.go

      • execution_wired: true → false(4 处:list/get/create/action)
      • handleCreateIndexGeneration 提前返回 ErrExecutionUnavailable
      • indexGenerationBuildAction 中 activate 分支返回 ErrExecutionUnavailable
      • writeIndexGenerationError 新增 ErrExecutionUnavailable 处理 → HTTP 503 execution_unavailable

    行为变化

    操作 之前 之后
    list/get 返回 execution_wired: true 返回 execution_wired: false
    create 成功创建但永远卡在 building 返回 503 execution_unavailable
    activate 可能激活空工作区 返回 503 execution_unavailable
    cancel/resume/rollback/discard 正常 正常(不变)

    不动

    • Worker executor 实现(应另立 issue)
    • docs/INDEX_GENERATIONS.md 更新(可在 PR review 时补)

    待办

    • 写测试断言 execution_wired 值与实际能力一致
    • 空工作区激活路径的测试
    • 提 PR

    基线:main @ 7a194f1eba4

  5. PeterGuy326 commented on Sep 10, 2026

    @PeterGuy326
    Collaborator

    Current-main evidence/status

    Clean replacement PR: #189, head 87389fab229cbeba026c1a3b59f0958de2eaf5c5.

    • Exact-head local go test ./internal/api ./internal/indexgeneration: PASS.
    • Exact-head local go test ./... from server/: PASS.
    • git diff --check: PASS.
    • The response assertions now read the actual error field, and the disabled activate path is explicitly verified not to call the service.

    The old #182 candidate is closed as superseded. #174 remains OPEN pending independent review, exact-head CI, and acceptance; this comment does not claim merge or live release.

  6. PeterGuy326 commented on Sep 10, 2026

    @PeterGuy326
    Collaborator

    补充状态:堆叠基线 PR #192 已通过完整 CI(含 Web、Go、PostgreSQL、依赖审计)。功能修复 PR #189 的当前差异仅包含 index-generation API、测试、文档和 CHANGELOG;PR 仍 OPEN,等待独立评审,Issue 暂不关闭。

  7. waterbro-8 commented on Sep 17, 2026

    @waterbro-8
    CollaboratorAuthor

    Still open. Draft candidate: #189 (codex/fix-174-execution-honest) — fail-closed execution_wired: false and 503 on create/activate. I am not implementing a generation executor here; that is out of the issue's smallest resolution.

    main still advertises "execution_wired": true in handlers_index_generations.go. Tracking #189. No merge or close.

  8. waterbro-8 commented on Sep 20, 2026

    @waterbro-8
    CollaboratorAuthor

    平台组分工补派(冯浩然 / waterbro-8)

  9. sun-970 commented on Sep 20, 2026

    @sun-970
    Contributor

    Chose the smallest resolution (honest flag, fail-closed create/activate/rollback). Did not implement a worker executor (ClaimTarget loop).

    PR: #223 (sun-970:fix/174-honest-execution-20260920)

    This is a clean successor to dirty/unmerged #189, not a duplicate of the worker slice. No follow-up executor issue exists yet; that should be filed when a real model migration is on the roadmap.

    @waterbro-8 please review.

  10. waterbro-8 commented on Sep 23, 2026

    @waterbro-8
    CollaboratorAuthor

    关闭验收:最小且明确的 resolution 已由 PR #223 合入 main(merge 3977b35;final head 7c9e002)。execution_wired 改为诚实状态,未接线的 create/activate/rollback fail closed,空激活不会被误接受,文档与测试同步。final head 已正式 APPROVED,全部 hosted checks 通过;合入后的 main 四套 workflow 也全绿。完整 worker executor 按 issue 的 scope boundary 不属于本单。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:serverGo API, CLI, MCP, storage, or server runtimeevidence:e2-sourceSource or log evidence identifies the likely causeseverity:s2-mediumMedium impact with a practical workaroundstatus:needs-triageAwaiting maintainer classificationtype:bugSomething is broken or behaves incorrectly

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions