Skip to content

chore: add manual metrics scripts - #8060

Open
Rathoz wants to merge 7 commits into
mainfrom
chore/metrics-scripts
Open

chore: add manual metrics scripts#8060
Rathoz wants to merge 7 commits into
mainfrom
chore/metrics-scripts

Conversation

@Rathoz

@Rathoz Rathoz commented Sep 7, 2026

Copy link
Copy Markdown
Member

What

Three more scripts in scripts/metrics/, following the three from #7939. Run by hand; --csv appends to a time-series file like the existing ones.

python3 scripts/metrics/customization_metrics.py   # metric 4
python3 scripts/metrics/annotation_coverage.py     # metric 5
python3 scripts/metrics/coverage.py                # metric 6, ~30s, runs the suite

Metric 4 — per-wiki customization. How much override code exists, the part that carries maintenance cost. Declarative data/config and legacy shims counted separately. Categories come from file content, not filename.

category        files     lines       loc    share
override          726     76307     50445   34.43%
declarative       255     32867     30493   20.81%
legacy             69      7758      5702    3.89%
commons           601     86806     59876   40.87%
per-wiki total   1050    116932     86640   59.13%

loc matches metric 1 (non-blank, non-comment-only), so this decomposes metric 1 rather than disagreeing with it — the totals reconcile exactly at 1651 files / 203738 lines / 146516 loc.

Metric 5 — annotation coverage. Exported functions in commons carrying @param/@return. At ~96% it's a regression guard. Functions taking no params and returning nothing are exempt, not counted as gaps.

coverage pct                        95.83%
needs annotation                      2400
unannotated                            100
exempt                                  19

Metric 6 — test coverage. Needs a config fix too: lua/.luacov has been dead since #5596 (2025-03-10) moved lua/standard/*lua/wikis/commons/standard/*, so its include matched nothing and exclude covered everything. Coverage was never actually measured.

coverage pct          49.38%
hits                   20920
missed                 21445
files at zero            271

Also: a README for the folder, un-anchored luacov.* gitignore entries (output lands in lua/, so they never matched), and the missing final newline on Widget/Table2/Row.lua.

Replaces #7862, #7863 and #7864. Review feedback from those (@ElectricalBoy, @hjpalpha) is folded in.

How it was tested

  • All six scripts run; the output above is real
  • coverage.py end to end on real Lua 5.1.5: 808 pass / 0 fail
  • Metric 4 totals cross-checked against metric 1 — identical on files, lines and loc
  • Metric 4 also cross-validated against an independent bash/awk implementation
  • --csv, --no-header, --base, --skip-run, missing-tool and missing-directory paths all checked
  • Passes ruff check + format

Rathoz and others added 3 commits September 7, 2026 12:06
The luacov config has been dead since #5596 ("wikis top level", 2025-03-10)
moved lua/standard/* to lua/wikis/commons/standard/*. Its `include` still
pointed at `standard/*`, which no longer matches anything, while
`exclude = {'wikis/*'}` covered the entire codebase. With `deletestats = true`
on top, every run produced an empty stats file, so no coverage was ever
measured.

Point include/exclude at the current layout, scoped to lua/wikis/commons, and
keep `includeuntestedfiles` so modules no spec loads count as 0% rather than
being dropped from the total.

Add scripts/metrics/coverage.py to run the suite under coverage and print the
summary. --skip-run reuses an existing report, worth having when a full run is
~25s against ~1s for the suite alone.

Also un-anchor the luacov entries in .gitignore. Output lands in lua/ because
busted runs with `-C lua`, so the root-anchored patterns never matched it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Share of exported functions in lua/wikis/commons whose doc block carries
@PARAM or @return. At ~96% it works as a regression guard rather than a growth
target: the useful signal is whether new unannotated functions appear.

Functions taking no parameters and returning no value need neither tag, so they
are reported as exempt and left out of the ratio rather than counted against
it. Whether a body returns a value is a heuristic scan to the `end` at the
function's own indentation, biased toward asking for an annotation rather than
excusing one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Everything in this repo is standardized; what varies is how much a wiki has to
override to get the behaviour it wants. Override code is the part that carries
maintenance cost, so it is the number worth watching. Declarative data/config
and legacy shims are counted separately -- a wiki adding 500 lines of faction
data is not the same event as one adding 500 lines of overrides.

Classification is by file content, not filename. GetMatchGroupCopyPaste/
wiki.lua, FilterButtons/Config.lua and NotabilityChecker/config.lua all read as
declarative from their names and are not. Shares are against all of lua/wikis
so "override code is 37.5% of all Lua" is answerable.

Also add the missing final newline to Widget/Table2/Row.lua, the only file in
the repo without one. It changes no count, but it did make a cat-based
cross-check of the commons total read one line short.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new metrics logic has correctness issues that can skew results (customization classification and coverage reproducibility) and should be addressed before landing.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a small set of manual, on-demand metrics scripts under scripts/metrics/ to measure Lua coverage, LuaLS annotation coverage, and per-wiki customization size, and updates luacov configuration + gitignore so coverage reporting works against the post-#5596 folder layout.

Changes:

  • Add three manual metrics scripts: coverage.py, annotation_coverage.py, customization_metrics.py.
  • Fix lua/.luacov include/exclude scope to target lua/wikis/commons and retain stats for later inspection/regeneration.
  • Update .gitignore for luacov output location and add the missing final newline in Widget/Table2/Row.lua.
File summaries
File Description
scripts/metrics/customization_metrics.py New manual script to classify and report per-wiki override/declarative/legacy LOC and shares.
scripts/metrics/coverage.py New manual script to run the busted suite under luacov and print a coverage summary table.
scripts/metrics/annotation_coverage.py New manual script to compute exported-function doc annotation coverage and output a table (optionally with deltas).
lua/wikis/commons/Widget/Table2/Row.lua Adds missing trailing newline at EOF.
lua/.luacov Fixes luacov scope/exclusions for the new folder layout and retains stats for later regeneration.
.gitignore Adjusts luacov ignore patterns to match outputs produced under lua/.
Review details
  • Files reviewed: 4/6 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/metrics/coverage.py Outdated
Comment thread scripts/metrics/customization_metrics.py Outdated
Rathoz and others added 2 commits September 7, 2026 12:21
scripts/metrics/ already had three scripts from #7939 and I had not matched
their conventions. Bring the new three in line:

- numbered as metrics 4-6, following 1-3
- `#!/usr/bin/env python3`
- --csv / --no-header with a leading date column, so all six can be appended to
  a time-series file. This is what the folder is for, and it is the trend
  history the markdown-table output could not give.
- plain aligned tables by default, not markdown. The markdown was for
  $GITHUB_STEP_SUMMARY, which these no longer write to.
- `from pathlib import Path`, type hints

Metric 4 now reports loc as metric 1 defines it -- non-blank, non-comment-only
lines -- alongside total physical lines. Previously it counted physical lines
and called them LOC, so two scripts in the same folder measured the same code
differently. Totals now reconcile exactly with metric 1: 1651 files, 203738
lines, 146516 loc. Metric 4 decomposes metric 1 rather than disagreeing with
it, and the share column is of loc.

Avoided `X | None` annotations: they need python 3.10, and while the workflows
pin 3.14 the scripts should still run on an older local interpreter.

README covers all six, what each answers, and what they need to run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both from Copilot's review, plus one it did not spot.

Coverage was reporting the previous run. lua/.luacov keeps stats so a report
can be regenerated, but nothing cleared them, so luacov reused whatever was on
disk. Running a single spec with full-suite stats present reported 49.38%
against a true 23.92% -- a 25 point error, and silent. run_suite() now clears
the stats and report first. collect() also fails with a message rather than a
traceback when no report is produced.

The classifier matched the bare `function` keyword, so a comment mentioning
the word could class a data file as override code, and it only learned local
names from `local function name`, missing `local name = function` exported via
`return`. Now matches actual definition forms, strips line comments first, and
records names from both local forms. No file in the repo hits either case
today, so the numbers are unchanged -- this removes latent fragility rather
than fixing a live miscount.

Third one, found while testing the above: export detection used a substring
match, so `local function h` plus `return {a = h()}` counted as exported, and
a short local name could match an unrelated identifier. It is now word
anchored and requires the name not be followed by `(`, which distinguishes
handing a local out (`return F`, `return wrap(F)`) from calling it to build
data (`return {a = h()}`) -- the latter leaves the file declarative, matching
how MainPageLayout data files are already treated.

Metric 4 still reconciles with metric 1: 1651 files, 203738 lines, 146516 loc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Rathoz Rathoz changed the title chore: add manual metrics scripts, fix luacov config chore: add manual metrics scripts Sep 7, 2026
@Rathoz
Rathoz requested a lite review from Copilot September 7, 2026 10:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The review found a reporting correctness issue in Metric 4 output and CLI inconsistencies versus existing metrics scripts that should be addressed before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/7 changed files
  • Comments generated: 5
  • Review effort level: Lite

Comment thread scripts/metrics/customization_metrics.py
Comment thread lua/.luacov Outdated
Comment thread scripts/metrics/annotation_coverage.py
Comment thread scripts/metrics/coverage.py
Comment thread scripts/metrics/customization_metrics.py
All from Copilot's second pass.

The three scripts took `--no-header` as a plain flag and built a bare
ArgumentParser, where the existing three use `description=__doc__` and a
`--header` BooleanOptionalAction. Copilot flagged this as inconsistent and it
was right -- I had checked the shebang, the metric numbering and --csv when
aligning to the folder, but not the parser itself. --help now shows what each
script measures, and --no-header still works, from the same declaration.

The per-wiki total share added up the already-rounded per-category shares
rather than dividing summed loc by total loc. Both give 59.13% today
(59.1300 against a true 59.1335), so nothing visible changes, but the wrong
one drifts as categories move.

Also the singular-subject grammar in the .luacov comment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

annotation_coverage.py currently reports params_total/params_annotated as function-counts rather than parameter-counts, making the params_* metrics misleading.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

scripts/metrics/annotation_coverage.py:100

  • params_total / params_annotated are currently counting functions with params (incrementing by 1 per function), but the field names and params_pct suggest these are parameter-level totals. As written, a function with 5 parameters counts the same as one with 1, which makes the params_* metrics misleading.
  • Files reviewed: 5/7 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Per Copilot: params_total and params_annotated incremented once per function
rather than once per parameter, so a five-parameter signature counted the same
as a one-parameter one while the field names said otherwise.

This was a regression from the move to the house table format. The markdown
version labelled the row "Functions with all params annotated", which was
accurate; deriving labels from field names dropped that and left the table
printing "params total 2046" for a count of functions.

Now counts individual parameters, which is what the names imply: 2991 of 3148
annotated, 95.01%, against the 95.60% the function-level count reported.
Clamped per function, since a doc block can carry more @PARAM than the
signature takes.

Worth doing now rather than later: no time series exists yet, so redefining
these columns costs nothing. After the first append it would break comparability.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Rathoz

Rathoz commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

On the latest review — the params_* point is right, and it was a regression I introduced. Fields counted functions (2046/1956) while the names said parameters; now they count parameters (2991/3148, 95.01% vs the 95.60% the function-level count gave). Clamped per function, since a doc block can carry more @param than the signature takes. Doing it now is free — no time series exists yet, so redefining the columns breaks nothing.

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.

2 participants