Skip to content

docs: finish MEI-124 pass two — reputation, search, the word filter, cookies and the CSP - #257

Merged
jouwdan merged 2 commits into
mainfrom
claude/mei-124-pass-two
Aug 26, 2026
Merged

docs: finish MEI-124 pass two — reputation, search, the word filter, cookies and the CSP#257
jouwdan merged 2 commits into
mainfrom
claude/mei-124-pass-two

Conversation

@jouwdan

@jouwdan jouwdan commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Closes MEI-124.

The gap list was stale — that is the first finding

MEI-124 named seven subjects with no documented home. Three were already covered, and covered well:

Subject Status
Navigation menu Covered — organiser-guide.md:218
Mass mail Covered, and accurate: "active, not closed, verified address" matches audienceWhere exactly
Administering themes Covered — organiser-guide.md:173
Reputation Gap → written
Search settings Gap → written
Word filter Partial → written
Cookies and the CSP Gap → written

The list came from headings 5da64210 deleted; the reframe and the organiser guide wrote several back and nobody re-checked. Four pages, not seven, and none of them duplicates existing prose.

Four new pages

guides/community/reputation.md — ratings are −1/0/+1 with no per-group multiplier; the daily allowance is a group numeric permission, so 0 means unlimited and the day is a UTC day; totals are rebuilt with a sum in the same transaction rather than incremented, which is why they cannot drift.

The part the settings screen cannot tell you:

Allow negative Require a comment What a post offers
off off Thanks button only
off on A form; the Thanks button is gone
on off Both
on on A form; the Thanks button is gone

Two independent switches decide between one-press thanks and a form, and neither says so alone.

guides/community/search.md — the fact that makes switching search off safe to try: the index is kept and goes on being maintained, so turning it back on needs no reindex. Separates the two limits people conflate — the flood interval is per member and about impatience, the hourly cap is aimed at a script, and setting one does nothing about the other.

guides/community/word-filter.md — a pattern is a literal, not a wildcard, so .* matches two characters and nothing else. The filter runs at render time and never edits stored text, which is why removing a rule brings the original word back everywhere and why quoting a filtered post carries the original.

guides/operations/cookies-and-headers.md — every cookie and why it exists, and why SameSite differs by purpose: Lax for the SSO handshake because a Strict cookie is not sent on the identity provider's navigation back (every federated sign-in would fail), Strict for the admin, second-factor and passkey exchanges because none of those ever start elsewhere. The nonce CSP, and REMOTE_IMAGES as the one environment variable that widens it.

The defect this pass found

moderation-guide.md told moderators the word filter rewrites words "wherever a post is shown — thread pages, excerpts, feeds, search results". It never touches a thread title.

All seven filterWords call sites guard a body, an excerpt or a summary. The clearest illustration is search-results.ts:82-83:

threadTitle: hit.threadTitle,
excerptHtml: filterWords(hit.excerpt, input.wordFilter),

An unfiltered title on the line above a filtered excerpt. A board filtering a slur still shows it in every forum listing, in the feed, and as the thread's own heading. Signatures and custom profile fields are not filtered either.

The guide is corrected here and the new page documents the real coverage with a table, so the docs are honest today. Whether the software should do more is MEI-148.

Everything else checked out

Pass one produced two production bugs, so I verified before writing. Two of these I had wrong first:

  • search.enabled really does 403 the REST route. requireSearchEnabled() is the first line of the /search handler. My grep returned nothing and I nearly reported the API as ignoring the setting — reading the file corrected it. The grep was the claim; the file was the fact.
  • search.flood_seconds is used, not merely computed — passed at search-page.ts:110, returned at :113. I suspected a dead local; wrong.
  • Both reputation interactions hold, at thread/[slug]/page.tsx:287 and :291.
  • searchScopeFor is built from the forum audience, and staff do see unapproved and deleted posts in results — so the page says that rather than the vaguer thing I first wrote.
  • REMOTE_IMAGES is what adds https: to img-src.

Mass mail reaches every active, verified member with no opt-out — and no opt-out preference exists anywhere to be ignored. That is the design, not a dormant control, so it is not filed as a bug. It may still be worth a product conversation.

Verification

pnpm docs:links:check (42 documents, 726 headings), pnpm docs:index:check (41 linked), pnpm site:docs:check (39 published). README.md's table regenerated with pnpm site:docs, never hand-edited.

Not run, per the standing instruction: pnpm verify, the full suite, e2e. This PR is documentation and manifest only — no source file changed, which pnpm comments:check confirms.

claude added 2 commits August 26, 2026 15:30
MEI-124 pass two. Reputation had no documented home: the two parity
references describe how it differs from MyBB and phpBB, which is no use
to somebody who has never run either.

Verified against the code rather than reproduced, and the audit found no
defects — every claim the settings already make is true:

- search.enabled really does 403 the REST route, through
  requireSearchEnabled() at the top of the /search handler. A first grep
  suggested otherwise; reading the file corrected it.
- search.flood_seconds is used, not merely computed.
- The two reputation interactions the setting descriptions promise both
  hold at thread/[slug]/page.tsx:287 and :291.

The page documents what the settings alone do not: that Allow negative
and Require a comment together decide between a one-press Thanks button
and a form, and that requiring a comment removes the one-click path
entirely. Neither switch says so on its own, and the table is the only
place the four combinations are written down.

Also recorded: ratings are -1, 0 or +1 with no per-group multiplier; the
daily allowance is a group numeric permission, so 0 means unlimited and
the day is a UTC day; totals are rebuilt with a sum in the same
transaction rather than incremented, which is why they cannot drift.

Registered in the manifest and both indexes, and linked from the
promotions criteria in groups.md, which is the main thing that reads a
reputation total.
…browser

MEI-124 pass two, finished. Three pages, and one corrected claim.

docs/guides/community/search.md — the four controls, and the fact that
makes switching search off safe to try: the index is kept and goes on
being maintained, so turning it back on needs no reindex. Also separates
the two limits people conflate, since setting one does nothing about the
other: the flood interval is per member and about impatience, the hourly
cap is aimed at a script.

docs/guides/community/word-filter.md — a pattern is a literal, not a
wildcard, so `.*` matches two characters and nothing else. The filter
runs at render time and never edits stored text, which is why removing a
rule brings the original word back everywhere and why a quote of a
filtered post carries the original.

docs/guides/operations/cookies-and-headers.md — every cookie, why it
exists, and why SameSite differs by purpose: Lax for the SSO handshake
because a Strict cookie is not sent on the identity provider's
navigation back, Strict for the admin, second-factor and passkey
exchanges because none of those ever start elsewhere. The nonce CSP, and
REMOTE_IMAGES as the one environment variable that widens it.

The corrected claim: moderation-guide.md said the word filter rewrites
words "wherever a post is shown — thread pages, excerpts, feeds, search
results". All seven filterWords call sites guard a body, an excerpt or a
summary; none guards a title. A filtered word stays visible in every
forum listing and beside its own filtered excerpt in search results. The
guide now says so, and the gap is filed separately.

Verified before writing rather than reproduced: searchScopeFor really is
built from the forum audience, and staff really do see unapproved and
deleted posts in results; the flood interval is bypassed by the
flood.bypass permission; REMOTE_IMAGES is what adds https: to img-src.
@jouwdan jouwdan changed the title docs(community): give reputation an operator reference docs: finish MEI-124 pass two — reputation, search, the word filter, cookies and the CSP Aug 26, 2026
@jouwdan
jouwdan merged commit ce3489a into main Aug 26, 2026
14 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants