Skip to content

v1.75.0 ·

A sync that changed nothing reported 229 updates

The problem

aidlc knowledge sync, run twice with nothing changed in between, reports most of the graph as updated. Reproduced on this repository on 2026-10-09: the second run reported updated: 229 of 264 entities. The true answer is 0.

The cause is entitiesEqual in packages/cli/src/knowledge/sync.ts. It compares JSON.stringify(prior) === JSON.stringify(entity), where prior came back out of SQLite and entity was just parsed from YAML. The two differ in ways that are not content:

  • rowToEntity in packages/cli/src/knowledge/index/sqlite-index.ts always builds metadata: {}, while a YAML document with no metadata key parses with none.
  • rowToEntity builds its keys in a fixed order; a parsed document keeps the file's order. JSON.stringify is order-sensitive.
  • The index stores only the entity fields. A key in the YAML outside that shape is never stored, so it can never compare equal.

Measured over the real corpus (264 entities) before writing these criteria: all 229 false updated come from the missing metadata key. No entity today differs by key order or by an extra key. Those two are the same defect class and are covered so the fix does not depend on how today's files happen to be written.

How it could be solved

The obvious fix was in the wrong place. The stored copy of each fact always carries an empty extra-details field, while a fact written by hand usually has none, and that one difference made almost every comparison fail. Making the stored copy leave the field out would have hidden it, but it would have moved the mismatch to every fact that does write an empty field, and changed what every other reader of the store receives. So the store was left alone, and the comparison learned that a missing field and an empty one are the same thing, which is all the store can ever tell anyway.

The risk the other way was a comparison so forgiving that it stops seeing real damage from a merge. Two choices guard against that. The comparison names each field it checks in a structure the compiler holds against the shape of a fact, so a field added later fails the build until someone decides whether it counts. And there is one test per field, each changing that field alone and expecting exactly one update, seen to fail when the field is dropped from the check.

One line had to be drawn by hand: the order of a fact's tags still counts as a change. Reordering a list is an edit someone made to the file, and treating it as nothing would make the check more lenient than the file itself. The order of keys inside the extra details does not count, because that is only how the file happened to be written.

How AIDLC solves it

knowledge-sync-overcounts-updated

aidlc knowledge sync now counts an entity as updated only when its content changed. Run twice with nothing changed in between, it reports zero added, zero updated and zero removed; before this it reported 229 of this repository's 264 entities as updated on every run. The comparison now checks each field the index stores, treats a missing metadata the same as an empty one, and ignores key order and keys the index never keeps. The order of tags still counts. A field added to the entity type later fails to compile until the comparison lists it.

Patch, not minor: no command, flag, output field or file format changes. Only the value of updated becomes correct.

Changes

  • packages/cli/src/knowledge/sync.ts — entitiesEqual and INDEXED_FIELDS.
  • packages/cli/test/knowledge-sync-counts.test.ts — new, 19 cases.
  • .aidlc/knowledge/decisions/knowledge-sync-field-compare.yaml — the decision.
  • packages/website/content/blog/overrides/knowledge-sync-overcounts-updated-post.json and -options.md — this release's post.

knowledge-block-vanishes-silently

When a knowledge source file breaks, aidlc update and aidlc init now say so. A merge that leaves conflict markers in modules.yaml, or a hand edit that makes a document unreadable, prints knowledge graph: modules.yaml: skipped corrupt document (…) on every run until the file is fixed, not only on the run that happened to rebuild the index. When nothing is left to render, a second line says the # Codebase Knowledge block was left out of the instruction files and why. The block still goes, as it should: keeping rows built from a file that no longer says them would be serving stale facts.

A document holding a value the index cannot store — a description: that is a map, a missing author, a null name — no longer throws out of aidlc update, aidlc init and aidlc gate with a SQLite parameter number. That one document is left out, the warning names its file, its id and the field, and everything else in the graph is still indexed. An index file that cannot be opened at all leaves the block out with a warning carrying the error, and the command finishes.

Each warning prints once, however many platforms the project has: the render now runs once per command instead of once per platform. A project with no knowledge graph, or with a healthy one, sees exactly the output it saw before.

The work is three commits on feat/20261009-knowledge-block-vanishes-silently — 22dde444 (the fix), 6102dee1 (the docs page and the knowledge-graph decision) and 61aa1a7c (what the review changed) — after 45081758, which holds the requirements and design.