Epic: aspect inventory, separation-of-concerns audit, and the prose/ad-hoc debt #9

Open
opened 2026-08-04 07:50:42 +00:00 by agent.odin · 0 comments
Collaborator

Stable ID: FM-ASPECT-AUDIT
Source: full-tree audit of modules/ and outputs/hosts/, 2026-08-04
Source status: new finding set, none of it previously tracked
Disposition: open epic — inventory published, twelve child items to split out
Blocked by: nothing

Why

Two questions, asked together: what is actually in the aspect registry, and
where does it fail to hold. The census is authoritative — nix eval --json .#aspects.graph, not grep. Every finding below was verified against the tree,
not inferred from a comment.

Inventory

metric value
catalog records 33
contracts 4
modules with no record (residual) 18
hosts 6
unreachable aspects 0
graph diagnostics 18 warnings, all residual
nix lines under modules/ + outputs/ 10 044
comment-only lines 2 490 (24.8%)

Kinds: 7 apps, 4 contracts, 2 hardware, 4 implementations, 2 integrations,
6 policies, 3 services, 4 systems, 1 user.

Host closures, before the residual set is added to each: bifrost 6,
fenrisulfr 6, heimdallr 6, modron 6, tamiel 8, odin 32.

Full census — every record with kind, classes, requires/provides/owns, source
file, LOC and comment ratio, plus the 18 residual and the schema utilisation
table — is FM-AUDIT-12 below, destined for the wiki.


FM-AUDIT-01 — nix flake check is red on the committed tree

nix build .#checks.x86_64-linux.aspects-shadow fails. 13 of the 14 aspect
checks pass; this one does not.

implementations/forgejo-agents entered odin's roots
(modules/aspects/hosts.nix:107) without regenerating
tests/aspects/fixtures/shadow-hosts.json:

+      "implementations/forgejo-agents": ["implementations/forgejo-agents"],
       "policies/sops-workstation": [
-        "contracts/agent-runtime", "implementations/hermes",
+        "implementations/forgejo-agents",

The check did its job — a closure moved and the pin caught it. What is broken
is that it was left red. A permanently-red check is a check nobody reads,
which is the argument modules/aspects/checks.nix makes against itself.

Distinct from #1 (FM-B3-PARITY): that one is parity-*.json, this one is
shadow-hosts.json and needs no deploy — regenerate and commit.

FM-AUDIT-02 — the registry governs 33 of 51 modules

modules/hosts.nix:52-56 imports residualIds class ++ closure. A host's
root list decides 33 modules and is silent about the other 18 — 2 069 lines,
26% of the module tree, and it includes every service the fleet actually runs:
services/forgejo, services/matrix, services/librefang,
services/syncthing, services/perses, policies/vaults, policies/base,
policies/identity, integrations/mesh-dns.

tests/aspects/fixtures/warning-budget.json pins this at {"residual": 18}.
A budget is a ratchet only if something lowers it; nothing has.

The five servers resolve to six aspects each. Their real configuration lives
in the residual set and in their host file. aspects-isolation guards exactly
one aspect on exactly one host (services/victoria on bifrost) — a proof of
concept, not a fleet property, and it cannot become one while 18 modules
bypass selection by construction.

Decide per module: annotate or delete, lowering the budget each time. Name the
date residual must be 0, or state that broad injection is the design and
retire the migration framing.

FM-AUDIT-03 — contracts/routes is bypassed by half its users

writer server uses mesh.routes?
implementations/caddy caddy yes — it is the renderer
integrations/victoria-dashboards caddy yes
integrations/vault-dashboards caddy yes
integrations/mesh-whoami:297 caddy no
implementations/hermes:743 nginx no
integrations/mesh-librefang:24 nginx no
outputs/hosts/heimdallr:132 nginx no
outputs/hosts/bifrost:154 caddy no

Four of eight bypass the contract; three of those run the other web server;
two are host files.

bifrost also hand-rolls what implementations/caddy already does, and gets it
wrong in the exact way that aspect documents fixing:

# outputs/hosts/bifrost/configuration.nix:231
environment.persistence."/persistent".directories = [ config.services.caddy.dataDir ];
# modules/implementations/caddy.nix:78 — same thing, with ownership
{ inherit (config.services.caddy) group user; directory = ...; mode = "0755"; }

The aspect explains that the bare-string form leaves /var/lib/caddy
root-owned where impermanence creates the bind mount first, and Caddy then
cannot write its ACME account. bifrost is latent, not broken — its directory
predates the entry — but a cold rebuild walks into the documented failure. The
aspect's fix does not apply because bifrost never sets
mesh.implementations.caddy.enable.

Either every vhost goes through contracts/routes and one renderer, or the
contract is deleted as aspirational. The current state costs the abstraction's
maintenance and delivers none of its guarantee.

Blocked by: FM-AUDIT-02 — contracts/routes and implementations/caddy are
both residual and cannot be required until they have records.

FM-AUDIT-04 — implementations/hermes is odin wearing an aspect's clothes

# modules/implementations/hermes.nix:147-148
agentsSecrets = ../../outputs/hosts/odin/secrets.agents.yaml;
adHocSecrets  = ../../outputs/hosts/odin/secrets.ad-hoc.yaml;

let bindings, not options. Any second host selecting
contracts/agent-runtime decrypts odin's secrets file. hermesState = "/data/odin" is hardcoded the same way; user and linkUsers default to
larandar. implementations/forgejo-agents has the same coupling, but at
least made it an option.

The record's header argues it bundles five things — Hermes, Modron, Firecrawl,
ByteRover, Ollama — "because they are only correct together", and that
splitting them would push coupling into the host root. 794 lines, one enable
flag. Its own history refutes it: opencode became the sixth limb and went into
outputs/hosts/odin/configuration.nix:194 as a raw systemd unit instead. When
the next thing to add goes in the host file rather than the record, the record
is not the right home.

Parameterise the secrets paths and the state root, or rename it to what it is
— an odin-only assembly — and stop it answering contracts/agent-runtime.

FM-AUDIT-05 — heimdallr restates policies/identity as four literals

policies/identity exists so that "moving identity is one edit here". The
host that runs identity does not read it:

outputs/hosts/heimdallr/configuration.nix literal option holding it
:67 firewall 9091 mesh.identity.port
:94 server.address tcp://0.0.0.0:9091 mesh.identity.port
:116 authelia_url https://auth.et0.pw mesh.identity.portalUrl
:114 session.cookies.domain et0.pw mesh.identity.cookieDomain

Only protectedDomains is read from the policy (:23).

Change mesh.identity.port and every consumer follows it while Authelia keeps
listening on 9091 and the firewall keeps opening 9091. Nothing fails at eval;
forward-auth just stops answering.

FM-AUDIT-06 — five host files grant blanket passwordless sudo

policies/sudo is careful. It grants NOPASSWD to podman and to exactly the
systemctl lifecycle verbs, with its own comment saying why: "unrestricted
systemctl (edit, set-property, link) would be passwordless root."

That aspect is in one host's roots: odin. The five servers do not select it.
Each instead writes, verbatim:

security.sudo.wheelNeedsPassword = false;

bifrost:85, fenrisulfr:77, heimdallr:56, modron:55, tamiel:20.

So the fleet's considered sudo policy protects the workstation, and every
server — including the one holding fleet identity secrets and the one holding
the vault NFS mounts — has blanket passwordless root for wheel. Five identical
lines is the tell: this is a fleet policy that was never written as one.

Decide the server posture deliberately, put it in policies/sudo or a
sibling, and add it to common in modules/aspects/hosts.nix.

FM-AUDIT-07 — odin's host file carries a service, a scrape config, a resolver and 90 lines of WireGuard

The file opens by defining what a host file is: "facts that are nobody else's
business … and option values for aspects the host record selected." Then:

  • :194-206 — a full systemd.user.services.opencode unit: package pulled
    straight from inputs.llm-agents, port 4999 hardcoded, and
    EnvironmentFile = /run/secrets/rendered/profile.omo.env — an undeclared
    dependency on implementations/forgejo-agents rendering the omo persona.
    apps/coding already installs llmAgents.opencode; this is a second
    reference to the same package from a different layer.
  • :211-216 — raw services.victoriametrics.prometheusConfig.scrape_configs.
    Skipping integrations/victoria-scrape is correct (it is tamiel's fleet
    edge); the answer is a scrape-self aspect, not raw upstream options.
  • :222-237mesh.services.perses, with the comment conceding it: "Perses
    is residual (no catalog record yet), so the old enable flag is the honest
    surface."
  • :59-149 — 90 lines of networking.networkmanager.ensureProfiles restating
    mesh membership contracts/mesh-nodes already holds: both addresses, the
    hub's public key, six allowed-ip ranges, both resolver addresses. The
    comment says the addressing "MUST match the hub's peer entry" — a second
    source of truth admitting it is one.
  • :32-33services.resolved.enable and networking.networkmanager.dns,
    the client half of integrations/mesh-dns, by hand.
  • :23time.timeZone, already mkDefault-ed in policies/base:25. Same
    duplicate at fenrisulfr:11.

Roughly 130 of 242 lines are not host facts.

The NetworkManager profile has a real justification — rootless toggling, and
the wg0-adoption incident. That argues for a NetworkManager provider for
policies/mesh, selected per host, not for hand-written config diffed against
the registry by eye.

FM-AUDIT-08 — agent persona content lives in host files, in two shapes

Prompt text is content. It is currently topology:

  • outputs/hosts/fenrisulfr/configuration.nix:114-192 — two full LLM system
    prompts (freki, geri) inside mesh.services.librefang.hands. The
    channel_send paragraph is copy-pasted verbatim into both, as is the
    comment above each explaining the 2026-08-01 duplicate-reply flail.
  • outputs/hosts/odin/brain.nix — a system prompt plus display settings,
    delegation models and council profiles for Hermes.

Neither has an aspect. Editing a persona means editing a machine; reviewing a
machine means reading prompts. The near-duplicate wolf prompts are what always
happens when content has no home — they will drift.

Related: #5 (restore chat presence for freki and geri) touches the same
declarations.

FM-AUDIT-09 — a quarter of the nix tree is comment prose

2 490 comment-only lines out of 10 044 (24.8%). Worst ratios, files over 40
lines:

file cmt code %
modules/integrations/victoria-whoami.nix 36 20 63%
outputs/hosts/tamiel/configuration.nix 35 19 59%
outputs/hosts/bifrost/configuration.nix 134 85 57%
outputs/hosts/modron/configuration.nix 69 59 51%
outputs/hosts/odin/configuration.nix 109 114 45%
outputs/hosts/heimdallr/configuration.nix 64 81 42%
outputs/hosts/fenrisulfr/configuration.nix 89 114 40%
modules/integrations/mesh-dns.nix 217 361 34%

Most is not "why this line" — it is history, and history has a home already:

  • 21 dated decision references (owner call, owner directive, 2026-08-01)
  • tombstones for deleted things: fenrisulfr's "llmwiki is gone", "No registry
    mirror. It was considered and rejected…", "No forge here"; bifrost's Buzz
    paragraph; modron's "Why the name changed" and "What was stripped"
  • mesh-dns.nix opens with ~70 lines under headings — "Why not /etc/hosts",
    "What used to be here, and why it changed" — before the first line of code
  • bifrost's "Why the animation is 8 KB and not 225", explaining a React bundle
    that is not in this repo

It also rots silently: modron:59 still says topology "lives in policies/mesh"
after it moved to contracts/mesh-nodes.

Rule to adopt: a comment explains the line above it, or it goes to the wiki.
Rationale worth keeping — the measured 1280 MTU, the stable-privacy fix, the
dns-priority trade, the Caddyfile backtick-quoting trap, the Authelia OIDC
bootstrap trap — becomes wiki pages linked from a one-line comment.

FM-AUDIT-10 — eight of the record schema's 21 fields have no user

modules/aspects/_registrar.nix:65-151. Zero uses anywhere in the catalog:
traits, references, instantiable, publishes, visibility,
stability, defaultImplementation, ownerWrites. One use each: params,
configures, tags. Two: optional.

traits is the expensive one — a 32-line submodule type (kind enum, peer,
direction, reason, tcp/udp port lists, path, route), a per-host
allow/unchecked override block in hostType, and a traits = {} line in
the graph builder. Declared by no record, read by no check. Its own comment
concedes it: "the checks that consume it land post-Milestone-C."

Delete what has no user. Each is re-addable in the commit that needs it, and
every one is currently something a reader must understand before concluding it
does nothing.

FM-AUDIT-11 — lib/forgejo.nu still ships a one-shot migration reading gitignored plans/

AGENTS.md: "just forgejo … is now admin work only … It does not read or
write issues." The file disagrees. Roughly 340 of 805 lines (450-790) are the
issue/wiki sync path: issue-records, backlog-items, backlog-preview,
create-issue, sync-issues, ensure-chain-dependencies, wiki-sources,
wiki-pages, sync-wiki, verify-sync, sync. Two are still live recipes
(forgejo.just:61, forgejo.just:69).

It also depends on files the repo forbids tracking:

const BACKLOG_FILE = "plans/backlog.md"        # lib/forgejo.nu:10
source: "plans/target-architecture-spec.md"    # lib/forgejo.nu:657
source: "plans/architecture-postmortem.md"     # lib/forgejo.nu:662

Tracked code reading untracked paths works on the one machine that has them
and errors everywhere else — and sync is a [confirm] recipe that writes
issues, so the failure mode is a half-applied write against this tracker.

Delete the migration half; keep probe, contract*, search-repos,
credential-status, auth, mint, whoami.

Blocked by: #7 — the same plans/ deletion set.

FM-AUDIT-12 — publish the aspect census as a wiki page

The inventory has no home. flake.aspects.graph is evaluable and
nix run .#aspects-graph renders it, but neither answers "what is in this
fleet" for someone who has not already loaded the model.

Content is ready: 33 records with kind/classes/requires/provides/owns/LOC, the
18 residual, per-host closures, schema utilisation, check status.

Blocked by: FM-AUDIT-02, FM-AUDIT-10 — the numbers move when those land.


Acceptance

  • Each FM-AUDIT-nn above is split into its own issue with a Blocked by row,
    or explicitly closed here as won't-do with the reason recorded.
  • just check passes in the devshell (FM-AUDIT-01 first — it is red today).
  • residual in tests/aspects/fixtures/warning-budget.json is lower than 18,
    or the migration framing is retired.
  • The census is a wiki page.

Provenance

Audit run 2026-08-04 by agent.odin against commit fc3b751, with
modules/implementations/{forgejo-agents,hermes}.nix,
outputs/hosts/odin/{configuration.nix,secrets.agents.yaml} dirty in the
working copy. Counts come from nix eval --json .#aspects.graph and from line
counts over modules/ and outputs/; check status from building each
.#checks.x86_64-linux.aspects-* derivation.

Stable ID: FM-ASPECT-AUDIT Source: full-tree audit of `modules/` and `outputs/hosts/`, 2026-08-04 Source status: new finding set, none of it previously tracked Disposition: open epic — inventory published, twelve child items to split out Blocked by: nothing ## Why Two questions, asked together: what is actually in the aspect registry, and where does it fail to hold. The census is authoritative — `nix eval --json .#aspects.graph`, not grep. Every finding below was verified against the tree, not inferred from a comment. ## Inventory | metric | value | | --- | --- | | catalog records | 33 | | contracts | 4 | | modules with no record (`residual`) | 18 | | hosts | 6 | | unreachable aspects | 0 | | graph diagnostics | 18 warnings, all `residual` | | nix lines under `modules/` + `outputs/` | 10 044 | | comment-only lines | 2 490 (24.8%) | Kinds: 7 apps, 4 contracts, 2 hardware, 4 implementations, 2 integrations, 6 policies, 3 services, 4 systems, 1 user. Host closures, before the residual set is added to each: bifrost 6, fenrisulfr 6, heimdallr 6, modron 6, tamiel 8, odin 32. Full census — every record with kind, classes, requires/provides/owns, source file, LOC and comment ratio, plus the 18 residual and the schema utilisation table — is FM-AUDIT-12 below, destined for the wiki. --- ## FM-AUDIT-01 — `nix flake check` is red on the committed tree `nix build .#checks.x86_64-linux.aspects-shadow` fails. 13 of the 14 aspect checks pass; this one does not. `implementations/forgejo-agents` entered odin's roots (`modules/aspects/hosts.nix:107`) without regenerating `tests/aspects/fixtures/shadow-hosts.json`: ``` + "implementations/forgejo-agents": ["implementations/forgejo-agents"], "policies/sops-workstation": [ - "contracts/agent-runtime", "implementations/hermes", + "implementations/forgejo-agents", ``` The check did its job — a closure moved and the pin caught it. What is broken is that it was left red. A permanently-red check is a check nobody reads, which is the argument `modules/aspects/checks.nix` makes against itself. Distinct from #1 (FM-B3-PARITY): that one is `parity-*.json`, this one is `shadow-hosts.json` and needs no deploy — regenerate and commit. ## FM-AUDIT-02 — the registry governs 33 of 51 modules `modules/hosts.nix:52-56` imports `residualIds class ++ closure`. A host's root list decides 33 modules and is silent about the other 18 — 2 069 lines, 26% of the module tree, and it includes every service the fleet actually runs: `services/forgejo`, `services/matrix`, `services/librefang`, `services/syncthing`, `services/perses`, `policies/vaults`, `policies/base`, `policies/identity`, `integrations/mesh-dns`. `tests/aspects/fixtures/warning-budget.json` pins this at `{"residual": 18}`. A budget is a ratchet only if something lowers it; nothing has. The five servers resolve to six aspects each. Their real configuration lives in the residual set and in their host file. `aspects-isolation` guards exactly one aspect on exactly one host (`services/victoria` on bifrost) — a proof of concept, not a fleet property, and it cannot become one while 18 modules bypass selection by construction. Decide per module: annotate or delete, lowering the budget each time. Name the date `residual` must be 0, or state that broad injection is the design and retire the migration framing. ## FM-AUDIT-03 — `contracts/routes` is bypassed by half its users | writer | server | uses `mesh.routes`? | | --- | --- | --- | | `implementations/caddy` | caddy | yes — it is the renderer | | `integrations/victoria-dashboards` | caddy | yes | | `integrations/vault-dashboards` | caddy | yes | | `integrations/mesh-whoami:297` | caddy | no | | `implementations/hermes:743` | nginx | no | | `integrations/mesh-librefang:24` | nginx | no | | `outputs/hosts/heimdallr:132` | nginx | no | | `outputs/hosts/bifrost:154` | caddy | no | Four of eight bypass the contract; three of those run the other web server; two are host files. bifrost also hand-rolls what `implementations/caddy` already does, and gets it wrong in the exact way that aspect documents fixing: ```nix # outputs/hosts/bifrost/configuration.nix:231 environment.persistence."/persistent".directories = [ config.services.caddy.dataDir ]; # modules/implementations/caddy.nix:78 — same thing, with ownership { inherit (config.services.caddy) group user; directory = ...; mode = "0755"; } ``` The aspect explains that the bare-string form leaves `/var/lib/caddy` root-owned where impermanence creates the bind mount first, and Caddy then cannot write its ACME account. bifrost is latent, not broken — its directory predates the entry — but a cold rebuild walks into the documented failure. The aspect's fix does not apply because bifrost never sets `mesh.implementations.caddy.enable`. Either every vhost goes through `contracts/routes` and one renderer, or the contract is deleted as aspirational. The current state costs the abstraction's maintenance and delivers none of its guarantee. Blocked by: FM-AUDIT-02 — `contracts/routes` and `implementations/caddy` are both residual and cannot be required until they have records. ## FM-AUDIT-04 — `implementations/hermes` is odin wearing an aspect's clothes ```nix # modules/implementations/hermes.nix:147-148 agentsSecrets = ../../outputs/hosts/odin/secrets.agents.yaml; adHocSecrets = ../../outputs/hosts/odin/secrets.ad-hoc.yaml; ``` `let` bindings, not options. Any second host selecting `contracts/agent-runtime` decrypts odin's secrets file. `hermesState = "/data/odin"` is hardcoded the same way; `user` and `linkUsers` default to `larandar`. `implementations/forgejo-agents` has the same coupling, but at least made it an option. The record's header argues it bundles five things — Hermes, Modron, Firecrawl, ByteRover, Ollama — "because they are only correct together", and that splitting them would push coupling into the host root. 794 lines, one enable flag. Its own history refutes it: opencode became the sixth limb and went into `outputs/hosts/odin/configuration.nix:194` as a raw systemd unit instead. When the next thing to add goes in the host file rather than the record, the record is not the right home. Parameterise the secrets paths and the state root, or rename it to what it is — an odin-only assembly — and stop it answering `contracts/agent-runtime`. ## FM-AUDIT-05 — heimdallr restates `policies/identity` as four literals `policies/identity` exists so that "moving identity is one edit here". The host that runs identity does not read it: | `outputs/hosts/heimdallr/configuration.nix` | literal | option holding it | | --- | --- | --- | | `:67` firewall | `9091` | `mesh.identity.port` | | `:94` `server.address` | `tcp://0.0.0.0:9091` | `mesh.identity.port` | | `:116` `authelia_url` | `https://auth.et0.pw` | `mesh.identity.portalUrl` | | `:114` `session.cookies.domain` | `et0.pw` | `mesh.identity.cookieDomain` | Only `protectedDomains` is read from the policy (`:23`). Change `mesh.identity.port` and every consumer follows it while Authelia keeps listening on 9091 and the firewall keeps opening 9091. Nothing fails at eval; forward-auth just stops answering. ## FM-AUDIT-06 — five host files grant blanket passwordless sudo `policies/sudo` is careful. It grants NOPASSWD to podman and to exactly the systemctl lifecycle verbs, with its own comment saying why: "unrestricted systemctl (edit, set-property, link) would be passwordless root." That aspect is in one host's roots: odin. The five servers do not select it. Each instead writes, verbatim: ```nix security.sudo.wheelNeedsPassword = false; ``` bifrost:85, fenrisulfr:77, heimdallr:56, modron:55, tamiel:20. So the fleet's considered sudo policy protects the workstation, and every server — including the one holding fleet identity secrets and the one holding the vault NFS mounts — has blanket passwordless root for wheel. Five identical lines is the tell: this is a fleet policy that was never written as one. Decide the server posture deliberately, put it in `policies/sudo` or a sibling, and add it to `common` in `modules/aspects/hosts.nix`. ## FM-AUDIT-07 — odin's host file carries a service, a scrape config, a resolver and 90 lines of WireGuard The file opens by defining what a host file is: "facts that are nobody else's business … and option values for aspects the host record selected." Then: - `:194-206` — a full `systemd.user.services.opencode` unit: package pulled straight from `inputs.llm-agents`, port 4999 hardcoded, and `EnvironmentFile = /run/secrets/rendered/profile.omo.env` — an undeclared dependency on `implementations/forgejo-agents` rendering the `omo` persona. `apps/coding` already installs `llmAgents.opencode`; this is a second reference to the same package from a different layer. - `:211-216` — raw `services.victoriametrics.prometheusConfig.scrape_configs`. Skipping `integrations/victoria-scrape` is correct (it is tamiel's fleet edge); the answer is a scrape-self aspect, not raw upstream options. - `:222-237` — `mesh.services.perses`, with the comment conceding it: "Perses is residual (no catalog record yet), so the old enable flag is the honest surface." - `:59-149` — 90 lines of `networking.networkmanager.ensureProfiles` restating mesh membership `contracts/mesh-nodes` already holds: both addresses, the hub's public key, six allowed-ip ranges, both resolver addresses. The comment says the addressing "MUST match the hub's peer entry" — a second source of truth admitting it is one. - `:32-33` — `services.resolved.enable` and `networking.networkmanager.dns`, the client half of `integrations/mesh-dns`, by hand. - `:23` — `time.timeZone`, already `mkDefault`-ed in `policies/base:25`. Same duplicate at fenrisulfr:11. Roughly 130 of 242 lines are not host facts. The NetworkManager profile has a real justification — rootless toggling, and the wg0-adoption incident. That argues for a NetworkManager provider for `policies/mesh`, selected per host, not for hand-written config diffed against the registry by eye. ## FM-AUDIT-08 — agent persona content lives in host files, in two shapes Prompt text is content. It is currently topology: - `outputs/hosts/fenrisulfr/configuration.nix:114-192` — two full LLM system prompts (freki, geri) inside `mesh.services.librefang.hands`. The `channel_send` paragraph is copy-pasted verbatim into both, as is the comment above each explaining the 2026-08-01 duplicate-reply flail. - `outputs/hosts/odin/brain.nix` — a system prompt plus display settings, delegation models and council profiles for Hermes. Neither has an aspect. Editing a persona means editing a machine; reviewing a machine means reading prompts. The near-duplicate wolf prompts are what always happens when content has no home — they will drift. Related: #5 (restore chat presence for freki and geri) touches the same declarations. ## FM-AUDIT-09 — a quarter of the nix tree is comment prose 2 490 comment-only lines out of 10 044 (24.8%). Worst ratios, files over 40 lines: | file | cmt | code | % | | --- | --- | --- | --- | | modules/integrations/victoria-whoami.nix | 36 | 20 | 63% | | outputs/hosts/tamiel/configuration.nix | 35 | 19 | 59% | | outputs/hosts/bifrost/configuration.nix | 134 | 85 | 57% | | outputs/hosts/modron/configuration.nix | 69 | 59 | 51% | | outputs/hosts/odin/configuration.nix | 109 | 114 | 45% | | outputs/hosts/heimdallr/configuration.nix | 64 | 81 | 42% | | outputs/hosts/fenrisulfr/configuration.nix | 89 | 114 | 40% | | modules/integrations/mesh-dns.nix | 217 | 361 | 34% | Most is not "why this line" — it is history, and history has a home already: - 21 dated decision references (`owner call`, `owner directive`, `2026-08-01`) - tombstones for deleted things: fenrisulfr's "llmwiki is gone", "No registry mirror. It was considered and rejected…", "No forge here"; bifrost's Buzz paragraph; modron's "Why the name changed" and "What was stripped" - `mesh-dns.nix` opens with ~70 lines under headings — "Why not /etc/hosts", "What used to be here, and why it changed" — before the first line of code - bifrost's "Why the animation is 8 KB and not 225", explaining a React bundle that is not in this repo It also rots silently: `modron:59` still says topology "lives in policies/mesh" after it moved to `contracts/mesh-nodes`. Rule to adopt: a comment explains the line above it, or it goes to the wiki. Rationale worth keeping — the measured 1280 MTU, the `stable-privacy` fix, the `dns-priority` trade, the Caddyfile backtick-quoting trap, the Authelia OIDC bootstrap trap — becomes wiki pages linked from a one-line comment. ## FM-AUDIT-10 — eight of the record schema's 21 fields have no user `modules/aspects/_registrar.nix:65-151`. Zero uses anywhere in the catalog: `traits`, `references`, `instantiable`, `publishes`, `visibility`, `stability`, `defaultImplementation`, `ownerWrites`. One use each: `params`, `configures`, `tags`. Two: `optional`. `traits` is the expensive one — a 32-line submodule type (kind enum, peer, direction, reason, tcp/udp port lists, path, route), a per-host `allow`/`unchecked` override block in `hostType`, and a `traits = {}` line in the graph builder. Declared by no record, read by no check. Its own comment concedes it: "the checks that consume it land post-Milestone-C." Delete what has no user. Each is re-addable in the commit that needs it, and every one is currently something a reader must understand before concluding it does nothing. ## FM-AUDIT-11 — `lib/forgejo.nu` still ships a one-shot migration reading gitignored `plans/` AGENTS.md: "`just forgejo …` is now admin work only … It does not read or write issues." The file disagrees. Roughly 340 of 805 lines (450-790) are the issue/wiki sync path: `issue-records`, `backlog-items`, `backlog-preview`, `create-issue`, `sync-issues`, `ensure-chain-dependencies`, `wiki-sources`, `wiki-pages`, `sync-wiki`, `verify-sync`, `sync`. Two are still live recipes (`forgejo.just:61`, `forgejo.just:69`). It also depends on files the repo forbids tracking: ```nu const BACKLOG_FILE = "plans/backlog.md" # lib/forgejo.nu:10 source: "plans/target-architecture-spec.md" # lib/forgejo.nu:657 source: "plans/architecture-postmortem.md" # lib/forgejo.nu:662 ``` Tracked code reading untracked paths works on the one machine that has them and errors everywhere else — and `sync` is a `[confirm]` recipe that writes issues, so the failure mode is a half-applied write against this tracker. Delete the migration half; keep `probe`, `contract*`, `search-repos`, `credential-status`, `auth`, `mint`, `whoami`. Blocked by: #7 — the same `plans/` deletion set. ## FM-AUDIT-12 — publish the aspect census as a wiki page The inventory has no home. `flake.aspects.graph` is evaluable and `nix run .#aspects-graph` renders it, but neither answers "what is in this fleet" for someone who has not already loaded the model. Content is ready: 33 records with kind/classes/requires/provides/owns/LOC, the 18 residual, per-host closures, schema utilisation, check status. Blocked by: FM-AUDIT-02, FM-AUDIT-10 — the numbers move when those land. --- ## Acceptance - Each FM-AUDIT-nn above is split into its own issue with a `Blocked by` row, or explicitly closed here as won't-do with the reason recorded. - `just check` passes in the devshell (FM-AUDIT-01 first — it is red today). - `residual` in `tests/aspects/fixtures/warning-budget.json` is lower than 18, or the migration framing is retired. - The census is a wiki page. ## Provenance Audit run 2026-08-04 by agent.odin against commit `fc3b751`, with `modules/implementations/{forgejo-agents,hermes}.nix`, `outputs/hosts/odin/{configuration.nix,secrets.agents.yaml}` dirty in the working copy. Counts come from `nix eval --json .#aspects.graph` and from line counts over `modules/` and `outputs/`; check status from building each `.#checks.x86_64-linux.aspects-*` derivation.
Commenting is not possible because the repository is archived.
No description provided.