Skip to content

fix(cli): auth-config guidance names a command that resolves - #11466

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-11313-auth-login-guidance
Aug 24, 2026
Merged

fix(cli): auth-config guidance names a command that resolves#11466
os-zhuang merged 1 commit into
mainfrom
claude/issue-11313-auth-login-guidance

Conversation

@claude

@claudeclaudeBot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Fixes#11313

readAuthConfig() (packages/cli/src/utils/auth-config.ts) throws the one instruction a user gets at the moment they are already stuck: no stored credentials, the command they wanted has just failed, and this string is what tells them how to recover. It said os auth login, which does not resolve. The second failure reads as "the tool is broken", not as "a typo in a help string" — which is why this was graded on its own terms rather than as another stale-examples docs nit.

The premise, re-derived rather than transcribed

login.ts sits at the root of packages/cli/src/commands/, so oclif's pattern-strategy loader registers it as login; no auth topic has ever existed. Measured here against the built CLI (packages/cli/bin/run.js, after building the package and its dependency closure):

$ node packages/cli/bin/run.js auth login --help
› Error: Command auth:login not found. (exit 2)
$ node packages/cli/bin/run.js login --help
Authenticate and store session credentials (exit 0)

Loading the built oclif Config enumerates 61 registered ids (the card measured 60 at 1fa05a69e2; one command has landed since), zero containing auth, and no auth topic — with login, logout, register, whoami, dev, serve all present as the control that the zero is a real absence rather than a broken probe.

The message now says os login, the spelling login.ts's own examples already used.

One further in-file fix, named with its evidence

AuthConfig.activeEnvironmentId's doc comment, four lines up in the same file, said os projects switch. projects became environments in v5.0 with no aliases (ADR-0006); the same enumeration shows no projects topic and no id containing project, while environments switch is registered.

It is fixed in place rather than deferred because all four bounded-fix conditions hold — same defect class as the card, mechanical with the correct form pinned by existing evidence, same declared file, same gate family — and because the pin below scans the whole file: the alternative was an exclusion, and an exclusion is how a line stops being checked without anyone deciding to stop checking it.

Nothing else in the file changed. The surrounding "project" prose (Active project id …) is v5.0 wording debt shared with environments/switch.ts's own description and is deliberately untouched.

The pin asserts the property, not the spelling

packages/cli/src/utils/auth-config.test.ts. A pin on the literal text os login would go green forever and catch nothing — it would still pass the day someone renames login.ts, which is the next way this string goes stale. What is asserted instead: every command invocation this file documents resolves to an id the CLI actually registers, with the id set re-derived from src/commands/** via oclif's own path→id algorithm (topicSeparator read from this package's manifest, not hardcoded). Its output was checked against the built Config while writing the pin: same ids.

Two legs, deliberately:

  1. Behavioural — the real readAuthConfig() is driven into its real ENOENT branch against a redirected $HOME, and the message parsed is the one a stuck user actually reads. The redirect is self-validating: the suite first asserts getCredentialsPath() really points inside the temp home, so it cannot silently degrade into a verdict about the developer's own machine.
  2. Source-wide — every backticked invocation in auth-config.ts, so a guidance string added here later is held to the same property without anyone remembering to extend the pin.

environments.test.ts's #10967 pin reads static override examples via AST and structurally cannot see a thrown-error string, which is why this class needed its own pin rather than an extension of that one. The id derivation is duplicated rather than shared because that file is outside this change's surface; hoisting it into one helper is a follow-up, not a rider here.

Anti-vacuity: the pin shown failing without the fix

Committed first, then mutated with a restore trap on EXIT INT TERM; each mutation confirmed on disk by grepping both the injected and the removed text before the run, and the tree confirmed clean afterwards.

legmutationresult
Aos loginos auth login2 failed / 3 passed — both the behavioural leg and the source-wide leg
Bos environments switchos projects switch1 failed / 4 passed — source-wide leg only; the behavioural leg reads the thrown message and is correctly unaffected
restorecommitted tree5 passed

Leg A's message, verbatim:

AssertionError: the message a user sees when they have no credentials names a command
this CLI does not register -- they are already stuck, and running what it tells them
prints "Command ... not found." ...; message was: No stored credentials found. Please
run `os auth login` first.: expected [ 'os auth login' ] to deeply equal []

Verification

All at 12f62c08, the final commit on this branch.

  • pnpm --filter @objectstack/cli typecheckTYPECHECK_EXIT=0
  • pnpm --filter @objectstack/cli exec vitest run src/utils/auth-config.test.ts src/commands/environments/environments.test.ts --maxWorkers=2Test Files 2 passed (2), Tests 77 passed (77). Scoped to the new pin plus the sibling #10967 pin rather than the whole package suite (~500s, past this container's foreground cap); CI runs the package suite in full.
  • Repo-wide eslint . --no-inline-config — exit 0, 5271 files in scope by eslint's own config, 0 errors, 0 warnings.
  • Gate families re-derived from the actual change set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack and all 18 run green at this commit: check:nul-bytes, check:changeset-gate-self-tests, check:objectui-changeset, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check:query-options-erasure, check:type-check-coverage, check:engine-double-contract, check:cross-package-test-inputs, check:where-matcher, check-adr-0087-registration, check-changeset-no-major, check-ci-filter-parity, check-empty-changeset, check-plugin-teardown-shape, docs-audit/check-affected-docs. The three ratchets each printed baseline key set verified against 5a916c4: no files added.
  • Not run locally: check:type-check-debt --re-measure, which refuses without the whole workspace closure built — CI's.

Out of scope, filed not swept

The card's proposed sweep of packages/cli/src/** was run as a read only. 536 documented invocations across 107 non-test sources, 6 unresolved; the two in this file are fixed here, and the other four are recorded in #11465 (finding, unassigned) with their triage — two genuinely stale, two true negatives that any naive gate would flag wrongly — plus the gate options and a recommendation. A gate over guidance strings is its own card with its own grading, so none is added here.

login.ts's AuthLogin class name is left alone: oclif derives ids from file paths, never class names, and the card records it as cosmetic and deliberately out of scope.


Generated by Claude Code

`readAuthConfig()` threw "Please run `os auth login` first." — the one
instruction a user gets when they are already stuck. `os auth login` does
not resolve: `login.ts` sits at the root of `packages/cli/src/commands/`,
so oclif's pattern-strategy loader registers it as `login`, and no `auth`
topic has ever existed. Measured against the built CLI: `auth login --help`
-> `Error: Command auth:login not found.` (exit 2); `login --help` -> exit 0;
the built oclif Config enumerates 61 ids, zero containing `auth`, with
login/logout/register/whoami/dev/serve present as the control.
Also in the same file, `AuthConfig.activeEnvironmentId`'s doc comment said
`os projects switch`; `projects` became `environments` in v5.0 with no
aliases (ADR-0006) and the same enumeration shows no `projects` topic.
The pin asserts the property, not the spelling: every invocation this file
documents must resolve to an id derived from `src/commands/**` via oclif's
own path->id algorithm, checked both on the real thrown message (real ENOENT
branch, redirected $HOME) and across the whole source file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/getting-started/quick-reference.mdx(via AuthConfig (symbol))
What this run could not see
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 8542bd457765444626023db6ffeb6f8105f7f83epackageMentionDocs.

Which tree this was computed on

This run read content/docs from 5fd3d96b00da6944fc93fd155cef1a7c9cb341ad — the merge of head 12f62c089276bff316597e966ea794c2859d4229 into base 8542bd457765444626023db6ffeb6f8105f7f83e, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 5fd3d96b00da6944fc93fd155cef1a7c9cb341ad && git checkout 5fd3d96b00da6944fc93fd155cef1a7c9cb341ad
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8542bd457765444626023db6ffeb6f8105f7f83e 12f62c089276bff316597e966ea794c2859d4229 && git checkout -B drift-repro 8542bd457765444626023db6ffeb6f8105f7f83e && git merge --no-ff 12f62c089276bff316597e966ea794c2859d4229
node scripts/docs-audit/affected-docs.mjs --json 8542bd457765444626023db6ffeb6f8105f7f83e

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 8542bd457765444626023db6ffeb6f8105f7f83e → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 23, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 24, 2026 00:55
@os-zhuang
os-zhuang added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 63da586Aug 24, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11313-auth-login-guidance branch August 24, 2026 01:07
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

packages/cli: auth-config.ts tells a stuck user to run os auth login — that command does not resolve

2 participants

@os-zhuang@claude