Skip to content

promote: staging → main (2026-10-18) — 576cbf189 - #2535

Closed
fedcourtsai-dev[bot] wants to merge 6 commits into
mainfrom
promote/2026-10-18
Closed

fedcourtsai-dev[bot] wants to merge 6 commits into
mainfrom
promote/2026-10-18

Conversation

@fedcourtsai-dev

Copy link
Copy Markdown
Contributor

Drafted by the promotion train at 2026-10-18T13:31Z for promote/2026-10-18 at 576cbf189 over main 06c9aaf26. The branch was cut from staging and never moves, so the integration evidence and an approval of this head stay valid while staging carries on. The train regenerates the block between the markers while this PR is open; write anything else outside it.

Gate status (promote/2026-10-18 at 576cbf189):

  • not read this run

Train: cut promote/2026-10-18 at staging 576cbf1 and opened its draft promotion PR

Next (maintainer): read the batch and approve this PR at 576cbf189. The train then merges it with a merge commit once the integration gate run, the required checks and the registry gate hold at this head and no predict/evaluate/back-test round is in flight. Merging it yourself instead is fine. To stop it, request changes or close it: a closed candidate is not re-cut at the same staging tip, and the next one is cut once staging moves.

The batch

Effect checks

#2522: fix(security): bind the production read-write role's jobs to a corpus-write environment

Once promoted, and with step 1 done:

  • A writer run's jobs show environment corpus-write. The run page shows a corpus-write deployment per writer job, and this lists fresh deployments dated after the promotion:

    gh api 'repos/ModelMirrorAI/fedcourtsai/deployments?environment=corpus-write&per_page=5' \
      --jq '.[] | [.created_at, .ref, .sha] | @tsv'
    

    The matching query for environment=prod no longer gains entries from run-pull pull/live windows.

  • The window succeeds: its pointer commit lands on main.

  • scripts/promotion-gate.sh contexts (maintainer's admin token, from main once promoted) passes, including the new "the corpus-write environment admits main alone and holds only the writers' names" line.

  • After step 4, an executed check: a prod-bound job that tries the RW role is refused. Nothing in the repo can show this, so it is the maintainer's IAM reading of the RW trust (sub names only environment:corpus-write).

After the merge

  1. Tag promotion/<YYYY-MM-DD> on the merge commit (-2 for a same-day second batch).
  2. Run the effect checks above and record what they printed.

claude and others added 6 commits October 17, 2026 16:21
…-write environment

The agent cell jobs bind `prod` with `id-token: write`, and the production
read-write corpus role's OIDC trust names `environment:prod`, so a
prompt-injected cell could mint a token that role accepts. Move exactly the
jobs that assume the read-write role (run-pull pull/live/enrich, run-seed
seed/ocr-sweep, run-repair repair/applications/decision-record/opinion-record)
to a dedicated `corpus-write` environment, so the role's trust can name that
environment alone.

- tests/test_workflow_corpus_write.py: every job naming the read-write role
  binds the literal `corpus-write`, no other job binds it, no agent job binds
  it, and the gate's admitted names equal what those jobs read.
- environment_checks + assert-corpus-write-environment + promotion-gate.sh
  contexts stage: the environment exists, admits `main` alone, and holds
  nothing outside the writers' names.
- docs/security.md, SECURITY.md, docs/data-pipeline.md, docs/pipeline.md,
  docs/cli.md and the workflow comments: the new trust design, and the Claude
  GitHub App's OIDC exchange recorded as an accepted residual.

Part of #2511: the repo half. The environment and IAM steps are the maintainer's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126DwPpqinYddgnRmh7Ljfn
Review round on the corpus-write environment change.

- Docs and comments that stated the read-write role's narrowed trust as fact
  now tie it to the maintainer's IAM step the gate cannot read (SECURITY.md,
  docs/security.md, docs/data-pipeline.md, run-predict, run-evaluate,
  run-pull, promotion-gate.sh, environment_checks).
- The gate requires the writers' names to be present, not only nothing extra
  (every secret and variable but the optional CourtListener rate caps), and a
  listing whose total_count disagrees with its items is a truncated page and
  reads as unread, for every environment the stage checks.
- docs/security.md: the Claude App residual names the full reach of a default
  installation token (unreviewed staging merges, gate-read labels, PR bodies,
  unprotected branches, public posts, Releases) and leaves its actual grant to
  the maintainer; corpus-write removes agents, not hostile parsers, from the
  write role's reach; the duplicated names rotate in both environments.
- tests: the agent detector moves to tests/agent_detector.py, code-owned with
  test_workflow_token_holder.py; the corpus-write invariant follows local
  composites, reads indexed secret/variable spellings, and pins the
  ref-name fall-through to the three branch-resolving workflows.

Part of #2511.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126DwPpqinYddgnRmh7Ljfn
…step everywhere; make copying the rate caps explicit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126DwPpqinYddgnRmh7Ljfn
chore: sync main into staging
…ironment

fix(security): bind the production read-write role's jobs to a corpus-write environment
@modelmirror
modelmirror deleted the promote/2026-10-18 branch October 18, 2026 23:07
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