Skip to content

IDS post-commit/pre-push hook destroys usedBy graph in entity-registry.yaml (35% of relationships lost per commit) #814

Description

@GiovaneLaurencio

Summary

The IDS registry hooks rewrite .aiox-core/data/entity-registry.yaml on every commit and every push, wiping the usedBy reverse-dependency graph for entities the commit never touched. A commit touching 12 agent files silently dropped 191 of 542 entities' consumer lists — 35% of the graph — leaving usedBy: [].

The corrupted file is left unstaged in the working tree, so it is easy to sweep into the next git add -A without anyone noticing.

Reproduction

Deterministic, on a clean tree at any commit that touches registered entities:

# count entities carrying a non-empty usedBy
node -e "const y=require('js-yaml'),f=require('fs');const r=y.load(f.readFileSync('.aiox-core/data/entity-registry.yaml','utf8'));let t=0,n=0;for(const c of Object.values(r.entities))for(const e of Object.values(c)){t++;if((e.usedBy||[]).length)n++}console.log(t,n)"
# → 821 542

node .aiox-core/hooks/ids-post-commit.js
# → [IDS-Hook] Registry updated: 12 entities processed.

# same count again
# → 821 351

git diff --stat .aiox-core/data/entity-registry.yaml
# → 1 file changed, 230 insertions(+), 818 deletions(-)

.husky/pre-pushids-pre-push.js reproduces it a second time, so a commit-then-push cycle corrupts the registry twice.

Sample of what is lost:

-      usedBy:
-        - aiox-master
-        - analyst
+      usedBy: []

Root cause

Two problems compound in .aiox-core/core/ids/registry-updater.js:

1. The rebuild is global, but the trigger is incremental. _resolveAllUsedBy() (L632-639) resets usedBy on every entity in the registry, then recomputes the whole reverse graph from entity.dependencies via resolveUsedBy() (.aiox-core/development/scripts/populate-entity-registry.js L609-633). Any usedBy edge that is not derivable from a currently-resolvable dependencies entry is gone — even for the ~809 entities the commit never touched.

2. The second call discards the enrichment the comment says it is preserving. In processChanges() (L301-312):

this._resolveAllUsedBy(registry);
this._refreshDerivedDependencyFields(registry, changedEntities);

// NOG-8: Apply code intelligence enrichment AFTER resolveAllUsedBy
// so that code-intel usedBy data is merged on top of static graph
await this._applyCodeIntelEnrichments(registry);
this._resolveAllUsedBy(registry);   // <-- wipes what line above just merged
this._refreshDerivedDependencyFields(registry, changedEntities);

_applyCodeIntelEnrichments() merges code-intel usedBy (L490-492), and the very next line resets usedBy = [] across the board and rebuilds from the static graph only. The stated intent of the NOG-8 comment is inverted by the call that follows it. On a host where the code-intel providers are unavailable the enrichment contributes nothing anyway, so the static rebuild is all that survives.

Impact

The versioned registry is the input to IDS impact analysis and gates G1-G6 (.claude/rules/ids-principles.md). With usedBy emptied:

  • *ids impact {entity} reports no consumers, so the usedBy BFS traversal returns nothing and ADAPT changes look consumer-free;
  • G3/G5 duplication and reuse checks lose the signal they rely on;
  • the damage is cumulative and committed by whoever runs git add -A next, so it propagates to every clone.

Workaround

After any commit or push in this repo:

git checkout -- .aiox-core/data/entity-registry.yaml

Note that the same hook run also regenerates and stages .aiox-core/install-manifest.yaml — that part is correct and should be kept. Only the registry diff needs discarding.

Suggested direction (not implemented here)

  1. Drop the second _resolveAllUsedBy() call at L308 so the code-intel merge survives, or make the enrichment run last.
  2. Make the reset incremental: instead of clearing every entity, clear only edges pointing at changedEntities — the hook already computes that list and passes it to _refreshDerivedDependencyFields().
  3. Add a guard that refuses to write when the rebuild would drop more than N% of existing edges — a silent 35% loss should not be writable.
  4. Consider whether a post-commit hook should mutate a versioned file at all; a check that fails loudly may be safer than a rewrite that succeeds quietly.

Environment

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions