Uh oh!
There was an error while loading. Please reload this page.
fix(cli): auth-config guidance names a command that resolves - #11466
Conversation
`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
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 23 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # 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
|
Uh oh!
There was an error while loading. Please reload this page.
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 saidos 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-examplesdocs nit.The premise, re-derived rather than transcribed
login.tssits at the root ofpackages/cli/src/commands/, so oclif's pattern-strategy loader registers it aslogin; noauthtopic has ever existed. Measured here against the built CLI (packages/cli/bin/run.js, after building the package and its dependency closure):Loading the built oclif
Configenumerates 61 registered ids (the card measured 60 at1fa05a69e2; one command has landed since), zero containingauth, and noauthtopic — withlogin,logout,register,whoami,dev,serveall present as the control that the zero is a real absence rather than a broken probe.The message now says
os login, the spellinglogin.ts's ownexamplesalready used.One further in-file fix, named with its evidence
AuthConfig.activeEnvironmentId's doc comment, four lines up in the same file, saidos projects switch.projectsbecameenvironmentsin v5.0 with no aliases (ADR-0006); the same enumeration shows noprojectstopic and no id containingproject, whileenvironments switchis 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 withenvironments/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 textos loginwould go green forever and catch nothing — it would still pass the day someone renameslogin.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 fromsrc/commands/**via oclif's own path→id algorithm (topicSeparatorread from this package's manifest, not hardcoded). Its output was checked against the builtConfigwhile writing the pin: same ids.Two legs, deliberately:
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 assertsgetCredentialsPath()really points inside the temp home, so it cannot silently degrade into a verdict about the developer's own machine.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#10967pin readsstatic override examplesvia 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
traponEXIT 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.os login→os auth loginos environments switch→os projects switchLeg A's message, verbatim:
Verification
All at
12f62c08, the final commit on this branch.pnpm --filter @objectstack/cli typecheck—TYPECHECK_EXIT=0pnpm --filter @objectstack/cli exec vitest run src/utils/auth-config.test.ts src/commands/environments/environments.test.ts --maxWorkers=2—Test Files 2 passed (2),Tests 77 passed (77). Scoped to the new pin plus the sibling#10967pin rather than the whole package suite (~500s, past this container's foreground cap); CI runs the package suite in full.eslint . --no-inline-config— exit 0, 5271 files in scope by eslint's own config, 0 errors, 0 warnings.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackand 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 printedbaseline key set verified against 5a916c4: no files added.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'sAuthLoginclass 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