The Confluence, Jira and GitHub config models get field validators for
the design's format rules: a numeric page ID, no lastModified filter or
ORDER BY in a CQL query, no ORDER BY in a JQL query, and repository names
without an owner. The plan endpoint returns them as form_errors, with
the design's line as the message. The checks no longer test these
formats themselves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
Outlook follows the design:
- Names and order: the 7 checks are the design's 6 rows. The mailbox
count shares the "List mailboxes" row, which now validates the
credential binding. Every row after it runs after it.
- Every failure gives a short line and a description that names the
grant the app needs. Passing checks give what they found ("Up to {n}
mailboxes", "Read mail in {mailbox}", "Read an event in {mailbox}"...).
A mailbox the admin did not name stays unnamed.
- Find selected mailboxes and Find selected groups are partial finds:
some missing is INCOMPLETE, none found is FAILED. The samples open the
first selected mailbox that answers and walk the groups that resolve.
- Checks that passed without testing anything now say so: an empty
mailbox and a calendar with no events are INCOMPLETE; the calendar check
with calendars off and the find checks with nothing selected do not
apply. Graph's empty pages are INCOMPLETE.
- The mailbox and group problem lists are typed, so the checks and the
connector's validation read one list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
OneDrive follows the design:
- Names and order: the 9 checks are the design's 8 rows. The drive count
shares the "List users" row, which now validates the credential binding.
Dependencies follow the design's run order; "Find a OneDrive" also runs
after "Find selected users", so no user found stops it.
- Every failure gives a short line and a description that names the
grant the app needs. Passing checks give what they found ("Up to {n}
OneDrives", "Opened the OneDrive of {user}", "Read members of
{group}"...).
- Find selected users is a partial find: some missing is INCOMPLETE and
the drive checks walk the found users; none found is FAILED. An entry
that is not an email and a list of blanks have their rows.
- Checks that passed without testing anything are now INCOMPLETE: an empty
OneDrive for the permission read, and groups that all hide their
members. A group that went away is INCOMPLETE, and a user list that does
not end is INCOMPLETE, not an unexpected error.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
SharePoint follows the design:
- Names and order: the 15 checks are the design's 15 rows ("Find selected
sites", "Sample documents", "Sign in with certificate", "Read nested
groups"...). Dependencies follow the design's run order. Sample
documents also runs after Find selected sites, and the two Entra group
checks also after Sign in with certificate, because they read SharePoint
REST.
- Every failure gives a short line and a description that names the
site, library, folder or group and the grant the app needs.
- Find selected sites and Find selected libraries are partial finds: some
missing is INCOMPLETE and the checks after them run on the found ones;
none found is FAILED. More than 5 sites or libraries pass with a caveat.
- Passing checks give what they found ("Read a file in {library}",
"Certificate valid until {date}", "Read members of {group}"...).
- Checks that passed without testing anything now say so: no file in
scope is INCOMPLETE; no library, page, site group or Entra group in
scope is SKIPPED; a file that went away passes with a caveat.
- An empty scope fails: every selected or listed site excluded fails the
site check.
- A SharePoint REST 401 is "No token for the SharePoint host".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
The SharePoint, OneDrive and Outlook checks share:
- One sign-in probe, "Sign in to Microsoft". Every check after it depends
on it, so a bad secret is one failed row and the rest are "Not run".
- The design's sign-in lines. Descriptions name the connector's account
fields for the account's sign-in method. An expired secret or
certificate says so.
- The certificate's end date, read from the .pfx: "Certificate valid
until ...", with a caveat in the last 30 days.
- "Signed in as {app name}", from the token's claims.
- Form runs fail an unreachable sign-in. OneDrive and Outlook sign-in
validate the credential binding, as SharePoint does.
- Graph errors: a 429 is RateLimitedValidationError and stops the run, no
answer is UnreachableValidationError, and a 401 and an unexpected answer
have their rows. Callers pass the rows of a 403 and a 404.
- MicrosoftGraphError is a SourceApiError with the request and the
response body, so Error Details show them.
- One MicrosoftTokenInfo for the three gateways' check_token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
HubSpot follows the design:
- Names and order: the 9 checks are the design's 9 rows ("Sign in to
HubSpot", "Read tickets", "List linked records", "Read record
viewers"...). Dependencies follow the design's run order. "List linked
records" and "Sample notes" run after the sign-in, not after the read
checks: an empty type would stop them, but they can sample a record of
another type. They leave out a denied type, which its read check
reports.
- Every failure gives a short line and a description. A denial names the
private app scope that HubSpot needs. Passing checks give what they
found ("Signed in to account {id}", "Found {n} tickets", "{n} users
found"...).
- Checks that passed without testing anything now say so. An empty type
is SKIPPED. No record of any selected type FAILS the first read check.
No record in scope skips the samples. A record without notes, and no
listed user, are INCOMPLETE.
Fixes from the design's review:
- A rate limit past the retries is RateLimitedValidationError, which
stops the run.
- Sign-in reports a missing token, an unreachable HubSpot and a timeout.
On a form run, an unreachable HubSpot or a timeout fails sign-in.
- HubSpotApiError is a SourceApiError with the request and HubSpot's
error body, so Error Details show them. The gateway raises
HubSpotConnectionError for a failed or timed-out connection.
- Permission sync validation accepts a portal with no records.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
Notion follows the design:
- Names and order: the 6 checks are the design's 6 rows ("Sign in to
Notion", "Find shared pages", "Find root page"...). Sample page content
runs after Find shared pages or Find root page; the other checks run
after sign-in.
- Every failure gives a short line and a description. A denial names the
integration capability the probe needs. Passing checks give what they
found ("Signed in as {bot} in {workspace}", "Found shared page
{title}", "Names readable for {n} users"...).
- Checks that passed without testing anything now say so. No page or no
database to sample is SKIPPED. A database with no rows passes with a
caveat.
- Find root page does not apply without a root page, also on a run
without a config: indexing then reads every shared page.
Fixes from the design's review:
- A rate limit is RateLimitedValidationError, which stops the run.
- Sign-in reports a missing token, an unreachable server and a request
timeout. On a form run, the last two fail sign-in.
- A 404 on a page or database that search returned is a denial.
- NotionApiError is a SourceApiError with the request and Notion's
response body, so Error Details show them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
Linear follows the design:
- Names and order: the 9 checks are the design's 8 rows ("Sign in to
Linear", "Find selected teams", "Sample issues", "Read account
role"...). The two account-role checks share one row. Dependencies
follow the design's run order.
- Every failure gives a short line and a description. A rejected
credential and a guest name what the credential needs for its kind:
a new API key, or a new OAuth connection. Passing checks give what
they found ("Signed in as {email}", "Found all {n} teams", "Read
issue {identifier}", "Listed all {n} workspace users"...).
- Find selected teams and Find selected projects are partial finds:
some missing is INCOMPLETE and Sample issues runs on the found ones;
none found is FAILED.
- An empty scope FAILS Sample issues. No team to test SKIPS List team
members. No issue to read sharing of is INCOMPLETE.
Fixes from the design's review:
- A rate limit (HTTP 429 or the RATELIMITED code) is
RateLimitedValidationError, which stops the run. The gateway does not
retry it.
- Sign-in reports an unreachable Linear, a timeout, a missing
credential, a refused OAuth renewal and a busy refresh lock. On a
form run, an unreachable Linear fails sign-in.
- The gateway's API errors are SourceApiErrors with the request and
Linear's body, so Error Details show them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC
Slack follows the design:
- Names and rows: the 16 checks show as the design's 13 rows ("Sign in
to Slack", "List Grid workspaces", "Find selected channels"...). The
three public listings share one row, and the two private listings
share one row. Dependencies follow the design's run order. Sign-in
and the Grid workspace listing unlock the form.
- Every failure gives a short line and a description. A missing scope
names the bot scope in one form. Passing checks give what they found
("Signed in as @{bot} in {workspace}", "2 workspaces in the org",
"Read a thread in #{channel}"...).
- Find selected channels is a partial find: some not visible is
INCOMPLETE, none visible is FAILED.
- Checks that passed without testing anything now say so. No sample to
read is INCOMPLETE. A bot in no private channel SKIPS the private
message and member checks. Find selected channels and Compile channel
patterns do not apply when there is nothing to find or compile.
Fixes from the design's review:
- Slack's empty first page: the public message sample and the
permission-sync listing follow the cursor across 2 pages of 100.
- A rate limit is RateLimitedValidationError, which stops the run.
- Sign-in reports "Cannot reach Slack", and fails on a form run. Each
rejected-token error has its own line.
- slack_api_error_detail puts the request and Slack's response body in
a SourceApiError, so Error Details show them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KFMazQbuG8ui3jxBxm47sC