SysEngineering

cos_lab: handoff

Current state and the next steps, so work continues on either machine. Update State and Next and append to Log at the end of every session (through a PR, like any change in this repo). Also comment on ITPROJ-1421, in first person, when a step lands.

Resuming on another machine

PR #193 (the move) is merged; the connector is not on main until its PR merges. Work from the connector branch, or from feat/cos-lab-updates, stacked on it, which holds everything:

cd ~/sysengscripts                       # or C:\Users\adams\Code\sysengscripts
git fetch origin
git switch feat/cos-lab-connector        # the first time, this creates it from origin
git pull
cd integrations/scsp_ai_initiative/cos_lab
python3.13 -m venv .venv                 # Windows: py -3.13 -m venv .venv  (CI uses 3.13)
# Windows: .venv\Scripts\python   ·   macOS: .venv/bin/python
<venv python> -m pip install -r requirements.txt
<venv python> -m pytest -q -p no:cacheprovider     # 164 pass
gcloud auth login && gcloud auth application-default login

Once the connector and the updates are merged, use git switch main && git pull instead.

Start Claude Code from this folder (claude): it loads this folder’s CLAUDE.md and sysengscripts’ root CLAUDE.md. SCSP context is one level up (../HANDOFF.md). Sessions and Claude’s memory don’t move between machines, so end each one by updating this file and start the next with “read HANDOFF.md”.

Branches and PRs (2026-10-16)

What Where State
The move into SysEngineering PR #193 Merged 2026-10-16 (8da5cad). Its reviewer’s four points are fixed on the connector branch.
The connector (Session B) branch feat/cos-lab-connector, rebased onto main In its own PR, so the reviewer sees only the connector. Adam reviews and merges.
The update loop (Vesa’s design) branch feat/cos-lab-updates, stacked on the connector Its PR opens once the connector merges.

The connector branch’s commits, in order: 099fc22 (the first draft, exactly as the first review saw it), then 9fbbea7, 9d634ee and 81f5a72 (the fixes from review rounds one, two and three), then docs. The commit messages say what each round changed and why.

The order from here:

  1. Adam reviews and merges the connector PR (the repo squash-merges).
  2. Rebase the updates branch onto the new main, dropping the connector’s commits, push it with --force-with-lease, and open its PR (the updates branch’s HANDOFF has the exact command).
  3. Before the last PR merges, drop the “SCSP prototype” entry from the root CLAUDE.md’s “In flight on feature branches”, and this section’s branch table. Then delete the branches, comment on ITPROJ-1421 (first person), and do the clean-up in “Adam’s tasks”.
  4. Session B’s live run (“Run it locally”) does not wait for any of this.

Decisions made 2026-10-15 (change any of them on purpose)

State (2026-10-15)

Step 2, in four sessions

Session What State
A Google sign-in setup (console) Done 2026-10-15
B The connector on Adam’s machine (localhost:8080): Gwen’s OAuth + Google sign-in, a whoami tool, record of use; Claude Code connects to it Code done; live run next
C Deploy to Cloud Run: runtime service account, secrets, a warm instance  
D GitHub tools on the deployed connector (needs the token below)  

Session A: what was configured (keep; it’s also the SCSP reference)

Run it locally (Session B’s live run)

  1. The signing key: done 2026-10-15. Secret cos-mcp-signing-key, version 1, in adam-cos-lab, made with the generator and checked to decode; created as aschembri@schmidtentity.com, the gcloud account to use for this project. It was made with (from this folder):
    <venv python> -m lab.connector.signer | gcloud secrets create cos-mcp-signing-key --project adam-cos-lab --replication-policy=automatic --data-file=-
    

    Nothing is printed or saved on disk. To rotate it later, pipe it the same way into gcloud secrets versions add cos-mcp-signing-key --project adam-cos-lab --data-file=-; that signs everyone out. The connector reads it with your application-default login, so run gcloud auth application-default login (as that account) on each machine first.

  2. Start the connector from this folder: <venv python> -m lab.connector. It reads both secrets from Secret Manager with your gcloud login and listens on this machine only. With op run --env-file=secrets.env.tpl -- in front, the Jira and Google tools are offered too.
  3. Connect Claude Code: claude mcp add --transport http adam-cos-local http://localhost:8080/mcp, then /mcp in a Claude Code session to sign in. Expect our consent page, then Google (the “unverified app” warning, Testing mode), then back to Claude Code.
  4. Check: ask for whoami. The terminal running the connector shows the record of use: client_registered, sign_in_started, consent_given, sign_in_ok, session_started, tool_call.

