Skip to content

feat(quality): the username-as-access-key screen now runs, and can fail on a new site (BACKLOG #1226) - #561

Open
wshallwshall wants to merge 17 commits into
mainfrom
claude/builder-2-1226-wire-username-screen
Open

feat(quality): the username-as-access-key screen now runs, and can fail on a new site (BACKLOG #1226)#561
wshallwshall wants to merge 17 commits into
mainfrom
claude/builder-2-1226-wire-username-screen

Conversation

@wshallwshall

@wshallwshall wshallwshall commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

The username-as-access-key screen existed on main and ran nowhere. This wires it, and gives it a way to fail.

It was unwired, measured with a control

git grep -l username_access_key origin/main  ->  BACKLOG.md, the screen, 2 test files
git grep -l control_char_check  origin/main  ->  ci.yml AND .pre-commit-config.yaml

A wired screen appears in those two files. This one appeared in neither. The control is what makes the zero mean "unwired" rather than "my search was broken".

Wiring it as-is would have installed a step that cannot fail

main() returns 0 whatever it finds, deliberately — several candidates are correct code, and the item forbids the screen from issuing verdicts.

--baseline is the shape the item's own re-score asks for: silent on judged sites, red on a key nobody has read. Candidate.key was already written stable (callee::slot, no line number, documented "stable across rebases") with zero consumers, so this completes an existing design rather than inventing one.

The finding, and it is the candidate the row predicted

GET /me/security-events reaches store.security_events_for_user, whose SQL is, verbatim on main at store.py:7493:

SELECT ts, action, detail FROM audit_log
WHERE actor = ? AND action LIKE 'auth.%' ORDER BY id DESC LIMIT ?

with (username, limit). The username is not labelling a row — it is the WHERE clause deciding which rows the caller sees. That is the item's definition of the defect.

The schema agrees. audit_log.actor is declared TEXT and its own column comment reads "who: a username or 'system'". The table's columns are id, ts, actor, action, channel_id, detail, client, row_hashthere is no immutable user id to key on.

Conditional severity, per CLAUDE.md section 0 — there are zero deployments and nothing is exposed today. On a first deployment, an operator who deleted a user and recreated the same username would hand the new holder the previous holder's sign-ins, lockouts and password changes.

One supporting claim was overstated, and correcting it improved the finding

The baseline originally said the sibling route scopes on user_id "two lines above", offered as the tell that this site is anomalous. Two things were wrong with that, and a second commit (5be1e508) fixes them.

It is thirteen lines, and it is the preceding route handler — not an adjacent line. The author inherited "two lines" from a dispatch brief and wrote it as confirmed.

And the contrast is not evidence of an oversight, which is how "the tell the item predicted holds" reads. I first looked in store.py and could not reproduce any user_id scoping at all; the author then pointed at auth_routes.py, where the contrast is real:

auth_routes.py  my_sessions         ->  list_sessions(identity.user_id)
auth_routes.py  my_security_events  ->  security_events_for(identity.username, limit=limit)

We had measured different files and both readings were correct — the same shape as the two rescue-ref namespaces earlier today.

The measurement explains why the two routes differ: 9 FROM audit_log sites in store.py, zero keyed on user_id, against a positive control of 1 keyed on actor = ?. The sessions table has a user id and audit_log does not, so the author of the second site had no immutable key available. The schemas differ, not the care taken.

That is a better account than the original. The defect stands on the SQL and the schema alone; the contrast was corroboration and it was overstated.

Two deviations from the author's supplied replacement text, both to fit the file's own rules: no line number is cited, because the file's header says keys carry none by construction and prose would drift for the same reason; and the new positive control is labelled with what it counts, since the block already carries a different positive control of 3 for actor over a broader sweep.

Not fixed here, and that is the right call

audit_log carries a row_hash tamper-evidence chain over (prev_hash + this row). Adding an immutable actor id is a schema change to a hash-chained table — different work with different risks, and it is named as a subject rather than a number, since none is allocated.

Verification

19 passed, screen suite plus the tooling partition. ruff and mypy clean. Both YAML files parse. The control-character regex was verified byte-identical after the edit. The new hook ran on its own installing commit and passed.

The parity arm uses control_char_check as its positive control, so a broken reading of the two wiring files fails the control first rather than passing quietly.

backlog-hygiene does not fire: touches_code globs only messagefoundry/, ide/ and messagefoundry_webconsole/, and this touches scripts/quality/, .github/, .pre-commit-config.yaml and tests/.

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

wshallwshall and others added 2 commits August 23, 2026 14:28
…il on a new site (BACKLOG #1226)

The screen shipped with 281 lines and five test arms and NOTHING INVOKED IT. Measured with a
control before touching anything:

  git grep -l username_access_key origin/main -> BACKLOG.md, the screen, 2 test files
  git grep -l control_char_check  origin/main -> ci.yml AND .pre-commit-config.yaml

A wired screen appears in those two files. This one appeared in neither, so it read as coverage
and produced none -- which is the same false-green family as the four checks.py sites.

WIRING IT AS-IS WOULD HAVE INSTALLED A STEP THAT CANNOT FAIL. main() returns 0 whatever it
finds, deliberately: several candidates are correct code, and the item forbids turning the
screen into a verdict-emitter. So the design the item's own re-score asks for is a shape that
fails on a NEW key without judging any of them.

--baseline is that shape. It is silent on judged sites and red on a key nobody has read yet.
`Candidate.key` was already written stable -- `callee::slot`, no line number, documented as
"stable across rebases" -- and had ZERO consumers. This completes what its author started
rather than inventing a mechanism.

THE BASELINE RECORDS THAT A HUMAN LOOKED, NEVER THAT A SITE IS CORRECT. One of its eight
entries is a confirmed defect, which is exactly why it must not be read as an approval list.

JUDGED THE CANDIDATE THE ROW NAMES, and it is real. `GET /me/security-events` reaches
`store.security_events_for_user`, whose SQL is:

    SELECT ts, action, detail FROM audit_log WHERE actor = ? AND action LIKE 'auth.%'

The username is not labelling a row there -- it IS the WHERE clause deciding which rows the
caller sees, which is this item's definition verbatim. The tell the row predicted holds: the
sibling route two lines up scopes on user_id.

NOT FIXED HERE, and the reason is measured rather than assumed: audit_log has NO immutable
actor id. Its columns are id/ts/actor/action/channel_id/detail/client/row_hash, and a grep for
user_id against the audit store returns ZERO against a positive control of 3 for actor. The fix
is a schema change to a table carrying a row_hash tamper-evidence chain. Named as a subject,
not a number, because none is allocated.

CONDITIONAL SEVERITY (section 0 -- zero deployments): on a first deployment, an operator who
deleted a user and recreated the same username would hand the new holder the previous holder's
sign-ins, lockouts and password changes.

10 passed. The wiring-parity arm uses control_char_check as its positive control, so a broken
reading fails the control first.
The CONFIRMED INSTANCE block said the sibling route scopes on `identity.user_id`
"two lines above". Two things were wrong with that sentence and neither changes
the finding.

IT IS THIRTEEN LINES, NOT TWO, AND IT IS THE PRECEDING ROUTE HANDLER rather than
an adjacent line. The author inherited "two lines" from a dispatch brief and wrote
it as confirmed. In a security claim, in a public repository.

AND THE CONTRAST IS NOT EVIDENCE OF AN OVERSIGHT, which is how "the tell the item
predicted holds" reads. Measured on main: 9 `FROM audit_log` sites in store.py,
ZERO keyed on `user_id`, against a positive control of 1 keyed on `actor = ?`.
The sessions table has a user id; audit_log does not. The author of the second
site had no immutable key available, so the two routes differ because the schemas
differ.

That is a better account than the original and it makes the finding cleaner. The
defect stands on the SQL and the schema alone; the contrast was corroboration and
it was overstated.

Two deviations from the replacement text as supplied, both to fit the file's own
rules rather than to change its meaning:

  NO LINE NUMBER. The file's header says the key carries no line numbers BY
  CONSTRUCTION, because the same site sits at different lines depending on your
  base. Prose citing auth_routes.py:464 would drift for exactly that reason, so
  the route is named instead.

  THE POSITIVE CONTROL IS LABELLED WITH WHAT IT COUNTS. The block already carries
  a positive control of 3 for `actor` over a different sweep, so an unqualified
  second "positive control 1 for actor" would have read as a contradiction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@wshallwshall
wshallwshall enabled auto-merge (squash) August 23, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant