1.1.0-release
What's new
commit 243dfab7b71581e276e9072d44f9c5d390097872
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Sep 11 00:55:14 2026 +0530
sync-docs: the target interface catches up to 12.1.0
DEPENDENCIES.md, ARCHITECTURE.md, scope.md and schema.md each asserted Interface
120007 as what this addon targets; all four now read 120100. The 12.0.7
references in core/Compat.lua and core/Constants.lua are untouched on purpose --
they record what a live 12.0.7 client actually did, and rewriting a measurement
to match a newer patch would falsify it.
testing.md and smoke-tests.md picked up the same correction in their client
preambles. No other drift: the slash table matches the README both directions,
the LibKa0s provenance line is in CLAUDE.md where the vendor gate reads it, and
docs/test-cases.md was already in sync.
luacheck clean, 849 tests green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4Xy2KrRa8NmD1843rgKnE
commit 0ab079a45ab8c8d7184e014ff7d9f5c99974b07b
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 10 23:54:50 2026 +0530
Release 1.1.0
The two id-lists became one Filters tab, the guild bank now arms on its frame
showing rather than on data arriving, a disabled addon can be re-enabled
without a reload, and Reset Everything clears the capture gate's stored
settings. 149 commits since the 1.0.0 tag.
Release gate passed at d4632ef: luacheck 0/0 across 61 files, 849 tests green,
zero functions above CCN 15. The perf suite did NOT run — this addon ships no
tests/perf.lua — so the gate covered three suites, not four. That is stated in
the Version History row and in docs/automated-tests/20260910-234511/ANALYSIS.md, because
notes that say only 'verified' over a three-suite gate read as four.
What's new is deliberately not recreated (documentation-§1, standard v2.42.0).
The WoW badge follows the Interface line to 12.1.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4Xy2KrRa8NmD1843rgKnE
commit d4632ef81b1563edac6bfd1743dfb7afa8fc1bac
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 10 23:44:51 2026 +0530
Target patch 12.1.0 — the TOC interface becomes 120100
Retail moved to 12.1.0, and an addon whose TOC still declares 120007 shows as
out of date in the client's addon list even though nothing in it broke.
The line is the only thing that changed. LibKa0s needed nothing alongside it:
it ships no TOC of its own, reads the client build from GetBuildInfo() at
runtime rather than declaring one, and the harness vendored as tests/_kit/
carries no build number at all — so no re-vendor was involved.
Verified before committing: luacheck clean and the headless suite green in
every repo.
Living documentation still says 120007 / 12.0.7 in the README badge and
DEPENDENCIES.md, and in several repos ARCHITECTURE.md, scope.md, module-map.md
and smoke-tests.md as well. That sweep is /wow-addon:sync-docs' job, not this
one-line change's. Comments recording measured 12.0.7 behavior stay as they
are — they are still true.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4Xy2KrRa8NmD1843rgKnE
commit 2cbe5063d65b83a24ce2e1bdae122ecb14dd9174
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 10 23:26:17 2026 +0530
README: drop the logo and the settings shots, re-point the rest
The logo block duplicates the CurseForge page header, and the two settings
screenshots show a panel nobody picks an addon for. What is left -- history
browser, insights, banking session -- moves to the new attachment IDs, and
Insights is named as the panel it is in both the caption and the alt text.
The five screenshot bitmaps and the logo are refreshed to match the upload.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G4Xy2KrRa8NmD1843rgKnE
commit 3c682896a71fed7cbaf8ef7b17e007a62fc4df94
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 10 03:31:36 2026 +0530
media: refresh the screenshot set
Local source images only. Every README serves its screenshots from the
CurseForge CDN, so nothing here is referenced by a doc and no page
changes until the new set is uploaded there.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVx7o2xdz6H1jH2CZviDw8
commit 7172bab3163c3df09d1f809899e8f8ff10b6a9dd
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 10 02:51:32 2026 +0530
README: drop `## What's new` (standard v2.42.0)
documentation-§1 no longer carries the section. It was bound to the top
`## Version History` row -- the two "MUST agree" and rolled forward
together on every bump -- so its content was a copy of another MUST's
content by definition, and the only work it could generate was keeping
the copy true. Version History is now the addon's single player-facing
history.
Anti-pattern #40 is retired in place upstream; the item list renumbers
6-13 to 5-12.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVx7o2xdz6H1jH2CZviDw8
commit 62d188864e579764fe16ed100651faa51a361a90
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 9 21:43:00 2026 +0530
README: adopt the standard's prose Usage section (v2.41.0)
documentation-§1 item 7 no longer mandates the two tables that made
Usage a contents page. The `Command | What it does` table said what
exists without ever saying what to do, and it was a second copy of a
list the addon already generates from NS.COMMANDS for `/<slash> help`,
so it could only be in lockstep or wrong. The `Tab | Covers` table has
moved to docs/settings-panel.md, its canonical home, where the finer
page-tab-row tree already sat beneath it.
Usage is now prose in the order a new player meets the addon: how the
display is shown, hidden, moved and locked, what preview or test mode
is for, what the chrome's controls do, and the core interactions. It
closes on one sentence pointing at Settings > AddOns and the slash
command list.
The whole README was humanized in the same pass -- em dashes cut,
`**Bold lead.**` bullets removed, see-saw pairs and repeated rhetorical
moves broken up, sentence and paragraph length varied.
Reviewed by an adversarial pass that checked every factual claim
against source; the corrections it found are folded in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVx7o2xdz6H1jH2CZviDw8
commit 5ba82ca0ca00b338d2dcadd69e1d3920852625e2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 9 01:40:17 2026 +0530
re-vendor LibKa0s v1.29.0 — the JSON dump folds into the report step
`report` printed the summary and `dump` printed the run as one line of JSON.
Both went to the same log, both described the same finished run, and
perf-analysis asks for both -- so two verbs and two panel rows meant a second
click, a second thing to remember, and a run reported without its dump was the
easy mistake to make. `report` writes both now, JSON last, because the summary
is what you read and the JSON is what you copy.
`perf dump` is no longer a verb and `Progress().dump` is gone. Folded rather
than aliased: an alias is the duplication the fold removes, and the unknown-verb
path prints the usage, where `report` now says it renders the JSON too.
Payload and kit copied whole from a clean checkout of the tag; the CLAUDE.md
provenance line rolls in this commit. Kit revision unchanged at 15.
diff -r against a clean v1.29.0 checkout: byte-empty, both payloads.
849 passed, 0 failed, 0 skipped, 849 total
Total: 0 warnings / 0 errors in 61 files
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 367670768cee1c05fc4bbe650c85bf2c8a54487c
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 9 01:26:45 2026 +0530
re-vendor LibKa0s v1.28.0 — the perf usage block renders correctly
Perf.lua 9 only. The usage block printed `<start|measure|finish|canceleport|
dump|showideoggle>`: the verbs are pipe-separated and the client reads `|r` as
a color reset, `|h` as a hyperlink and `|t` as the end of a texture, so it ate
all three and the words fused. The eaten `|r` also swallowed the reset closing
the gold run, which is why the line stayed yellow. The pipes are doubled now.
The block also hand-aligned a second column with spaces and wrapped each
description onto a continuation line, which a proportional chat font cannot
honor. It is one row per verb through the collection's own FormatRow now, the
same formatter the slash-command help uses.
Payload and kit copied whole from a clean checkout of the tag, not from the
library's working tree, and the CLAUDE.md provenance line rolls in this commit.
Kit revision is unchanged at 15. No member added, removed or renamed, so nothing
in this addon had to adopt anything.
diff -r against a clean v1.28.0 checkout: byte-empty, both payloads.
849 passed, 0 failed, 0 skipped, 849 total
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 2b9e2bbfb2d3956e309eb039d8da632ce1b3b199
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 22:18:11 2026 +0530
M5-06-C1: the lint citations name a section that does not exist
The 2026-09-08 standards audit found these, and this cycle wrote them. M4c-03,
M4c-04 and M4c-06 removed the blanket luacheck ignores across the collection and
cited the rule as `lint-§1` in the configs, the gate cases and the testing docs.
standards/lint.md carries exactly one heading and no numbered subsections, so
`lint-§1` resolves to nothing. The house rule for a section-less file is the bare
filename -- the same ruling M1-STD-04 made for standalone-windows.md, whose
line-number citations exist for this reason.
Seventy citations across nine repositories, all authored 2026-09-08, all now
reading `lint.md`. No behaviour changes and no count moves: every hit is a
comment or an assertion message.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 141fce6d916d5d8c0d6e8b7e3c94c07192c15ba3
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 21:47:41 2026 +0530
M5-06: the v2.39.0 audit, and four findings only the amendments can see
A read-only compliance measurement of Ka0s Bank Ledger against the Ka0s WoW
Addon Standard **v2.39.0**, resolved from master and confirmed at line 1 of
`standards/STANDARDS.md` before anything was measured. All 26 section files
were fetched and read; diffed against a verbatim v2.38.0 copy, exactly the
fifteen sections the changelog claims had changed.
**Seven roots down to five, and every one of them is Low.** The 2026-09-07
bundle left seven open. Six are closed and verified closed: `BL-04` and
`BL-28` by the two register rows `M5-02` wrote, `BL-29` by `.pkgmeta:9`
and `:22`, `BL-30` by the regenerated record, `BL-31` forward at
`20260908-181253/ANALYSIS.md:73-77`, `BL-33` by the re-vendored kit's
`skipped` capture. `BL-32` is closed too, and it is the one worth naming:
`line-endings-§7`'s command reports **0** where the last bundle measured 4,
`git ls-files --eol` agrees, and the repo now carries the vendored gate that
owns the question. `M4-10` never appeared as a commit here; the tree is clean
anyway, and the measurement is what counts.
**Four of the five new findings exist only because the standard was amended,
which is the thing this pass was run to test.** `BL-35` — 62 British
spellings across 21 authored files, found by running `localization-§5`'s
canonical `BRITISH`/`ALLOWED` lists, published for the first time this
version; four earlier audits read the prose table and none of them could run
it. One of the 62 is a tooltip a player reads, four lines below a comment
that spells the same word correctly. `BL-36` — one unannotated load-bearing
TOC position, visible only once `toc-file-§5` stated its denominator: eight
of nine positions are annotated and correct, and `modules\Insights.lua` takes
`NS.InsightsWidgets` at file scope with nothing saying so. `BL-37` — the
hollow composer is right, the suite pins 6 canonical rows on the loaded arm,
and `options-ui-§1`'s new MUST wants the gap: 16 / 10 / 6. `BL-38` — the
close-button decline meets all four conditions `standalone-windows` now
states and is correctly ratified, but seven live sites, two of them `docs/`
pages, still say the host factory serves four title bars where it serves
three; the fourth window is the library's, which is condition 1 exactly.
`BL-39` is one stale label: issue #3 still open asking for the row that
landed. `BL-40` is Info — a comment describing six tabs where five render.
The register's three MUSTs were all run. Five rows, every trigger evaluated
against the tree and none fired, every evidence id resolved. One row cites a
rule the standard has since changed — `standalone-windows` — and it is
reported and **stands**: the amendment made its shape the compliant one and
names this repository by path as the collection's only live decline.
Read-only: five documents under `docs/audits/2026-09-08/`, no addon source,
TOC, config or prior bundle touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit de47bb963bf83fbaf7562480f5865d2182e01ee2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 20:12:36 2026 +0530
M4c-06: the blanket ignore goes, and the eighteen inline ones go with it
`.luacheckrc:11` carried `ignore = { "212/self", "212/event" }`. This one is worth
naming precisely, because it is the variant the other eight repos are most likely to
hold: it was already spelled in the `<code>/<variable>` form `M4-11` steers towards,
so it read as though the narrowing had already been done. It had not. The rule is
about SCOPE, not spelling -- a top-level `ignore` reaches all 60 files however
precisely its entries are written, and this one silenced both names in every file
that has no business producing them.
`212/event` is the proof, and it is a cleaner one than I expected to find. Removing
the blanket produced not a single unused `event` anywhere in the tree. The addon had
been carrying a live suppression for a warning it did not have, which is exactly the
state the rule describes: it read as coverage and provided none, and the first
handler to drop its event argument would have landed green under a 0/0 badge.
THE NUMBERS, as the working tree reported them. Removing the two lines took
`luacheck .` from 0/0 to 119 warnings in 12 of the 60 files -- settings/Slash.lua 23,
modules/Browser.lua 17, core/Database.lua 13, core/DebugLogSetup.lua 12,
modules/Ledger.lua 10, settings/Schema.lua 9, modules/SessionWindow.lua 8,
modules/Filters.lua 7, settings/Panel.lua 6, modules/LedgerTable.lua 6,
modules/Export.lua 4, core/BankLedger.lua 4. Every one of them is `212/self`. There
is no 542 in this repository and, as above, no 212/event.
WHERE THIS REPO DIFFERS FROM `M4c-04`, and I want to be exact rather than tidy: none
of the 119 turned out to be a defect. WhatGroup found fifteen of its twenty-four were
real, and I went looking for the same thing here -- every warned method was matched
against its call sites before a single stanza was written, not assumed. They are one
shape, and the calling convention forces it. Each module publishes itself as
`NS.X = NS.X or {}` / `local X = NS.X` and defines its surface as `function X:Method()`;
the bodies reach the module through that file-local upvalue and through `NS`, because
both are in scope and resolve at load time. The receiver is still load-bearing --
every call site is a colon call through the namespace (`NS.Browser:Show()`,
`NS.Schema:Set(path, v)`, `NS.Database:Add(entry)`), roughly 900 of them across the
addon and the suites -- so deleting the receiver would shift every argument one place
to the left at all of them. Deleting it is what would break the addon, so a stanza is
the correct answer and not a smaller blanket. The only methods in the 119 with no
colon caller in this tree are the framework and library callbacks, which is a point
FOR the stanza rather than against it: AceAddon calls OnInitialize/OnDisable on the
addon object, AceEvent invokes OnCombatChanged and OnEnterWorld by name as
`self[handler](self, event, ...)`, and core/DebugLogSetup.lua's four uncalled stubs
(UpdateScrollBar, UpdateStatus, RefreshHeader, ShowCopy) exist because their callers
live inside the library -- libs/LibKa0s/DebugLog.lua calls `D:UpdateStatus()` at :557,
:625 and :680. A no-op stub still has to accept the receiver its live counterpart is
called with.
WHAT WAS REAL WAS SOMEWHERE ELSE, and there was more of it than the blanket held.
Eighteen files opened `local addonName, NS = ... -- luacheck: ignore addonName`
over a folder name they never read. An inline pragma standing in for a variable
nothing reads is the blanket again at file scope, and it is worse in one respect: it
had been copied eighteen times. All eighteen are fixed at source rather than moved
somewhere narrower. Seventeen now open `local _, NS = ...`, which is what
core/CoreSetup.lua, core/ItemSetup.lua and core/PoolSetup.lua already did and why
they never needed a pragma. The eighteenth, locales/PostLoad.lua, read NEITHER name:
it is a documented empty seam whose body is entirely comment, so its header was dead
outright and is deleted rather than renamed -- the note above it already shows the
line to write back when a derived key first needs one. Seven files still bind
`addonName` and every one of them genuinely reads it: core/Namespace.lua (NS.name),
core/EnvSetup.lua, core/MediaSetup.lua, core/Database.lua (the AceDB store name),
core/DebugLogSetup.lua, core/BankLedger.lua (the AceAddon name) and
modules/Export.lua. There is now no `luacheck:` directive anywhere in this addon's
own Lua -- not a narrowed one, not a bare one.
THE NARROWING IS MEASURED, not asserted. A method with an unread `self` added to
core/Util.lua and an unread `event` parameter added to core/Compat.lua -- two files
that have no stanza -- both report under the new config
(`core/Util.lua:306:17: (W212) unused argument 'self'`,
`core/Compat.lua:177:35: (W212) unused argument 'event'`). The same tree re-linted
against a reconstructed copy of the old blanket comes back 0 warnings / 0 errors.
Both probes were reverted before staging.
THE GATE is the other half, and it follows `M4c-03` and `M4c-04` because re-adding
one line is trivial and noticing it is not. tests/test_lintconfig.lua loads
`.luacheckrc` as Lua under a sandbox that auto-creates tables the way luacheck's own
config loader does, so it inspects the table luacheck obeys rather than text a
different spelling would slip past -- which matters more here than in either sibling,
since this repo's blanket was already narrowly spelled and a text scan for a bare
`212` would have sailed past it. Four cases, and all four were watched red in the
working tree before this commit: a top-level `ignore`; a class switched off wholesale
(`unused_args = false` and eight relatives); a `files[...]` ignore whose key is a
directory and whose entry names no variable; and a bare `-- luacheck: ignore` in
tracked Lua. It fails rather than skips when it cannot look -- no config, an
unreadable one, a chunk that will not compile, no io.popen, no git -- the bargain
test_docs and test_eol already strike here. tests/test_harness.lua asserts the suite
list against tests/test_*.lua in both directions, so the new suite had to be declared
in tests/run.lua to go green, and cannot later be dropped quietly.
VERIFICATION, observed rather than predicted. `luacheck .` 0 warnings / 0 errors in
61 files. `lua tests/run.lua` 849 passed, 0 failed, 0 skipped -- 845 before, plus the
gate's four. docs/test-cases.md regenerated through `--list` and diffs empty against
the committed copy; its Totals row reads 849 and the README `[tests]` badge moves 845
to 849 in this commit. Both re-run from a clean clone of this commit, not from the
working tree. No version anywhere: Decision 5 stands, no TOC version, no version
badge, no CHANGELOG -- the badge that moved is a count.
Documentation moved with the code. docs/testing.md gains the suppression gate beside
the green gate it qualifies, with the 119, the dead `212/event`, the eighteen inline
pragmas and the measurement written down; and its loader note no longer says every
file spells the header `local addonName, NS = ...`, because seventeen of them no
longer do.
No smoke step, and none is claimed. Nothing here changes behavior: `.luacheckrc` and
tests/test_lintconfig.lua are not packaged, and the eighteen edits that do reach the
client rename or delete a local that nothing reads. Lua binds the second vararg to
`NS` identically whether the first is called `addonName` or `_`, and
locales/PostLoad.lua declared no function and assigned nothing before this change or
after it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit de47bb963bf83fbaf7562480f5865d2182e01ee2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 20:12:36 2026 +0530
M4c-06: the blanket ignore goes, and the eighteen inline ones go with it
`.luacheckrc:11` carried `ignore = { "212/self", "212/event" }`. This one is worth
naming precisely, because it is the variant the other eight repos are most likely to
hold: it was already spelled in the `<code>/<variable>` form `M4-11` steers towards,
so it read as though the narrowing had already been done. It had not. The rule is
about SCOPE, not spelling -- a top-level `ignore` reaches all 60 files however
precisely its entries are written, and this one silenced both names in every file
that has no business producing them.
`212/event` is the proof, and it is a cleaner one than I expected to find. Removing
the blanket produced not a single unused `event` anywhere in the tree. The addon had
been carrying a live suppression for a warning it did not have, which is exactly the
state the rule describes: it read as coverage and provided none, and the first
handler to drop its event argument would have landed green under a 0/0 badge.
THE NUMBERS, as the working tree reported them. Removing the two lines took
`luacheck .` from 0/0 to 119 warnings in 12 of the 60 files -- settings/Slash.lua 23,
modules/Browser.lua 17, core/Database.lua 13, core/DebugLogSetup.lua 12,
modules/Ledger.lua 10, settings/Schema.lua 9, modules/SessionWindow.lua 8,
modules/Filters.lua 7, settings/Panel.lua 6, modules/LedgerTable.lua 6,
modules/Export.lua 4, core/BankLedger.lua 4. Every one of them is `212/self`. There
is no 542 in this repository and, as above, no 212/event.
WHERE THIS REPO DIFFERS FROM `M4c-04`, and I want to be exact rather than tidy: none
of the 119 turned out to be a defect. WhatGroup found fifteen of its twenty-four were
real, and I went looking for the same thing here -- every warned method was matched
against its call sites before a single stanza was written, not assumed. They are one
shape, and the calling convention forces it. Each module publishes itself as
`NS.X = NS.X or {}` / `local X = NS.X` and defines its surface as `function X:Method()`;
the bodies reach the module through that file-local upvalue and through `NS`, because
both are in scope and resolve at load time. The receiver is still load-bearing --
every call site is a colon call through the namespace (`NS.Browser:Show()`,
`NS.Schema:Set(path, v)`, `NS.Database:Add(entry)`), roughly 900 of them across the
addon and the suites -- so deleting the receiver would shift every argument one place
to the left at all of them. Deleting it is what would break the addon, so a stanza is
the correct answer and not a smaller blanket. The only methods in the 119 with no
colon caller in this tree are the framework and library callbacks, which is a point
FOR the stanza rather than against it: AceAddon calls OnInitialize/OnDisable on the
addon object, AceEvent invokes OnCombatChanged and OnEnterWorld by name as
`self[handler](self, event, ...)`, and core/DebugLogSetup.lua's four uncalled stubs
(UpdateScrollBar, UpdateStatus, RefreshHeader, ShowCopy) exist because their callers
live inside the library -- libs/LibKa0s/DebugLog.lua calls `D:UpdateStatus()` at :557,
:625 and :680. A no-op stub still has to accept the receiver its live counterpart is
called with.
WHAT WAS REAL WAS SOMEWHERE ELSE, and there was more of it than the blanket held.
Eighteen files opened `local addonName, NS = ... -- luacheck: ignore addonName`
over a folder name they never read. An inline pragma standing in for a variable
nothing reads is the blanket again at file scope, and it is worse in one respect: it
had been copied eighteen times. All eighteen are fixed at source rather than moved
somewhere narrower. Seventeen now open `local _, NS = ...`, which is what
core/CoreSetup.lua, core/ItemSetup.lua and core/PoolSetup.lua already did and why
they never needed a pragma. The eighteenth, locales/PostLoad.lua, read NEITHER name:
it is a documented empty seam whose body is entirely comment, so its header was dead
outright and is deleted rather than renamed -- the note above it already shows the
line to write back when a derived key first needs one. Seven files still bind
`addonName` and every one of them genuinely reads it: core/Namespace.lua (NS.name),
core/EnvSetup.lua, core/MediaSetup.lua, core/Database.lua (the AceDB store name),
core/DebugLogSetup.lua, core/BankLedger.lua (the AceAddon name) and
modules/Export.lua. There is now no `luacheck:` directive anywhere in this addon's
own Lua -- not a narrowed one, not a bare one.
THE NARROWING IS MEASURED, not asserted. A method with an unread `self` added to
core/Util.lua and an unread `event` parameter added to core/Compat.lua -- two files
that have no stanza -- both report under the new config
(`core/Util.lua:306:17: (W212) unused argument 'self'`,
`core/Compat.lua:177:35: (W212) unused argument 'event'`). The same tree re-linted
against a reconstructed copy of the old blanket comes back 0 warnings / 0 errors.
Both probes were reverted before staging.
THE GATE is the other half, and it follows `M4c-03` and `M4c-04` because re-adding
one line is trivial and noticing it is not. tests/test_lintconfig.lua loads
`.luacheckrc` as Lua under a sandbox that auto-creates tables the way luacheck's own
config loader does, so it inspects the table luacheck obeys rather than text a
different spelling would slip past -- which matters more here than in either sibling,
since this repo's blanket was already narrowly spelled and a text scan for a bare
`212` would have sailed past it. Four cases, and all four were watched red in the
working tree before this commit: a top-level `ignore`; a class switched off wholesale
(`unused_args = false` and eight relatives); a `files[...]` ignore whose key is a
directory and whose entry names no variable; and a bare `-- luacheck: ignore` in
tracked Lua. It fails rather than skips when it cannot look -- no config, an
unreadable one, a chunk that will not compile, no io.popen, no git -- the bargain
test_docs and test_eol already strike here. tests/test_harness.lua asserts the suite
list against tests/test_*.lua in both directions, so the new suite had to be declared
in tests/run.lua to go green, and cannot later be dropped quietly.
VERIFICATION, observed rather than predicted. `luacheck .` 0 warnings / 0 errors in
61 files. `lua tests/run.lua` 849 passed, 0 failed, 0 skipped -- 845 before, plus the
gate's four. docs/test-cases.md regenerated through `--list` and diffs empty against
the committed copy; its Totals row reads 849 and the README `[tests]` badge moves 845
to 849 in this commit. Both re-run from a clean clone of this commit, not from the
working tree. No version anywhere: Decision 5 stands, no TOC version, no version
badge, no CHANGELOG -- the badge that moved is a count.
Documentation moved with the code. docs/testing.md gains the suppression gate beside
the green gate it qualifies, with the 119, the dead `212/event`, the eighteen inline
pragmas and the measurement written down; and its loader note no longer says every
file spells the header `local addonName, NS = ...`, because seventeen of them no
longer do.
No smoke step, and none is claimed. Nothing here changes behavior: `.luacheckrc` and
tests/test_lintconfig.lua are not packaged, and the eighteen edits that do reach the
client rename or delete a local that nothing reads. Lua binds the second vararg to
`NS` identically whether the first is called `addonName` or `_`, and
locales/PostLoad.lua declared no function and assigned nothing before this change or
after it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 2242ac9725480b735cfa42fce2c812a08bd1c68e
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 19:23:12 2026 +0530
M5-08: S-27, and the three seams that only a German client can answer
This addon had no non-English-client step, and it is one of the six that did
not. The gap is not cosmetic here. entry.itemType and entry.itemSubType are
C_Item.GetItemInfo's LOCALIZED display strings, and modules/Ledger.lua stores
them on every row: core/Database.lua keys byItemType, byItemSubType and the
type-by-sub pivot on them, they persist into SavedVariables, and Export emits
them raw. The same API call hands back classID and subClassID, and
core/Compat.lua:156 drops both on the floor. quality and store each carry a
*Raw sibling column for precisely this reason; these two carry none. A player
who switches client language therefore gets two Insights facets for one
category, and no suite in this repo can see it -- tests/wow_mock.lua answers
enUS for every localized global, so the test and the bug agree.
S-27 is written from that enumeration rather than from a template. Beside the
type strings it names QualityLabel's ITEM_QUALITY*_DESC read, Util.FormatDate,
whose comment claims "unambiguous across locales" while %b is the month in the
client's own language, and the six case-folding sites in LedgerTable and
Database -- Lua's string.lower folds ASCII and nothing else, so sorting and
the search box are the step most likely to fail on an accented name. What is
English by design is called out as such so it is not filed as a defect: the
store, direction and kind labels, all UI prose, and the CSV header row, which
is a key another tool parses and must stay byte-identical to the enUS one.
Every step names its failure and not only its pass, and two of them ask the
operator to write down what the client actually rendered -- the date cell's
month token and the itemType pair -- because those answers exist nowhere else
and decide whether the CSV contract is locale-independent in fact or only in
the comment. The sign-off paragraph says there is no headless stand-in for
steps 1 to 3: the cases that touch those paths feed the mock's English strings
in and check the same English strings come back.
Nothing here has been performed. No WoW client was available to this
execution, so S-27 is marked NOT YET RUN and no step in this repository is
recorded as passed.
The gate lands with the section. tests/test_docs.lua is new -- one case
asserting the smoke document still carries a non-English-client section, that
it names a client to run it on, that it says what failing looks like, and that
it is more than a heading with a sentence under it. It was watched failing on
the "has no heading naming a non-English client" message before the section
existed. It proves the section is there and claims nothing about it having
run. The suite is registered in tests/run.lua's SUITES, which is what
tests/test_harness.lua walks.
luacheck . is 0 warnings / 0 errors in 60 files; lua tests/run.lua is 845
passed, 0 failed, 0 skipped, 845 total. The count moved by the one new case,
so docs/test-cases.md is regenerated and the README badge moves with it here.
No version bump: decision 5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 55d4a36f33137fb2faec48cfc4f3ad9410d7cbe7
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 18:17:44 2026 +0530
M5-01: the record is regenerated, and three stale line counts go with it
This addon's watch list was current as of `20260807-115101` — three runs back — and it was
not merely out of date, it was wrong when it was written. It recorded `modules/Browser.lua`
at 1358 against an actual 1245 and `modules/LedgerTable.lua` at 1052 against 1091. Nobody was
careless: until test-kit revision 15 the runner wrote one table row and a fixed lead-in, so
`automated-tests-§4`'s two mandated tables had no producer at all and the only way to have
them was to type them and watch them rot. That is `C08`, and this is the first run in which
the file has a producer.
Everything in `RESULTS.md` is now the runner's — the lead-in, the four standing suite
sections and both watch-list tables, all out of this run's own manifest and `lizard` output.
The one authored column is Disposition, and three cells in it are rewritten here because
their prose asserted line counts that are no longer true: Browser is 1251, LedgerTable 1096,
and `tests/test_ledger.lua` 1478.
That last one is the only thing in this run worth an eyebrow. It took 76 lines pinning the
guild-bank arming change, which leaves **22 lines of headroom** before `layout-§1`'s 1500 cap
— and over the cap is a bug, not a band entry. Its disposition now says to split by concern
at the next case rather than at 1500, which is the only version of that instruction that can
still be followed.
Green on all three suites that ran. Lint 0/0 over 59 files, 844 cases passed with none failed
and none skipped, zero functions above CCN 15. `perf` is a permanent skip — no `tests/perf.lua`
here — so the record is silent about runtime cost rather than claiming it, which the generated
Perf section now says in as many words.
Against `20260825-103400` the totals rose 8% and 7% while every average held to the decimal:
NLOC 13409 → 14455, functions 2043 → 2181, avg NLOC 6.0, avg CCN 2.0, avg tokens 47.4
unchanged. Bigger, not denser. Four functions sit at exactly CCN 15 with no headroom left.
`20260807-110442` and `20260825-103400` still have no ANALYSIS.md and are not getting one:
dating a reading to a day nobody took it is worse than a gap, because a gap is legible. This
bundle has one and it notes the gap once.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 14c86de9c028a88d61e1155db2b95bd4d70f111f
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 17:37:35 2026 +0530
M5-02: two declines that were only ever comments become rows
The register held three rows and neither of this addon's two live declines. Both
had been argued at length in the right places and ratified in none of them, which
`documentation-§3` is explicit about: a deviation not in the register is not
ratified, and an audit is right to file it. Four consecutive audits did.
English-only. The reasoning sits at `locales/enUS.lua:8-13` and in issue #3, both
of which say the decision was made deliberately. `localization-§3` names
English-only as one of the routing SHOULD's two terminal compliant states — and
names the row as what makes it terminal. Without the row the addon was in the
other state, the one an audit keeps re-opening; `BL-04` of `docs/audits/2026-09-07/`
is the fourth time.
The close control. `core/CoreSetup.lua:117-129` declines to republish
`lib.MakeCloseButton` and says why, at length and correctly. Until `M1-STD-04`
that decline was ungradeable: `standalone-windows` said both "every close control
it builds, on any window, MUST be built through that wrapper" and "a host MAY draw
a different one on its own windows", so which grade this addon got depended on
which bullet the auditor read first. The section now resolves it — a reasoned
decline is terminal, and it costs four conditions. The row states all four against
this tree: the library's own windows keep the library's control, the mark is still
the catalog's `close` through `NS.Icon`, there is exactly one host factory with
three callers and no two-argument call anywhere, and the fourth condition is the
row itself. `BL-28`.
Neither row changes a line of code and neither is meant to. The cure `M1-STD-04`
describes is one row in one file, not four rewritten title bars a player would see
change for no reason.
`tests/test_register.lua` is new and is the gate: it slices `## Documented
deviations`, pulls every `BL-`-shaped id out of it and requires each to be
ASSIGNED by a file under `docs/audits/` — heading a table cell, a heading or a
bullet, not merely appearing somewhere in the prose. A bundle that reports a dead
citation quotes the dead id while doing so, so a substring search would go green on
exactly the defect it is for. The suite also refuses to pass on an empty register:
before these two rows landed it went red with "the register cites no deviation id
at all", which is what it should say about a register that cites nothing, and is
how it was watched fail. Then `BL-28` was mutated to `BL-A-28` and it went red
naming that id alone.
`tests/run.lua` declares the suite in `SUITES`; the kit's inventory assertion is
what makes that mandatory rather than optional.
Issue #3 asked for precisely this row and stays open until the sweep's issue pass
touches it. luacheck 0/0; 844 passed, 0 failed. The count moved by one, so
`docs/test-cases.md` and the README `[tests]` badge moved in this commit. No
version, no TOC, no CHANGELOG. `libs/` and `tests/_kit/` untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit ed6156bcd326172c155045f555d25a7ce637ceae
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 17:31:22 2026 +0530
M5-05: the stub's MasterControls is unreached, and now says so
`settings/OptionsSetup.lua`'s degraded arm claimed the opposite. Its comment
opened "MasterControls is REACHED: settings/Schema.lua's S:ComposeMaster runs it
from this file's live arm below" -- and the words "live arm below" are the tell.
`S:ComposeMaster` is the member's only caller and it runs at :227, twenty lines
past the `return` on :214 that ends the stub arm. On a degraded install the stub
table is built and then nothing ever asks it for a row.
So the four composers this addon has no use for and the one it does are all
unreached here, and the comment now separates the two reasons rather than
asserting the wrong one: ColorPair, FontGroup, BorderGroup and BarGroup are
unreached on both arms because this addon has no color, font, border or bar row
anywhere; MasterControls is unreached on this arm alone, because the arm returns
before its caller runs.
The consequence paragraph was right and is kept whole -- in a degraded install
the General page's Master controls rows really are absent from NS.Schema.Schema,
and `/bl list`, `/bl set` and `/bl reset` really cannot reach them. It was only
ever the premise above it that had drifted.
Comment-only: 0/0 from luacheck, 843 passed / 0 failed, unchanged.
Closes BANKLEDGER-R-10.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 465f25c48ad66b9ca5c885001792e358f7103896
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 14:12:48 2026 +0530
M4-24: the exemption sweep names the file the timer is actually in
`docs/performance.md`'s sweep table is the evidence behind this repo's
ratified `performance-§12` no-combat-path exemption, and the page tells its
reader to re-run the grep printed at `:30` and treat any row it cannot explain
as the end of the exemption. That instruction was unfollowable: the row at
`:39` named `core/Compat.lua` `LoadItem`, and `LoadItem` has not been in
`Compat.lua` since the item primitives moved to the LibKa0s-Item-1.0 seam --
`core/Compat.lua:15` records the move itself. The only `C_Timer.After(0.4, cb)`
in the tree is `core/ItemSetup.lua:67`. The sweep returns 19 hits across five
files today and every one of them still maps onto a row; only this file name
was stale.
The work cell was also wrong, and in the direction that matters for criterion
(a), so it is rewritten rather than carried across. It said the timer arms "on
a path reached from a bank scan". `LoadItem` arms nothing unless the caller
passes a callback, and the bank-scan caller -- the quality gate at
`modules/Ledger.lua:427` -- passes none: on the scan path the addon requests
the item and returns. The one caller that does pass a callback is
`settings/Panel.lua:229`, relabelling a filter-list row once the client has
cached the item name, which is an options-panel repaint the player opened by
hand. So the row now reads as weaker cost than it claimed, on a path further
from combat than it claimed. Both callers predate the exemption, so this was
never drift -- the cell was written wrong in M3-09 and the 2026-09-07 review
(`docs/reviews/2026-09-07/02_PROPOSED_CHANGES.md:256`) proposed carrying it
over verbatim, which would have preserved the error.
The exemption is NOT withdrawn and is not re-litigated: it still rests on
criterion (a) plus (b), the register row at `docs/ARCHITECTURE.md:230` is
unchanged, and the re-check trigger is untouched. Documentation only -- no Lua
changed, no test case moved, so no `[tests]` badge or `docs/test-cases.md`
movement and no smoke step to record: there is no runtime behaviour here for a
client to exercise.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit a686466ff7dd48fac8fc68ea32cfb2e10665c389
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 14:08:44 2026 +0530
M4-11: the test tree comes into lint scope
`.luacheckrc` excluded bare `tests/`, which is what `lint`'s own template shipped until `M1-STD-01`
amended it, so all 30 of this repo's test files sat outside the gate. `luacheck .` covered 28 files
and reported 0/0; it now covers 58 and reports 0/0, and only the second figure means what
`docs/testing.md:13` and every RESULTS.md row have been quoting it to mean. `tests/_kit/` stays
excluded, for a reason that does not generalise to the rest of the tree: it is a byte copy of
LibKa0s' `testkit/`, linted there as source, so linting the copy would report every finding twice
and let the copy drift green while the original went red -- the one state `tests/test_vendor_sync.lua`
exists to forbid.
The harness global is declared in a `files["tests/"]` stanza rather than in the top-level
`read_globals`, and the difference is not cosmetic. A name at the top level is a permission granted
to `core/`, `modules/` and `settings/` as much as to a suite, and a shipped file reaching for the
test harness is exactly the thing lint is here to refuse. It is spelled `_G.BL_TEST` and not bare
`BL_TEST` because every suite reads it through `_G`, `tests/run.lua:74` being the writer, and it is
`globals` rather than `read_globals` because the suites reach through it to stage fixtures --
`BL_TEST.NS.db.global.ledger`, `BL_TEST.mocks.InCombatLockdown` -- which a read-only field refuses
with W122. That one declaration is worth 255 of the 272 warnings the narrowing first raised. Unlike
AbsorbTracker, no SavedVariables table is named beside it: BankLedger ships one, `BankLedgerDB`, and
no suite here touches it.
The remaining seventeen are fixed rather than ignored. An ignore wide enough to silence the wall
would read as coverage in every table quoting the 0/0 and provide none, which is worse than the
exclusion it replaced.
The one error is the one worth reading twice. `tests/test_slash.lua:269` wrote the Lua pattern
`"^ |cff3399ff%\[(.-)%\]"`. `\[` is not a Lua escape; 5.1's lexer silently drops the backslash, so
the pattern has always MEANT `%[` ... `%]` and the case has always passed. Under 5.2+ the same file
is a syntax error. Writing `%[` is not a behaviour change, it is the line finally saying what it
did.
`tests/test_ledgertable.lua` defines an entry factory `local function e(over)` at :13 and then five
later loops bind `for _, e in ipairs(testData())`, shadowing it. Nothing breaks today because no
loop body calls the factory, but a case added inside one of those loops that reaches for `e(...)`
would get a table. The loop variable is an entry, so it is now `entry`.
`tests/test_panel.lua:265`'s `renderAllTabs` is deleted rather than wired up. It concatenates every
tab's widgets into one list, and no case wants that: the two places that walk all five tabs
(`:399`, `:533`) assert per tab and must, because the claim in both is that a widget is on the tab
its group names and not on the others. A helper that flattens the tabs away cannot express that.
The rest are local: `assertTrue` destructured and never called at `tests/test_compat.lua:4` and
`assertEqual` at `tests/test_harness.lua:16`; `openMenu`'s dead first parameter at
`tests/test_export.lua:320` and its two call sites; `tests/test_schema.lua:469,471`, where the
`SetColorTexture` stub asserts on alpha alone and r/g/b are arity rather than data, now `_`; and
`tests/wow_mock.lua:642`, whose `AceGUI-3.0.Create` receiver was shadowed by the implicit `self` of
the `SetTitle` it installs -- the outer one is the AceGUI table, so it is `gui`.
Nothing under `core/`, `modules/` or `settings/` changed, so there is no smoke step: this commit
cannot alter anything a client would render. No case count moves either -- 843 before and after --
so `docs/test-cases.md` and the README badge are untouched, and `--list` still diffs clean against
the doc.
One trap for the next repo: appending to `.luacheckrc` with a shell heredoc wrote LF into a file
`.gitattributes` declares `crlf`, and the EOL suite caught it on the first run. The gate is doing
its job; write the file through git-aware tooling or renormalise before believing a green lint.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 54e0321ef811cdd942d1c1f903420ce09253b2ac
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 14:02:36 2026 +0530
M4-09: the four stub-parity cases get a file name and the kit's by-name form
The plan's premise for this repo is stale and the correction matters, because it
changes what the item is. 01_CONSOLIDATED_FINDINGS.md CX02 records that six of the
nine addons have "nothing checking the stub against the live surface"; BankLedger is
not one of them. M3-04 (f5d3c5f) wrote a parity case for all four seams that carry a
degradation stub -- Core, DebugLog, Slash, Options -- and M4-01 already revisited the
Options one to name __print. So nothing here was uncovered, and this item does not
repair a live divergence. What it does is give that coverage the file name the whole
collection greps for, and move the half of it that has a major to look up onto the
factory that arrived with kit 15.
tests/test_surface_parity.lua is where the four cases live now, lifted out of
tests/test_libka0s.lua, which was carrying them mixed in among its byte-parity,
wiring and load-order cases for the same four seams. Collecting them is not
housekeeping: as four scattered precedents nobody had counted, a fifth seam growing a
stub with no case beside it looked like nothing. As a file of four, it is a hole.
Two of the four use assertSurfaceParity's by-name form and two deliberately do not,
and the split is the thing a reader would otherwise get wrong. DebugLog and Options
stub a major's INSTANCE -- NS.DebugLog and NS.Helpers are what lib:New(descriptor)
returned -- so there is a name to resolve, and by-name compares Kit.publicMembers,
which drops every __-prefixed key. That is why the Options ignore list is one entry
shorter: __print had to be exempted by hand under the four-argument form, and
libs/LibKa0s/Options.lua's own comment at O.__print says a stub does not mirror it
BECAUSE the parity check skips the prefix -- true of by-name and not of the form the
case was using. It is the kit's rule now, so the next internal a re-vendor adds needs
no edit here.
Core and Slash keep the four-argument form because neither has a name to resolve.
Core publishes onto NS rather than returning an object, so its two arms are two
blocks of core/CoreSetup.lua compared at the namespace. Slash is where this repo
genuinely differs from AbsorbTracker, which landed this item first: AbsorbTracker's
stub mirrors the dispatcher instance and publishes it as Sl.__cli for exactly this
lookup. BankLedger does not stub the dispatcher, it WRAPS it -- settings/Slash.lua
keeps the instance as the file-scope local `cli` and publishes host methods that
forward to it -- so both arms are NS.Slash, and the library's own surface
(HelpHeader, Text) was never something this addon's stub owed anybody. Publishing a
__cli here to unlock the by-name form would be changing shipped code to suit a gate.
The load-bearing half is one table in tests/run.lua. Kit.expose auto-wires the mock's
LibStub as the surface source, which is right for a repo whose stubs mirror LIBRARY
tables and wrong for both of ours. Watched fail in the working tree with the suite
declared and the registration absent: "LibKa0s-Options-1.0" resolved the module table
and went red on LAYOUT, New and STRINGS, "LibKa0s-DebugLog-1.0" on MAX_BUFFER, New
and STRINGS -- six members no stub was ever meant to carry. It sits before Kit.expose
deliberately; expose registers a source only when none is registered yet, precisely
so a runner like this one keeps its own.
The DebugLog case gained four ignore entries and they are a fact about the LIVE arm,
not the stub. Its live half used to be a second partial load stopping at
core/DebugLogSetup.lua; by name it is the instance a real session holds, and by the
time the case runs tests/test_debuglog.lua has shown the console, so the library has
stamped _frameForTest and _toggleClickForTest on it (DebugLog.lua:477, :482). Single
underscore, so publicMembers does not filter them. All four _...ForTest seams are
named rather than the two set today: they are one class, and a suite that later shows
the copy window should not have to rediscover this. The old arm was equal to the new
one only because nothing decorates NS.DebugLog after its setup file -- a fact the
case had no way to state and would have gone on assuming after it stopped being true.
tests/degraded_env.lua is the extraction that makes the move possible. loadDegraded
and loadUpTo were file-scope locals in tests/test_libka0s.lua; two suites now need the
same environment, and a second copy of an environment builder is a second environment
free to drift. Both are bound back to their old local names there, so all twenty call
sites read exactly as they did. It is not a suite, so the runner does not list it and
test_harness.lua's `ls tests/test_*.lua` sweep does not see it.
What the gate catches, each watched red in the working tree and then reverted:
dropping SetRenderer from settings/OptionsSetup.lua's stub gives "SetRenderer is
missing (live: function)"; degrading Section to `false` gives "Section is a function
live but boolean degraded", which is the arm a present-but-not-callable member falls
into and the one a plain "is the key set?" check waves through; dropping Clear from
the DebugLog stub names Clear; dropping CliVersion from the Slash stub names
CliVersion; and adding a live-only NS publication to core/CoreSetup.lua's library arm
names it against the fallback.
Nothing under the TOC changed -- this is tests/ and docs/ only -- so there is no
smoke step to record. 843 passed, 0 failed, unchanged either side, so the README
[tests] badge does not move; docs/test-cases.md is regenerated because the grouping
did, and the --list diff is empty. luacheck 0/0. docs/testing.md's tests/ tree gains
both new files and a paragraph on why run.lua registers a surface source at all.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit c9ac140651c46d3b8aea10ca15b719f70a3c4155
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 11:22:42 2026 +0530
M4-01: adopt LibKa0s v1.27.0, and wire the gate that came with it
Both payloads move whole from the v1.27.0 tag -- `libs/LibKa0s/` and `tests/_kit/`
-- and the CLAUDE.md provenance line moves with them, because that line is what
`tests/test_vendor_sync.lua` reads to decide which tag to check the bytes
against. A line ahead of the bytes and a copy taken from untagged master both
fail there, which is the point of it.
`diff -r` between a clean `v1.27.0` checkout and each vendored tree is empty
BYTE for byte, not merely content-with-CR-ignored.
TWO THINGS IN THE PAYLOAD REACH THIS REPO'S OWN FILES, and invariant 1 says a
gate and its fix land together rather than leaving the tree red between commits.
* The kit now SHIPS a suite. `tests/_kit/test_eol.lua` arrives with kit 15 --
the working-tree line-ending gate, over every path `git ls-files` reports --
and `Kit.assertSuiteInventory` fails the whole run until the runner declares
it, deliberately, so a gate cannot arrive with a re-vendor and then quietly
run nothing. It is APPENDED at the `Kit.run` call rather than written into
`SUITES`, because `SUITES` is also what `tests/test_harness.lua` walks as
plain basenames under `tests/`; a table entry in that list would break three
cases that have nothing to do with this. That is the +1 in the case count,
and `docs/test-cases.md` and the README badge move with it here. It passes
on this tree: the only tracked file that is LF on disk is the kit's own
`.sh`, which `.gitattributes` pins that way on purpose.
* Options minor 8 publishes `O.__print`, the one instance print sink the shell
hands OptionsWidgets so it stops building a second one from the same
descriptor. The library's comment there says a degradation stub does not
mirror it because `Kit.assertSurfaceParity` skips the `__` prefix. That is
true of the kit's BY-NAME form, which filters through `Kit.publicMembers`,
and NOT of the four-argument form this repo's parity case uses, which walks
every key of the live table. So it joins `IGNORE` rather than the stub.
No version bump anywhere: this cycle does not ship. The `[tests]` badge is a
count, not a version.
The client-only half is written into `docs/smoke-tests.md` as S-26 and is NOT
claimed here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 60a3eefb22ebbfaa9ac7c4d46da9a6d51387c910
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 11:01:50 2026 +0530
M4-C1: say when the vendored-copy diff is supposed to be non-empty
docs/testing.md gave four `diff -r` commands against `../LibKa0s` and marked two
of them "MUST be empty". Neither can be empty right now, and neither is wrong.
The library is tagged v1.27.0 and this addon vendors v1.26.0, so the library diff
reports 306 lines and the test-kit diff 947 — measured just now, not estimated —
and it stays that way for the whole of M4 until this repo re-vendors. A gate that
is red by design and does not say so trains a reader to ignore it, or worse,
invites them to re-vendor an untested library release purely to make it quiet.
What the section was missing is that the four commands compare against the
sibling checkout's WORKING TREE — whatever `../LibKa0s` has checked out — which
is a different question from "is the vendored payload the release this addon
claims". Those two questions only agree while the library has tagged nothing
newer than this addon has taken, and the gap between a release and the re-vendor
carrying it is normal rather than exceptional.
So the note names the authoritative check: against the TAG the root CLAUDE.md
names, extracted from the provenance line and pulled out of the sibling with `git
archive`. Run here it reports zero lines for both payloads, which is the answer
that means something. And it is not new work — tests/test_vendor_sync.lua already
asks precisely this inside the suite, greping the tag out of CLAUDE.md and
reading that blob from git, so a green suite has answered it. The run below is
0 SKIPPED, which matters more than usual here: those cases degrade to a skip when
the sibling or git is missing, and a skip is not a pass.
The two inline "MUST be empty" comments become "empty vs the CLAIMED tag" so the
code block no longer contradicts the paragraph under it, and the working-tree
diffs are re-framed as what they honestly answer — how far behind the library
this addon is, which is release planning rather than a gate.
Verified here: luacheck 0 warnings / 0 errors in 28 files; 842 passed, 0 failed, 0 skipped, 842 total; the test inventory is in sync with
docs/test-cases.md. Documentation only — no behavior, no test moves, no
case-count change, so docs/test-cases.md and the README badge stay put. No client
needed and no smoke step added: nothing here reaches the game.
No version bump anywhere: this cycle does not ship.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 284f00037d165ce679ea504cb9bbe416eca4094a
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 10:33:51 2026 +0530
M2-04 + M2-05: the AceDB fake stops hiding the migration defect, and the stamp leaves the defaults
Two work items, one commit, and the second half is why. Invariant 1 of the
cycle's spec is that no commit leaves the repository red: splitting a gate and
its fix across two WORK ITEMS is the point -- the reddening has to be seen --
but splitting them across two COMMITS leaves a bisector standing on a broken
tree. These landed as two commits and were squashed before the branch was
pushed.
The red was seen. With the fake fixed and the stamp still in the defaults
table, tests/run.lua reports 831 passed / 1 failed -- the RunMigrations case,
failing for the reason it exists.
`tests/test_database.lua:330` claims to pin the case a schema migration exists
for -- a store that carries no schemaVersion key at all -- and it has been green
since the day it was written. It could not have been anything else. The fake at
tests/wow_mock.lua handed back a deepcopy of NS.defaults.global, which turns
every default into an ordinary stored value, so clearing the key really did
leave it absent and `g.schemaVersion or 1` really did read 1. In the client it
does not: defaults/Global.lua ships schemaVersion = NS.SCHEMA_VERSION, AceDB
rawsets that into the store when the database is built and strips it back out
when the session ends, so a reader cannot tell a stored 2 from the default 2 and
there is no such thing as an install whose global lacks the key. The runner's
`< NS.SCHEMA_VERSION` arm has therefore never run against a real pre-stamp
store, and the case that was supposed to catch that was measuring the mock.
So the fake models the fallback rather than flattening it. Table defaults are
still materialised eagerly, which is what AceDB's copyDefaults does with them
and which keeps the nil-ledger defensive case reachable; scalar defaults now sit
off the store behind __index, so an unwritten one -- or one that has been set
nil -- answers with the default. Writes still go to the store and still shadow
it, so the suites that seed an explicit version are untouched.
The consequence is the deliverable, not an accident: the boundary case is red,
with vendorPrice still 20 on a ledger the migration never walked. Its first
assertion still passes, reading 2 straight back off the defaults, which is
exactly how it managed to look like proof. No assertion was weakened to get
there and none was removed. M2-05 drops the key from the defaults and takes it
green.
One case is added to hold the new fake honest, because once schemaVersion stops
being a default the migration cases stop exercising the fallback at all and
nothing else would notice it reverting. It was watched failing against the old
deepcopy fake first. That moves the inventory to 832, so docs/test-cases.md and
the README badge move with it -- the badge reads 831/832 because that is what
the run reports today.
Verified: luacheck 0 warnings / 0 errors over 28 files; lua tests/run.lua 831
passed, 1 failed, 832 total, the one failure being the case above;
tests/_kit/run-automated-tests.sh --no-bundle verdict red on that same failure,
lint and complexity pass, perf skipped as this addon ships no scenarios.
No version, TOC, changelog or "What's new" line was touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
---
BANKLEDGER-R-02. `schemaVersion = NS.SCHEMA_VERSION` was put into NS.defaults.global
so the shipped default and the migration runner's target could not drift apart.
They cannot, and that identity is the whole defect. AceDB's logout handler
re-registers nil defaults, which strips every stored key still equal to its
default, so the stamp left the SavedVariables file at every logout and was
re-supplied at the next login as whatever the current default said. RunMigrations
read its own target straight back, `g.schemaVersion < NS.SCHEMA_VERSION` was never
true, and the v1 -> v2 pass has never once run against a player's store. What is
wrong today is small -- vendorPrice sits inert in old entries while the store
reports itself as v2 -- but the mechanism is not: at v2 -> v3 every existing
install would be disarmed the same way, for a seam whose entire purpose is to be
the one place that cannot be skipped.
So the key is gone from the defaults and RunMigrations seeds it. An unstamped
store is a fresh install or a pre-stamp database and those want opposite answers;
the ledger is the discriminator, and it is a safe one because a migration over an
empty ledger is a no-op whichever way it is read. Misreading a fresh install as v1
costs a wasted walk, never a wrong result. `rawget` was considered and is not
enough on its own -- with no default left there is nothing to distinguish a stored
value from, and it still would not tell a fresh install from a stripped one.
The interesting part of this item was the tests, and it is worth writing down
because the first version of them was wrong. The obvious case for the empty-store
half asserts the stamp lands on NS.SCHEMA_VERSION -- and it stays green under
seeding unconditionally at 1, because that seeds 1, migrates an empty ledger, and
stamps it current anyway. The stamp cannot witness the difference; both paths end
in the same place. The [Migrate] line can, and it is also what the operator reads
in the client, so the three new cases assert on the debug output instead and the
headless check and the smoke step now rest on the same evidence. All three were
watched failing under the specific mistake each names before being accepted, and
so was M2-04's boundary case going green.
The case that asserted the default equalled the target is retired for one that
asserts there is no default at all. It is not a weakening: the equality it pinned
is the bug, and what needs guarding now is that nobody reinstates the key, because
a reinstated default is silent -- every suite here would stay green and only a real
player's logout could tell.
Verified: luacheck 0 warnings / 0 errors over 28 files; lua tests/run.lua 835
passed, 0 failed, 835 total, up from 831/832 with M2-04's failure now green;
tests/_kit/run-automated-tests.sh --no-bundle verdict green, complexity 0 warnings
at avg CCN 2.0 / max 15, perf skipped as this addon ships no scenarios;
docs/test-cases.md byte-identical to `run.lua --list` CR-normalised, badge at
835/835. The client half is S-25 in docs/smoke-tests.md and has NOT been run -- no
WoW client was available -- and it is the only place the logout strip itself can
be proved. No version, TOC, changelog or "What's new" line was touched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 92df67f5052adb08cab158d7ab13bc440672813c
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 10:00:51 2026 +0530
M3-05: re-vendor LibKa0s v1.26.0
The payload only. BankLedger consumes MasterControls and ColorPair and no
media composer, so nothing here moves behaviourally -- the pass count is 842
before and after -- and that is the point of putting these five last in the
milestone. What arrives is OptionsCompose minor 3, which gives every composed
media dropdown its options back, and OptionsWidgets minor 14, which stops the
tab strip leaking a set of buttons and a content panel on every click. This
addon draws no media dropdown, so the first is dead weight it carries for the
session rather than for itself; the second is real here, because the settings
panel does draw a tab strip.
The provenance line in CLAUDE.md moves in this commit and not a later one on
purpose. tests/test_vendor_sync.lua greps that line to pick the tag both
vendored payloads are compared against, so the line is the gate's input rather
than a note about it: rolled to v1.26.0 with the old bytes still in place, the
suite goes red on "libs/LibKa0s is the LibKa0s release CLAUDE.md says this
addon bundles" -- 841 passed, 1 failed -- which is exactly the drift the gate
exists to catch, and was watched happening before the copy landed.
A reader checking this by hand will reach for `diff -r ../LibKa0s/LibKa0s
libs/LibKa0s` and get noise, because the LibKa0s working tree is already at
v1.27.0. The tag is the reference, not the sibling checkout: extract it with
`git -C ../LibKa0s archive v1.26.0 LibKa0s testkit`, which applies the
.gitattributes eol conversion and therefore reproduces the CRLF bytes a plain
`git show` would not. Against that, both forms are empty here -- content with
--strip-trailing-cr, and bytes with plain `diff -r`. The byte-empty form is
the one M1-LK-00 repaired the library's line endings for, and this is the
first consumer in the collection where it has ever passed.
The test kit does not move. Kit.VERSION is 14 at both v1.25.0 and v1.26.0, so
tests/_kit was already byte-identical to the tag; the copy is run anyway
because the two payloads are re-vendored together by rule, and a kit that
happens to match is not the same fact as a kit that was checked.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 80a7896bf8ce828070002b3833dc105b1c213a0e
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 09:39:24 2026 +0530
M3-C3: the two dev documents this repo was still shipping
M2-19 fixed the .pkgmeta ignore lists in seven repos and stated its acceptance
over the collection: `git ls-files`, filtered through each repo's own ignore
list, returns no media/screenshots, no CLAUDE.md and no DEPENDENCIES.md. Its
Repo column names seven addons, and BankLedger is not one of them, so the
acceptance was true of the rows and false of the collection. Filtering this
repo's 216 tracked files through this file as it stood named CLAUDE.md (4,681
bytes) and DEPENDENCIES.md (10,552 bytes) as shipped — the same two documents,
for the same reason as everywhere else: nobody listed them. There is no local
reason to keep them. Nothing in the TOC or in any loaded Lua file reads either
one; the only code that opens CLAUDE.md is tests/_kit/vendor_sync.lua, which
reads the LibKa0s provenance line, and tests/ has never been in the package.
README.md and LICENSE stay, deliberately: players read those.
media/screenshots was already ignored here, which is why no audit in this cycle
noticed the rest of the gap — 3.9M of project-page art was the visible half, and
being clean on it made the file look finished.
The .superpowers line is bookkeeping, not savings, and it says so. The directory
holds one entry, `git ls-files .superpowers` returns nothing, and the packager
clones git, so no player download has ever carried a byte of it and this line
changes none. It is here because packaging.md:28's strong form MUSTs every root
dot-entry present in the repo be named or justified whether or not git tracks
it: an enumeration goes stale the moment a new tool writes a new directory, and
that failure is silent in the direction where the package grows. Its comment
carries "untracked; listed under packaging.md:28" verbatim, the phrasing the
seven use, so the next reader does not re-file it as shipping. There is
deliberately no .claude line: no such directory exists at this root, and naming
one that does not is how the identical filing was rejected in two sibling repos
this cycle. .pkgmeta now names itself for the same strong-form reason; the
standard's own minimum template omitted it and cites this repo by name as one
of the seven that failed the check on a line it was never given.
DEPENDENCIES.md moves in this commit because it cites this file by line number
twice — `.pkgmeta:5-18` for the ignore list and `.pkgmeta:14-18` for the logo
block — and both ranges shifted. A stale line citation in a document about how
the package is built is exactly the drift that makes the next reader distrust
the rest of it.
Verification is the filter itself; there is no packager dry-run, because no repo
here carries .github/workflows and packaging.md:32 puts CI out of scope. Run
before the edit it reported 216 files, 2,393,787 bytes, and flagged two. Run
after, it reports 213 files, 2,377,829 bytes, and flags none. It is not a filter
that says nothing: run unchanged against AbsorbTracker, PanelMaster and
ConsumableMaster it flags nothing there either, and it flagged this repo and
MultiMeters and no others.
luacheck 0 warnings / 0 errors in 28 files; 842 passed, 0 failed, 0 skipped. No
test moved, so docs/test-cases.md does not move either — `lua tests/run.lua
--list` still diffs empty against it. No version moves; nothing in this cycle
ships. No smoke step: the packager reads this file, the client never does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 1108bbd4f29bea7937072a36acd472edc1247756
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 01:34:59 2026 +0530
M2-22: the full reset says so, so the capture gate stops using dead settings
Every Schema row broadcasts Ka0s_BankLedger_SettingsChanged when a single key
changes. ResetEverything rewrites every key there is and broadcast nothing. The
Ledger caches its capture gate in upvalues and re-caches on that message and on
nothing else, so from the moment the confirm dialog was accepted until the next
/reload the gate judged bank movements by settings the player had just
destroyed. A blacklist emptied by the reset went on blocking the item it no
longer contained.
One SendMessage at the end, reason "reset". Not one per restored key: the reset
is a single act, every subscriber would rebuild several times over for one
button press, and no subscriber wants finer grain than "all of it changed". The
consumers stay unenumerated -- they subscribe, which is what architecture-4 has
the bus for, and a list here would be a second place to keep current.
The case is the observable rather than the message: blacklist an item so the
gate reports "blacklist", reset, and ask the gate again with no /reload and no
hand-called RefreshUpvalues in between. It also pins the count at one, because
"broadcasts" and "broadcasts once" are different claims and only the second one
is safe to build on. Red first, and it failed on the count -- expected 1, got 0
-- before the fix; 842 passed, 0 failed after. luacheck 0/0 in 28 files, and the
four-suite run is green at 0 complexity warnings.
The reason string is now listed in ARCHITECTURE.md's message table beside the
eight it joins, and ResetEverything is named there as a publisher, because that
table is where somebody looks to find out who talks.
Smoke S-16 gained a step 4 and it is UNRUN -- no client is available here. It
asks for a bank movement after the reset with no /reload, and it says to
blacklist an item first, because the reset restores defaults and a movement that
was already going to be recorded proves nothing about whether the gate re-read
anything.
The test badge moves 841 to 842 with docs/test-cases.md. No version moves;
nothing in this cycle ships.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit ab356024594cebff7cec0759ed392041dc441843
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 00:51:57 2026 +0530
M2-27: /bl test stops confirming a toggle that never happened
BANKLEDGER-R-07. The verb collapsed three outcomes into two. `NS.LedgerTable
and NS.LedgerTable.ToggleTestMode and NS.LedgerTable:ToggleTestMode()` yields
nil when the guard falls through and a boolean when the toggle actually ran, and
`on and "on" or "off"` renders both nils and honest falses as "test mode off".
So a player whose ledger table is not there is told test mode is off -- a
confirmation of a state change, about a module that could not have changed
anything. The session verb two rows up already had the shape this needed: it
tests `on == nil` separately and prints what actually happened instead of
folding the third case into one of the other two. This is that shape, applied to
the verb beside it.
The nil is unambiguous, which is what makes the branch safe. ToggleTestMode
returns IsTestMode() and IsTestMode returns `NS.State.testRecords ~= nil`, so
the method can only ever hand back true or false; nothing but the guard can put
a nil in that variable. The grade is Low and stays Low: modules/LedgerTable.lua
is unconditionally in the TOC, so no shipping configuration reaches this today.
It is a lie sitting in a guard waiting for the load order to change, not a lie a
player has seen.
The case was watched failing against the old expression -- it caught
`|cff00ffff[BL]|r test mode off` with NS.LedgerTable nil, which is the finding's
own evidence reproduced -- and test_schema.lua picks up the chat-capture helper
the slash and debuglog suites already carry, since asserting on a verb's words
means reading the sink it writes to. 840 -> 841, badge and inventory moved here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 455b7aa95a197577cf88374974f31236e3ff445d
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 8 00:46:16 2026 +0530
M2-26: a disable is now something a re-enable can come back from
BANKLEDGER-R-04. Four modules -- Ledger, Browser, SessionWindow and Insights --
open their Enable with `if self._enabled then return end`, and nothing in the
addon ever put that latch back. There was no OnDisable at all, so AceAddon's
disable path stopped none of them and the Enable that followed did none of its
work: capture events unbound by the embed and never rebound, the bus
subscriptions rebuilt into nothing, four inert modules and not one error line to
say so. It is graded Low and the grade is right -- no in-addon route and no
in-game route reaches OnDisable today, so the latch cannot currently bite a
player. What it can do is bite the first caller who adds one, which is the sort
of thing that gets found in a client rather than here.
The half of this that was not obvious is the teardown. Releasing the latch alone
looks sufficient and is not, and the case that proves it was watched failing
against exactly that version -- one moved stack recorded five times over a run
that had cycled three times. AceAddon's AceEvent embed unregisters what was
registered on the addon OBJECT, which covers the Ledger's capture events, but
each module subscribes on a private target from NS.NewBusTarget() precisely so
CallbackHandler cannot key two consumers to one target. AceAddon has never seen
those and cannot reach them, so a latch-only disable leaves the next Enable
standing a second target beside a first that is still live. Most of the doubled
handlers are idempotent and would have hidden this for a while;
SessionWindow:OnEntryAdded is not, it appends unconditionally, and one deposit
would land twice in the session view. So the targets go with the latch.
`_guildHooked` deliberately stays set, which is why the finding asked for it to
be verified separately rather than swept up here. It is not a member of this
cycle: it records a hook installed on GuildBankFrame's OnShow that nothing
removes, and clearing it would let the next Enable hook the same frame again --
turning a dormant latch into a live double-count.
The suite is new because the behaviour belongs to no single module, and
core/BankLedger.lua had no suite of its own. wow_mock gains
UnregisterAllMessages and UnregisterAllEvents, which real AceEvent has always
had; the message one is pinned by its own case in test_mock because the
lifecycle case is only worth its ink if the fake unregisters per target the way
CallbackHandler does, and a mock that swept the whole registry would have made
the teardown look right while it silenced every module still enabled. That case
was watched failing under precisely that wrong sweep. 835 -> 840, badge and
inventory moved here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit b54f10d91bf5aaef823ff51001a84201a5aad5d9
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Sep 7 19:21:33 2026 +0530
docs: commit the frozen 2026-09-07 review and standards-audit bundles
Produced by /wow-addon:review and /wow-addon:standards-audit as part of a
collection-wide pass over all nine addons and LibKa0s. Both passes re-ran this
repo's out-of-game suites from scratch first, so every finding rests on the
numbers measured that day rather than on the committed record.
Frozen dated artifacts. Never rewritten after the fact — a later correction
belongs in the next dated bundle, not in this one.
The consolidation these fed is Ka0sAddonsCommonTasks
docs/2026-09-07-REVIEW_AND_STANDARDS_AUDIT_REMEDIATION/, which triaged both
bundles against the code, re-graded severity by reachable impact, and dropped
what did not survive re-reading. Read the consolidation for what is actually
being done; read these for the evidence behind it.
No code, TOC, doc or config file outside docs/ was touched by either pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016nbSbqagQQGGgyab6o86Mj
commit 5f3080896af088b42696b11730c46d94aa64e205
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 3 18:26:52 2026 +0530
docs(settings): the doc set catches up with the Filters tab
The strip lost a tab in 11080ad and three pages were still describing the one
before it. settings-panel.md is the topic-detail doc that owns the panel's
shape, and it was the only one that merge did not touch, so it still drew six
primary tabs -- Master controls, Capture, Interface, History, Blacklist,
Whitelist, at 6/4/4/1/1/1 -- where the page has drawn five since the two
id-lists became one Filters tab.
Corrected there: the strip table and its row counts; the number of tabs with no
settings behind them, which is one and not two, since S.BespokeRows collapsed to
a single renderer-only row; the thin-tab exemption, which is History and Filters
and now matches THIN_TAB_EXEMPT in tests/test_schema.lua; and the afterGroup
hook table, which named a `renderFilterTab` that no longer exists. The hook is
buildFiltersTab, and it draws the secondary strip before handing one list to
makeFilterSection. That sub-strip was documented only in ARCHITECTURE.md; the
page that owns the panel carries it too now, including that ctx.activeSubTab is
keyed by the primary tab and is never persisted.
ARCHITECTURE.md's documentation map still called it a six-tab strip.
smoke-tests.md sent the reader to General > Blacklist, a route missing a level
since the merge; it reads General > Filters > Blacklist / Whitelist now, the
same three levels the right-click chat lines were corrected to name.
Content only -- no doc was created, moved or restructured.
Verified: 831 passed / 0 failed, luacheck 0/0 over 28 files, and both vendored
payloads content-identical to LibKa0s v1.25.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8JfG34HppHBvEHHVjCMTK
commit 5f3080896af088b42696b11730c46d94aa64e205
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 3 18:26:52 2026 +0530
docs(settings): the doc set catches up with the Filters tab
The strip lost a tab in 11080ad and three pages were still describing the one
before it. settings-panel.md is the topic-detail doc that owns the panel's
shape, and it was the only one that merge did not touch, so it still drew six
primary tabs -- Master controls, Capture, Interface, History, Blacklist,
Whitelist, at 6/4/4/1/1/1 -- where the page has drawn five since the two
id-lists became one Filters tab.
Corrected there: the strip table and its row counts; the number of tabs with no
settings behind them, which is one and not two, since S.BespokeRows collapsed to
a single renderer-only row; the thin-tab exemption, which is History and Filters
and now matches THIN_TAB_EXEMPT in tests/test_schema.lua; and the afterGroup
hook table, which named a `renderFilterTab` that no longer exists. The hook is
buildFiltersTab, and it draws the secondary strip before handing one list to
makeFilterSection. That sub-strip was documented only in ARCHITECTURE.md; the
page that owns the panel carries it too now, including that ctx.activeSubTab is
keyed by the primary tab and is never persisted.
ARCHITECTURE.md's documentation map still called it a six-tab strip.
smoke-tests.md sent the reader to General > Blacklist, a route missing a level
since the merge; it reads General > Filters > Blacklist / Whitelist now, the
same three levels the right-click chat lines were corrected to name.
Content only -- no doc was created, moved or restructured.
Verified: 831 passed / 0 failed, luacheck 0/0 over 28 files, and both vendored
payloads content-identical to LibKa0s v1.25.0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8JfG34HppHBvEHHVjCMTK
commit fe1f88fd4f4c8970a271ddbb99968ec058fb907c
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 3 18:03:16 2026 +0530
Carry LibKa0s v1.25.0
One file moved: OptionsCompose.lua, COMPOSE_MINOR 1 -> 2. MasterControls takes a
`leadButton` -- one page-wide act of the host's own, drawn beside options-ui-§15's
reset buttons: into the pair's empty right half on a frameless addon, or on its
own row above the full pair on a framed one. It exists because §15 fixes the
resets' wording and the composer is the only thing that writes it, so an addon
wanting a button beside them had to keep a second copy of "Reset all settings" in
its own source.
NOT ADOPTED HERE, and the reason is structural rather than a preference: this
addon passes the composer's afterGroup tail through unchanged, with no single
page-wide act of its own for the seam to place. A leadButton with nothing to put
in it is a field set to nil. Nothing is filed as a decline -- a decline records a
decision about work that was offered, and nothing was.
Both payloads copied WHOLE from the tag (git archive v1.25.0), never file by file
and never from the sibling working tree: tests/test_vendor_sync.lua resolves the
tag the provenance line names and compares both payloads against it, so a copy
taken from a dirty checkout passes a local diff and then fails the gate. The
provenance line moves in this same commit, which is what that gate reads.
The kit is unchanged at revision 14 and is copied whole anyway: the two payloads
move together so a consumer can never hold a kit that cannot compare the library
it ships.
No cross-major skew, and no file removed upstream -- so no deletion inside libs/
was warranted.
Frozen bundle at docs/revendor/2026-09-03/.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8JfG34HppHBvEHHVjCMTK
commit 11080ade835f648fd4aaeb5c1521edd017447126
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Sep 3 11:41:56 2026 +0530
feat(settings): the two id-lists become one Filters tab
Converges this panel's strip with Ka0s Loot History's. The two addons keep the
same shape of record and a player compares their panels directly, so a subject
should carry one name in both — and it did not: Loot History held three id-lists
under a single Filters tab with a secondary strip, while this addon spent two
primary tab slots on Blacklist and Whitelist.
The strip is now Master controls · Capture · Interface · History · Filters, and
Loot History draws the same five with AH Price after Capture. Inside Filters, a
secondary strip (O.SubTabStrip, options-ui-§13) divides Blacklist from Whitelist;
the selection is ctx.activeSubTab["Filters"], session state keyed by the primary
tab, never persisted. That is also the shape that scales — a third list here
would otherwise be a third primary tab pushing this page's own subjects along the
band for something that is not their peer.
The two renderer-only rows in S.BespokeRows collapse to one. A tab name is a
`group` and never a stored path, so nothing stored moved and no migration is owed
(options-ui-§15).
The right-click chat lines named "Settings ▸ General ▸ Blacklist", a tab that no
longer exists; they name the full route now — General ▸ Filters ▸ Blacklist and
▸ Whitelist — and their two cases were rewritten to pin three levels rather than
two.
Verified: 831 passed / 0 failed (one new case: the sub-strip draws one button per
list, opens on the first, and shows exactly one add-row), luacheck 0/0 over 28
files, lizard clean at CCN 15.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8JfG34HppHBvEHHVjCMTK
commit c28d16e5a6442775c07fc1d77a3f30fa9fe1bcd2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 2 21:55:00 2026 +0530
docs(comments): correct citations the revamp left pointing at nothing
Comment text only. A comment naming a file, a line or a caller is documentation and
drifts exactly like a README, except that nothing reads it but the next person to
touch the function and no gate can see it — luacheck does not read prose and no test
covers a comment.
The recurring one is afterRestoreAll, a descriptor hook that stopped existing when
the global reset became a profile reset, still being cited by name. Alongside it: a
line number past end of file, a caller that does not call, and counts that no longer
match what the code returns.
Deliberate history was left alone — a comment explaining what a deleted file used to
do is doing its job, and rewriting it would delete the explanation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188vqPV1edgfp1WQsZnsJaF
commit 372a86fbdbb9ba7eeb3f1d88f9b1fd5346f8449f
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 2 18:24:58 2026 +0530
docs: sync the doc set to the revamped settings panel
The settings surface moved a long way in one pass — a Master controls tab, a strip
on every page, class-colour companions, the library's composers in place of
hand-written groups, and in some addons whole pages folded into General — so the
docs describing it had drifted correspondingly far.
Counts re-derived from the schema rather than carried across, the LibKa0s payload
description corrected to the file it gained, and the documentation map reconciled
against the directory in both directions.
Documentation only: no .lua, .xml, .toc, .luacheckrc or .pkgmeta was touched, and
the suites are unchanged and green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188vqPV1edgfp1WQsZnsJaF
commit ab620712de66032b9cdc16d7670a8fc096b27a19
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Sep 2 17:56:10 2026 +0530
feat(settings): master controls, and the Filters page folded into General
The Filters page's tabs move into General and the page retires. This addon has no
colour control at all, so the class-colour rule is satisfied vacuously rather than
by inventing swatches.
Re-vendors LibKa0s v1.24.0 and adopts the settings-revamp-v2 contract: a
General page opening on a composed Master controls tab, a tab strip on every page,
a class-colour companion beside every colour control, and the canonical font,
border and bar groups built by the library's composers rather than spelled out
per addon.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188vqPV1edgfp1WQsZnsJaF
commit abed21a4abd8a84efae14faf8a2191af92463e62
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 1 19:32:21 2026 +0530
Adopt options-ui-13: both settings pages become tab strips
General was three headed sections stacked down one scroll -- Master
Controls, then Capture, then Storage -- and the reader met all of it at
once, in an order that was inherited rather than argued: the master
switch, the minimap button, the session window and the debug console
first, because that is the shape the sibling addons used, and what the
addon is actually for third. Filters was the same stack with two
sections, Blacklist above Whitelist, each with a heading whose only job
was to say which list the box below it belonged to.
Both are strips now, pinned in the page's chrome band above the scroll,
and no section heading remains anywhere -- the tab is the heading.
General reads Capture, Interface, History, in that order and for that
reason: what is recorded, what is on screen, how much is kept and the
only place anything is destroyed. It is one line of adoption --
H.RenderTabbedSchema partitions on the schema's own `group` field -- so
the schema array's declaration order IS the tab order and a group's rows
must stay contiguous. tests/test_schema.lua holds the page -> tab ->
count table that catches a row drifting into the wrong tab, and names
History's one-stored-row exemption rather than loosening the rule.
Filters drives the same H.TabStrip by hand, because it has no schema
rows to partition -- both id-lists are storage carve-outs mutated
through NS.Filters -- and keeps the active tab in the same ctx.activeTab
the library reads, never a private second copy that would go stale the
first time the page was marked dirty while hidden.
Two literals become settings. settings.rowStripeAlpha (0.03) and
settings.rowHoverAlpha (0.10) were hardcoded in two files each --
modules/LedgerTable.lua and modules/SessionWindow.lua both built every
pooled row with 1,1,1,0.03 behind the even rows and 1,0.82,0,0.10 under
the cursor. They are two answers to two questions, rest and hover, so
they are two sliders and not one "row emphasis". Each default IS the
literal it replaced, so an install that touches neither is drawn exactly
as it was, and both are clamped at the read in NS.Util.RowTintAlpha:
these arrive from SavedVariables, where a hand-edited 5 is not an error,
it is a table drawn opaque white. Neither is Core.SKIN's -- the shared
skin owns the window edge, fill, border and title, and the table
interior is this addon's own.
Three recorded decisions are overturned by this.
"Reset all sits to the right of Window scale", the pairWith companion
the panel has carried since the library's flow engine was adopted, is
gone. A button that wipes the whole ledger has no business one pixel
from the slider a player drags to size the window; it belongs in the
page's destructive corner, on History, beside Purge ledger and under the
read-out that says how much there is to lose. Both remain confirm-gated.
"The store grid is a bespoke cell in the library's flow, between the
same spacers" no longer describes anything that works. RenderTabbedSchema
re-renders the schema and nothing else on a tab click -- it clears the
scroll and calls itself -- so a block drawn by the page renderer after
it survives exactly one render and is gone the moment the player clicks
away and back. The store grid and the storage read-out are afterGroup
hooks on their own tabs now (GENERAL_AFTER_TAB), which is also why
renderStorage parks its refresher on P.__storageRefresh: the bus handler
is registered once for the session, while the label it writes into is
released and rebuilt on every render of that tab.
And makePairButton, the host's own seam for "a paired action button's
width", is deleted rather than kept. It existed to inset a button to
O.BUTTON_PAIR_REL so the ScrollFrame clip would not shave its right
border; the page's only two such buttons go through H.InlineButtonPair
now, which owns that inset, the pcall around a host onClick and the
tooltip in one place.
settings/OptionsSetup.lua's degraded stub gains the whole options-ui-13
and -14 surface -- SetChromeHeight, TabStrip, PageBanner,
RenderTabbedSchema, the six __-prefixed placement helpers and the three
chrome constants at zero, never the library's numbers. PageBanner is
carried, not drawn: the parity case compares the stub against every name
the live instance publishes, so a name this addon does not call still
has to be there.
Docs move with it: the settings-panel page gains the strips, the
afterGroup rule and the promoted literals; ARCHITECTURE, module-map and
slash-dispatch pick up "group names a tab, declaration order is tab
order"; common-tasks gains "promote a hardcoded chrome value to a
setting"; smoke-tests gains S-12a and rewrites the panel walk tab by
tab, including the click-away-and-back step that would have caught the
afterGroup mistake by hand. The case inventory and the README badge move
to 813 together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwXwe2nXFJD4797u1U2fxb
commit bd16e58e3f165b69d6420e8187574f02e1854ce9
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Sep 1 19:31:32 2026 +0530
Carry LibKa0s v1.23.0
Re-vendor both payloads whole from the library's v1.23.0 tag: libs/LibKa0s/
and tests/_kit/. Options minor 9 -> 13, OptionsWidgets minor 8 -> 12, and the
test kit revision 13 -> 14 alongside them.
What arrived that this addon actually needed is the tabbed page. v1.19.0 had
no RenderTabbedSchema, no TabStrip and no chrome band at all -- the only way
to divide a settings page was to stack headed sections down one scroll, which
is what this addon did. options-ui-13 lands all of it: RenderTabbedSchema
partitions a page's schema rows by their `group` field in declaration order
and pins the strip in a reserved band above the scroll, TabStrip is the same
strip for a page with no schema rows to partition, and the __-prefixed
placement helpers underneath them are what a host stub has to mirror.
options-ui-14 lands PageBanner in the same band. This addon adopts 13 on both
of its settings pages; 14 is carried but not drawn, and appears only in the
degraded stub, which must answer every name the live instance publishes.
The kit moved with it for the same reason it always does -- mock_base.lua
now TRACKS the enabled state instead of no-opping it. The frame metatable
answered any capitalized call with the frame itself, so IsEnabled() came back
truthy whatever SetEnabled was told, and no assertion against it could fail.
Blizzard's own tab groups mark the selected tab by disabling it, so a tab
strip is exactly the surface that gap made untestable.
The provenance line in CLAUDE.md moves to v1.23.0 in this same commit:
tests/test_vendor_sync.lua reads that line, resolves the tag it names, and
asserts both vendored trees match the library at that tag, so the bytes and
the line are only ever true together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CwXwe2nXFJD4797u1U2fxb
commit 1aa997a1d700a1dd48a97eca14d4780fe6eaa0fa
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 27 15:34:48 2026 +0530
Arm the guild bank on its frame showing, not on data arriving
A banking session opened in the middle of a field, and could not end.
Tab contents arriving -- GUILDBANKBAGSLOTS_CHANGED -- was standing in for
the open event the guild bank never fires, and it is not a proxy for "the
player is at the vault": the server pushes that data on login and reload
sync, and again whenever ANOTHER guild member moves something. A live
capture caught it exactly, with the player nowhere near a bank:
12:00:48 | [Store] queried 8 guild bank tabs
12:00:48 | [Store] GUILD_BANK opened (baseline: bags 25 kinds; stores GUILD_BANK 0)
The zero is the corroboration -- a real guild bank in front of you never
scans empty across all eight tabs.
Worse than a stray window, it was a stray window that stuck. With
Blizzard_GuildBankUI unloaded there is no GuildBankFrame, so
IsGuildBankVisible() answers nil ("cannot tell") rather than false, and
disarmGuildBankIfGone rightly declines to act on nil; HookGuildBankFrame
found no frame to hang OnHide on either. Nothing ended the session except
an unrelated BANKFRAME_OPENED stealing the context, while every
BAG_UPDATE_DELAYED rescanned bags plus eight 98-slot tabs.
The obvious guard -- arm only when the window is visible -- trades the bug
for a silent regression in RECORDING if tab data can ever arrive before
the frame is shown, which is what 47e62db was written to fix and is not
establishable offline. So the frame's own OnShow becomes the open instead,
which takes that ordering out of the decision entirely: whichever arrives
first, the window showing arms the context and OpenContext issues its own
QueryGuildBankTabs. OnShow is what GUILDBANKFRAME_OPENED was supposed to
be, and unlike the event it actually fires.
GuildBankFrame lives in a load-on-demand addon, so there is no frame to
hook until the player opens a guild bank once -- and that first open is
precisely the one whose OnShow would otherwise be missed. Ledger:
OnAddonLoaded installs both hooks the moment Blizzard_GuildBankUI lands,
Enable tries once for a /reload with the UI already loaded, and
OnGuildBankData retries per event. All three are idempotent; a hook cannot
be removed, so it goes on once.
OnGuildBankData keeps a narrower arming role -- IsGuildBankVisible() ==
true, a backstop for a build whose OnShow never fires, where the
three-valued read does the work because nil can never satisfy == true.
Its ScheduleReconcile call is untouched: tab contents arriving mid-visit
is still the only way the guild side of a deposit is ever seen.
The four cases pinning the old contract are replaced by eight, including
the reported failure verbatim and the guildmate case. The end-to-end "a
guild-bank deposit is recorded with no open event at all" passes
untouched, which is the contract that must not regress. wow_mock gains
__openGuildBank(), mirroring the close mock it already had.
docs/performance.md claimed GUILDBANKBAGSLOTS_CHANGED "only ever fires
while a guild bank frame is open" -- the very assumption behind this bug,
now corrected with the handler's real cost. /bl debug scan renames its
line to "guild bank frame hooks", since it now explains a session that
never starts as well as one that never ends.
Closes #12.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RdmN6JvqATh4xG6Rue3dut
commit 11cc825df3fc340d6b98b782834ac80f811cb2c3
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 27 11:26:01 2026 +0530
docs: catch the case inventory and the tests badge up to 794
The three "Reset Everything is wholesale" cases added in 4077fb1 went in
without regenerating the case inventory, so docs/test-cases.md and the
README badge both still claimed 791 while the harness ran 794. That file
is the authoritative pass count, which makes a stale one worse than no
count at all: the next reader has no way to tell a drifted inventory from
a suite that genuinely lost three cases.
Regenerated with `lua tests/run.lua --list` and moved the badge in the
same change, which is what docs/testing.md requires of any count move.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FtWnLGKT5uimw6X63zTzr
commit 8a1c7c6060056543d0572b2b1a52fdee6cd1c2db
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 27 11:07:32 2026 +0530
Carry LibKa0s v1.19.0
Re-vendor both payloads whole from the library's v1.19.0 tag: libs/LibKa0s/
and tests/_kit/. Widgets minor 8 is the only library file that moved, and it
moved to add ReorderList; the test kit moved with it because framework.lua and
mock_base.lua changed in the same release.
The change is additive, so this addon needs no code change of its own. Nothing
outside libs/ and tests/_kit/ calls ReorderList, and every existing surface
behaves exactly as it did on v1.18.1.
The provenance line in CLAUDE.md moves to v1.19.0 in this same commit, which is
what the release doc requires: tests/test_vendor_sync.lua reads that line,
resolves the tag it names, and asserts both vendored trees match the library at
that tag, so the bytes and the line are only ever true together.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FtWnLGKT5uimw6X63zTzr
commit c9ced65c379b61fef02c6de20e4347cf443d7900
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 26 19:01:19 2026 +0530
Re-vendor LibKa0s v1.18.1: the landing logo stops pooling its texture
OptionsWidgets minor 7 -> 8, and nothing else moves. The library's
landing page drew its logo with frame:CreateTexture() on an AceGUI
SimpleGroup -- and a texture is not a widget, so when AceGUI recycled
that frame the texture rode along and drew again on whatever the page
used the frame for next. The settings page grew a second logo,
intermittently, on pool order alone.
Minor 8 keeps the texture on the frame and reuses it, and hides it when
the widget is released. No signature, descriptor field or spec key
moves, so this addon adopts it by carrying the bytes.
The kit is unchanged at revision 13. The provenance line moves in this
commit with the payload, which is what tests/test_vendor_sync.lua
resolves the tag from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FtWnLGKT5uimw6X63zTzr
commit 4077fb1021ddf10dc7d031797a5b0cf680fc5aca
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 26 17:30:45 2026 +0530
Adopt options-ui-§12: Reset Everything is wholesale, not a list of things
The standard settles what a global reset is, and v2.36.0 settles what it
means for an addon with NO PROFILE -- which this is. Everything here is
account-wide: NS.defaults.global carries the ledger, the filter lists AND
the settings, and there is no profile section at all. So
db:ResetProfile() would be a no-op, and the rule translates: empty the
account-wide store wholesale and merge the declared defaults back, so
what comes back is indistinguishable from a fresh install.
ResetEverything was five enumerations -- a purge, a schema walk, a
filter-list clear and two window carve-outs -- which between them
happened to cover the whole table. That is the shape the rule forbids,
for the reason it forbids a row-by-row sweep: a hand-written list fails
one release later, when something new is stored beside the ones it names,
and it fails silently.
Three cases pin the replacement. A probe key no enumeration could have
named must not survive. db.global must keep its IDENTITY, because modules
capture that table at load and replacing it would leave every one of them
on a stale one. And the restored store must not ALIAS the defaults table
-- a later write would otherwise reach back into NS.defaults.global and
change what the next reset restores, a bug that only shows up on the
second reset of a session.
The confirmation takes the SECOND canonical wording, the one for an addon
with no profile: the first closes with "your other profiles are not
affected", which is a promise this addon cannot keep.
`/bl resetall` is unchanged and still non-destructive -- the ledger is not
settings, and clearing it is a separate, separately-confirmed act.
Re-vendors LibKa0s v1.18.0 with it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FtWnLGKT5uimw6X63zTzr
commit 5bc836e921a7237978331bb76d9d5d90f1ba8a5d
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 26 11:23:35 2026 +0530
feat(media): new logo art, regenerated down to the runtime .tga
Replaces the master, the render, and both derivatives with the new
collection-wide art. The 512x512 .tga and the 256 CurseForge avatar are
re-derived from the new 2000x2000 master with the recipe docs/media.md
already documented — Lanczos, and the unsharp mask the avatar needs to hold
this artwork's fine detail together at that size.
The .jpg is no longer a 1024 downscale of the master: the new art ships a
full-size render alongside it. docs/media.md and DEPENDENCIES.md are
corrected on both counts, and the jpg step drops out of the regeneration
snippet since nothing derives it any more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YPd19uJ5DWdV4mKquWCSYZ
commit 7f02e31307475f271ec9e70da547ce47e521076e
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 17:45:53 2026 +0530
docs: record the Pool release-order contract in the seam's own row
Pool minor 3 made backward release a published contract and the last commit flipped
core/PoolSetup.lua's degraded half to match, but docs/module-map.md's PoolSetup row —
the one place the seam is documented — still described the API as if the loop direction
were incidental. It is not: Acquire pops the free list from the end, so releasing
backward is what hands a widget back to the rank it already held, and rank is what
InsightsWidgets assigns PaletteColor by. Walking forward recolors widgets between
passes.
Also states why the fallback mirrors the direction rather than merely working: a shim
that contradicts a published contract only misbehaves on the installs nobody tests.
Documentation only. luacheck 0/0 in 28 files, suite 791/791.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGhst8Q5wkcqhtnXYjMAQ8
commit 8bde9437a6dc7685c1f72eafa6b4549f159780e3
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 17:30:08 2026 +0530
Re-vendor LibKa0s v1.17.0
Pool minor 3 fixes an ordering defect this addon never showed: ReleaseAll now parks
the active set backward so a position gets its own object back. All five Pool sites
here are array pools whose redraws are event-driven and whose figures are plain, so
the churn was invisible — no adoption change is needed and none is made.
core/PoolSetup.lua's degraded fallback is flipped to match. Nothing here depends on
the order, but as of minor 3 it is a published contract and a shim that mirrors the
library should not quietly contradict it.
luacheck 0/0 in 28 files, suite 791/791.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGhst8Q5wkcqhtnXYjMAQ8
commit 0e76b36d29c2596da510bf2243b9db173fda0496
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 16:05:18 2026 +0530
docs: repoint four Browser citations and the Delete call site
Comment-only corrections found by the doc sync; no behavior changes. Lint and the full suite pass unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGhst8Q5wkcqhtnXYjMAQ8
commit e2597b2e98d7b5d9be8183de691517096d631676
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 15:36:22 2026 +0530
docs: repoint the stale line citations and correct two counts
A doc sync walked DEPENDENCIES.md and docs/ARCHITECTURE.md against the
tree and found the citations had drifted since the v1.16.0 re-vendor.
DEPENDENCIES.md
- setfenv now at tests/_kit/loader.lua:72 and :91 (the old :31/:50 were
comment lines), git popen at vendor_sync.lua:184, sibling resolution at
:70, vendor-gate skip reason at :285, Pillow script at docs/media.md:131-141.
- The kit no longer shells `ls -A` to list a vendored directory; it runs
`find . -type f` (:122) with a cmd.exe `dir /b /s` fallback (:125). Split
into separate `ls` and `find` entries and retitled the section.
docs/ARCHITECTURE.md
- Event Subscriptions said eight registrations; eight is the capture
engine's count and the addon registers eleven. Names the three outside
the guard — PLAYER_ENTERING_WORLD and PLAYER_LOGOUT on each window's own
event frame — matching the table already in docs/data-flow.md.
- Added docs/revendor/ to the frozen-store list in the documentation map.
Documentation only: no .lua/.xml/.toc touched, no version bumped. The five
stale citations sitting in code comments are reported, not applied.
commit 4455ea637196b894610251d4a5152a5e17088cfe
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 14:26:27 2026 +0530
Re-vendor LibKa0s v1.16.0
Both payloads whole, from the tag, with the CLAUDE.md provenance line in the same
commit — tests/test_vendor_sync.lua reads that line to pick the tag it compares
against, so a split would fail the gate.
Pool minor 2 (keyed pools, and ReleaseAll now raises on one instead of silently
recycling nothing), Widgets minor 7 (two optional CopyWindow fields, both absent
by default), DebugLog minor 12 (its copy window is Widgets.CopyWindow now, with a
hard NEEDS_WIDGETS floor), kit revision 13 (CreateFrame records its arguments, so
a suite can ask what a frame was named).
Nothing in this addon's own code changes. Lint and the headless suite green.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6ncqntR9JgUoSgPf8PmCS
commit 2f0563c178210fe4a85a095951ecc1873f08d554
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 12:32:35 2026 +0530
Record the 2026-08-25 LibKa0s re-vendor
The re-vendor found an empty delta: this addon was already carrying LibKa0s
v1.15.0, every shipped file at the tag's minor, both payloads byte-identical to
`git archive v1.15.0`, and test kit revision 12 paired as the rule requires. No
bytes were copied and the provenance line did not move.
The bundle is the record of that. 01_DELTA.md carries all five reads with the
command that produced each, including the consumption map and the majors this
addon does not consume; 05_SUMMARY.md carries the gate results that confirm it
independently — the addon's own tests/test_vendor_sync.lua passes both its
assertions inside a full green run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6ncqntR9JgUoSgPf8PmCS
commit 66d5edf3030a23aca03b11e103e31ffbbddaf5f8
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 11:04:28 2026 +0530
docs: the fast test gate, the 791 badge and a fixed-bug smoke step
The gate the collection just adopted (testkit revision 12) runs this repo's
791 cases in 3.35s wall at 18% CPU, down from 10.7s. `--jobs` sharding stays
off: the suite is under the roughly-10s threshold testing-§14 sets, so the
serial path is still the cheaper one and forking would only add process
startup to a run that no longer waits on anything.
The README's [tests] badge had drifted to 781. docs/test-cases.md regenerated
byte-identical, so the badge was the only thing behind the suite.
docs/smoke-tests.md's menu-dismissal step (dropdown section, item 4) told the
tester that an outside click closes the menu WITHOUT reaching the modal,
because it lands on the full-screen catcher first. That catcher was removed in
LibKa0s v1.13.0 (Widgets minor 5) and this repo vendors v1.15.0, so the step
was pinning a defect the library no longer has — a tester following it would
have reported the fix as the bug. One press now dismisses the menu and reaches
what is under the cursor, and a right-click does the same.
Three counts in the doc hub had fallen behind the Env, Item and Pool seams
landing: 25 source files is 28 (the TOC's own count, and luacheck's), core/
holds six LibKa0s seams rather than three, and the compat-layer.md trigger row
said 19 shims where core/Compat.lua exports 13 — the figure module-map.md and
compat-layer.md both already carried.
Bundle 20260825-103400 recorded green: lint 0/0 over 28 files, tests 791/791,
perf skipped (no tests/perf.lua, the ratified performance-§12 exemption),
complexity 0 warnings with max CCN 15.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K6ncqntR9JgUoSgPf8PmCS
commit df59750286172e15bcead257a99f4d83f9425584
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 09:50:27 2026 +0530
docs(module-map): name core/PoolSetup.lua in the architecture hub
The LibKa0s-Pool-1.0 seam landed with its siblings documented and itself
missing, so the one file that ended four hand-rolled pools was the one file
the hub did not mention.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit 14c13c410f1b7fa9f978a8213ee231fce4620d30
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 04:48:40 2026 +0530
refactor(export): the copy window comes from LibKa0s-Widgets-1.0
This was the copy the collection's register never recorded — fifty-two lines
identical to LootHistory's but for one assignment and one line wrap, which is
why MultiMeters' comment could call itself the third of three and be wrong.
No visual change to the window itself: width, height, editWidth, backdrop and
font size are omitted from the descriptor because the library's defaults are
this file's own former constants, checked against the builder being deleted
rather than assumed. The one difference is the close glyph, which is
Core.MakeCloseButton's 18x18 red-hover control rather than this addon's 24x24
class-coloured one — the library draws its own and the descriptor has no seam
for it. Recorded as step 7 of smoke test S-24.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit fac41930624a520319ceb220956926ec020c4e0c
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 04:31:52 2026 +0530
refactor(item): item primitives through LibKa0s-Item-1.0
ItemIDFromLink, QualityLabel and LoadItem leave core/Compat.lua, and this addon
gains QualityFromLink, which only LootHistory had.
The resolver stays: Compat.GetItemDetails refuses an uncached item and records
the skip, LootHistory's guesses, and both are right for their addon. A shared
resolver would have picked one.
tests/wow_mock.lua's ITEM_QUALITY_COLORS grew real per-quality hexes. It had a
blanket __index answering one colour for every id, which a consumer reading the
table BACKWARDS — hex to quality, which is what QualityFromLink does — collapsed
to a single entry, so every real link resolved to nil.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit bd6404b2ffa66abde8f82d9baca9dc074ac5df93
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 04:24:35 2026 +0530
refactor(pool): pool widgets through LibKa0s-Pool-1.0
Three hand-rolled pools in this addon, all correct — and a fourth in LootHistory
that was not, which is why the code moved rather than being left alone. The
nested panel/row release becomes the library's `before` hook instead of two
loops that only one of the two addons ever had.
The two row pools also stop pushing the acquired row onto `pool.active` from
their render loops: the library's Acquire does that itself, where the
hand-rolled AcquireRow left it to the caller.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit 3f4feef71b1007c413cb3814feb84ddd60c031b3
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 04:03:19 2026 +0530
refactor(env): read the TOC, the map and the zone through LibKa0s-Env-1.0
Three shims leave core/Compat.lua for the library. GetAddOnMetadata was written
eleven times across nine addons in four spellings and none of them behaved
differently; GetPlayerMapID and GetZone were byte-identical to LootHistory's.
Compat keeps what is genuinely this addon's: the container, guild-bank and item
readers. No behaviour change — NS.Zone still answers "" and never nil, which
storage and the zone filter both depend on.
core/EnvSetup.lua is the reference seam for the rest of the collection, so its
fallbacks are written out in full rather than left to answer nil: an install
missing LibKa0s reads its own TOC and stamps its own zone exactly as before, and
tests/test_envsetup.lua drives that arm through a library-less reload rather
than a stub.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit 1361f22075aafbfce4e4a587fab607a3d24b3c22
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Tue Aug 25 03:49:18 2026 +0530
chore(libs): re-vendor LibKa0s v1.15.0
Adds LibKa0s-Env-1.0, LibKa0s-Pool-1.0 and LibKa0s-Item-1.0 to the payload and
takes Widgets to minor 6 (CopyWindow) and DebugLog to minor 11 (the console
holds 1500 lines, not 500). Nothing in this addon adopts the new majors yet —
this commit is the copy, the load list and the provenance line.
The three new files join the hand-listed LibKa0s load list in tests/run.lua in
LibKa0s.xml order, so the headless suite loads what the client loads.
The one behaviour change is the console history depth, which is the library's
and arrives with the copy. docs/smoke-tests.md step 4/5 followed the number:
the counter now reads `N / 1500 lines` and Clear resets it to `0 / 1500`.
tests/_kit moves with it, to revision 12. That is NOT a change this release
makes — LibKa0s' testkit is untouched by v1.15.0. Revision 12 landed at
v1.14.0 ("testkit revision 12: the green gate stops waiting on the disk") and
this addon never took it. The kit is bound to the library payload by the single
provenance line: vendor_sync.lua's DEFAULT_PAIRS compares BOTH libs/LibKa0s and
tests/_kit against the one tag bundledVersion() greps out of CLAUDE.md, so
rolling that line to v1.15.0 obliges the kit to move with it. There is no green
state otherwise. Revision 12 is backward compatible (a compiled-chunk cache in
the loader, opt-in --jobs/--shard defaulting to 1), so tests/run.lua needed no
change for it and no kit-owned case name moved — docs/test-cases.md is
unchanged and still totals 775.
diff -r against the source repo is empty for both payloads. Gate green:
775 passed / 0 failed, luacheck 0 warnings / 0 errors in 25 files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012gBvKiVQXxvbMBzpFYTbDN
commit df0381f5837735c36d714415acb3ce23f6f9753b
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 16:15:11 2026 +0530
Docs: S-22 step 3 names the slash verbs that actually close the ledger
`/bl show` is not a toggle — it calls NS.Browser:Show() unconditionally, so the
step as written left the menu open and recorded a false failure on the one check
that proves W.CloseMenu() is reachable from a slash close. The verbs that close
the window are `/bl hide` (lands on B:Hide, which calls W.CloseMenu itself) and
`/bl toggle` (hides the frame, firing the OnHide hook).
Also drops the `/bl session` half: modules/SessionWindow.lua owns no
LibKa0s-Widgets-1.0 dropdown and never touches the shared popup, so a ledger
filter menu correctly survives that window closing. The step now says so instead
of asking for a behavior that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit df0381f5837735c36d714415acb3ce23f6f9753b
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 16:15:11 2026 +0530
Docs: S-22 step 3 names the slash verbs that actually close the ledger
`/bl show` is not a toggle — it calls NS.Browser:Show() unconditionally, so the
step as written left the menu open and recorded a false failure on the one check
that proves W.CloseMenu() is reachable from a slash close. The verbs that close
the window are `/bl hide` (lands on B:Hide, which calls W.CloseMenu itself) and
`/bl toggle` (hides the frame, firing the OnHide hook).
Also drops the `/bl session` half: modules/SessionWindow.lua owns no
LibKa0s-Widgets-1.0 dropdown and never touches the shared popup, so a ledger
filter menu correctly survives that window closing. The step now says so instead
of asking for a behavior that does not exist.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit 642251ac503bc01766266b24dd04aa24be9c3422
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 16:04:30 2026 +0530
Docs: the four in-client Widgets checks, a degraded step that can be performed,
and two version restatements removed
docs/smoke-tests.md gains S-22, the checks no headless suite can make: the FIRST
click opens the menu (the v1.11.0/v1.11.1 crash, which a second click cannot
catch), Escape closes the window AND the menu on both the ledger window and the
export modal, a slash-command close closes the menu, and two dropdowns do not
fight (including the modal's DIALOG strata against the menu's FULLSCREEN_DIALOG
catcher). Its existing dropdown steps at S-21 are all art and geometry and stay
that way. Step 5 records the Widgets minor 4 label change as a deliberate change
to what the button says: a saved column filter naming a value absent from today's
dataset now reads the value's own name where it read "<Prefix>: All" while the
filter was on.
docs/smoke-tests.md S-21 step 9 asked the tester to confirm "Export to CSV with
its words and no art" on the rung where libs/LibKa0s is renamed aside. That step
could not be performed: with no library the filter bar refuses to build, so the
Export button never exists and NS.Export:Open has no caller. It now asks for the
absence, and points at the healthy-rung steps that do check the modal's marks.
docs/module-map.md's Export row said nothing about the library at all, though the
modal is the addon's second Widgets consumer. It now names the B:MakeDropdown
seam it draws through, the direct LibStub lookup it keeps for CloseMenu() alone,
and what it does with nil.
DEPENDENCIES.md:25 and docs/media.md:15 restated the bundled LibKa0s version in
prose. The defect is the restatement, not the number, so both now point at the
provenance line in CLAUDE.md — the one place the vendor-sync gate reads — and say
why a version written in prose does not move with the copy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit 2f1b7bcf3e148bce67a0892772458f9216590b23
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 16:01:44 2026 +0530
Pin the Widgets minor 4 collapsed-label change against this addon's filter bar
Minor 4's UpdateMultiLabel labels every value in _selected — from its option row
when there is one, from the raw value when there is not — where minor 3 walked
_options and fell back to the "All" label for a selected value with no row.
This addon's five column filters ARE data-driven (typeOptions, subTypeOptions,
storeOptions, qualityOptions, charOptions all walk dataset()), and a saved view
is applied against whatever option lists today's ledger produces, so the
selection genuinely can outlive its list here. The visible behavior changes: a
saved Type filter naming a type absent from today's ledger used to read
"Type: All" on a bar that was filtering, and now reads the type's own name.
Three cases pin it: the changed case, the unchanged case (a selection that does
have a row still labels from that row), and the Character dropdown, which cannot
be reached by the change because charOptions() inserts both its sentinels by hand.
Red first: with libs/LibKa0s/Widgets.lua swapped back to the v1.11.2 (minor 3)
blob, "a saved filter with no row in today's option list is NAMED, not hidden
behind All" fails; the other two stay green, which is what makes them pins
rather than duplicates.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit ebe33f96d03a6eab479c6b353496286d969e86fc
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 16:01:43 2026 +0530
Export modal: guard the Data Set dropdown, and close the shared menu on every close path
Two defects, both in this addon's SECOND LibKa0s-Widgets-1.0 consumer, and no
case in tests/test_export.lua touched either — a grep for MakeDropdown or
CloseMenu in that file returned nothing.
1. modules/Export.lua:353 called NS.Browser:MakeDropdown(frame, 148) and then
SetHeight / ClearAllPoints on the result with no guard, though MakeDropdown is
documented at modules/Browser.lua:277 to answer nil with no library. With W
nil this raised rather than refusing the surface. It is guarded now: the modal
draws NO Data Set control instead of a dead one, and still hands over the CSV
for the default data set. Latent in game only because the filter bar that
carries the Export button also refuses to build without the library. Review
2026-08-03 filed this as C-012 / F-013 T4.5 and it was never applied
(docs/reviews/2026-08-03/02_PROPOSED_CHANGES.md:359-362).
2. The modal owned a Widgets dropdown and called W.CloseMenu() from no close path
at all — no OnHide hook, a close button that hid the frame directly, and
UISpecialFrames registration for Escape. The popup is a process-wide singleton
parented to UIParent at FULLSCREEN_DIALOG; this frame's Hide() cannot reach
it, so either route left the Data Set menu floating over the game. Export.lua
now looks the library up soft-optionally for CloseMenu() alone (the dropdown
itself still arrives through the one NS.Browser:MakeDropdown factory) and
hooks OnHide, which is the seam every non-click close route goes through.
Red first, against the unmodified modules/Export.lua: 4 of the 5 new cases
failed, two of them with "modules/Export.lua:354: attempt to index local 'ds'
(a nil value)" (768 passed, 4 failed).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit 2a65a2f1ae3b92ddab6e0981fbb7e580a1756f01
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 15:52:54 2026 +0530
Mock: a FontString is its own object, and it has the client's font rule
tests/wow_mock.lua answered CreateFontString with the frame itself, so a pooled
menu row's label and its glyph were one object carrying one font — a glyph that
never had a face of its own was indistinguishable from one that did, which is
precisely what LibKa0s-Widgets-1.0's glyphFont precondition is about. And SetText
was a plain recorder, so a bare FontString never raised
"FontString:SetText(): Font not set" — the crash LibKa0s v1.11.0/v1.11.1 shipped
and that 553 green library cases sailed over.
CreateFontString now mints its own stub (__objectType = "FontString", __template
recorded on it as well as, still, in creation order on the parent). SetText
raises on a FontString with neither __font nor __template. SetFont and
GetStringWidth become real stubs alongside — GetStringWidth because
LibKa0s-Widgets-1.0's menuWidth adds a number to it, which the catch-all made
impossible and which tests/test_browser.lua was locally papering over.
../LibKa0s/tests/test_widgets.lua's geomFrame is the reference implementation.
Red first: the five new tests/test_mock.lua cases fail against the unmodified
mock (763 passed, 5 failed). Nothing else reddened.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit 0cb0e857632a2f872a523697dfa6f42851f94252
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 15:48:54 2026 +0530
Vendor LibKa0s v1.12.0 (Widgets minor 4) and roll the provenance line
Both payloads copied whole from the v1.12.0 tag: libs/LibKa0s/ and tests/_kit/.
Widgets.lua moves minor 3 -> 4, adding the two optional seams (opt.isActive,
dd.presets) and changing UpdateMultiLabel to count a selected value with no row
in the current option list instead of falling back to the "All" label.
CLAUDE.md's provenance line moves to v1.12.0 in the same commit, because
tests/test_vendor_sync.lua reads that line as its input.
Widgets.lua was already listed in libs/LibKa0s/LibKa0s.xml.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XgeH9p4y2RR44ezmS7pLpN
commit eaff17bf2cba7fd92013c92334ba041cd2cd4758
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 14:37:11 2026 +0530
docs: the two prose copies of the bundled LibKa0s version follow the provenance line
DEPENDENCIES.md and docs/media.md each restate which LibKa0s this addon carries — the payload it
ships, and the font that arrives inside it. Both still said v1.11.1 after the v1.11.2 re-vendor.
Same class as 1d137b2, which caught docs/media.md at v1.10.2: a version claim written in prose does
not move with the copy, so it has to be swept every re-vendor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J785A26A4LchQjtEiGPTwe
commit eaff17bf2cba7fd92013c92334ba041cd2cd4758
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 14:37:11 2026 +0530
docs: the two prose copies of the bundled LibKa0s version follow the provenance line
DEPENDENCIES.md and docs/media.md each restate which LibKa0s this addon carries — the payload it
ships, and the font that arrives inside it. Both still said v1.11.1 after the v1.11.2 re-vendor.
Same class as 1d137b2, which caught docs/media.md at v1.10.2: a version claim written in prose does
not move with the copy, so it has to be swept every re-vendor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J785A26A4LchQjtEiGPTwe
commit 5986327e4209de66cefae14289649ca4c62ab444
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 14:01:50 2026 +0530
Re-vendor LibKa0s v1.11.2 — the dropdown menu no longer raises on the first click
Widgets minor 3, and it is a crash fix rather than a refinement: the shared popup's row glyph
FontString was created with no font and then had its text set on every paint, which the live client
answers with FontString:SetText(): Font not set. The first click on any dropdown in this addon would
have errored, and not only on a row with no glyph — the face is set only on a glyphed row, so every
glyphless row reached the same unconditional SetText.
No code here changes: opts.glyphFont behaves as it has always been documented to, and this is the
first library version in which that is true. libs/LibKa0s/Widgets.lua and the CLAUDE.md provenance
line are the whole diff. Green gate re-run against the tag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J785A26A4LchQjtEiGPTwe
commit 1d137b2e8998230f7570eccf42850be515bd144b
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 03:35:09 2026 +0530
Fix surviving v1.10.2 reference in docs/media.md
Line 15 named the LibKa0s vendored version as v1.10.2, missed by the
prior sweep despite this file's own tick paragraph having already
moved to v1.11.1. Now matches CLAUDE.md's provenance line and every
other version reference in the live doc set.
lua tests/run.lua: 760/760. luacheck .: 0/0. Neither moved.
commit ef585ec69b1239fa1f6e67e68e9e8d069c7fd1a4
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 03:32:36 2026 +0530
Sweep docs for the shared-dropdown adoption; fix one stale comment
README's test badge, DEPENDENCIES.md's LibKa0s version and its missing
LibKa0s-Widgets-1.0 major, module-map.md's Browser.lua ownership claim,
media.md's tick-location claim, and smoke-tests.md S-21's CHECK_MARKUP
reference and degraded-pass description were all still describing the
dropdown/menu as this addon's own, or predated the filter bar's new
refusal-without-LibKa0s behavior. Corrected each to match 51f1497/7f3cd41.
Also re-points modules/Browser.lua:308's comment, which named the
deleted local makeMenuRow, at LibKa0s-Widgets-1.0's own row builder.
lua tests/run.lua: 760/760. luacheck .: 0/0. Neither moved.
commit 7f3cd41b483dd15441438a30ef20b74658d1191d
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 03:22:54 2026 +0530
Re-vendor LibKa0s v1.11.1; close the shared menu on hide again
Part 1: libs/LibKa0s bumps to v1.11.1 (tag 40861da), which adds
lib.CloseMenu() at Widgets minor 2. CLAUDE.md's provenance line moves in the
same commit; test_vendor_sync stays green.
Part 2: restores the close-on-hide behavior the previous adoption dropped.
Before v1.11.0 modules/Browser.lua closed its dropdown menu directly, on a
file-local the addon no longer owns (git show 7146d2d:modules/Browser.lua,
:1328 and :1358). The library's popup is now a process-wide singleton
parented to UIParent at FULLSCREEN_DIALOG, so this window's own Hide/OnHide
can no longer reach it — closing the ledger by Escape or a slash command
left the menu orphaned. Both sites now call W.CloseMenu(), the library's new
seam for exactly this, which the docs describe as a safe no-op with the menu
never opened or already hidden. A new test opens the real shared menu with a
genuine click and confirms B:Hide() reaches it.
Part 3: tests/test_browser.lua stops reading dd.__check and dd.__glyphFont,
which version-2-docs.md marks internal to the library's own suite. The two
MakeDropdown cases now spy on W.Dropdown, capture the opts table
B:MakeDropdown hands it, and assert the three host-resolved paths
(opts.chevron, opts.check, opts.glyphFont) the forwarder exists to inject —
reading no dropdown internal. dd.arrow stays, since the API doc keeps it a
legitimate host-readable field.
docs/test-cases.md regenerated (`lua tests/run.lua --list`) for the new case
and the two renamed assertions; 760 passed, 0 failed. luacheck 0/0. lizard
holds zero functions above CCN 15.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vi5eg4hqmw3FmHyiQUncYi
commit 51f14974eee87e7f8272b09949f53e50a2cb1001
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 02:58:26 2026 +0530
Adopt LibKa0s-Widgets-1.0; the dropdown stops being ours
Re-vendors LibKa0s at v1.11.0 and deletes the ~245 lines of dropdown and
shared-menu machinery this addon has carried since the filter bar was built.
It is the library's now, along with the suite that specified it.
B:MakeDropdown stays, as the forwarder it already half was: the library
resolves no art of its own -- Media.Icon needs an addon name a vendored copy
does not have -- so the chevron, the tick and the mono face are injected from
here. Every call site is untouched.
The mark suite is re-pointed rather than relaxed: those two paths are still the
ones that draw nothing and raise nothing when they are wrong.
Also fixes two dangling references the deletion left behind: B:Hide() and the
window's OnHide hook read the now-gone `menu` local (a global no-op, not an
error, so nothing caught it), and eight BuildFilterBar call sites called the
deleted local MakeDropdown directly rather than through self.
commit 7146d2db5c491995be3dbe8134c1aab051c3baa3
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 01:02:36 2026 +0530
build: re-vendor LibKa0s v1.10.2 (kit revision 11)
One shipped file moves — PerfPanel.lua, minor 3 → 4 — closing the last
place in the library where a close button was built without being told who
was asking. Its no-decorate path called Core.MakeCloseButton(frame, Hide),
two arguments onto the three-argument function Core grew at minor 6, so a
host that draws no chrome of its own got a perf panel closing with a
multiplication sign beside a debug console closing with the collection's
mark. Nothing raised, every suite green, and the only witness was someone
looking at the two windows side by side.
BankLedger holds the performance-§12 no-combat-path exemption and wires no
perf harness, so nothing here draws that panel today. It is vendored anyway
because the payload is vendored whole and the provenance line has to name
what is on disk — and the library's own note applies if that ever changes:
addonName falls back to name, and every host in the collection already
passes its folder name there, so the fix arrives on the re-vendor alone
with no call-site change.
tests/_kit is unchanged between the two tags (revision 11 in both); it is
re-exported from v1.10.2 so both payloads come from one tag.
The provenance line moves in this commit with the bytes, as does every
other place naming the payload's version: DEPENDENCIES.md, the two font
comments in core/, and docs/media.md.
765 tests pass, luacheck 0/0, both vendored payloads byte-identical to the
v1.10.2 tag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1v7BVzQBPheKTdLa5NVPU
commit 1c96da3496d12a9623f4be5ae5d024d3964ff31f
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Mon Aug 24 00:51:33 2026 +0530
Tint the header marks to the header's gold, and unmark the filter bar
Two UX corrections after the LibKa0s-Media adoption, both about a mark
reading as its own thing rather than as part of the control it belongs to.
The four inline marks in the History header — both sort arrows and both
group expanders — drew near-white beside labels painted 1/0.82/0, which
read as a second colour inside one string. They carry the LONG form of the
|T…|t escape now, whose last three arguments are the vertex colour: one
HEADER_RGB constant feeds the tint and both SetTextColor calls, so a mark
cannot drift off the word beside it. The tint rides on the escape rather
than on the art, so it survives the fall to the Blizzard rung.
The filter bar's Export button loses its mark. It was the only button on
that row wide enough for art beside a centred label, but "the only one
that fits" is not "the one that should have it", and one marked button
among four read as an inconsistency. The mark argument and its branch come
out of makeBarButton entirely rather than staying as a caller-less branch.
The export modal's "Export to CSV" keeps its mark: it is alone in its
window with no unmarked siblings to be odd against.
Docs follow, and the doc sync turned up seven stale comment citations —
four file:line references that no longer resolve to what they name, a
docs/data-model.md that is docs/schema.md, a load-order note naming the
deleted modules/DebugLog.lua, and a tests/test_toc.lua that is
tests/test_libka0s.lua. All comment-only.
765 tests pass, luacheck 0/0, vendored payload byte-identical to v1.10.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E1v7BVzQBPheKTdLa5NVPU
commit e726f3fae91a34f4c5551b5d8673b759ba099f40
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Sun Aug 23 23:21:56 2026 +0530
Draw from the collection's art rather than from our own copy of it
The icon set, JetBrains Mono and the bar textures ship inside LibKa0s now
(v1.9.0, LibKa0s-Media-1.0) and arrive with Core.lua in the same payload, so
this addon has no reason left to carry its own font and every reason not to.
media/fonts/ is gone, and the hand-rolled LSM:Register in OnInitialize went with
it -- two registrations of one key against two paths is the collision the
library exists to end.
core/MediaSetup.lua is the seam, and it exists because a vendored library cannot
work out which addon folder it was copied into: a texture path is absolute from
Interface\AddOns\, so this file, which has the addon name as its first vararg,
is where the library is told. It loads BEFORE core/Constants.lua, which resolves
FONT_MONO from it, and a test pins that position.
SEVEN MARKS, ON THE SURFACES THAT WERE DRAWING WORDS AND BLIZZARD ART.
modules/Browser.lua's B:MakeCloseButton is the chokepoint all four title bars
reach through -- ledger, session, export modal, export copy -- so one edit moved
all four from a 24pt multiplication sign to the collection's close. The filter
bar's collapse arrow, the ledger table's sort arrows and its group expander stop
being Blizzard's Arrow-Up-Up / UI-PlusButton-Up and become chevron, sort-up,
sort-down. Export and search keep their WORDS and gain a mark beside them: on a
button that reaches outward, "which one writes the file?" must not become a
question answered by hovering.
Every one of them keeps the rung below it. The × still draws under the close
mark, the Blizzard atlas still draws under each arrow, because NS.Icon answers
nil twice over -- no library, or no such name -- and a texture that fails to load
draws nothing and raises nothing.
The close control's SHAPE stays this addon's, deliberately: a data browser
reasonably closes with a larger target than a diagnostic window, and
standalone-windows reserves the shared shape for the windows the LIBRARY draws.
Those get it through the DebugLog descriptor, which now passes addonName beside
`name` -- the same string here, and two different questions everywhere.
Tests: tests/test_marks.lua and tests/test_mediasetup.lua. The cases worth having
test the ARGUMENT rather than the appearance -- a spy on what the close wrapper
passed, a spy on the descriptor the console is handed -- and the catalog scan now
takes its file list from the TOC. It walked a hardcoded four-file list first and
a three-directory list second; both left blind spots where a misspelt name
answers nil forever under a green suite. A bogus mark in locales/ goes red now
and did not before.
Also fixed a mock that this change exposed: CreateTexture resolved to the frame
itself, so a button's derived offsets were wrong-but-plausible.
Bundles LibKa0s v1.10.1: libs/LibKa0s/ and tests/_kit/ re-vendored, provenance
line moved with them. Kit revision 11 is required for this tag.
727 -> 764 tests, luacheck clean, no function above CCN 15.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018YFaMp5sN1mFQHxp8JqtEr
commit 49bfe22930f7f0baf9395f2918610cc2c6e47df8
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 14:00:56 2026 +0530
docs: name the in-game capture store docs/perf-analysis/
The standard retired the flat docs/perf-runs/ store in favour of one frozen
dated bundle per capture under docs/perf-analysis/, so a record and the
reading of it cannot drift apart. BankLedger ships neither store and still
ships neither: it holds the ratified performance-§12 no-combat-path
exemption, the sweep behind it is docs/performance.md, and the register row
is in docs/ARCHITECTURE.md. Nothing about that argument changed here.
What changed is only the name the four live docs use when they say the store
is absent — the exemption prose, the "not applicable" row in the
documentation map, and the two automated-test docs that explain why every
perf cell reads skip. A reader who goes looking for the directory these docs
say this addon does not have will now look for the one that exists upstream.
Frozen audit, review and dated test bundles keep the old name, as they should.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgcWFzX8WzaWuDHSU9kqZp
commit b19f8ecd254a0d3d303c9553ef6b8050d050d67f
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 12:08:21 2026 +0530
automated-tests: record run 20260807-115101 — green
Four-suite run through the vendored kit (LibKa0s v1.8.2, test-kit
revision 10). lint 0 warnings / 0 errors over 24 files; tests
727 passed, 0 failed, 0 skipped; perf skipped — no tests/perf.lua;
complexity 0 warnings, max CCN 15 (recorded, non-gating).
First bundle written since kit r10's normalize_eol pass landed, so it
is also the evidence that a run no longer leaves LF stragglers in a
CRLF-pinned repo: every file in the bundle has CR count == LF count.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE5Tjrv46E2iZfFVS47WmK
commit d888c217f3e6b2e9445fd588584e019c8bac4e4a
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 11:37:43 2026 +0530
chore(gitattributes): re-sync to the canonical body updated in v2.28.1
line-endings-§5 requires .gitattributes to match the canonical body
byte-for-byte. Correcting §7's working-tree check also rewrote the
verification advice inside that body — `file <path>` cannot report line
terminators for JSON or for a binary, so it passes files it never
examined — and no repo was re-synced when that landed.
Comment text only; no rule, pin or carve-out changes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE5Tjrv46E2iZfFVS47WmK
commit d0b47c6172c56aabc2cf5e461c087317d29879a2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 11:07:57 2026 +0530
build: re-vendor LibKa0s v1.8.2 (kit revision 10)
Both payloads move together to the v1.8.2 tag — libs/LibKa0s/ and
tests/_kit/ — with the provenance line in CLAUDE.md and its echo in
DEPENDENCIES.md moved in this same commit. test_vendor_sync.lua reads that
line, resolves the tag and compares both folders against it, so a line that
moves separately from the bytes fails the gate; the previous attempt failed
exactly there, against an untagged master.
The revision 10 change that matters here is in run-automated-tests.sh: the
bundle is now written to whatever .gitattributes declares for each path, read
with `git check-attr text eol`, instead of down a plain shell redirect that
bypassed git's filters. Before this, every run in a CRLF-pinned repo left a
crop of LF stragglers that `git status` never mentions and `git add
--renormalize` never fixes. Run 20260807-110442 is the first bundle here to
land CRLF on disk unaided — equal CR/LF counts on all five files.
lint 0/0 in 24 files, tests 727/727, complexity 0 warnings (max CCN 15),
perf skipped by the ratified performance-§12 exemption. Verdict green.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE5Tjrv46E2iZfFVS47WmK
commit b11c48949c3f81942c78f388d19d02d1900535ad
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 03:17:01 2026 +0530
docs: point accepted deviations at the ARCHITECTURE.md register
The `## Standards compliance (read first)` block told an agent to record an
accepted deviation "e.g. in the TOC/README/docs/ and in the audit bundle" —
pre-register wording that leaves the home of a ratified deviation open, so
one gets written wherever the agent happens to be. documentation-§6's
canonical block now names a single home: a row in docs/ARCHITECTURE.md →
`## Documented deviations`, with the audit bundle or issue cited from the row
rather than standing in for it. Adopted verbatim in substance; the rest of
the section (stop-and-flag, the two-way classification, the MUST) is
unchanged. Standard resolved at v2.28.0, 2026-08-06.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE5Tjrv46E2iZfFVS47WmK
commit b85652e3d20f3dea9a8e35dbf04be45f2972f79f
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 02:35:43 2026 +0530
test: record run 20260807-023005, and point one comment at the vendored kit
The automated-test record was stale against the tree. Seven commits landed since
`20260804-233144` — the doc-tier split, the testkit-9 re-vendor, the link-versus-id
ledger fix — and none of them was measured. This run measures them.
Green: lint 0/0 over 24 files, 727/727 cases with none skipped, zero functions over
CCN 15, nothing over the 1500-LOC cap. Everything moved the right way: NLOC 12788 ->
12735, functions 1946 -> 1942, avg CCN 2.1 -> 2.0, and `modules/Insights.lua` left the
on-notice size band (1023 -> 992), taking `bandFiles` 4 -> 3. `perf` is the usual
standing skip under the ratified performance-§12 exemption, and it is recorded as a
skip rather than softened into a pass.
Two things the reader needs that the table cannot carry, so both go in RESULTS.md's
standing sections. `L:GateReason` (modules/Ledger.lua:402) reached CCN 15, making five
functions at the cap rather than four — none is a warning, but there is no headroom
left in any of them. And the Lint section's scope claim was wrong: it said luacheck
covers the addon's source "and its tests/", when `.luacheckrc` excludes `tests/`
outright. A 0/0 over half the repo's Lua reads very differently once you know that,
which is exactly why automated-tests-§4 asks for the exclusions by name.
Also corrects one comment citation in tests/test_harness.lua: the kit's own doc for
the missing-suite skip was cited as `testkit/framework.lua`, its path upstream in
LibKa0s. In this repo the reader reaches it at `tests/_kit/framework.lua`. Comment
text only; nothing that runs was touched. Other stale-looking paths found in the same
sweep were left alone on purpose — the `modules/DebugLog.lua` mentions are deliberate
history about a file that was replaced, and `tests/test_toc.lua` and
`docs/data-model.md` have no single successor to name.
commit 70c2e5484afa53b92afe6d01bce6f39008b139f5
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Fri Aug 7 02:10:02 2026 +0530
docs: correct the citations the testkit-9 re-vendor moved
DEPENDENCIES.md pointed at vendor_sync.lua line numbers from before the
kit revision 9 re-vendor, so a reader following them landed on unrelated
code, and it cited the logo-regeneration script at docs/ARCHITECTURE.md
:633-643 — a range past the end of a 204-line file since the tier-model
split moved the script to docs/media.md.
ARCHITECTURE.md's Event Subscriptions section counted six registrations
through Ledger:RegisterEventSafely; there are eight (2 open, 2 close,
3 change, 1 guild-data).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QuCbofydwVeUp61YW4DiJ4
commit 4c51858f502090066becc3a0574a9c19cfc93ce6
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 21:21:21 2026 +0530
docs: retire docs/pending/LEDGER.md; the pending store is GitHub issues
Implements Ka0s WoW Addon Standard v2.26.0. The decision record lived in
the working tree, which is the one place a decision record cannot be
reached from: a deferral was invisible to the issue list the maintainer
actually opens, nothing notified on it, and nothing outside this repo
could reference it.
Every row anything still refers to is now a GitHub issue on this repo,
with status carried as a title prefix — [deferred] and [untriaged] open,
[done] and [will-not-do] closed. Deferrals and reasoned refusals migrated;
finished work did not, except where live documentation cites it by id,
because the pointer is the thing being preserved.
Every doc and comment reference now points at its issue rather than at a
deleted file. The terminal rows the file held remain in git history at
this commit's parent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QuCbofydwVeUp61YW4DiJ4
commit 6063ff4dfe761d7f7846bcb1503faeb01886e122
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 18:48:58 2026 +0530
Point the vendor-sync shim header at CLAUDE.md
tests/test_vendor_sync.lua still described the provenance line as living in
README.md and as read out of it. Kit revision 9 (LibKa0s v1.8.1) moved that
input to CLAUDE.md with no fallback, and renamed the first case with it. The
shim is this repo own file, not vendored, so the re-vendor did not carry the
correction.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4FjrPatWaye1gFsfS4vpb
commit 6123d92eea1c662494db72f409603213800e8ab7
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 18:39:55 2026 +0530
Move the LibKa0s provenance line to CLAUDE.md; re-vendor testkit 9 (LibKa0s v1.8.1)
The line "Bundles [LibKa0s](...) vX.Y.Z (MIT)." moves out of README.md and into
the root CLAUDE.md, and the tag it names moves to v1.8.1 in the same commit as
the vendored bytes it describes.
Why it moved: the line answers "which LibKa0s does this build carry?", which is
a maintainer's question asked on a page written for players. README.md is being
reduced across the collection to what a player actually needs, and a
library-provenance string was never part of that. CLAUDE.md is already where
this repo keeps the build facts an agent or a maintainer needs, so the line —
and the gate that reads it — follows.
testkit goes to revision 9 (LibKa0s v1.8.1). Its only change is that
tests/_kit/vendor_sync.lua greps the provenance line out of CLAUDE.md instead of
README.md, named through a new provenanceFile opt. There is deliberately no
fallback to README.md: a half-migrated repo with two lines that can disagree is
exactly the drift this gate exists to catch, so a consumer that has not moved
its line fails with a message naming CLAUDE.md.
No shipped library file changed in v1.8.1, so libs/LibKa0s/ is untouched and
remains byte-identical to the tag; only tests/_kit/ was re-copied.
Also here:
- README: the Standard badge loses its link wrapper, matching the rest of the
badge row.
- docs/test-cases.md regenerated. The first vendor-sync case is renamed to
"libs/LibKa0s is the LibKa0s release CLAUDE.md says this addon bundles"; the
counts are unchanged at 727.
- DEPENDENCIES.md: the two places that pointed at README.md:11 for the tag now
point at CLAUDE.md, and the version reads v1.8.1.
Green: 727 passed, 0 failed, 0 skipped, 727 total; luacheck 0 warnings / 0
errors in 24 files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4FjrPatWaye1gFsfS4vpb
commit 28a70338cdf87bd858b1fbcd1f2427c20a0cdd59
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 17:30:57 2026 +0530
fix(ledger): judge an item by the link that moved, not by its id
An upgraded drop was recorded at its base item's quality: BuildEntry enriched
from move.itemID, and GetItemInfo(itemID) can only ever answer with the BASE
item. A deposit whose bonus IDs made it Epic was stored as the Rare its base
item is - and that verdict was permanent, since nothing re-resolves a stored
row. The table colors it, the filters match it, the byQuality aggregates count
it and the CSV exports it, all from that one wrong write.
The link was already in hand. ScanStore has captured it since the wowhead-URL
work, and BuildEntry used it for entry.itemLink alone - the insight was applied
to one field and not to the three beside it.
GateReason had the same flaw, and a sharper consequence: under a non-zero
threshold it judged base quality, so an Epic-upgraded drop was skipped with
reason "quality" and never entered the ledger. Unreachable at the default
threshold of 0, which is why it went unseen.
Compat.ItemNameQuality now documents itself as link-preferring; it already
forwarded to GetItemInfo, which accepts either. Panel's filter UI still passes
an id and is still right - the user typed an id, so no link exists.
The suite was green through all of this because wow_mock resolved a link back
to its id and returned the same row, so no test could tell the two paths apart.
It now carries __itemVariants, keyed by link, exactly as the live client
distinguishes them. Two of the three new cases fail against the old code.
Existing rows keep their base quality - capture is fixed, history is not
re-enriched. Recorded under Known limitations, together with the variant
collision this exposes: the scan keeps the first link seen per itemID and the
counts map is keyed by id alone, so two variants of one item in a single
snapshot resolve to the same link.
lua tests/run.lua: 727 passed, 0 failed. luacheck .: 0/0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XL9sd9DXPYQcq7eVhRmCU2
commit 5e65fb3333d5ef57bb4da66af09a34d3b0519d41
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 17:23:19 2026 +0530
Pin line endings with the canonical .gitattributes (line-endings-§2)
This repo is client-bound, so the working tree is pinned CRLF on every
platform: `* text=auto eol=crlf`, the `*.sh text eol=lf` carve-out that
keeps the vendored test runner executable, and the shared binary block.
The body after the pin is byte-identical to every other repo in the
collection, so diffing a client-bound repo against a non-client one shows
one decision rather than two documents.
Working-tree stragglers written by tools that bypass git's filters were
re-materialized from the index; the headless suite passes unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9nTGYaHsqCEf6obganps6
commit a6b4c399243f7598f135748616dd1421e9098f76
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 15:59:41 2026 +0530
docs: adopt the documentation tier model (standard v2.23.0)
ARCHITECTURE.md was 669 lines because nothing required the doc set to split,
so every subject went into the hub. documentation-§3 now makes ARCHITECTURE.md
a capped hub — a mandated section past ~60 lines MUST spill to its canonical
topic doc — and mandates six Tier 1 docs under fixed names.
The hub goes 669 -> 197 lines. Nothing was rewritten from scratch: the new
docs are carved out of what the hub already carried, so a reader looking for
the settings schema now lands in a file about the settings schema instead of
scrolling a thousand lines with no landmark.
Created (Tier 1): scope, module-map, schema, settings-panel, data-flow,
common-tasks. Created (Tier 2, triggers fired): slash-dispatch (12 verbs,
threshold 8), compat-layer (19 addon-specific shims), midnight-quirks.
Created (Tier 3): windows, insights, media.
The remaining four Tier 2 docs are recorded as "Not applicable" rows in the
new `## Documentation map` with the trigger that would re-arm each — profiles
because the addon is account-wide by design and the AceDB profile namespace is
unused, message-bus because four messages is under the >10 threshold,
debug because the console is the library's, and perf-runs because the
performance-§12 exemption is held. A directory listing cannot tell "not
applicable" from "not written"; the row can.
Verified: 724 tests passing, luacheck clean, every relative doc link resolves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KQxehYAMgZtvAJDCJxXYRs
commit e1c82dff0fb46e6d8acb856cc6bb6fded7d74eda
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 01:12:26 2026 +0530
docs: the citations that moved when the vendor gate did
Content-only doc sync at the end of the audit/review remediation. Three files
had claims the branch's own commits invalidated, and one had a claim the branch
made obsolete.
DEPENDENCIES.md - M3-02 collapsed tests/test_vendor_sync.lua from ~150 lines to
a one-line adoption, and every file:line the toolchain section cited pointed
into the lines that went away:
* git tests/test_vendor_sync.lua:53 -> tests/_kit/vendor_sync.lua:140
* ls -A tests/test_vendor_sync.lua:89 -> tests/_kit/vendor_sync.lua:106
* no lfs tests/test_vendor_sync.lua:74-76 -> tests/_kit/vendor_sync.lua:91-93
* sibling tests/test_vendor_sync.lua:38 -> tests/_kit/vendor_sync.lua:62-63
The "sibling checkout is optional" paragraph also still described the old
behavior - gitOut returning nil and the checks reporting "could not answer".
That was the silent-PASS path M3-02 replaced: it is a SKIP carrying its reason
now (tests/_kit/vendor_sync.lua:179), so the paragraph says so and names the
runner's skip count as the thing that proves the comparison ran.
Separately, the Pillow section cited docs/ARCHITECTURE.md:626-638 for the
regeneration script; M3-08's deviation-register reshape pushed it to :633-643.
docs/testing.md - the harness tree drew tests/_kit/ as four files. The v1.8.0
re-vendor brought two more: vendor_sync.lua and run-automated-tests.sh, the
latter being the runner the same page documents three sections higher.
test_vendor_sync.lua is listed alongside test_harness.lua for the same reason
that one is - it is not a per-module suite, and a reader looking for it in the
test_<module>.lua line will not find it.
docs/automated-tests/RESULTS.md - the Perf section still read the absent
tests/perf.lua, docs/performance.md and docs/perf-runs/ as gaps tracked under
BL-15/BL-16. M3-09 ratified them: the performance-§12 no-combat-path exemption
now has a register row in ARCHITECTURE.md and its sweep in docs/performance.md,
and the README.md sitting beside RESULTS.md was updated to say so while
RESULTS.md was not. The two now agree. The audit bundle keeps its original
wording - it is frozen evidence of what was true when it was written.
Nothing measured was touched. The RESULTS.md table rows, docs/test-cases.md and
every dated bundle are records of runs that happened and are left as recorded -
which is why the table still reads 726 while the suite is at 724 today. That
gap closes at the next release run, not by hand.
Verification:
lua5.1 tests/run.lua -> 724 passed, 0 failed,
0 skipped, 724 total
luacheck . -> 0 warnings / 0 errors,
24 files
lizard -l lua -x "./libs/*" -x "./tests/_kit/*" . -> Warning cnt 0, nothing
over CCN 15
diff -r --strip-trailing-cr <LibKa0s>/LibKa0s libs/LibKa0s -> empty
diff -r <LibKa0s>/LibKa0s libs/LibKa0s -> empty
diff -r <LibKa0s>/testkit tests/_kit -> empty
0 skipped is the reading that matters on the first line: the sibling checkout
is present, so both vendor-sync cases actually compared something.
Counts re-verified against the code rather than assumed, and all held: 24
source files and 19 Compat exports (docs/ARCHITECTURE.md), 10 schema rows, 15
NS.COMMANDS verbs against the README table, 14 CARD_DEFS and 17 SECTION_TITLES
behind "fourteen headline figures and seventeen charts", the 724/724 README
badge, and docs/test-cases.md byte-identical to a fresh --list.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit e1c82dff0fb46e6d8acb856cc6bb6fded7d74eda
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Thu Aug 6 01:12:26 2026 +0530
docs: the citations that moved when the vendor gate did
Content-only doc sync at the end of the audit/review remediation. Three files
had claims the branch's own commits invalidated, and one had a claim the branch
made obsolete.
DEPENDENCIES.md - M3-02 collapsed tests/test_vendor_sync.lua from ~150 lines to
a one-line adoption, and every file:line the toolchain section cited pointed
into the lines that went away:
* git tests/test_vendor_sync.lua:53 -> tests/_kit/vendor_sync.lua:140
* ls -A tests/test_vendor_sync.lua:89 -> tests/_kit/vendor_sync.lua:106
* no lfs tests/test_vendor_sync.lua:74-76 -> tests/_kit/vendor_sync.lua:91-93
* sibling tests/test_vendor_sync.lua:38 -> tests/_kit/vendor_sync.lua:62-63
The "sibling checkout is optional" paragraph also still described the old
behavior - gitOut returning nil and the checks reporting "could not answer".
That was the silent-PASS path M3-02 replaced: it is a SKIP carrying its reason
now (tests/_kit/vendor_sync.lua:179), so the paragraph says so and names the
runner's skip count as the thing that proves the comparison ran.
Separately, the Pillow section cited docs/ARCHITECTURE.md:626-638 for the
regeneration script; M3-08's deviation-register reshape pushed it to :633-643.
docs/testing.md - the harness tree drew tests/_kit/ as four files. The v1.8.0
re-vendor brought two more: vendor_sync.lua and run-automated-tests.sh, the
latter being the runner the same page documents three sections higher.
test_vendor_sync.lua is listed alongside test_harness.lua for the same reason
that one is - it is not a per-module suite, and a reader looking for it in the
test_<module>.lua line will not find it.
docs/automated-tests/RESULTS.md - the Perf section still read the absent
tests/perf.lua, docs/performance.md and docs/perf-runs/ as gaps tracked under
BL-15/BL-16. M3-09 ratified them: the performance-§12 no-combat-path exemption
now has a register row in ARCHITECTURE.md and its sweep in docs/performance.md,
and the README.md sitting beside RESULTS.md was updated to say so while
RESULTS.md was not. The two now agree. The audit bundle keeps its original
wording - it is frozen evidence of what was true when it was written.
Nothing measured was touched. The RESULTS.md table rows, docs/test-cases.md and
every dated bundle are records of runs that happened and are left as recorded -
which is why the table still reads 726 while the suite is at 724 today. That
gap closes at the next release run, not by hand.
Verification:
lua5.1 tests/run.lua -> 724 passed, 0 failed,
0 skipped, 724 total
luacheck . -> 0 warnings / 0 errors,
24 files
lizard -l lua -x "./libs/*" -x "./tests/_kit/*" . -> Warning cnt 0, nothing
over CCN 15
diff -r --strip-trailing-cr <LibKa0s>/LibKa0s libs/LibKa0s -> empty
diff -r <LibKa0s>/LibKa0s libs/LibKa0s -> empty
diff -r <LibKa0s>/testkit tests/_kit -> empty
0 skipped is the reading that matters on the first line: the sibling checkout
is present, so both vendor-sync cases actually compared something.
Counts re-verified against the code rather than assumed, and all held: 24
source files and 19 Compat exports (docs/ARCHITECTURE.md), 10 schema rows, 15
NS.COMMANDS verbs against the README table, 14 CARD_DEFS and 17 SECTION_TITLES
behind "fourteen headline figures and seventeen charts", the 724/724 README
badge, and docs/test-cases.md byte-identical to a fresh --list.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 0524bd3865108c2bd324e885fea4617288927325
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 15:06:13 2026 +0530
fix(bankledger): the five one-offs - a read key, two guards, a tooltip, an enum [M4-19]
1. settings/Panel.lua's `/bl debug panel` dump read
LibStub.minors["O.AceGUI-3.0"], a major nothing has ever registered - the
v2.17.1 dialect sweep rewrote the KEY along with the identifier - so the
line always printed `minor=nil`. Reads "AceGUI-3.0" now, and
tests/test_panel.lua asserts the LINE'S VALUE instead of matching `^AceGUI=`,
which was happy with nil. tests/wow_mock.lua gains override 12: LibStub.minors,
the public map the real LibStub keeps and the kit's LibStub holds in a local,
seeded with the fake AceGUI's minor and kept current for every major that
registers. (BL-R-01)
2. modules/SessionWindow.lua's PruneMissing built a presence set over the WHOLE
stored ledger before ever asking whether the session list had anything in it.
It checks #self:Entries() first. (BL-R-04)
3. core/Database.lua's PruneOld fired LedgerChanged unconditionally, repainting
both windows and the Insights charts on every login to report that nothing
had aged out. It fires only when a row actually went - the same rule the
`days == 0` early return above it already followed. (BL-R-04)
4. The General page's Defaults tooltip promised only "every setting"; the button
also calls both ResetWindow()s, clearing the stored geometry of the ledger
and session windows. The tooltip says so. (BL-R-08)
5. modules/Ledger.lua:713 and :743 passed C.Store.GUILD_BANK where the value is
a frame CONTEXT. Both use C.Context.GUILD_BANK now - core/Constants.lua:35-36
sanctions the shared value, and this is a rename, not a behavior change.
(BL-R-06)
Two cases go red on the defect and green on the fix, both shown:
* "Database:PruneOld broadcasts LedgerChanged only when a row actually went"
(new) - with `fireLedgerChanged()` unconditional again:
"a prune that removed nothing must not repaint every view (expected 0, got 1)"
* "Panel:Diagnose dumps the frame, its parent chain and every scrap of its art"
(strengthened) - with the key back to "O.AceGUI-3.0":
"expected AceGUI=yes minor=41, got AceGUI=yes minor=nil"
Case count 723 -> 724 (the new PruneOld case). docs/test-cases.md regenerated
from `tests/run.lua --list` (diff empty, CR-normalized both sides); README badge
follows.
Closes BL-R-01, BL-R-04, BL-R-06, BL-R-08.
Verification: luacheck . 0/0 in 24 files; lua5.1 tests/run.lua 724 passed /
0 failed; lizard warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 7899993789e701799d9fd148f019a7fb51be699a
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 15:01:06 2026 +0530
refactor(browser): delegate the window edge to Core.ApplySkin [M4-14]
modules/Browser.lua kept a private copy of the normative Ka0s window edge - a
SKIN table carrying bg/border/innerBorder/divider/title and an ApplySkin that
built the backdrop by hand - while Core.SKIN and Core.ApplySkin define exactly
those values for every Ka0s window (standalone-windows). The copy is gone:
- core/CoreSetup.lua publishes NS.ApplySkin. On a working install it IS
lib.ApplySkin. Its degraded branch carries the pre-library skin, alongside the
other fallbacks that file owns, so the degraded stub-surface parity case stays
green and a degraded install's windows still look like a working one's.
- modules/Browser.lua's B:ApplySkin is now one delegating line. Its SKIN table
keeps only what this addon owns: the tab colors and the layout heights.
- The four windows that skin through this seam (ledger, session, both export
popups, debug console) are unchanged - they still call NS.Browser:ApplySkin.
Visual identity, diffed rather than asserted: a recording frame was skinned
before and after (both install paths) and every resolved call compared.
* degraded path: byte-identical to the old output, call for call.
* live path: identical except two inert differences - Core's skin table
carries its own extra keys (bg/border/innerBorder/divider/title) into
SetBackdrop, which WoW's backdrop system ignores, and its texture path is
spelled WHITE8x8 rather than WHITE8X8 (same file; WoW paths are
case-insensitive). Colors, alphas, edgeSize, insets, the inner border's
anchors and the title/divider tints are all identical.
settings/Schema.lua's carve-out list follows the two Browser line numbers this
commit moved (M4-03's citations stay true).
Closes BL-A-10 (C30).
Verification: luacheck . 0/0 in 24 files; lua5.1 tests/run.lua 723 passed /
0 failed; lizard warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 98bcd273d08c3789e7564b957062dd47798829c2
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 14:54:00 2026 +0530
docs(schema): enumerate all four storage carve-outs, not two [M4-03]
settings/Schema.lua's carve-out NOTE is the file a maintainer reads before
writing a key under db.global directly, and it named only two of the four
seams that bypass Schema:Set. It now names all four, each with its writer and
its clearer:
1. settings.window B:SaveGeometry / B:ResetWindow (modules/Browser.lua)
2. settings.sessionWindow SW:SaveGeometry / SW:ResetWindow (modules/SessionWindow.lua)
3. savedView B:SaveView / B:ResetView (modules/Browser.lua)
4. blacklist / whitelist the copy-on-write sites in modules/Filters.lua
Comment only - no write path changes. The two additions are the ones the
finding named; the cited line numbers are the current ones (the plan's
Browser.lua:916/:923 moved to :917/:925 under M2-08).
Closes BL-R-09 (C19).
Verification: luacheck . 0/0 in 24 files; lua5.1 tests/run.lua 723 passed /
0 failed; lizard warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit cf3b4762b7acdaf14b3825ace2e513738ea1da29
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 14:52:45 2026 +0530
docs(panel): the options-panel comments say what is actually there [M4-10]
settings/Panel.lua only:
- :11 was self-referential - "O.AceGUI is reached through `O.AceGUI` rather
than a second LibStub call", a v2.17.1 dialect sweep that rewrote the subject
along with the object. It now names what it means: AceGUI is reached through
the LibKa0s-Options instance's O.AceGUI field.
- The __pagesForTest paragraph was written twice, once against "the private
registry" (stale - the library owns it now) and once against the library's
page registry. The stale copy is gone.
- The "Scalar re-sync only" header describing P:Refresh had drifted up onto
P.__pagesForTest; it now sits on P:Refresh.
- The five blank runs (2-7 blank lines at the old :39-42, :61-67, :77-80,
:119-120, :171-173) collapse to the file's one-blank-line separator.
Per the plan's correction to BL-R-05, only :11 is genuinely incoherent of the
six sites that finding listed; :13, :21, :23, :302 and :402 read correctly and
are untouched.
Every symbol the surviving comments name resolves: O.AceGUI, O.__panels,
O.RefreshScalars and O.BUTTON_PAIR_REL are all exports of libs/LibKa0s/Options.lua.
Closes BL-R-05, BL-R-07 (C3).
Verification: luacheck . 0/0 in 24 files; lua5.1 tests/run.lua 723 passed /
0 failed; lizard warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit b22d9bcaea1b12106494d8e34cffbc10ba049636
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 14:51:02 2026 +0530
refactor(surface): every exported member has a caller or a stated reason [M4-07]
Dead exported surface, one disposition per symbol:
- Compat.QualityFromLink (+ its private qualityByHex/buildQualityByHex):
REMOVED. No caller anywhere, not even a test; Compat.QualityLabel is the
live quality reader. docs/ARCHITECTURE.md's Compat table drops it and the
count claim goes 20 -> 19 exports.
- Database:DeleteAt: KEPT, ANNOTATED. The comment claimed the table's
right-click menu as its caller; the menu deletes by identity through
Database:Delete (modules/LedgerTable.lua:1005). The comment now says so and
records the real consumer, the test cleanup at tests/test_ledger.lua:572.
- Filters:IsBlacklisted / :IsWhitelisted: KEPT, ANNOTATED. The capture path
reads the cached DB_BLACKLIST/DB_WHITELIST upvalues (modules/Ledger.lua:62-63,
:412-413), so neither has a production caller; both are the read half of the
Add*/Remove* list API and are pinned by tests/test_filters.lua.
- Insights.RankRows / Insights.BarFraction: REMOVED. Both are superseded
duplicates - every production ranking goes through W.SortedByCount and
W.NormalizeFractions (modules/InsightsWidgets.lua:188, :200), which carry
their own cases. Their 7 cases in tests/test_stats.lua went with them.
Case count 730 -> 723: the only movement is the 7 cases that referenced the
two removed Insights helpers. docs/test-cases.md regenerated from
`tests/run.lua --list` (diff empty, CR-normalized both sides); README badge
follows.
Closes BL-R-03 (C7).
Verification: luacheck . 0 warnings / 0 errors in 24 files; lua5.1 tests/run.lua
723 passed / 0 failed; lizard warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 5565a23bf1fb8bbf321dc027e77e948e4b4299dc
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 13:10:38 2026 +0530
docs(performance): claim the no-combat-path exemption, with the sweep [M3-09]
BankLedger wires no performance harness. That was true before this commit and
recorded only as a `docs/pending/LEDGER.md` row (LIBKA0S-17), which
`documentation-§3` is explicit is NOT ratification — so an audit re-filed the
same seven MUST rows every cycle. `performance-§12` is the narrow recorded
exit, and this commit takes it properly.
- `docs/ARCHITECTURE.md` ▸ Documented deviations gains the `performance-§12`
row: criterion (a) plus (b), decided 2026-08-05, re-check trigger stated in
the section's own words — the first OnUpdate handler, repeating ticker or
in-combat event handler doing real work re-arms the full wiring MUST — made
concrete for this addon (a handler that stops checking NS.State.openContext
first, or a scan moved off the bank-open gate onto a bag event).
- `docs/performance.md` is new and one screen: what the exemption suspends,
what it does NOT (whole-folder libs/LibKa0s/ vendoring, `perf` reserved as a
verb, this page, the `perf: skip` release-notes line), and where the numbers
still come from.
- The committed sweep criterion (a) requires is the table in that page: every
hit of `RegisterEvent` / `SetScript("OnUpdate"` / `C_Timer` across core,
modules, settings, defaults, locales and the TOC, with the per-event work
named. No OnUpdate handler exists in the repo; no repeating ticker exists
(every timer is a one-shot); the three events that CAN fire in combat —
BAG_UPDATE_DELAYED, PLAYERBANKSLOTS_CHANGED, PLAYER_MONEY — reach
L:ScheduleReconcile, which opens `if not NS.State.openContext then return
end`, and openContext is set only by a bank or guild-bank frame opening,
which is an out-of-combat NPC interaction. Criterion (b) follows: every
declared bucket would read 0.000 by construction.
- `docs/automated-tests/README.md`'s perf paragraph said "BankLedger has
neither yet" and pointed at three audit rows as open deviations. It now says
the state is ratified and names the register. It also records a limit of the
vendored runner rather than patching it: the runner writes only the first of
automated-tests-§3's two sanctioned perf skip reasons ("no tests/perf.lua")
and cannot write the second (the performance-§12 exemption), which is the one
that applies here. tests/_kit/ is never edited in an addon repo; that fix
belongs upstream in LibKa0s's testkit/.
- CLAUDE.md's doc pointer list gains docs/performance.md.
Closes BL-A-02, BL-A-03, BL-A-04, BL-A-05, BL-A-06, BL-A-07, BL-A-08.
Verification: docs/performance.md exists and the register carries exactly one
performance-§12 row with a Decided date and a re-check trigger; the sweep
commands re-run clean (`grep -rn "OnUpdate" core modules settings` and
`grep -rn "NewTicker\|ScheduleRepeatingTimer" …` both return nothing);
luacheck 0/0 in 24 files; lua5.1 tests/run.lua 730 passed, 0 failed; lizard
Warning cnt 0; `--list` diff against docs/test-cases.md empty.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 01f9e032e1bd7a7414c9cc802d96d9f9d1e7259b
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 13:07:00 2026 +0530
docs(architecture): reshape the deviation register to the mandated row shape [M3-08]
`## Documented deviations` was a prose bullet list. It is now the table
documentation-§3 MUSTs — `| Rule | What differs | Why | Decided | Re-check
trigger |` — with a lead-in stating what the register is for: a departure
argued for in a code comment or a docs/pending/LEDGER.md row and NOT carried
here is not ratified, and an audit is right to file it.
One row today, the M5-02 deviation: `savedvariables-§2`'s file-placement MUST
is unmet because BankLedger is account-wide by design. `defaults/Profile.lua`
is NOT created — an empty defaults file nothing reads would satisfy the
filename while weakening the rule's real invariant, that there is exactly one
place a default value is hardcoded. Decided 2026-07-27 (the commit that wrote
the original record, f697a95). Re-check trigger: the first per-profile setting.
The AceDB detail the table cannot hold sits as a note beneath it.
The performance-§12 row is M3-09's and lands next.
Closes BL-A-01 (DEFERRED — ratified deviation, register row only; M5-02) and
the BankLedger half of M3-08.
Verification: `grep -n "Documented deviations" docs/ARCHITECTURE.md` resolves;
the row carries a `filename-§N` Rule, a Decided date and a Re-check trigger;
luacheck 0/0 in 24 files; lua5.1 tests/run.lua 730 passed, 0 failed; lizard
Warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit d65475d442551d55b984c839bb350eac19357e84
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 13:05:14 2026 +0530
docs(refs): every standards citation resolves to a section that exists [M3-07]
The notation sweep across code, tests and config, excluding libs/,
tests/_kit/, docs/audits/, docs/reviews/ and docs/automated-tests/.
Comment-and-prose only; no behavior, no test, no name changed.
Retired global §N.M (documentation-§6 SHOULD), 3 sites:
- settings/Panel.lua:11, settings/OptionsSetup.lua:65 "Ka0s standard §3.4"
-> library-stack-§4 ("call LibStub once and stash it on the namespace",
which is what both comments describe).
- settings/OptionsSetup.lua:74 "Ka0s standard §3.1" -> library-stack-§1
(AceTimer is a mandatory embedded lib reached through the addon object).
Out of range (documentation-§6 MUST) — options-ui ships §1–§11:
- options-ui-§41 x3 (settings/OptionsSetup.lua:43, settings/Schema.lua:184,
docs/ARCHITECTURE.md:106) -> options-ui-§1, whose bullet is the one the
comments quote: "MUST route get/set through the addon's own single write
seam (the same function /<slash> set calls)". ARCHITECTURE's row also claims
the repaint, so it cites §1 and §11 together.
- options-ui-§190 (settings/Panel.lua:58, coalescing a bulk write) ->
options-ui-§11, "a global reset runs the host's afterRestoreAll hook BEFORE
the refresh, not after".
- options-ui-§189 (tests/test_panel.lua:175, the re-entrancy guard cleared on
the error path) -> options-ui-§11, which makes exactly that point.
Bare-filename citations: standalone-windows-§2 -> standalone-windows in
core/DebugLogSetup.lua (x2), docs/ARCHITECTURE.md (x2), docs/smoke-tests.md
and modules/Browser.lua. That file carries one unnumbered heading, so any §N
against it is dangling. No packaging-§N site exists in this repo.
DELIBERATE EXCEPTION, reported rather than swept: docs/pending/LEDGER.md rows
LIBKA0S-27 and -28 quote `standalone-windows-§2` inside settled decisions.
That file matches each row on "its ID plus its evidence hash (the first 8
characters of sha1 over the verbatim evidence text)" and says in its own
header not to hand-edit rows — a notation tidy-up there re-opens closed
decisions. The plan's own BL-A-11 correction likewise treats those rows as
frozen. They carry no §N.M, so the acceptance grep is unaffected.
Closes BL-A-11.
Verification: `grep -rEn '§[0-9]+\.[0-9]'` over the repo excluding all five
paths -> 0 (was 3). A range-check of every `<file>-§N` reference in the repo
against `grep -c '^### '` on the matching v2.22.0 section file now reports no
out-of-range and no missing-file reference. luacheck 0/0 in 24 files; lua5.1
tests/run.lua 730 passed, 0 failed; lizard Warning cnt 0; `--list` diff against
docs/test-cases.md empty.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 1eb16115a86d4ad1f957dea683a0b5a1067ac454
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 13:02:12 2026 +0530
docs(testing): every gate statement names its checkpoint [M3-06]
Sweep 3f applied to this repo's three hand-written gate statements. There are
two checkpoints and a suite's answer differs between them, so `Gates? no —
recorded only` cannot stand unqualified (testing-§6, automated-tests-§3 *The
release gate*):
- docs/testing.md — the Gates? table splits into "gates the run and the commit"
and "gates the tag", and the "Commits are gated on lint + tests only"
paragraph now names the tag gate beside it.
- docs/automated-tests/README.md § "What gates, and what only records" — same
table, plus the release-gate paragraph and the "a skip is NOT EVALUATED"
sentence.
- docs/automated-tests/RESULTS.md — the lead-in, replaced word for word with
the text tests/_kit/run-automated-tests.sh emits (automated-tests-§4).
PLAN CORRECTION. M3-06 says the RESULTS.md lead-in comes from the re-vendored
runner rather than from this sweep. It does not: the runner writes the lead-in
only in the branch that CREATES RESULTS.md (tests/_kit/run-automated-tests.sh,
the `else` of `if [ -f "$RESULTS" ]`) — an existing file gets a row inserted
under its header and keeps its old lead-in forever. Proven by a full runner
invocation on a scratch copy of this repo: it wrote a new row and left the
unqualified sentence untouched. So the lead-in was updated here, to the
runner's exact bytes; no trend row was touched, and no frozen bundle was.
No CLAUDE.md change was needed — this repo carries no "recorded, never a gate"
sentence.
Closes BL-A-12.
Verification: `grep -rn "never fail a run" docs CLAUDE.md README.md` (frozen
audit/review/bundle paths excluded) returns two hits, both checkpoint-qualified;
luacheck 0/0 in 24 files; lua5.1 tests/run.lua 730 passed, 0 failed; lizard
Warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 228a916b587841a99eaab15ad9bb23978b13cd22
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 12:58:44 2026 +0530
chore(tests): record the vendored runner as executable in the index [M3-05]
`git update-index --chmod=+x tests/_kit/run-automated-tests.sh`. The bit lives
in the git index, not in the working tree: on a DrvFs / core.fileMode=false
checkout a plain `chmod +x` is invisible to git, so a fresh clone on a
case-sensitive filesystem got a 100644 script and had to be invoked through
`bash` (automated-tests-§2).
Closes BL-A-13.
Verification: `git ls-files -s tests/_kit/run-automated-tests.sh` -> 100755
(was 100644, blob unchanged); `tests/_kit/run-automated-tests.sh --help` runs
with no interpreter prefix; luacheck 0/0 in 24 files; lua5.1 tests/run.lua 730
passed, 0 failed; lizard Warning cnt 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit f5d3c5f662c318f218ff737f571d574d2bdba175
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 12:58:06 2026 +0530
test(libka0s): assert each adopted seam's stub surface as a set [M3-04]
Four Kit.assertSurfaceParity cases, one per adopted LibKa0s seam — Core,
DebugLog, Slash and Options. Every degraded arm is produced by feeding the
loader a PARTIAL FILE LIST (the TOC's files, with libs/LibKa0s/*.lua left out
or truncated at the seam), never by hand-stubbing the member under test, and
each case names the grep that produced its member list in its comment. The two
seams with a deliberate live-only surface encode it as data through `ignore`:
DebugLog's ten console internals and Options' five unreferenced constants plus
the explicitly-nil AceGUI.
Nothing reddened on adoption — BankLedger's four stubs already carried their
whole surface — so each case was proven able to fail instead: deleting
NS.IsConcatSafe from the Core fallback, D:Clear from the DebugLog stub,
Sl:LandingRows from the Slash stub and RefreshScalars from the Options stub
each reddened exactly its own parity case with the missing member named, and
each was reverted green.
Closes the BankLedger half of M3-04 (testing-§8, anti-pattern #56).
Verification: luacheck 0/0 in 24 files; lua5.1 tests/run.lua 730 passed,
0 failed (726 + 4); lizard Warning cnt 0; `--list` diff against
docs/test-cases.md empty after CR-normalizing both sides. README test badge
726 -> 730.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
commit 1633371ac21deff92a1470f8310cf398c410c053
Author: Tushar Saxena <tushar.saxena@gmail.com>
Date: Wed Aug 5 12:49:58 2026 +0530
chore(vendor): re-vendor LibKa0s v1.8.0 and adopt the shared vendor-sync gate [M3-01][M3-02]
Re-vendors BOTH payloads from the LibKa0s v1.8.0 tag in one commit, because the
gate reads the README provenance line as its input and compares both pairs
against it — bumping the line without re-vendoring `libs/LibKa0s/` would point
the gate at a tag whose library bytes this repo does not hold.
* `cp -r <LibKa0s>/LibKa0s/. libs/LibKa0s/` — Core, DebugLog, Options,
OptionsWidgets, Perf, Slash change.
* `cp -r <LibKa0s>/testkit/. tests/_kit/` — README, framework, loader,
run-automated-tests.sh change; `vendor_sync.lua` is new.
* `README.md:11` provenance line v1.7.0 -> v1.8.0, and the claim that cites it
at `DEPENDENCIES.md:25`.
M3-02: `tests/test_vendor_sync.lua` drops ~140 hand-copied lines for a
`VendorSync.register(_G.BL_TEST, {})` call into the vendored kit. The case names
are unchanged, so `docs/test-cases.md` does not move. The single
`gsub("\r\n","\n")` on the working-tree side lives in the kit and stays: the
other side is a `git show` blob (LF) while this working tree is CRLF. The
header from AbsorbTracker/tests/test_vendor_sync.lua explaining that one
normalization is carried in verbatim. A missing sibling now reports SKIP with a
reason instead of returning early as a silent PASS.
The executable bit on `tests/_kit/run-automated-tests.sh` is deliberately NOT
set here — that is M3-05.
Closes: BL-R-02, BL-A-14 (and M3-01's enabling half for this repo).
Verification:
Pre-flight suite-inventory balance (20 files vs 20 declared): empty diff.
diff -r <LibKa0s>/LibKa0s/ libs/LibKa0s/ -> empty
diff -r <LibKa0s>/testkit/ tests/_kit/ -> empty
luacheck . -> 0 warnings / 0 errors, 24 files
lua5.1 tests/run.lua -> 726 passed, 0 failed, 0 skipped
lizard -l lua -x "./libs/*" -x "./tests/_kit/*" . -> Warning cnt 0
--list vs docs/test-cases.md (CR-normalized both sides) -> empty
grep -c "if not tag then return end" tests/test_vendor_sync.lua -> 0
Red/green proof, gate fires:
planted `-- PLANTED VIOLATION` at the tail of libs/LibKa0s/Core.lua
-> FAIL "libs/LibKa0s is the LibKa0s release the README says this addon
bundles", 725 passed / 1 failed, exit 1
reverted by re-copying from the tag -> 726 passed / 0 failed, exit 0
Skip proof, sibling absent (the shared LibKa0s checkout was NOT moved — other
agents are running against it; the sibling path was temporarily pointed at
"./../LibKa0s-NO-SUCH-CHECKOUT" via the gate's `sibling` opt, then reverted):
-> SKIP on both cases carrying "... checkout absent — the vendored payload
was NOT compared", 724 passed / 0 failed / 2 skipped, exit 0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This mod has no additional files