Settings (all optional): COS_ALLOWED_EMAILS (default: the owner alone; add aschembri@schmidtentity.com to try the second identity, which gets whoami only), LAB_OWNER_EMAIL, COS_PUBLIC_URL. Restarting the connector signs everyone out (by design), so Claude Code asks to sign in again.

Not yet proven against the real thing, so watch for it on the first run: Google accepting PKCE alongside the Web client’s secret, and the hd claim for hillspire.com accounts. A refusal shows up as sign_in_failed in the connector’s log with a reason and the facts to tell causes apart: google_refused_code with Google’s error code (invalid_grant, redirect_uri_mismatch, invalid_client…), id_token_invalid with which check failed (issued_in_the_future means this machine’s clock is behind), not_a_workspace_account with the hd Google sent and the email’s domain. The consent page is proven in real Chrome and Edge, over http and https, but not yet in Safari or Firefox.

Deferred to Session C (deploy), decided then

Before step 7 (claude.ai)

Claude’s hosted apps share one callback, https://claude.ai/api/mcp/auth_callback, and the connector refuses it today (redirect_uri_allowed in oauth.py). Someone could start a sign-in in their own claude.ai account and forward the link; our consent page would look exactly like the person’s own, and whether claude.ai then finishes the flow in the forwarder’s account is unproven. Before allowing it:

  1. With two claude.ai accounts, start the flow in one and finish it in the other’s browser. It must fail.
  2. Add hosted wording to the consent page (“only if you clicked Connect in your own Claude just now”), and the hosted origins to /mcp’s allowed origins if Claude’s servers send one.
  3. Write down what is left in this folder’s CLAUDE.md for SCSP, which should look at Enterprise Managed Auth (claude.com/docs/connectors/building/enterprise-managed-auth).

The calendar’s time zone is cached per process (lab/tools/google.py); step 3, which reads each person’s own calendar, must key it per person.

Adam’s tasks

  1. Merge PR #193. Done 2026-10-16.
  2. Delete the Hillspire/adam-cos-lab GitHub repo (unblocked: #193 is merged). Everything in it is in this folder. Use github.com → the repo → Settings → Danger Zone → Delete, or run gh auth refresh -h github.com -s delete_repo so Claude can run gh repo delete Hillspire/adam-cos-lab.
  3. On the MacBook, delete ~/adam-cos-lab, the old clone.
  4. After the connector PR merges, delete C:\Users\adams\Code\adam-cos-lab on the Windows PC. Its only extra was the uncommitted connector draft, now in this folder.
  5. For Session D, the read-only GitHub token (below).

The separate Anthropic key and service account planned for the old repo’s reviewer are not needed: this repo’s own Claude reviewer covers this folder.

A read-only GitHub token for the deployed connector

Locally the lab borrows gh auth token, but Cloud Run has no gh login. Create a fine-grained personal access token at github.com → Settings → Developer settings:

Hillspire may need to approve it. Store it: gcloud secrets create cos-github-token --project adam-cos-lab --replication-policy=automatic --data-file=- (paste the token, press Enter, then Ctrl-Z Enter on Windows or Ctrl-D on macOS).

Next

How the connector fits together: lab/connector/__init__.py maps its modules, and oauth.py’s docstring walks the sign-in journey step by step. The rules it must keep are in this folder’s CLAUDE.md, each pinned by a test.

Log