Skip to content

Tools: the gh sign-in sets the person's Git identity where git has none (F19 addendum 2026-10-14) - #416

Merged
immakermatty merged 4 commits into
mainfrom
agent/git-identity-gh-onboarding
Oct 14, 2026
Merged

immakermatty merged 4 commits into
mainfrom
agent/git-identity-gh-onboarding

Conversation

@immakermatty

@immakermatty immakermatty commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Plan: DEV-6664 (task task-2026-10-14-727), HumanAndMachine-ai Mission Control.

Status: ready for review

Why

On 2026-10-14 a person's assigned Remote Environment had no user.name / user.email. Publishing data from a repository-db app (git commit inside its data checkout) failed after staging with "Author identity unknown". Laptops work because their owners configured an identity long ago.

Until now the Platform deliberately wrote no Git configuration (F9's addendum of 2026-10-14, #377 / #412): Lazurio's own commits got the person's GitHub identity per commit (git -c), and every other commit in the Environment had nothing. Matěj decided: "Git Identity by měl řešit Launchpad při nastavování GH … v rámci onboardingu, kdy lidem nastavujeme nástroj GH, aby mohli fungovat ve svém environmentu, by se měla nastavit i ta identita."

Target state

After a person signs in gh through Lazurio, every commit in their Environment has an author, with no per-app workaround.

What changes

  • The curated gh session (src/tools/login.ts, new src/tools/git-identity.ts). After the SSH key step, whatever its outcome, and before the session ends signed in, Lazurio checks each of user.name and user.email. A field that neither git's global nor system configuration names (includes followed) is written with git config --global -- <key> <value>. The value is the person's GitHub identity from gh api user, exactly as githubCommitIdentity builds it: the profile's name, else the login, and <id>+<login>@users.noreply.github.com. This is the one path behind Settings → Nástroje, the Environment's first run (which leads there), lazurio tools login gh and --ssh-key.
  • Never overwrite. A field git already has is left alone. When both are present, GitHub is not even asked, so a workstation's identity stays as it is. Because gh api user runs between the first check and the writes, each field is checked again (global, then system) right before its own write. A field set meanwhile is skipped; a check that cannot answer writes nothing more (fail closed).
  • Never on a Team Environment. The gh sign-in is already refused there (F19's addendum of 2026-09-28), so this step never runs. The session also asks the Team rule again right before each field's write and ends blocked before the next one. A field written before the Environment became a Team one stays, like an SSH key linked before.
  • Clear status, not fatal. The signed-in state carries gitIdentity:
    • present;
    • set: fields names exactly what was written (never empty). If a later field was not written, incomplete: { reason: write-failed | config-unreadable, missing } says so;
    • not-set (this step wrote nothing) with git-missing, config-unreadable, viewer-unavailable (GitHub silent, or it returned another account than the one gh confirmed) or write-failed.
    • Recovery in every case: sign in again (it adds only what is still missing), or git config --global by hand.
    • The CLI prints what was set and, for the rest, the reason and the fix.
    • The Launchpad says aloud (cs/en) when publishing from the Environment would fail (a partial write included), and shows the set identity in the folded details.
    • The exit code still follows the SSH key alone.
  • Agent fallback text. The gh catalog target state (activation.installation, also the "Set up with an agent" prompt) now includes the same identity rule and its proof.
  • Docs. New F19 addendum of 2026-10-14 in docs/decisions.md (decision, alternatives, tests, risks). F9's addendum point 4 is marked as refined. content-sync.md, environment-tools.md and the commit-identity.ts / module-create-run.ts comments match.

What deliberately does not change

  • identitySettings (the per-commit git -c fallback for Lazurio's own commits: Personalspace bootstrap, gbrain, module create) stays. It still covers Environments whose gh was signed in by hand or before this change, and those where the write failed. It never overrides git and writes nothing, so keeping it costs nothing. Removing it would bring back git-identity-missing on exactly those Environments.
  • Existing Environments are not touched. They are fixed separately with the same rule; this PR covers new sign-ins.
  • No Machines/provisioning change, no Doctor check, no rollout.

Verification

  • bun run check (lint, typecheck, check:public, build:proof, smoke:proof, full bun test) after the review fixes: 3542 pass, 0 fail (3550 tests across 252 files), exit 0. After the rebase onto 64978cb (integrations only): check:static exit 0 and the affected suites 151 pass, 0 fail; CI runs the full suite on the head.
  • New tests/tools-git-identity.test.ts runs the real login sessions (the consumer of the Launchpad server and the CLI). gh and ssh are fakes; git is the system's own, confined to a temporary home with its own system config. Cases covered:
    • missing → both fields set from gh api user;
    • no profile name → login as GitHub spells it;
    • present → file byte-identical and GitHub not asked;
    • only the name present → only the email added;
    • system config or an [include]d file counts as present;
    • GitHub silent or another account → nothing written;
    • a refused write → write-failed, still signed in;
    • Team rule turning true right before the write → blocked, nothing written;
    • Team Environment → git never run;
    • review regressions: the Team rule turning true between user.name and user.email; a value filled while GitHub answers (kept, reported as git's own); a malformed configuration appearing meanwhile (fail closed); the second write refused after the first (set + incomplete, completed by the next sign-in).
  • tests/tools-curated-cli.test.ts: the CLI lines (not set, set, then silent once present). tests/tools-view.test.ts: strict parsing and the people-facing sentence.

Risks and open points

  • Not run on Windows here. The curated session runs there too, and git config --global with HOME set to the operator's home is the same on Git for Windows, but this is untested on that platform.
  • A person who wants a different address than GitHub's noreply one changes it themselves, as on a workstation.
  • The noreply address only links commits to the account on GitHub. It does not reveal the person's private email.

🤖 Generated with Claude Code

@immakermatty

Copy link
Copy Markdown
Contributor Author

CI note: tests (1/2) failed on the first run in tests/launchpad-prompts.test.ts ("hosted personal: new-personal-module …", expect(own.calls).toEqual([])). That is the known race #413: the Launchpad's start peeks the Personalspace holder in the background. It does not touch the gh sign-in path. It passes locally (full bun run check on this head: 3538 pass, 0 fail) and alone 3/3. The failed job was re-run.

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested on 030bafe. The focused local suite passes (38/38), and CI is green, but two promised safety invariants are not enforced:

  1. src/tools/git-identity.ts:136-157: proceed() checks the Team rule only once before the loop. If the Environment changes to Team after writing user.name but before writing user.email, the second write still runs; the final mayProceed blocks only after both writes. Please re-check the Team rule before EACH write and test the transition between the two fields. A deterministic runner probe produced state: set, two writes, and only one Team check despite the rule becoming true after the first write.
  2. src/tools/git-identity.ts:113-156: the presence check and later git config --global write are separated by the GitHub API call. If the operator or another process fills a missing value during that interval, this step overwrites it. The same deterministic probe filled user.email with chosen@example.com in that interval; the returned state was set and the chosen value was replaced. This violates the explicit never-overwrite contract. Revalidate immediately before each write and make the read/write coordination fail closed where possible; cover the interleaving in a regression test.

Documentation also says no fields are written in any not-set case (docs/decisions.md F19 addendum point 5; docs/environment-tools.md), but a failure on the second write leaves the first field written and reports not-set/write-failed. The probe reproduced this. Either ensure an all-or-nothing outcome or document the actual partial-write behavior and recovery accurately.

The existing tests cover Team transition before the first write and existing configuration before the initial read; they do not exercise these interleavings. Windows and device sign-in remain unverified as disclosed.

immakermatty and others added 4 commits October 14, 2026 19:00
A Remote Environment had no user.name or user.email, so the first commit
that Lazurio does not make itself (a repository-db data publish, an
agent's commit) failed with "Author identity unknown". Matěj decided on
2026-10-14 that the gh onboarding sets it.

The curated gh session (Settings → Nástroje, the Environment's first run,
lazurio tools login gh, and --ssh-key) now ends, after the SSH key step
and whatever its outcome, by writing each field that neither git's global
nor system configuration names with git config --global: the GitHub
profile's name, else the login, and <id>+<login>@users.noreply.github.com
from gh api user, exactly githubCommitIdentity. An existing value is never
changed. A Team Environment never reaches the step (its gh sign-in is
refused) and the session asks the Team rule again right before the write.
The outcome is gitIdentity on the signed-in state (present, set, or
not-set with git-missing, config-unreadable, viewer-unavailable,
write-failed); the CLI prints it with the fix, the Launchpad says aloud
when publishing would fail. The exit code still follows the SSH key.

Lazurio's own per-commit fallback (identitySettings) stays for an
Environment whose gh was signed in otherwise or before this change.

Tests: tests/tools-git-identity.test.ts runs the login sessions with the
system's git under a temporary home (missing, present, partial, system and
included configuration, GitHub silent or another account, a refused write,
the Team rule before the write, a Team Environment); the CLI's lines and
the Launchpad's parsing and sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… sign-in

The decision log gets the addendum (Matěj's words, where, what, never
overwrite, never on a Team Environment, failures, why the per-commit
fallback stays, the alternatives and the tests). F9's addendum of the same
day, which said a Remote Environment has no identity and nothing is ever
configured, is marked as refined by it; content-sync.md and
environment-tools.md say the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a partial write

Review of #416. Three gaps in the gh sign-in's Git identity step:

- The Team rule was asked once before the writes. It is now asked right
  before each field's write, so an Environment that becomes a Team one
  between user.name and user.email stops before the email.
- gh api user runs between the presence check and the writes. Each field
  is now checked again (global, then system, includes followed) right
  before its own write: one set meanwhile is skipped, never replaced, and
  a check that cannot answer writes nothing more (fail closed).
- A second write that failed after the first succeeded was reported as
  not-set. `set` now names exactly the fields written, with `incomplete`
  (`write-failed` or `config-unreadable`, and what git still lacks) when a
  later one was not; `not-set` means the step wrote nothing. The CLI says
  what was set and what not, and the Launchpad treats a partial write as
  not set for people and claims no name it did not write.

Tests: regression tests for each (the Team rule turning true after
user.name, a value filled while GitHub answers, a malformed configuration
appearing meanwhile, the second write refused and completed by the next
sign-in), and the parser and CLI lines for the new shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…partial writes

Points 3 to 5 of the addendum and environment-tools.md now say that the
Team rule and the field's presence are checked right before each write,
that a field written before an Environment became a Team one stays, what
`set` with `incomplete` and `not-set` mean exactly, and how to recover (a
new sign-in adds only what is missing, or git config by hand).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty
immakermatty force-pushed the agent/git-identity-gh-onboarding branch from 030bafe to 7643d9b Compare October 14, 2026 17:01

@agentrozjedemeai agentrozjedemeai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved on exact head 7643d9b. The three prior blockers are addressed: Team eligibility is rechecked before each field write; a fresh global/system lookup avoids replacing a field populated during the GitHub call and stops on unreadable config; a second-field failure reports the first write as set+incomplete, with CLI/UI and documentation reflecting recovery. I inspected the caller and consumers, ran 42 focused tests (0 failures) and check:static locally, and confirmed the exact-head CI rollup is green with no open review threads. Windows and real device sign-in are not locally qualified here.

@immakermatty
immakermatty merged commit 13f5448 into main Oct 14, 2026
7 checks passed
immakermatty added a commit that referenced this pull request Oct 14, 2026
…a partial write

Review of #416. Three gaps in the gh sign-in's Git identity step:

- The Team rule was asked once before the writes. It is now asked right
  before each field's write, so an Environment that becomes a Team one
  between user.name and user.email stops before the email.
- gh api user runs between the presence check and the writes. Each field
  is now checked again (global, then system, includes followed) right
  before its own write: one set meanwhile is skipped, never replaced, and
  a check that cannot answer writes nothing more (fail closed).
- A second write that failed after the first succeeded was reported as
  not-set. `set` now names exactly the fields written, with `incomplete`
  (`write-failed` or `config-unreadable`, and what git still lacks) when a
  later one was not; `not-set` means the step wrote nothing. The CLI says
  what was set and what not, and the Launchpad treats a partial write as
  not set for people and claims no name it did not write.

Tests: regression tests for each (the Team rule turning true after
user.name, a value filled while GitHub answers, a malformed configuration
appearing meanwhile, the second write refused and completed by the next
sign-in), and the parser and CLI lines for the new shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@immakermatty
immakermatty deleted the agent/git-identity-gh-onboarding branch October 14, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants