Repository navigation
Tags
Tags
fix(logs): reject Unix-seconds timestamps in `--from`/`--to` (#617) * fix(logs): reject Unix-seconds timestamps in `--from`/`--to` A bare number below 100000000000 (March 1973 in milliseconds) is now a usage error instead of a January 1970 time. One that reads as Unix seconds suggests the millisecond value or an ISO 8601 date-time. Fixes CORE-4361 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(logs): wait longer for the user.created log in the list E2E The log pipeline has taken over 30s to surface a new user's user.created log in CI. Poll for up to 60s instead, and raise the beforeAll timeout to 120s to cover the wait plus user creation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(logs): drop the Unix-seconds error details from the README Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): reject any bare number before 2020 in `--from`/`--to` Replace the seconds-specific and "before March 1973" branches with one fixed cutoff (2020-01-01T00:00:00Z) and one message. This also catches millisecond values like 1975 that the old threshold accepted. ISO dates and date-times before 2020 still parse, since they aren't ambiguous. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat(logs): add `--follow` to `clerk logs list` (#616) * feat(logs): add `--follow` to `clerk logs list` Print the normal backfill oldest first, then poll `GET /v1/logs` every `--interval` (default 2s) with `event_time_after` set to the newest log printed, deduping the boundary millisecond by log ID. `--json` streams NDJSON. Transient failures retry with backoff; Ctrl-C exits 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): look back 10s on each follow poll, exit 130 on Ctrl-C, give up after 5m Each poll now asks for `event_time_after = newest printed - 10s` (never past `--since`/`--from` or a `--limit`-cut backfill) and skips logs it already printed by ID, so a log the pipeline delivers late is still printed once, after newer ones. IDs older than the window are evicted, so memory stays bounded. Ctrl-C is left to the CLI's handler and exits 130 by SIGINT, like `webhooks listen`. Transient failures give up with the last error after 5 minutes with no successful request. Also from review: a poll that hits the request cap resumes from its cursor instead of skipping the rest, pages fetched before a stop are printed, a page without a `data` array is rejected, and the E2E waits for the first poll by its page size and checks lines buffered before the user existed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): anchor an empty follow at the API's Date and time out stuck requests An empty backfill anchored the follow at `searched_to`, which on a first page is 5 minutes past the API's clock, so the logs of those minutes never printed. Anchor at the first response's `Date` header instead, falling back to the local clock read before the request. Each follow request now gives up after 30 seconds and is retried like a network error, so a connection that stops answering can't hang the follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): reject a Logs API page whose data holds non-object items `readLogsPage` now checks that every item of `data` is an object, so a response like `data: [null]` fails with the usual "The Logs API returned an unexpected response." error instead of a `TypeError` later on. `list` and `--follow` share the reader, so both get the check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): cap each --follow poll's lookback at 5 minutes before the API's clock Each poll's `event_time_after` is now the later of the newest printed `event_time` minus 10s and the API's clock minus 5 minutes, read from the latest response's `Date` header (or the local clock read before the request), and still never before `--since`/`--from` or the `--limit` backfill floor. A quiet stretch no longer makes every poll rescan everything since the last log, and the dedupe set is pruned to the same window. A log that takes more than 5 minutes to become visible may be missed; the bound can be tuned once arrival delay is measured. The help text, README and changeset now say `--follow` is for live debugging and point to log drains for exporting or syncing logs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(logs): wait longer for the user.created log in the list E2E The log pipeline has taken over 30s to surface a new user's user.created log in CI. Poll for up to 60s instead, and raise the beforeAll timeout to 120s to cover the wait plus user creation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
-
Nov 12, 2026 - f4631dc 10000
- zip
- tar.gz
- Notes
- Downloads
feat(migrate): make clerk migrate generally available (#594) * feat(migrate): make clerk migrate generally available Remove the CLERK_EXPERIMENTAL=migrate gate. `clerk migrate` now registers like every other command, so it shows in `clerk --help` and completion and runs without an env var. Drops lib/experimental.ts, the experiment_disabled error code, the gate test, and the env setup in tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: list clerk migrate in the README and match its help Add `migrate` to the root README's help output. In the migrate README, show `export [platform]` and `import [file|export-run-id]` as optional the way `--help` does, say `sources` takes no `--runs-dir`, match the export `-y` wording, and restore the libsql token sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changeset): Make clerk migrate available without CLERK_EXPERIMENTAL Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changeset): stop calling clerk migrate experimental in the earlier changesets None of them has shipped, and this slice makes migrate generally available, so the release notes would have said "still experimental" beside the entry announcing it. Lands only with the flip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): point migration doc links at the per-platform guides `clerk init`'s auth-library scan and `clerk migrate export workos` linked to the old `/docs/migrations/*` pages or the migration overview. They now link to the guide for each platform under `/docs/guides/development/migrating/`: authjs, auth0, supabase, firebase, better-auth and workos. Passport.js and Kinde have no built-in source, so they link to the custom-source guide. URL strings only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): point the custom-source link at the migration guide `CUSTOM_SOURCES_URL` linked to the "Custom sources" section of the CLI's README on GitHub. Errors from a source you wrote, `clerk migrate sources`, and the `--source` help now link to the custom-source guide under /docs/guides/development/migrating/, which covers the same validation errors and hook signatures for someone migrating, not developing the CLI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(completion): check completion offers migrate and its subcommands With `gate.test.ts` gone, nothing checked that completion offers `migrate` or its subcommands. Applies the review's suggested test as written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changeset): add the Oxford commas to the migrate changesets Applies the review's suggestion for `migrate.md` ("Better Auth, or Auth.js"), and the same fix in `migrate-custom-sources.md` ("passwords, MFA, and metadata"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say the CLI redacts a libsql token, in the active voice Applies the review's suggestion as written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor: keep the CLERK_EXPERIMENTAL gate, and document it `migrate` was the gate's only user, but it won't be the last command to ship to `main` before it's ready. This keeps `lib/experimental.ts`, its test and `ERROR_CODE.EXPERIMENT_DISABLED` in place, with full JSDoc on `enabledExperiments`, `isExperimentEnabled` and `requireExperiment`. A new section in `.claude/rules/commands.md` explains how to gate a command and how to remove the gate when it ships. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say "auth platform" in the migrate help and README The help said "another auth provider" for `clerk migrate` and "a source platform" for `migrate export`, and the README used both. The docs say "platform" throughout and never "auth provider", and `migrate` already uses "provider" for OAuth providers, so `clerk migrate` now says "auth platform" in its help text and README. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): add the missing Oxford commas Adds the comma before the "and" or "or" that ends a list of three or more items across `clerk migrate`'s README, help text, and code comments: 113 commas in 32 files, with no other wording changed. Log, error, and prompt strings are left as they are, since tests may assert on them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say "auth platform" in the --reserve-unverified text In this CLI a "source" is the mapper `import --source` reads a file with, but the --reserve-unverified flag help, the unverified-only reject hint, the prompt, the changeset, and the README said "the source never verified" for the platform the users come from. They now say "the auth platform never verified", matching the rest of the help text. Mentions of a custom source, and "each source declares" in the README, still mean the mapper and are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): drop the save-location prompt from export clerk's example `clerk migrate export clerk`'s help example said it prompts for "the source instance and where to save the file". It never asks where to save: the file goes to `--output` or the run folder, as the README says. It now says "Prompts for the instance to export from", which also drops "source", since a source is the mapper `import --source` reads with. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say --service-account takes the key's JSON too `clerk migrate export firebase --service-account` takes a path to the key file or the key's JSON pasted in whole, but the flag help, the README, and the agent-mode usage error all said a path. The flag is now `--service-account <key>`, and all three name both forms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): correct the dry-run exit codes, Firebase key errors, and null spellings - `--dry-run` also exits 1 on an API, auth, or runtime error, not only 2 when the run would be refused and 0 otherwise. - Only a Firebase key with the wrong `type` names the console page. A web app config has no `type`, so it fails with `"project_id" is missing`. - A timestamp verification field also reads `nil` and `undefined` as not verified, in any case, and any other value counts as verified. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): name --source in export clerk's comment The comment said the export feeds `clerk migrate import --transformer clerk`. The flag is `--source`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say Firebase phones import verified, and why Firebase has no phone-verified field: a user confirms a number by SMS, and an admin vouches for one set through the Admin SDK, as with a phone an admin adds in Clerk. So every Firebase phone imports verified, and `--reserve-unverified` never applies to them, but nothing said so. `clerk migrate sources firebase` now lists it as a caveat, the README and the changeset say so, and a test pins the behavior. The README also says a number on the record signs in by SMS when the project has the Phone provider on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: match the experimental @example to the migrate stub Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say a continued run stays in an import's JSON A dry run, a refusal, and a consent-required run report the run they would continue, not `null`, and an empty file settles that run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say which imports --require-password still writes a run for A dry run or a run without consent writes no run, even when `--require-password` leaves out every user. Only an import that goes ahead writes one, with those users recorded as skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): drop the README pointer from the release changeset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changeset): stop calling clerk migrate experimental in the final-testing changeset #604 landed with the sentence this branch drops from the other migrate changesets. Also adds the Oxford comma to a comment #604 added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): steer --service-account to the key file's path A key passed to --service-account as JSON shows up in `ps`, even when the shell expands it from a variable, and lands in shell history when typed. The README, the flag table, and --help now point to the file's path, and a warning covers the JSON case and CI. The no-prompt error names only the path, and the README no longer claims a CI secret stays off disk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat(logs): add event filter flags to `clerk logs list` (#615) * feat(logs): add event filter flags to `clerk logs list` Add --type, --user, --identifier, --actor, --subject, --session, --client, --ip, --trace, --impersonator, --payload-filter, --fields, --match and --end-user-only, mapped to the GET /v1/logs query parameters and sent on every auto-paged request. Combinations the Backend API rejects fail as usage errors before a key is resolved. --fields adds a PAYLOAD column to the table. Refs CORE-4343 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): escape payload control characters and scope the E2E poll to the test user The PAYLOAD column prints end-user strings, so escape control characters (C1 included) before they reach the terminal. The E2E setup waited for the new user's log among the 100 newest of a test application shared with concurrent CI runs, which pushed it out; poll with --user instead, and drop the --type test's dependence on that one log. Refs CORE-4343 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): reject empty filter values and leave payload limits to the API An empty string filter (`--user "$UNSET"`) was dropped, and the API treats an empty value like no filter, so the run listed every log. Every string filter flag now fails as a usage error on an empty value. The CLI no longer copies the API's payload path limits (count, depth, value length, duplicates): they would go stale when the API changes, the API's 422 is more useful, and the value length counted UTF-16 units while the API counts bytes. Syntax checks stay. Also clarify that --match any includes --payload-filter and that --session matches the session that made the request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(migrate): count users a continue only attached to apart from impo… …rts (#609) * fix(migrate): count users a continue only attached to apart from imports A continue that finished attaching identifiers to a user an earlier attempt created counted that user as imported again: "2 users already imported are left alone", then "Imported: 2" for one new user. Once nothing is left pending, those users now count under "Finished attaching" (`result.attached` in --json), and the continue's opening line says they get the identifiers left to attach rather than calling them left alone. An attach that fails again counts as neither, and the summary's "Identifiers left to attach" names it. Fixes AIE-2013 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): don't count a refused re-attach as finished A continue whose re-attach Clerk refused left nothing pending, so the user still counted under "Finished attaching". Only a user whose identifiers all attached counts there now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(migrate): pin that an earlier refusal doesn't block "Finished attaching" A continue that attaches what was left counts the user as finished, whatever the earlier attempt's notes say: they hold its 429 retries, a phone dropped on the create, and refusals it already reported. The test pins it, and the docs now say "every identifier left to attach". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): list "Finished attaching" next to "Imported" Keeps the two green counts together, and clears the formatSummary conflict with #606, which adds "Not confirmed" after "Failed". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(migrate): pin that a partial attach isn't counted as finished Every attach-only test had one pending identifier. This one has two: the email attaches and the phone gets no answer, so the user is counted neither as attached nor as ok on the progress bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): say "finished" on the bar when a continue has attaches left An attach-only user counts in the bar's ok tally, so "created" contradicted "Imported: 0" in the summary right under it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): say every pending identifier in the attached changeset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): qualify progress output by mode; scope "once attached" in changeset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(migrate): list no unconfirmed user an undo deleted (#610) * fix(migrate): list no unconfirmed user an undo deleted Undo deletes a user whose create was cut off when it finds it by the run's marker, and writes the delete to its own run. `runs <id>` read only the import's lines, so it kept listing that user under "Not confirmed (Clerk may have created them; a continue checks)" after the undo. It now leaves out any user an undo of the run deleted, in the human output and in `unconfirmed` in --json. A user no undo deleted stays listed, since Clerk may still hold it. Fixes AIE-2014 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): count a user an undo deleted as created in runs `runs <id>` stopped listing a deleted user under "Not confirmed", but its Result line, `run.counts` in --json, the `runs` listing and the error breakdown still counted it. Undo found the user by the run's marker, so the import did create it: those now count it as created, and the error breakdown leaves out its "a re-run checks" error. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): make runs counts and lists agree after an undo After a partial undo, runs <id> leaves the users the undo deleted out of the warnings, importedWithout and leftToAttach, and out of the left-to-attach count. Once the run is undone in full, it lists no user as not confirmed and counts none: undo looked each one up by the run's marker. A run with an undo shows "the undo checks", since no continue is offered then. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(migrate): pass the loaded runs to undoneInFull in the runs listing shownRun already holds every run; undoneInFull listed them again for each one. Also rewraps the runs README paragraph. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(migrate): tighten the undone-in-full wording in runs, README and changeset The comment now says no user with the marker is left, since findInFlight skips users without it. The README splits the counts sentence and both docs get the Oxford comma. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat(logs): add `--since`, `--from` and `--to` to `clerk logs list` (#… …613) * feat(logs): add `--since`, `--from` and `--to` to `clerk logs list` `--since <duration>` (30m, 2h, 7d, 1h30m) sends `event_time_after = now - duration`; `--from` / `--to` take an ISO 8601 date-time (UTC without an offset), a YYYY-MM-DD date or Unix ms and map to `event_time_after` / `event_time_before`. `--since` and `--from` are mutually exclusive, and a range that starts after `--to` is a usage error raised before any request. Refs CORE-4342 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): reject impossible dates in the time range flags - --from and --to now reject calendar dates that don't exist (2026-02-30, 2027-02-29) instead of letting the runtime roll them into the next month. - The logs list e2e test creates its user and waits for the log in beforeAll, so the --since test also passes when run alone. Refs CORE-4342 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(logs): fail the e2e setup clearly when the user's log never shows up The `beforeAll` poll now throws after its last attempt with the exit code or log count of that attempt, instead of finishing silently and leaving the first test to fail on an undefined result. Refs CORE-4342 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
feat(logs): add `clerk logs list` (#612) * feat(logs): add `clerk logs list` Add the `clerk logs` command group with a default `list` subcommand that reads an instance's Application Logs from BAPI `GET /v1/logs` (flat view, newest first). It pages through partial scan windows (`next_page_status: unknown`) until `--limit` logs are in hand, supports `--cursor` to continue a previous run, warns when plan retention cut the results, and prints `{ data, cursor }` with `--json`. `bapiRequest` gains an optional `apiVersion`; logs requests pin `Clerk-API-Version: 2026-08-20`. The root `.gitignore` `logs` entry is scoped to the repo root so it no longer ignores `commands/logs/`. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): stop paging on a repeated cursor and harden the e2e test - `logs list` errors out when the API hands back the cursor it was just given, instead of repeating the request until the 50-request budget. - The e2e test matches the new user's `user.created` log by subject and retries a failed `logs list` run while it waits for the log to land. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): keep the logs already fetched when a page fails, soften the retention warning When a request fails after some logs were collected, `clerk logs list` now prints them (JSON or table) with `cursor.next` pointing at the page that failed, warns that listing stopped early and how to continue with `--cursor`, then exits with the original error. A failure before any log is collected still just throws. The retention warning no longer claims older logs exist: on a new instance the search reaches the retention window even when there is nothing older. It now says how far back results go. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(logs): treat a malformed logs page as a failed request `fetchLogs` cast the response body to a logs page without checking it, but `bapiRequest` returns the raw text when the body isn't JSON and null for a literal JSON null. A null body threw on `page.data` outside the per-request error handling, losing the logs already fetched, and a string body quietly ended paging as if there were no more logs. A body that isn't an object now throws inside the same try as a failed request, so it takes the partial-result path: the logs in hand are printed, `next` points at the page that failed, and the command exits non-zero. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(logs): keep `clerk logs` behind CLERK_EXPERIMENTAL until it ships Merging the command group to main shouldn't expose an unfinished command in a stable release. `clerk logs` is now hidden from help and completion and refuses to run (exit 2, experiment_disabled) unless CLERK_EXPERIMENTAL=logs is set, the same gate `clerk migrate` uses. The changeset is empty so release notes don't announce it. A later PR removes the gate. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changeset): describe the experimental clerk logs Follows the `clerk migrate` convention of real changesets for gated features. Refs CORE-4339 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(migrate): address findings from the pre-release smoke test (#604) * fix(migrate): address findings from the pre-release smoke test - Report users created without an extra or unverified email or phone Clerk refused once the user existed: the summary lists them, `--json` adds them to `result.warnings`, the user's line records `refused`, and `runs <id>` shows them under warnings. Before, only users.ndjson said so. - A secret key Clerk refuses (401) stops the import with Clerk's error, rather than a stand-in instance that sent the run checks off course. A run undone in full no longer blocks a stand-in import with "did not confirm which instance". - Replace a gateway's HTML error page with "Clerk returned HTTP <status> with no error details" in import, undo and runs breakdowns. - A refused dry run says so after the checks, with the --allow-partial command (`refused: true` in --json). - Explain the APP_ID placeholder in fix commands (`checks.placeholders` in --json). - `--json` carries the notes the human summary adds to Clerk's messages, such as the development-instance user limit (`result.notes`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): only call an attach refused when Clerk refused the identifier A 401 says the key stopped working, and an exhausted 429 says nothing about the email or phone, so neither is recorded as `refused` or warned about. Also lists `refused` among the users.ndjson fields, splits the `runs <id>` README sentence, and rewrites the changeset in short sentences. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): leave an attach pending when the failure isn't about the identifier A 401 or a 429 that outlasted its retries says nothing about the email or phone, so the attach stays pending and a re-run finishes it, rather than being dropped with only a note. Also types `createUser`'s response and adds the Oxford commas the README and changeset were missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): restore createUser's untyped response, which tsc needs Typing it made tsc report a use before assignment on a path it can't follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): leave an attach pending on a 403 that isn't about the phone A 403 can mean the key isn't permitted, which says nothing about the email or phone, so the attach stays pending for a re-run. A country Clerk doesn't support (`unsupported_country_code`) is still a refusal of the phone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): report dropped phones in runs <id>, and match the refused dry run to the real one A first phone Clerk refused on the create now lands on the user's line as `phoneRefused`, so `runs <id>` warns about it the way the import summary does. A refused dry run now gives the real refusal's advice and next steps, including the earlier run's undo or continue. The README also says which attach errors stay pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): address Roy's second review of the final-testing fixes - An attach Clerk answers `form_identifier_exists` looks the user up first: when the user already holds it, an earlier attach with no answer landed, so it counts as attached, not refused. A failed lookup leaves it pending. - A refused first phone's reason goes on the retry's `creating` line too, so a continue that adopts the user keeps `phoneRefused` and its note. - A created user's first line carries its notes, so a stop before the second line doesn't lose why the phone was dropped. - A refused `--dry-run --json` carries the real refusal's `advice` and `steps`. - `runs <id>` lists those users under "Imported without an email or phone", and both commands share the warning prefixes. - Tests for a 429 attach that outlasts the retries, the taken-by-this-user lookup, the adopted phone refusal, and the SMS note on a refused attach. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): address Roy's third review of the final-testing fixes - An email the instance's allowlist or blocklist refuses (`not_allowed_access`) is a refusal, not a key problem left pending. - A taken phone counts as attached when the user holds it in E.164, whatever the source wrote; emails are trimmed. - A lone reserved phone a retry already counts as refused isn't also pending, so a continue that adopts the user warns about it once. - An attach left pending on a Clerk error notes why, once across continues, and `runs <id>` lists those users. - `runs <id>` drops its warnings once the run is undone in full. - A refused dry run prints its next steps in agent mode too, and a real refusal's `--json` carries `advice` and `steps`. - A key Clerk rejects names where it came from, with Clerk's long message. - Tests for the continue's phone wiring, the dry run's next steps, and a 403 for the country; README and changeset brought up to date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(migrate): group refusals without the identifier, and word an HTML 401 by status - A refusal's summary key drops the email or phone Clerk's message names, so `not_allowed_access` refusals group as one warning instead of one per user. - A 401 whose body is a proxy's HTML page reads "Clerk returned HTTP 401 with no error details" instead of the markup. - Spread the modes into `test.each`, per the testing rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(migrate): address Roy's nits on the final-testing fixes - One `adoptedLines` map of `creating` lines replaces the `adoptedReserved`, `adoptedPending` and `adoptedPhoneRefused` options. - `RegExp.escape`, no spread for an unset body, notes and the key source each built once, and a one-line warning loop without a status check that never skipped anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bump. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Use the canonical audit-clerk-skill in clerk/skills (#540) * Point CLI audit guidance to canonical skills repo * Keep CLI audit skill as pointer to canonical guidance * Resolve skills checkout before CLI skill audit * docs: resolve canonical audit skill from local checkout * Apply batched suggestions from code review Co-authored-by: Michael Novotny <manovotny@gmail.com> --------- Co-authored-by: Michael Novotny <manovotny@gmail.com>
PreviousNext