docs(email-security): bulk remediation, verdict revisions, link detonation and a troubleshooting page - #383
Merged
Merged
Conversation
…reopen Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…wildcard and the release note Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
maximelb
marked this pull request as ready for review
September 7, 2026 17:41
lcbill
previously approved these changes
Sep 7, 2026
…propagation bound, safer installation-key advice Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
… path, in every list that enumerates them Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tually co-occur on one link is_ip and domain/root are mutually exclusive by construction, so the 'wrong' example could never have matched a single link either — which made it a bad demonstration of the trap it exists to show. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
…xt to the two that were listed Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
…ions - troubleshooting: unsubscribing refuses the WHOLE API surface, reads included; the previous text claimed reading and remediating still work. A 403 on `coverage` is now called out as itself the answer. - troubleshooting: `resolved: false` omits only plan/clock/cap, not every field; `gate_reason` is absent rather than empty; the trial note no longer asserts a posture that is per-deployment. - custom-rules: `rule validate` does NOT check that a named lookup exists — that happens on save only. This is the one exception to validate/save parity, and api-reference now says so where it claimed parity outright. The verbatim refusal quote is paraphrased so it stops publishing a deployment detail. - remediation: exit 0 is narrower than "something was acted on" — an all-skipped or all-alert_only batch exits 0. The SDK caveat is a version note, not a stale claim. - policy: `banner_message` is a modification, not a placement change; only the extension stamps `lc:system`; the console replaces the switch with a badge rather than showing it read-only. - detections: `disabled` is reserved and never stamped; the detonation block's `url` field was missing; omitempty means absent, not false; the mode allow-list also refuses an unknown prior mode. - pipeline: `no_messages_emitted_in_window` also covers an all-backfill window. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
…overage is what 403s Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…annot read through the gated route Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
…phs above the box saying it may not Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lcbill
previously approved these changes
Sep 7, 2026
- policy.md: took master's 'It also happens without a request' table and its 'You are told before it happens' section wholesale; they are strictly better than the version this branch had and cover cancellation non-interchangeability. - policy.md: fixed two links to api-reference.md#get-coverage, an anchor that does not exist. They arrived on master with #382 and neither mkdocs --strict nor the repo's check-links job validates fragments. - troubleshooting.md: dropped the hedge about trial limits now that the terms are published, and cross-linked the plans section instead. Added the three mailbox-cap fields the coverage layer adds on top of the entitlement RPC (mailboxes_active, mailboxes_over_cap, mailbox_cap_reached), and called out that a lapsed trial keeps the API while an unsubscribe does not.
lcbill
approved these changes
Sep 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The Email Security section was written ahead of several features and behind
others. This closes the remaining gaps identified in the pre-GA docs sweep, and
corrects three sections that had become wrong rather than merely incomplete.
Everything here was verified against current
origin/masterof the gateway, theCLI/SDK, the extension, the hive definitions and the backend before it was
written — several claims that "looked right" turned out not to be, and are
called out below.
What is new
remediation.md(new)confirm,action,msg_uuids,reason,state,items[].result, all sevencountskeys), why the confirmation is a derivation rather than a nonce,interruptedas a terminal state versus thestalledflag, the CLI's two-step--confirmgrammar, and the exit codestroubleshooting.md(new)entitlementblock on coverage, the silent-mailbox stall,EMAIL_INGEST_ERROR, connection handover during a platform update, installation keys, and the 30-day deletion clockpolicy.mdmanaged_rulessection — the record, the console switch, the two extension actions, absent-means-enabled, and the propagation bounddetections.mdcustom-rules.mdscopeversus the?path wildcard, and the two backtest refusalspipeline.mdprocessing_latency_p95api-reference.mdcli.md,messages.md,user-reports.md,index.mdCorrections to things that were wrong
policy.mdclaimedsubmit_to_triageandcrawl_link"validate but do notexecute". Both are implemented now. They are documented as the two asking
actions — they touch no mailbox and promote nothing to the retained lane — with
the load-bearing warning that a trigger rule on
submit_to_triagemustfilter on
result: ok, because analert_onlyorganization emits the actionas audit and a rule matching the action alone would start paid work for exactly
the organizations that opted out.
pipeline.mdsaid we do not publish a latency figure. We do:overview.processing_latency_p95reports p50/p95/p99 with itsbasis, itsdenominator, and — when there is no number — which of two honest reasons
applies. Backfilled and re-driven mail is excluded, so a recovery cannot report
a latency measured in days.
policy.mddocumentedhunt_defaultsas if something read it. Nothingdoes. The record still validates and composes, the server-side hunt routes are
registered and answer a refusal, and the console's Hunt screen is an LCQL search
that does not read this record. Said plainly, with the explicit instruction not
to treat its
dry_run: trueas a safety control over anything.What was deliberately not written
/huntsroutes are registered and not served. Theyare documented as unavailable, in one short section, rather than as a feature.
The section also warns not to branch on the refusal: only the
GETreturns thetyped
not_implemented; the twoPOSTs currently fail differently.code and are reported through the
entitlementblock, but they ship inert andthere is no announced Email Security pricing. The troubleshooting page therefore
documents the mechanism and every field, and states that limits are reported
before they are enforced —
mailbox_cap_enforcedis the field that says whichyou are looking at — rather than publishing a duration and a cap as a promise.
Assumption stated for review: if the intent is to announce the trial terms
publicly, that is a one-paragraph follow-up, not a rewrite.
lc-aimailsec-triagebundle did not exist and the docs link 404s. It does exist onmasterand the link resolves — the ticket (and my first check) were reading astale clone. Verified against the live repository; the page's claims about the
bundle's three trigger rules match what is checked in.
Verification
.venv/bin/mkdocs build --strict— cleanmarkdownlint-cli2— 0 issues across 388 filesscripts/check-release-note-headings.py,scripts/check-release-feeds.py,scripts/check-list-numbering.py— cleanpytest tests/— 210 passedbuilt HTML — 0 broken. The new API sections are linked by their generated
anchors, so a heading rename would fail this check rather than silently 404.
Release note added under
2026-09-07in the versionless announcement form, as### Extensions: …, matching how the tenant-purge entry did it.🤖 Generated with Claude Code
Update: reconciled with #382, and an independent review
#382 landed the trial terms while this was open. This branch is merged with
it, and the overlap resolved in its favour:
policy.md's deletion table andnotices section are master's, and the troubleshooting page now cross-links
[Plans, the free trial, and the mailbox cap] instead of hedging about whether
limits are enforced. The two pages no longer say different things.
Two links
#382introduced pointed atapi-reference.md#get-coverage, an anchorthat does not exist —
GET /coverageis a table row, not a heading. Fixed here,because neither
mkdocs build --strictnor the repo'scheck-linksjobvalidates fragments. This PR was checked with a per-anchor pass over the built
HTML instead; it also caught one of my own.
An independent reviewer read the whole diff against the backing repos and found
11 factual errors, all fixed (commit
9e692a20). The two worth knowing about:not —
mailsecGateruns the subscription check on every/v1/mailsec/*route including reads, so an unsubscribed organization gets
403everywhere.That also made the page's own "run
coverageand readgate_reason" adviceunreachable. A lapsed trial is the case where reads do still work, and the
page now distinguishes the two.
rule validatechecks a namedlookupexists. It does not — thevalidate path passes no hive helpers, so the check runs on save only. That
is the one exception to "valid here means savable there", and
api-reference.mdclaimed that parity outright; both corrected.
The reviewer also caught the only internal detail that had leaked into this
public repo: a verbatim backtest-refusal quote naming a deployment component.
Paraphrased.