Skip to content

fix(backend): a repository's furniture is not part of its architecture - #451

Closed
parthrohit22 wants to merge 3 commits into
feat/445-architecture-shows-real-modulesfrom
fix/444-non-code-files-are-not-modules
Closed

parthrohit22 wants to merge 3 commits into
feat/445-architecture-shows-real-modulesfrom
fix/444-non-code-files-are-not-modules

Conversation

@parthrohit22

Copy link
Copy Markdown
Collaborator

Closes #444. Stacked on #450 — base is feat/445-architecture-shows-real-modules, so review that first; this PR's own diff is the last commit. I'll retarget it to dev once #450 merges.

The defect

On pallets/click, 7 of the 16 reported modules were not code:

.devcontainer  .editorconfig  .github  .gitignore
.pre-commit-config.yaml  .readthedocs.yaml  changes.md  license.txt  uv.lock

Each typed shared-library, in the Shared layer. A reader scanning the module list saw the linter config given the same weight as the library.

Why the previous fix missed it

#396 excluded manifests and lockfiles by filename. Correct, but none of the files above is a manifest or a lockfile — no filename list was ever going to catch .gitignore.

The rule

A file is a module when the extraction observed something about it: symbols it defines, a relationship it takes part in, or a role the snapshot classified it into. Nothing here judges a file unimportant — a file that produced no facts gave nothing to describe, and inventing a module from a path is exactly how .gitignore ended up beside the library.

The relationship clause matters: a package initialiser that only re-exports defines nothing of its own but is genuinely part of the structure, and the sealed edges say so.

uv.lock

A second, separate failure — it reached no extractor at all, so a uv-managed repository looked identical to one that pins nothing.

The issue asks for it in SUPPORTED_LOCKFILE_FILENAMES. I did not do that: that set is derived from capabilities marked SUPPORTED, and nothing reads uv.lock. Putting it there would claim extraction support the product does not have, in the registry that generates the public capability doc.

Instead it follows the pattern already there for package-lock.json v1 — registered as SupportStatus.UNSUPPORTED, claimed by the extractor solely so it can disclose itself:

RI-EXT-UNSUPPORTED  info  uv.lock is recognised but not read, and no resolutions are claimed

That is the outcome the issue wanted (recognised, not silently dropped) without the false claim. supported_lockfile_filenames() is unchanged and a test now pins that distinction. Benchmark fixture adv-src-lockfile-uv holds the expected graph; the poetry capability's limitation text, which used to lump uv.lock in with Pipfile.lock and pdm.lock, now points at the new entry.

Also

A parent directory that repeats the module's own name no longer qualifies it. examples/termui/termui.py read as termui/termui; it now reads examples/termui, beside click/termui.

Verified live

pallets/click: 16 modules → 34, no furniture, headed by core (21), types (27), decorators (14), exceptions (14).

fastapi: 345 modules, no furniture, and the four utils resolve to dependencies/utils, openapi/utils, security/utils, fastapi/utils.

Tests

New test_repository_furniture_does_not_become_an_architecture_module — a fixture repo of dotfiles, a licence, a changelog, a CI workflow and uv.lock alongside one real source file; confirmed failing without the rule. New test_a_recognised_but_unread_lockfile_is_disclosed_rather_than_skipped. Full backend suite green; ruff check and ruff format --check clean.

…fine

The engine was already right and the view threw it away. On pallets/click,
a 177-file library with 1,963 observed symbols, the Architecture view
rendered one node -- "Click", "Owns utility concerns", "derived from
repository intelligence at /src/click" -- which is a directory listing with
extra steps, not an explanation of the system behind the code.

Three things changed, none of them an inference:

A file that defines symbols is now a module in its own right. The old
grouping collapsed everything under `src/<package>/` into one box, so a
single-package repository was reduced to a single node. Files that define
nothing (documentation, tests, config) keep their existing role grouping.

Descriptions state what the snapshot observed instead of restating the
path: how many symbols the module defines and which ones a reader would
recognise it by. Responsibilities report the classified role where there
was one, and say nothing where there was not -- "Owns unknown concerns"
was a statement about the classifier, dressed up as a statement about the
code.

Symbols reach the analyzer through a key-only query. The documented bound
on architecture_facts deliberately excludes symbol rows because they
dominate a large snapshot; stable keys are short strings and already carry
the file and the qualified name, which is all the module inventory reads.

Only top-level definitions count. A method is defined by its class, not by
the module, and counting every one turns "what does this module define"
into a line-count proxy -- the kind of synthesized measure #217 rules out.

Names that would collide take on as much of their parent path as they need
(FastAPI has four modules called `utils`); unique names are left short.

On click: core 38 symbols, exceptions 22 including Abort and
BadArgumentUsage, decorators 18 including command and group, parser 17.
On fastapi: 3,139 files, 358 nodes, `applications` reads "Defines 1
symbol: FastAPI."

Closes #445
@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
partha-frontend Ready Ready Preview Sep 11, 2026 5:31pm UTC

`.first()` picked whichever node the graph happened to order first, so the
assertion silently re-pointed at the README module as soon as module ids
changed -- it was asserting on position while claiming to assert on a name.

The fixture's three modules still carry long names
(`customer-subscription-entitlement-orchestration/component`), so the case
under test is unchanged; the test now locates that node by its accessible
name and additionally checks the on-screen label really is overflowing,
which is the half of "truncated visually and recoverable accessibly" that
was never actually verified.
On pallets/click the Architecture view reported 16 modules, 7 of which were
not code: .devcontainer, .editorconfig, .github, .gitignore,
.pre-commit-config.yaml, .readthedocs.yaml, changes.md, license.txt and
uv.lock -- each typed shared-library and placed in the Shared layer, so a
reader scanning the list saw the linter config given the same weight as the
library.

#396 excluded manifests and lockfiles by filename, which was right but
narrower than the defect: none of those files is a manifest or a lockfile,
so no filename list was ever going to catch them.

A file is now a module only when the extraction observed something about
it -- symbols it defines, a relationship it takes part in, or a role the
snapshot classified it into. Nothing is judged unimportant; a file that
produced no facts simply gave nothing to describe, and inventing a module
from a path is how .gitignore ended up beside the library itself.

uv.lock is a second, separate failure: it reached no extractor at all, so a
uv-managed repository looked exactly like one that pins nothing. Adding it
to the supported set would have been a false capability claim -- nothing
reads it. It is instead registered as an unsupported format and claimed by
the lockfile extractor solely to disclose itself as RI-EXT-UNSUPPORTED,
with a benchmark fixture holding that expectation.

Also: a parent directory that repeats the module's own name no longer
qualifies it. examples/termui/termui.py read as "termui/termui"; it now
reads as "examples/termui", beside "click/termui".

click: 16 modules -> 34, none of them furniture, and the top of the list is
core, types, decorators, exceptions. fastapi: 345 modules, no furniture,
four utils resolved to dependencies/utils, openapi/utils, security/utils
and fastapi/utils.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(backend): non-code top-level files still become Architecture modules

1 participant