Skip to content

--update --compare-to never regenerates invalidated modules in whole-repository documentation mode (always ends in IncompleteGenerationError) #99

Description

@kiboook

Summary

When a repository is small enough that module clustering is skipped
("whole-repository documentation mode" — see the "Skipping LLM module
clustering ... using whole-module documentation mode" log line),
--update --compare-to <commit> correctly detects which sub-module
doc is affected by a change and deletes it, but never regenerates it.
The run always ends with:

✗ Documentation generation finished but these module docs are missing: <module>.md
IncompleteGenerationError

This is 100% reproducible, not transient — confirmed by tracing the actual
code path (details below), and confirmed twice against two different repos.

Root cause

In codewiki/src/be/documentation_generator.py,
generate_module_documentation() computes the processing order from
first_module_tree.json:

processing_order = self.get_processing_order(first_module_tree)

But first_module_tree.json is only ever written by the LLM clustering
path (cluster_modules(), in the _run_module_clustering stage). For a
repo that goes through whole-repository mode, that file stays {}
forever — even though a prior whole-repo agent run can still insert real
sub-modules into module_tree.json via
generate_sub_module_documentation_tool.

Sequence:

  1. First generate run: whole-repo mode, the agent creates e.g.
    module_a.md, module_b.md. module_tree.json now has 2 entries;
    first_module_tree.json stays {}.
  2. A file under module_a changes. generate --update --compare-to <sha>
    runs. _invalidate_affected_modules() (in cli/commands/generate.py)
    correctly matches the change to module_a using module_tree.json,
    deletes module_a.md (and overview.md, since any invalidation also
    invalidates the overview).
  3. generate_module_documentation() loads module_tree.json
    (non-empty → 2 entries) so it takes the if len(module_tree) > 0:
    branch, but processing_order (from the still-empty
    first_module_tree) is []. The regeneration loop runs zero
    iterations — module_a is never reprocessed.
  4. It falls through straight to building the repo overview, which logs
    Module docs not found at .../module_a.md, and then
    validate_generated_docs() raises IncompleteGenerationError.

Minimal reproduction

No secrets/private code needed — a tiny throwaway repo reproduces this
every time:

src/auth/auth.service.ts, src/payment/payment.service.ts,
src/notification/notification.service.ts, src/app.ts — each a small
class with a couple of methods (~20 leaf components total, well under
the default 36,369-token clustering threshold, so whole-repo mode is
guaranteed).

git init && git add -A && git commit -m "baseline"
# <baseline_sha>

codewiki config set --provider openai-compatible --api-key <key> \
  --base-url <url> --main-model <model> --cluster-model <model> \
  --fallback-model <model>
codewiki generate --output ./docs
# → module_a.md / module_b.md / module_c.md / overview.md created,
#   module_tree.json has 3 entries, first_module_tree.json is {}

# add one function to auth.service.ts, commit again
git add -A && git commit -m "add a function to auth service"

codewiki generate --output ./docs --update --compare-to <baseline_sha>
# → fails every time with IncompleteGenerationError

I have the exact 4 source files used and I'm opening a PR with this fix,
the fixture, and a regression test.

Suggested fix (implemented + tested in the linked PR)

Fall back to module_tree for the processing order only when
first_module_tree is empty — this only changes behavior for the
whole-repo-mode case; the normal clustered path is untouched, since
first_module_tree is non-empty there.

processing_order = self.get_processing_order(first_module_tree)
if not processing_order and module_tree:
    processing_order = self.get_processing_order(module_tree)

Verified against the repro above: the affected module regenerates,
unaffected modules are correctly skipped ("✓ Module docs already
exists"), and the overview is rebuilt referencing all modules.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions