diff --git a/CHANGELOG.md b/CHANGELOG.md index ff60ea83..3aa370fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased +- learn(lint): **known answers for four stock-rule findings on the toolkit's own conventions, and four modelling-error rule candidates.** New table in `lint-that-actually-runs.md`, with one entry for each finding. QUAL004 on a `DS_` flow used only as a Data grid 2 datasource (seen on v0.26.0 although the rule counts `datasource` refs) is accepted into the baseline with a reason, never renamed or given a fake caller (#360, BUG-DRAFT-qual004-dg2-datasource). QUAL004 on `TEST_` helpers and CUSTOM002 on `TEST_`/`GET_`/`PRS_`/`STUB_` are handled by baselining them with a reason, or (CUSTOM002) by `enabled: false` recorded in `PROJECT.md` (#241). QUAL002 on import mappings and published REST services, which MDL cannot document (MDL089), is handled by the rule's existing per-kind options (`check_import_mappings`, `check_published_rest_services`; #246). `process-coherence-pass.md` warns against deleting either QUAL004 kind. Upstream drafts for all three are in `bug-logs/pending-github-issues/`. `process/lint-backlog.md` Batch 7 adds the four missing #169 candidates (rows 51–54), and its inbox file is promoted and deleted. — joshuamoesa (#360), MendixMau (#241, #246, #169) - process(build): **Stage 5 drafts the ready set in parallel and applies serially.** The runbook dispatch row said "sequential within a module", which serialised drafting even for independent scripts; it now points to `iterative-build-loop.md` → Gate cost, the single statement of the rule. The unit is the build-plan row: every `BUILD` row whose "Depends on" rows are all applied, minus overlapping writers (two rows that replace, alter or drop+create the same document never share a ready set, and no row is drafted while it overlaps a queued unapplied one; `brd-to-build-plan.md` has Produces name every document a row writes), runs at once, its scripts in order, one `mdl-agent` per script. Applies stay one at a time through `bin/exec.sh`, and the ready set is recomputed after each. A queued draft made before a sibling apply landed is re-checked with `mxcli diff --format struct` (preflight STOP rule 23); any removal line (checked against `describe` per BUG-146) or STOP rule 21 target means re-draft, never patch, because a stale restatement of a shared document is exactly rule 21's silent seed-call loss and passes `check --references`. Report `ready set N, drafted N, applied k`; the `mdl-agent` template says a drafter never applies and never depends on an unapplied draft. Field evidence: none yet. — MendixMau - fix(platform-link): **CLI commits now show their changed documents in Studio Pro History.** Studio Pro's History → Model changes pane reads a `ModelChanges` array from the commit's `refs/notes/mx_metadata` note. Trap 6c's hand-written template set it to `[]`, so every mxcli/agent commit showed "Model changes 0" (field project: 270 of 270 notes empty). New `project-bin/mx-metadata-note.py` (stdlib only) computes the array from the commit's changed `.mxunit` files, in Studio Pro's entry shape: Status, UnitID, UnitType, UnitName and Module (walked up the `.mpr` Unit tree). It has `changes` / `write` / `backfill` / `sync ` subcommands. `install-project-hooks.sh` now also installs post-commit and post-rewrite hooks that write the note and never block, and `--check` covers them, so `sync-project.sh` rolls them out to wired projects. Notes are published with `mx-metadata-note.py sync ` after the branch push. Push config is never touched. With mxcli v0.26+ in the project root the hooks call `mxcli git note --write` instead (mendixlabs/mxcli#1337); the Python script is the fallback for older pins, and on 160 field commits both wrote the same 3,594 entries. Trap 6c rewritten. Measured against Studio Pro's own notes, 51 commits in 5 apps: 3387/3389 entries exact on all five fields. Field run on a 357-commit Mendix 11.15.0 app: 158 notes backfilled and pushed, and Studio Pro History lists the documents per revision. Review fixes: an earlier revision added `HEAD` + the notes ref to `remote..push`. It was dropped because any push refspec replaces `push.default`. A branch tracking a differently named upstream got a new same-named branch on Team Server where `simple` refuses, a missing local notes ref failed the whole push, and nobody knows whether Studio Pro's own push reads that config. The installer now only fetches the remote's notes (non-forced). It removes that exact `HEAD` + notes pair if present, and leaves and reports any other entry. `sync` handles a remote with no notes ref (it used to exit 1) and retries a rejected push once after re-merging. A note that already lists changes (Studio Pro's) is never overwritten by `write`/`backfill` without `--force`, and `sync` keeps a colleague's non-empty note over ours. The mxcli probe greps `git note --help` for `--write`. Hooks and docs call the script through `resolve_py`. Fixture `tests/wave2/test-mx-metadata-note.sh` (40 cases, over real captured template units; single- and two-tree layouts, amend, backfill and `--force`, push exit codes identical to a no-hooks clone, leftover-refspec cleanup, sync to an empty remote and its retry, notes fetch at install, a diverged notes merge; green under `init.defaultBranch` master and main). — field project (Mendix 11.15 app), MendixMau - learn(fixture-seeding): **explore the data structure first, choose rows by use case, scan actions for resets before any run.** Two seeding runs on public template apps: seeding raised journey passes 33 -> 58 and 20 -> 51 of the same sets (FAIL 93 -> 68, 163 -> 132; INVALID unchanged), and a "Reset all demodata" journey wiped accounts so 104 of 127 journeys reported `The username or password you entered is incorrect`, identical on empty and seeded databases. Skill gains three questions a seed must answer, a use-case checklist, a destructive-action scan, a generic traps table with symptoms, measured expectations and a denominator checklist; routing trigger now fires on seed/test/demo data and empty pages. Independent review fixes: object ids follow the Mendix bigint pattern with a business-key guard (no uuids), associations are seeded where the page's path stores them, four new traps (enum names, inheritance chain, calculated attributes, view/external entities), and the port-offset and container-label traps moved to `harness-architecture.md`. — MendixMau diff --git a/bug-logs/mxcli-bugs.md b/bug-logs/mxcli-bugs.md index c068c838..7a2b03a2 100644 --- a/bug-logs/mxcli-bugs.md +++ b/bug-logs/mxcli-bugs.md @@ -5949,7 +5949,9 @@ service and its callers. 4. **Doc comments** (`/** */`) set documentation on microflows, entities and Java actions. Before an import or export mapping, or a published REST service, they are silently ignored ("already in sync"). Before a Java action parameter they are a syntax error. QUAL002 therefore - cannot be cleared for those from MDL. Add a baseline entry with the reason. + cannot be cleared for those from MDL. Add a baseline entry with the reason, or switch that kind + off in QUAL002's options (`check_import_mappings`, `check_published_rest_services`; mxcli + >= 0.24.0). The MDL089 warning on v0.25.0 names the case (#246). 5. **`import from mapping … ($Call/ResponseBody)` is a parse error** ("mismatched input '/'"). The source must be a variable: `declare $Body String = $Call/ResponseBody;` then `($Body)`. 6. **Import mappings, re-run.** @@ -6950,3 +6952,26 @@ Two related findings from reading the v0.26.0 source (not field-run): matching the project. Recipe and the rule-set comparison to run before any diff: `skills/lint-that-actually-runs.md` → "Linting a copy of the model". `lint-gate.sh`'s collapse guard catches this inside the gate. Nothing catches it when lint is run directly. + +## BUG-DRAFT-qual004-dg2-datasource: QUAL004 reports a `DS_` microflow as "not called from anywhere" when its only caller is a Data grid 2 datasource, and the lint ratchet fails a correct script (2026-10-14) + +> **NOT YET FILED.** Upstream draft: `bug-logs/pending-github-issues/qual004-dg2-datasource-ref.md`. + +**Discovered:** 2026-10-14, on an existing-app change project (a connector app). A build row created a page whose Data grid 2 is driven by a new `DS__` microflow, in one script; the navigation entry came in a later script. +**Reproducible:** seen twice on one model, not yet reduced to a blank app. **mxcli version:** v0.26.0. **Mendix:** 11.15.0. macOS. + +**What happens.** `check` and mxbuild were clean. `exec.sh`'s lint ratchet then reported: + +``` +[QUAL004] at ..DS__GetConfiguration +✗ lint: rule(s) rose vs baseline — QUAL004 (+1) +✗ Script applied and mxbuild is clean, but LINT ROSE — this is not a pass. +``` + +Both flagged flows are Data grid 2 datasources on pages in the same module and work in the running app. The datasource is the only reference they will ever have, so the finding never clears. + +**What is odd.** The v0.26.0 `orphaned_elements.star` that `mxcli init` writes already counts `datasource` as an inbound kind (`MICROFLOW_ENTRY_KINDS`), and its header names a `DS_` page-datasource false positive as fixed. So the gap is most likely in what the catalog records for a **pluggable** widget's datasource (Data grid 2), not in the rule. Not probed: whether a classic data view or list view datasource clears the same finding on the same model. + +**Toolkit side:** do not rename the flow or add a fake caller. Accept the rise with `bin/lint-gate.sh --update-baseline --reason "QUAL004 DS_ datasource false positive (BUG-DRAFT-qual004-dg2-datasource)"`. `skills/lint-that-actually-runs.md` → "Stock rules against the toolkit's own conventions". + +**Expected:** a `DataSource: microflow X` on any widget, pluggable or not, is an inbound reference to X. `describe page` already prints it. diff --git a/bug-logs/pending-github-issues/lint-naming-prefixes-as-options.md b/bug-logs/pending-github-issues/lint-naming-prefixes-as-options.md new file mode 100644 index 00000000..cbed1185 --- /dev/null +++ b/bug-logs/pending-github-issues/lint-naming-prefixes-as-options.md @@ -0,0 +1,22 @@ +Repo: mendixlabs/mxcli +Source: toolkit issue #241 (173 of 759 findings on one toolkit-built project were CUSTOM002/QUAL004 on the toolkit's own prefixes) +Status: **NOT FILED.** + +--- everything above this line is stripped before filing --- + +**Title:** Let projects set QUAL004's entry-point prefixes and CUSTOM002's valid prefixes via lint-config.yaml + +## Environment + +- mxcli **v0.25.0 / v0.26.0** (the `orphaned_elements.star` and `example_microflow.star` that `mxcli init` writes) + +`orphaned_elements.star` (QUAL004) hard-codes `ENTRY_POINT_PREFIXES = ["ACT_", "SCH_", "WS_", "REST_", "OData_"]` +and `example_microflow.star` (CUSTOM002) hard-codes `VALID_PREFIXES`. Neither calls `get_option()`, so +a project can only change them by editing the seeded file, which the next `mxcli init` overwrites without +warning. On one project 84 QUAL004 findings (83 of them `TEST_` helpers called only from +`tests/*.test.mdl`) and 121 CUSTOM002 findings (`TEST_` 90, `GET_` 25, `PRS_` 4) were conventions, +not defects. + +Asks: +1. Read both lists with `get_option()` (one comma-separated text, since list options arrive as `None`), keeping today's values as defaults. +2. QUAL004: count a microflow named in `tests/**/*.test.mdl` as called, or exempt a configurable test prefix. diff --git a/bug-logs/pending-github-issues/qual002-skip-mdl089-types.md b/bug-logs/pending-github-issues/qual002-skip-mdl089-types.md new file mode 100644 index 00000000..4d484b72 --- /dev/null +++ b/bug-logs/pending-github-issues/qual002-skip-mdl089-types.md @@ -0,0 +1,28 @@ +Repo: mendixlabs/mxcli +Source: toolkit issue #246; `bug-logs/mxcli-bugs.md` (published REST services section, item 4) +Status: **NOT FILED.** + +--- everything above this line is stripped before filing --- + +**Title:** QUAL002 asks for documentation on document types MDL cannot document (MDL089) + +## Environment + +- mxcli **v0.25.0** (measured), v0.26.0 rule text unchanged on this point + +On one project 45 QUAL002 findings: ImportMapping 29, JavaAction 11, PublishedRestService 2, +Page/ImageCollection/JsonStructure 3. For published REST services and import mappings a `/** */` +comment cannot store documentation: + +``` +⚠ the doc comment before `create or modify published rest service … "ServiceBulletins"` is lost: that statement cannot store documentation, so the comment is ignored; the next statement that can take it is `create or modify microflow …` [MDL089] +``` + +Without the warning the comment lands on the next microflow and overwrites its documentation. In an +mxcli-only workflow those findings can only be closed in Studio Pro. + +Asks: +1. For the MDL089 set, change the suggestion to "document it in Studio Pro (MDL cannot store documentation here, MDL089)", or report at a lower severity. +2. Better: let MDL store documentation on these types. + +Today's workaround: the per-kind options `check_import_mappings` / `check_published_rest_services` set to `false` in `lint-config.yaml`. diff --git a/bug-logs/pending-github-issues/qual004-dg2-datasource-ref.md b/bug-logs/pending-github-issues/qual004-dg2-datasource-ref.md new file mode 100644 index 00000000..0b5d9b22 --- /dev/null +++ b/bug-logs/pending-github-issues/qual004-dg2-datasource-ref.md @@ -0,0 +1,21 @@ +Repo: mendixlabs/mxcli +Source: toolkit issue #360; `bug-logs/mxcli-bugs.md` BUG-DRAFT-qual004-dg2-datasource +Status: **NOT FILED.** Reduce to a blank app first: one page, one Data grid 2 on a `DS_` microflow, `mxcli lint`, then the same with a classic list view. + +--- everything above this line is stripped before filing --- + +**Title:** QUAL004 flags a microflow used only as a Data grid 2 datasource as "not called from anywhere" + +## Environment + +- mxcli **v0.26.0** +- Mendix **11.15.0** +- macOS + +A microflow whose only reference is the `DataSource: microflow` of a Data grid 2 is reported by +QUAL004 ("is not called from anywhere. Remove if unused"). `describe page` prints the datasource, +and the app works. `orphaned_elements.star` already lists `datasource` in `MICROFLOW_ENTRY_KINDS`, +so the catalog probably records no `datasource` ref for a pluggable widget's datasource. + +Ask: record a pluggable widget's microflow/nanoflow datasource as a `datasource` ref, the same as a +classic data view or list view. Following the rule's suggestion deletes a live datasource. diff --git a/contrib/inbox/2026-09-29-lint-rules-recurring-modelling-errors.md b/contrib/inbox/2026-09-29-lint-rules-recurring-modelling-errors.md deleted file mode 100644 index 017a5b22..00000000 --- a/contrib/inbox/2026-09-29-lint-rules-recurring-modelling-errors.md +++ /dev/null @@ -1,21 +0,0 @@ -# Four modelling errors hit BOTH builds — they are lint rules, not tool bugs - -**From:** a field comparison (mxcli + toolkit build vs a Studio Pro MCP build, same frozen spec) -**Date:** 2026-09-29 -**Kind:** learning -**Field evidence:** defect tables of both builds (~37 vs ~38 defects; modelling errors 16 vs 15); four defects recurred in both builds independently, so they come from the spec/platform, not the authoring tool. -**Proposed target:** `skills/learned-detection-gaps.md` / `skills/learned-microflow-patterns.md`, and upstream mxcli lint - ---- - -| Rule | Defects it would have caught | -|---|---| -| Picker/selectable XPath reads rows the current role cannot read under entity access (e.g. via System.UserRoles / Account) | reassign picker listed only self / was empty — both builds | -| Microflow called from a page button changes/creates objects shown on that page without refreshInClient | "draft with agent" page not refreshed; goods-receipt lines never load — both builds | -| `change` without commit at the end of a timer/workflow-called microflow | escalation change lost (found only by a timer run) | -| addDays / trim on a possibly-empty attribute | null runtime error | -| Navigation without logout item; home page needs role read (CE2729) | caught late in both builds | - -Also shared, spec-level: vendor legal-suffix matching; workflow admin centre access for the -AP manager role. Proposed: add the rules to the detection-gaps skill now, and file them as -mxcli lint requests. Hypothesis: prevents ~6–8 of ~75 combined defects. diff --git a/process/lint-backlog.md b/process/lint-backlog.md index c15eaead..60f29079 100644 --- a/process/lint-backlog.md +++ b/process/lint-backlog.md @@ -140,6 +140,34 @@ says a role is external. Lint Batch 4 shipped both rows; the field-run counts ar | 49 | External role (configured) can write all members, a state enumeration (`Status`/`State`/`Result`/`Stage`/`Phase`), an audit `…At`/`…By` attribute, or the association its own XPath scopes ownership on (SEC014) | security | #268: a partner role held `create, read *, write *` on two self-owned entities with a state attribute; lint showed only CONV006 +4, re-baselined; a manual review found it five days later | `permissions_for()` `MEMBER_WRITE` rows, `attributes_for().data_type` (`Enumeration`), `user_roles()` / `role_mappings()`; `external_roles` option, text not list; absent = sync NOTE, not a `_rule` finding. Probed 2026-10-13 | drafted (Batch 4) | | 50 | CONV009 threshold read from `lint-config.yaml` (`maxObjects`, `countLoopBodies`) instead of the stock hard-coded 15 | quality | #267: the generated config's own example does nothing; three apps at 36 / 30 / 5 findings, one app split flows only to satisfy the 15 | `get_option()`; `total_activity_count` (mxcli >= 0.25.0) for loop bodies. Default kept at 15: raising it for every project is a maintainer call | drafted (Batch 4) | +## Batch 7 — modelling errors that hit both arms, and stock-rule false positives + +Source: issue #169, from a field comparison on 2026-09-29: an mxcli + toolkit build and a Studio Pro +MCP build of the same frozen spec, about 75 defects between them. Four or five modelling errors +appeared in both builds independently, so they come from the spec or the platform, not the authoring +tool. Hypothesis: these rules would have prevented 6–8 of the 75. The page-button refresh candidate +is row 7. The other four are below. + +| # | Rule | Category | Evidence | Needs | Status | +|---|---|---|---|---|---| +| 51 | Reference-selector / picker XPath that reads rows the current role cannot read under entity access (e.g. through `System.UserRoles` or an account association) | security | #169: a reassign picker listed only the user, or nothing, in both builds | widget selectable-objects XPath; `permissions()` per entity and role (probe) | idea | +| 52 | `change` with no later `commit` in a microflow reached from a scheduled event or a workflow (no client to commit it) | correctness | #169: an escalation change was lost, found only by a timer run | `activities_for()` order and `commit` targets; callers from `scheduled_events()` / workflow refs (probe) | idea | +| 53 | `addDays`/`addHours`/`trim`/`length` on an attribute that can be empty, with no empty guard before it | correctness | #169: a null runtime error | expression text in the catalog: same blocker as row 36 | blocked: expressions not indexed (upstream) | +| 54 | Navigation profile with no logout item, or a home page whose role has no read access to its entity (CE2729) | security | #169: caught late in both builds | navigation in projection (row 12's blocker) | blocked: field unconfirmed | + +Two spec-level items from the same comparison are not lint rules: legal-suffix matching on company +names, and workflow admin-centre access for an approver role. + +**Stock-rule false positives.** These are not candidates. They are findings on the toolkit's own conventions. The +answer for each is in `skills/lint-that-actually-runs.md` → "Stock rules against the toolkit's own +conventions". The rules are mxcli's own, so any Starlark change waits for fixtures and a toolkit replacement rule (rollout rule 1). + +| Rule | Issue | Disposition | +|---|---|---| +| QUAL004 on a `DS_` Data grid 2 datasource | #360 | baseline with reason; upstream `bug-logs/pending-github-issues/qual004-dg2-datasource-ref.md`; BUG-DRAFT-qual004-dg2-datasource | +| QUAL004 on `TEST_` helpers; CUSTOM002 on `TEST_`/`GET_`/`PRS_`/`STUB_` | #241 | CUSTOM002 `enabled: false` (user decision, recorded in `PROJECT.md`) or baseline with reason; QUAL004 baseline with reason; upstream `lint-naming-prefixes-as-options.md` | +| QUAL002 on import mappings / published REST services (MDL089) | #246 | QUAL002 per-kind options in `lint-config.yaml`; upstream `qual002-skip-mdl089-types.md` | + ## Cheap extras (one afternoon each, when a batch has room) Microflow not in a folder · non-persistent entity committed to the database · many-to-many @@ -171,6 +199,8 @@ a second backlog section. Forum only to weight by incident frequency. ## History +- 2026-10-14 — Batch 7 added from #169 (rows 51–54), plus dispositions for the stock-rule false + positives in #360, #241 and #246. The 2026-09-29 inbox file for #169 was promoted here and deleted. - 2026-10-13 — Batch 6 added from #268 / #267 (rows 49–50). Lint Batch 4 shipped them as `SEC014` and a toolkit-owned `CONV009` (status `drafted`, 32 unit cases). First field run, mxcli 0.26.0: SEC014 1 finding with the one external role configured, positive control 7 that a SQL replay diff --git a/skills/lint-that-actually-runs.md b/skills/lint-that-actually-runs.md index c1932eac..dd3329c0 100644 --- a/skills/lint-that-actually-runs.md +++ b/skills/lint-that-actually-runs.md @@ -159,6 +159,24 @@ and report score first → last. Lint counts say whether a rule fires; this says got better and the sessions faster while it did. `bin/harvest-learnings.sh` carries the trend summary (never the rows — those name scripts) into the toolkit inbox at wrap-up. +## Stock rules against the toolkit's own conventions + +Three stock rules flag things this toolkit does on purpose. Each needs a known answer; otherwise an +agent "fixes" the finding by renaming a working flow, adding a fake caller, or moving a doc comment +onto the wrong document. Each answer is the user's decision, recorded in `PROJECT.md`, like any opt-out +(`lint-rules/README.md` → "Opting out of a rule"). + +| Finding | Why it is not a defect | What to do today | +|---|---|---| +| **QUAL004** on a `DS_` flow whose only caller is a Data grid 2 datasource (#360) | The stock rule already counts `datasource` refs (v0.24.0+), yet v0.26.0 flagged two working Data grid 2 datasources. The catalog likely misses the pluggable widget's ref. | Confirm with `describe page`, then accept the rise: `--update-baseline --reason "QUAL004 DS_ datasource false positive"`. Never rename or add a caller. Upstream: `pending-github-issues/qual004-dg2-datasource-ref.md` | +| **QUAL004** on a `TEST_` helper (#241) | It is called from `tests/*.test.mdl`, which lint cannot see. `ENTRY_POINT_PREFIXES` is a constant, not an option. | Baseline with a reason. Keep QUAL004 on: it is the orphan check `process-coherence-pass.md` runs. Upstream: `pending-github-issues/lint-naming-prefixes-as-options.md` | +| **CUSTOM002** on `TEST_`, `GET_`, `PRS_`, `STUB_` (#241) | `example_microflow.star` is an *example* rule from `mxcli init` ("customize the prefixes"). Its `VALID_PREFIXES` is a constant. | `rules: CUSTOM002: enabled: false`, recorded in `PROJECT.md`, or baseline with a reason. Never edit the `.star` file. Upstream: `pending-github-issues/lint-naming-prefixes-as-options.md` | +| **QUAL002** on import mappings, published REST services and Marketplace Java actions (#246) | MDL cannot store documentation on the first two (MDL089); a doc comment above them moves to the next microflow. | The rule has per-kind options (mxcli >= 0.24.0): `rules: QUAL002: options: check_import_mappings: false`, `check_published_rest_services: false`, `check_java_actions: false`, `check_java_action_params: false`. Or document them in Studio Pro. Upstream: `pending-github-issues/qual002-skip-mdl089-types.md` | + +The QUAL002 option names come from the `missing_documentation.star` that `mxcli init` v0.24.0–v0.26.0 +writes (it reads them with `get_option`). Grep your copy for `get_option` before relying on them; +they have not been field-run yet. + ## Two false-pass bugs already designed out of `lint-gate.sh` — keep them out Both were found by testing the gate, not by reading it: diff --git a/skills/process-coherence-pass.md b/skills/process-coherence-pass.md index 1ee20e95..9d722f2a 100644 --- a/skills/process-coherence-pass.md +++ b/skills/process-coherence-pass.md @@ -47,6 +47,10 @@ open-ended search without a specific term in mind. `Home`/`Login`/`Index`/`Dashboard`-pattern pages — excluded). Run it before anything else; it is the exact shape of the 2026-08-13 originating defect (a correctly built, correctly granted microflow with zero references anywhere). + Two kinds of finding are not orphans: a `TEST_` helper called only from `tests/*.test.mdl` + (lint cannot see test files) and a `DS_` flow used only as a Data grid 2 datasource (seen on + v0.26.0). Check `describe page` or grep `tests/` before removing either + (`lint-that-actually-runs.md` → "Stock rules against the toolkit's own conventions"). - `mxcli` graph tables (`REFRESH CATALOG FULL` then `REFRESH CATALOG COMMUNITIES`, once): `GRAPH_DEAD_ASSETS` for unreachable elements project-wide, `GRAPH_INTEGRATION_SURFACE` for what actually crosses module boundaries, `GRAPH_MODULE_COUPLING`/`GRAPH_CENTRALITY` for the