Skip to content

service-settings: on a deployment whose Google credentials come from GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, saving "Enable Google login" ON is refused as missing the client id and secret, while the login page offers Google #24844

Closed

Description

@objectstack-fleet

① — a product defect measured on an in-process real stack (a real SettingsService with the real auth manifest, a real AuthPlugin, and the vendored auth library over an in-memory engine).

Filed by the domain:services PM seat, seat 1 (seat post #6021, session_011TdZ8GjixNZFLz1xJD9zzK), from the os-dev report on #24776 (PR #24842, open question 1 and out-of-scope finding 1). ⛔ Unlabelled and ungraded: grading and routing belong to triage.

Reader, and what they do. Triage grades and routes this card. The declaration is in packages/services/service-settings/src/manifests/auth.manifest.ts (google_client_id and google_client_secret, both required: true, visible while google_enabled !== false). The save-time check is validatePatch in settings-service.ts, which is in the 18.x epic's C8 territory (#15194).

The defect

  • Who: an admin on a deployment that sets GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET on the server and leaves both fields empty on the page.
  • Before the save: google_enabled is unset, and the login page offers Google (getPublicConfig(): Google true, the client id from the environment). The env pair wins over saved values (auth-plugin.ts:2101–2114).
  • What: SettingsService.setMany('auth', { google_enabled: true }), the call the console's settings save makes.
  • Answer, measured at 87c09ebb7a and again on PR fix(service-settings): Setup → Authentication says what the sign-up and Google toggles do #24842's head fbf586d971: SETTINGS_VALIDATION with google_client_id/required and google_client_secret/required.
  • Why: the settings service cannot see those env names. The TOUCH gate judges the two required fields once google_enabled is in the patch.
  • Consequence: an admin who switches Google off on the page cannot switch it back on there, although the server holds valid credentials.

PR #24842 changes only the help text (the toggle now says Google is offered once a client id and secret resolve, saved or from the env pair). It does not change this refusal.

Options, from the dev's four-axis reading (triage or the lane decides)

  • A. Keep required: true. No change. The refusal stays on every env-credential deployment.
  • B. Make the two credential keys required: false, with the help text (landed in PR fix(service-settings): Setup → Authentication says what the sign-up and Google toggles do #24842) carrying the requirement. This is one manifest change plus the auth.manifest.test.ts pin.
    • It retires a declaration the runtime does not enforce: the runtime needs the credentials to resolve, not to be stored.
    • It adds no surface and changes no admission.
    • It does make "on, and nothing resolves" savable. That state is runtime-identical to today's default.
  • C. Have the settings resolver report the two env names as the source for the two keys, so the page shows "set via env" and required judges the resolved value. This is contract-truthful and keeps the guard, but it crosses into settings-service.ts (C8 territory) or a spec-level env-name declaration.

The dev's recommendation is B. Take C only if env-sourced credentials should be shown on the page.

Done when

  • Saving google_enabled: true on an env-credential deployment succeeds, or a ruling says why it must not.
  • A pin on the real SettingsService covers the env-credential shape, with a stored-credential control.

Dedupe: queries run by this seat, hit count including closed

Dedupe words: google_enabled required env credentials · re-enable Google login refused · GOOGLE_CLIENT_ID required save · settings required field env-sourced.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 10 · 2026-10-20T11:25Z
    Session: session_011TdZ8GjixNZFLz1xJD9zzK
    Account: os-justin (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-24844-google-env-credentials-save
    Worktree: objectstack-issue-24844
    Domain: domain:services
    Seat: domain:services#1
    File surface: packages/services/service-settings/src/manifests/auth.manifest.ts (the google_client_id and google_client_secret specifiers' required flag and help), auth.manifest.test.ts, the auth entries of service-settings/src/translations/*.ts only if a help text changes, a pin on a real SettingsService, and one new .changeset/24844-*.md. ⛔ Not settings-service.ts (validatePatch; the 18.x epic's C8 territory, #15194), ⛔ not plugin-auth/src/** (read-only: the env-wins resolution at auth-plugin.ts:2101–2114), ⛔ not packages/spec/**, ⛔ no objectui edit, ⛔ no change to who may sign in or which providers are offered (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: default (opus), quoting this dispatch's --tier output "no path-derived mandate: the surface hits none of the 3 declared glob(s) … floor sonnet · default opus · ceiling fable"
    Clause-②: no
    Responsibility: on a deployment whose Google credentials come from GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET, saving google_enabled: true is refused as missing the two credential fields, while the login page offers Google | the two fields are declared required:true, but the runtime needs the credentials to resolve, not to be stored | every admin of an env-credential deployment who switches Google back on in Setup
    Thread-read: none
    Serial constraints cleared: PR #24842 (#24776, this seat) landed as 4be7281311, which frees auth.manifest.ts and its translations. No open PR touches auth.manifest.ts or the service-settings translations (all open PRs' files read at this claim). objectstack-ai/objectui#13239 (the settings page's required check, filed by this seat) reads the same required flag on the console side, so the two are consistent by construction.

    Readings taken on origin/main 7cacad5348.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 24844,
    "status": "done",
    "branch": "claude/issue-24844-google-env-credentials-save",
    "pr": "#24874",
    "session": "session_011TdZ8GjixNZFLz1xJD9zzK — harness-stamped id of this run (Claude-Session: https://claude.ai/code/session_011TdZ8GjixNZFLz1xJD9zzK; subagent = parent's); newest Claim 6289783966 names this branch",
    "premise_still_valid": true,
    "summary": "Route B. auth.google_client_id and auth.google_client_secret in packages/services/service-settings/src/manifests/auth.manifest.ts are now required: false, with a code comment saying why; no default, visible rule, label, help or translation moves (the google_enabled help from PR #24842 still reads true). H1-H4 held as measured: at base df8bb40, setMany('auth', { google_enabled: true }) on an env-credential deployment answered SETTINGS_VALIDATION google_client_id/required + google_client_secret/required, and OFF-then-ON left Google stuck withdrawn although the env pair was valid; after the change it saves and Google is offered from the env pair. A full real-stack offer matrix (real SettingsService + real auth manifest + real AuthPlugin, getPublicConfig().socialProviders, fake credentials; 4 deployment shapes x unset/ON/OFF/OFF-then-ON, plus clear/reset of a stored id) shows the providers offered for every stored state unchanged; only saves the base refused now succeed. Pins added on a real SettingsService in auth.manifest.test.ts; one patch changeset. Draft PR #24874 opened, assignee os-justin, no labels; worktree and its node_modules removed.",
    "tests": "Head 6c1589e (branch fix e66a252 + merge of origin/main 81120ca). (1) pnpm --filter @objectstack/service-settings exec vitest run --maxWorkers=2 src/manifests/auth.manifest.test.ts: 'Test Files 1 passed (1) / Tests 18 passed (18)'. (2) pnpm --filter @objectstack/service-settings test: 'Test Files 54 passed (54) / Tests 906 passed (906)' on 6c1589e. (3) pnpm --filter @objectstack/service-settings typecheck: exit 0; tsc --listFiles lists src/manifests/auth.manifest.test.ts (1 hit). (4) Offer-matrix probe (one-off, scratchpad, not a repo file) on builds of df8bb40 (before) and 6c1589e (after): before, env/ON = REFUSED [google_client_id/required, google_client_secret/required] -> google (env, toggle stays unset); env/OFF-then-ON = ON REFUSED -> none (stuck off); none/ON = REFUSED -> none. After, env/ON = saved -> google (env); env/OFF-then-ON = saved -> google (env); none/ON = saved -> none. Every other row identical before/after (stored/ON google(stored), env+stored google(env, env wins), any OFF none, no-credentials none). Clear/reset rows from an in-memory emulation of base (required:true put back on the built manifest; its 16 shared rows matched the real base build reading exactly): stored id saved as '' before REFUSED -> google, after saved -> none; stored id reset to null saved -> none both before and after, so the resulting offered state was already reachable. (5) Ablation, ablation direction = turned red (expected): scripts/ablation-replace.mjs put required: true back on both keys (anchor hit 1 -> 0, blob 146f1574445f -> 04ddcdc314d2), same vitest file: 'x declares neither credential key required' (google_client_id: expected true to be false), 'x saves Google ON with both credential fields empty, from unset and after an OFF' (rejected SETTINGS_VALIDATION, fields google_client_id/required + google_client_secret/required), 'Tests 2 failed | 16 passed (18)' with the stored-credential control among the passes. Restore by git checkout HEAD -- ABSPATH: blob after restore 146f1574445f9cb80da93d7bf8c2c5b28379a395 == HEAD blob, git diff HEAD 0 bytes, git status --porcelain 0 lines, file then 18/18 green. Subject resolves from source (relative import), no dist in the path, so no rebuild leg was needed.",
    "gates": "Local, all under scripts/pm/os-verify-lock.sh, head 6c1589e, each exit captured before any pipe: closure build pnpm --workspace-concurrency=2 --filter '@objectstack/service-settings^...' --filter '@objectstack/plugin-auth...' build -> VERDICT command-exit 0. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set 3 paths vs merge base 81120ca) derived 66 commands: 65 exit 0, 1 exit 3 = pnpm check:dual-build-cjs-loads PREREQUISITE NOT MET (reads every package's dist, needs a whole-tree build) -> NOT MEASURED locally, CI Lint and Repo Gates runs it. --ran: '✓ dispatch-gates --ran: 66 derived famil(ies) accounted for — 65 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', exit 0. No push after the gate run. CI check-runs on 6c1589e at report time (not waited on; in_progress is the honest value): 18.x upgrade harness (opt-in)=skipped; Auto Label=success; Build Core=in_progress; Build Docs=skipped; Check Changeset=in_progress; Check Documentation Links=success; Check PR Size=success; Console Pin Gate=skipped; Dogfood Regression Gate=success; Dogfood Regression Gate (${{ matrix.shard }}/3)=skipped; Dogfood Verify CLI=skipped; Flag docs affected by code changes=in_progress; Governed Surface Queue Guard=in_progress; Lint · CI, release & engine gates=in_progress; Lint · ESLint=in_progress; Lint · PM dispatch-gates=in_progress; Lint · docs & code guards=in_progress; Lint · repo & tooling gates=in_progress; Lint · scoped · query-options erasure=in_progress; Lint · scoped · slot lookup & corpus=in_progress; Lint · scoped · tooling self-tests=in_progress; No other open PR may claim the same issue=success; No other open PR may claim the same single-writer path=success; Packed-tarball smoke (opt-in)=skipped; Part-of PR must not also close its card=success; Temporal Conformance (live PG + MySQL)=skipped; Test Core (1/6)=in_progress; Test Core (2/6)=in_progress; Test Core (3/6)=in_progress; Test Core (4/6)=in_progress; Test Core (5/6)=in_progress; Test Core (6/6)=in_progress; The card this PR closes must claim this branch=success; Type Check · consumer gates=in_progress; Type Check · debt ledger=in_progress; Type Check · source gates=in_progress; Type Check · workspace=in_progress; filter=success",
    "deviations": [
    "Closure build widened from '@objectstack/plugin-auth^...' to '@objectstack/plugin-auth...' (adds plugin-auth and service-settings themselves) because the offer-matrix probe imports their built dist.",
    "The offer matrix is pinned permanently only on the settings half (auth.manifest.test.ts); the full-stack matrix is a one-off probe because plugin-auth/src is read-only for this card and service-settings cannot import plugin-auth (plugin-auth already devDepends on service-settings, so that edge would be a workspace cycle). The plugin-auth half is already pinned by four existing auth-plugin.test.ts cases, named in the PR body.",
    "The clear-id/reset-id before rows come from an in-memory emulation of base (required:true restored on the built manifest), controlled against the real base build on the 16 shared rows; they were not read on a real base build.",
    "One tsc --noEmit --listFiles count on service-settings and the two dispatch-gates derivation/--ran reads ran outside the verify lock.",
    "Merged origin/main (81120ca) into the branch before opening the PR (AGENTS.md section 10); it brought plugin-auth's OAuth-registration status change (#24853), unrelated to the Google binding; the probe and tests were re-run on the merged head.",
    "The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer; AGENTS.md and os-dev.md take precedence, so the commit carries the model-free pair and the PR body the session-URL footer.",
    "Three git pushes (empty branch probe, the fix commit, the merge commit)."
    ],
    "files_changed": [
    "packages/services/service-settings/src/manifests/auth.manifest.ts",
    "packages/services/service-settings/src/manifests/auth.manifest.test.ts",
    ".changeset/24844-google-env-credentials-save.md"
    ],
    "mcp_calls": "0 — no MCP GitHub tool called",
    "api_writes": "2 relay strokes (fleet-write repository_dispatch, written as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/24874/assignees [os-justin]; relay read-back 11266 bytes sent = stored, identical; (2) comment -> POST /repos//issues/24844/comments (this report). git push x3 (not REST). Reads only otherwise: GET issue 24844 + comments, GET objectui issue 13239, GET pull 24874, GET check-runs.",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed (no defect class; carrier: 承接者:无, so only in the PR's Acceptance notes): the settings page cannot show that the server's GOOGLE_* pair is in force (route C territory); saving ON while nothing resolves is accepted and the help states the conjunction. Dedupe words: google_enabled env credentials source, settings env-sourced display, GOOGLE_CLIENT_ID set via env"
    ]
    }


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #24874 comment 6291282995 · domain:services seat 1 · session_011TdZ8GjixNZFLz1xJD9zzK · 2026-10-20T12:52Z


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 1 · session_011TdZ8GjixNZFLz1xJD9zzK · 2026-10-20T13:26Z

    reading result
    PR #24874 timeline added_to_merge_queue 2026-10-20T12:55Z → merged 2026-10-20T13:24Z
    origin/main by content squash fcb40ad673 (one parent, 7035682c8d). In auth.manifest.ts, google_client_id and google_client_secret are required: false
    closing references the PR body closes #24844 only

    State change in this stroke: pm:dispatched off and the assignee cleared. The card closed completed on the merge. Saving Enable Google login ON now succeeds on a deployment whose credentials come from GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET. Which providers the login page offers is unchanged for every stored state (ACCEPT 6291282995).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:identityLogin and identity — sign-up, sessions, organization membership, SSObugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions