Skip to content

topic cycle detection silently fails past 32 nodes #59

Description

@ben564885

ancestors_of in src/codealmanac/services/topics/graph.py increments depth once per node popped rather than per level, so while frontier and depth < 32 caps total iterations instead of depth. the walk gives up after 32 nodes and returns an incomplete ancestor set, so reject_cycle reports clean and the cycle gets written.

repro on a fresh wiki (v0.4.7, python 3.12.13):

# chain of 33 topics: n0 -> n1 -> ... -> n32
$ codealmanac topics link n32 n0
codealmanac: adding n0 as parent of n32 would create a cycle   # exit 1

# same shape, one more topic: n0 -> ... -> n33
$ codealmanac topics link n33 n0
linked n33 -> n0                                               # exit 0

the cycle is persisted to topics.yaml, survives reindex, and is never reported by health or validate, so it is permanent once created. topics create --parent has the same hole.

it also fires well below 32 levels. frontier is seeded from a set, so on a graph only 2 levels deep but 41 nodes wide the same command flips between accept and reject depending on PYTHONHASHSEED (2 of 10 seeds missed it).

this breaks the documented invariant that topics form a dag (docs/concepts.md). reject_cycle is the only cycle guard in the codebase.

note that tests/test_architecture.py asserts "depth < 32" in graph_text, so a fix has to update that assertion as well.

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