Skip to content

docs(server): re-point forge scope-enforcement record to the governed server root (RIG-2679) - #601

Open
rigel-mintaka wants to merge 1 commit into
mainfrom
compass-server/rig-2679-forge-scope-enforcement
Open

docs(server): re-point forge scope-enforcement record to the governed server root (RIG-2679)#601
rigel-mintaka wants to merge 1 commit into
mainfrom
compass-server/rig-2679-forge-scope-enforcement

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

The deferred A8 forge scope control, in two enforcement legs sharing one per-account allowlist (account_forge_scopes).

A1-A4 — forge-API write chokepoint

requireForgeScope in every write arm of ExecuteForgeCallAsAccount — after coordinate resolution, on create arms after the F3 idempotency-memo check, before any provider call — rejecting out-of-scope targets as the in-band ForgeCallError{code:"not_found"}, byte-identical to the 403/404 flatten (no probe oracle). Gated by ForgeConfig.EnforceScopes: off for Dogfood, mandatory for Beta.

A5 / T4-T5 — git clone/push/pull scope (Matt's 2026-08-26 expansion)

Scope the credential, not the git call: mint a GitHub App installation token narrowed via the repositories field to the workstream repo + the account's granted repo set, so an out-of-scope git push/clone fails at GitHub itself — zero enforcement code inside the agent-controlled container. The self-clone invariant keeps the agent's own workstream repo always clonable (else provisioning succeeds but the agent is dead on arrival); zero/insufficient-grant provision fails loud. Both credential surfaces (.git-credentials + gh-CLI hosts.yml) carry the narrowed token from provision (t=0), not only after first refresh. A new T4.5 (proto) task carries the minted token across the Server→Runner process boundary (ProvisionAgentWorkspaceRequest carries no credential today) with a server-driven refresh push keyed by a per-live-container scheduler registry; T5 hardens refresh (margin, backoff, keep-old-token-on-failure, atomic rewrite, both surfaces).

Review history

Prior design-critic + code-review passes on the forge-API core (A1-A4) folded. Git-op leg (A5/T4/T4.5/T5) added; a design-critic red-team folded (self-clone invariant, Server→Runner wire path, loud PAT-fallback); then a three-round code-review loop reached all-clear at the high+medium floor — round 1 (1 high / 3 med / 2 low: SEA-1527 miscitation → OQ-8, hosts.yml refresh surface, refresh-scheduler state, read/write asymmetry, 404/403, owner-alias), round 2 (provision-time hosts.yml gap + co-resident gh-host preservation + projection phrasing), round 3 all-clear. Sub-floor items (same-host token precedence, citation normalization) dispositioned inline.

Open questions — SIX load-bearing, four handed to Matt by name

Corrected: earlier status lines from me named only OQ-1 and OQ-6. Enumerated from the record's own bullet labels — 6 load-bearing / 2 not / 8 total.

  • OQ-1 (load-bearing, deferred to Matt): enforcement default fail-open vs fail-closed.
  • OQ-2 (load-bearing): grant surface for Beta operators. MVP ships the seeded/owner-grant path; the operator-facing surface is unspecified.
  • OQ-3 (load-bearing): grant granularity — grants attach to the account, not to a session or task.
  • OQ-6 (load-bearing, deferred to Matt): git-op mechanism (credential-narrowing rec) + PAT fallback (fine-grained-PAT option (c) + loud-Warn) + docs(product): rework forge agent-notification to webhook-only (RIG-2732) #634 sequencing sub-fork (recommend contingent split).
  • OQ-7 (load-bearing, deferred to Matt): git-op allowlist = write set + implicit workstream read, vs a distinct read superset (carries the clone-narrowing regression against RIG-1527 as first-class).
  • OQ-8 (load-bearing, deferred to Matt): where the workstream-repo input comes from — requires re-introducing a per-agent workstream-repo association RIG-1527 removed (provision/spawn field, agent_accounts column, or store-side spawn-target record); reverses a prior Matt ruling, so it is his call. Blocks T4/T4.5/T5.

Non-load-bearing, recorded not asked: OQ-4 (read arms stay ungated this slice) and OQ-5 (wildcard grammar).

Status: at freeze gate pending six rulings. A record cannot freeze with live load-bearing OQs.

Ledger-impact: none in this PR — the record is Status: Draft and proposes one Comms & tools row that lands at freeze, amending DL-200's inherited no-scope-rejection A8 clause without superseding it. DECISIONS.md is untouched here. Row id assigned at freeze: origin/main at c374bb98 carries 304 rows, max DL-337 (sort -n over a full extraction, not a rendering; POS-CTRL DL-337 present, NEG-CTRL DL-999 absent), which alone licenses next free ≥ DL-338 — DL-338..343 all measured absent on that tree. Folding in-flight mints raises it, but each belongs to a different tree and is stated as such: #900 at ad63e1b4 mints DL-340, and #913 at 15e86552 mints only DL-338/339#913 is stacked on #900 (baseRefName == #900's headRefName; merge-base --is-ancestor YES; the DL-340 row is byte-identical at both heads, md5 9159954a06ba), so its DL-340 is inherited, not double-minted. A stacked PR must be diffed against its base, not trunk, or its parent's rows are attributed to it. and DL-341 reported by #932's owner, which I have not measured — no local ref for that branch. So: ≥ DL-338 on main; ≥ DL-341 on the trees I verified; ≥ DL-342 only if #932's mint holds. Note the ledger is not id-ordered — 22 descents, last positional row DL-323 against a max of DL-337 — so an extremum must come from a full extraction, never from a prefix or a truncated listing.

Refs RIG-2679
Spec-impact: none

@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

RIG-2679

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-server-rig-2679-forg.compass-eng-docs.pages.dev

Deployed from compass-server/rig-2679-forge-scope-enforcement at b8b4cd7.

Changed pages:

rigel-mintaka added a commit that referenced this pull request Aug 25, 2026
…9 B)

Review PR #601 (2 low, advisory on a design record):
- L1: reword the Ledger-impact amend attribution — the A8 no-scope posture
  lives in DL-200's implementing comment (forge.go:16-18) tracing to the
  board 'Resolved decision 2' ruling, not in the DL-200 row text.
- L2: name the exhaustiveness test's residual (a mis-classified write arm)
  and add the T2 signature cross-check (read/unimplemented sets hold only
  no-caller handlers) that closes it structurally, in §A3 and T2.

Ledger-impact: deferred to freeze
rigel-mintaka added a commit that referenced this pull request Aug 26, 2026
…9 B)

Review PR #601 (2 low, advisory on a design record):
- L1: reword the Ledger-impact amend attribution — the A8 no-scope posture
  lives in DL-200's implementing comment (forge.go:16-18) tracing to the
  board 'Resolved decision 2' ruling, not in the DL-200 row text.
- L2: name the exhaustiveness test's residual (a mis-classified write arm)
  and add the T2 signature cross-check (read/unimplemented sets hold only
  no-caller handlers) that closes it structurally, in §A3 and T2.

Ledger-impact: deferred to freeze
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2679-forge-scope-enforcement branch from 20634fb to f2900f5 Compare August 26, 2026 06:16
@rigelbuild-bot rigelbuild-bot changed the title docs(product): design server-side forge-write scope enforcement (RIG-2679 B) docs(product): forge scope enforcement — API writes + git operations (RIG-2679 B) Aug 26, 2026
@rigel-mintaka rigel-mintaka changed the title docs(product): forge scope enforcement — API writes + git operations (RIG-2679 B) docs(product): design server-side forge-write scope enforcement (RIG-2679 B) Aug 26, 2026
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2679-forge-scope-enforcement branch 3 times, most recently from e6b2a3e to 40cd38d Compare August 26, 2026 06:49
@rigelbuild-bot rigelbuild-bot changed the title docs(product): design server-side forge-write scope enforcement (RIG-2679 B) docs(product): forge scope enforcement — API writes + git operations (RIG-2679 B) Aug 26, 2026
@rigel-mintaka
rigel-mintaka marked this pull request as draft August 30, 2026 17:27
@rigel-mintaka

Copy link
Copy Markdown
Contributor Author

Heads up from RIG-2577 T10 (#832, now open): that PR removed the transitional docs/designs/product/ bucket entirely — it's dissolved, the directory is deleted, and product is no longer in the design-ledger-gate's GOVERNED_ROOTS.

This PR adds docs/designs/product/compass-forge-scope-enforcement/design.md, which after a rebase onto current main lands in a deleted, ungoverned bucket. Two consequences:

  1. The record would sit outside GOVERNED_ROOTS, so the design-ledger-gate would not govern its Status: header or ledger-coupling — a silent governance escape.
  2. It's mis-filed against the pre-reorg flat taxonomy anyway: compass-forge-* is a server-domain surface, so post-reorg it belongs under docs/designs/server/.

Before merge, please re-point the record to docs/designs/server/compass-forge-scope-enforcement/design.md (and any DECISIONS.md Record cell / inbound links with it). Rebasing onto main will also surface the removed product/ dir.

rigel-mintaka added a commit that referenced this pull request Sep 1, 2026
…ve-family sweep (RIG-3113)

The final task (T10) of the RIG-2577 design-docs reorg. T8 dissolved `product/` into `agent/`/`server/`/`ui/`/`meta/` but left the native-app family under the transitional `product/` governed root while RIG-2542's client-only direction settled. RIG-2542 is closed (Done, 2026-08-23) and T8/T9 have merged (#825), so this empties and removes `product/`.

## What moved

The 8 remaining records `product/` → `ui/` (all UI/native-shell surface):

- `compass-native-app/`, `compass-native-client-mode/`, `compass-native-client-only/`, `compass-native-packaging/`
- `compass-multi-window/`, `compass-stack-cross-process-teardown/`, `compass-gtk4-migration/`
- `compass-tauri-shell.md` (stays flat at the `ui/` root; the corpus's only supersession pointer)

## Re-points (same PR, per CONTRIBUTING §"Moving a record is not a freeze violation")

- **Ledger:** 18 `DECISIONS.md` Record cells `product/<r>` → `ui/<r>`.
- **Supersession pointer:** `compass-tauri-shell.md`'s `Status: Superseded by compass-native-app/design.md` is already the final same-bucket record-relative form; verified it resolves under the generalized `resolveRecordRelative` (no `../` escape) now that both endpoints sit in `ui/`.
- **Cross-bucket citations to the 8:** `infra/ci/compass-agent-image-publish`, `infra/ci/compass-dogfood-e2e`, `infra/release/compass-release-bundling`, `infra/release/compass-distribution`, `infra/runtime/compass-runner-arbitrary-uid`, plus the two moved records' own self/sibling citations.
- **Code/config:** `go/e2e/client_mode_test.go`, `tools/renovate/config.json5`.

## De-govern + delete

- Removed `"product"` from `GOVERNED_ROOTS` in `tools/design-ledger-gate/index.ts` (now `[ui, agent, server, meta, infra, observability, repo]`) and updated its doc comment.
- Deleted the now-empty `docs/designs/product/` directory.
- Dropped CONTRIBUTING §6's `product/ is transitional` subsection and the "plus the transitional `product/` root" clause; the transitional state is over.
- Updated the gate's own test fixtures (`index.test.ts`) that used `product/` as a governed-bucket stand-in to `ui/` (a de-governed bucket would flip their `touchesRecord(...).toBe(true)` assertions).

## Verification

- `design-ledger-gate`: OK — 284 ledger rows, 120 record headers valid, exit 0.
- Gate test suite: 81 pass / 0 fail.
- eng-docs `gather`: 187 docs + index + sidebar, exit 0.
- Relative `.md` link resolution across all 135 design records: 0 dangling.
- The ~108 remaining `docs/designs/product/...` mentions are pre-existing historical prose in frozen records (old `product/DECISIONS.md` ledger path relocated at T2, deleted `compass-0.x` version records, the old `PRODUCT_DIR` gate constant, and the separate `docs/specs/product/` tree) — none reference a moved record, and rewriting frozen records' decision prose is out of scope.

## Coordination note

Two open draft PRs add new NON-native records into `product/` against the pre-T8 flat taxonomy: #601 (`compass-forge-scope-enforcement` → belongs in `server/`) and #662 (`compass-first-run-tour` → belongs in `ui/`). Both must re-point their record to the correct bucket before merge (mis-filed against the dissolved taxonomy regardless). Flagged on both PRs.

Refs RIG-3113. Refs RIG-2577.
Ledger-impact: 18 Record-cell path re-points (product/ → ui/); no decision rows added, flipped, or removed.
Spec-impact: none.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
mattwilkinsonn added a commit that referenced this pull request Sep 2, 2026
…ve-family sweep (RIG-3113) (#832)

* refactor(design-docs): dissolve transitional product/ into ui/ — native-family sweep (RIG-3113)

The final task (T10) of the RIG-2577 design-docs reorg. T8 dissolved `product/` into `agent/`/`server/`/`ui/`/`meta/` but left the native-app family under the transitional `product/` governed root while RIG-2542's client-only direction settled. RIG-2542 is closed (Done, 2026-08-23) and T8/T9 have merged (#825), so this empties and removes `product/`.

## What moved

The 8 remaining records `product/` → `ui/` (all UI/native-shell surface):

- `compass-native-app/`, `compass-native-client-mode/`, `compass-native-client-only/`, `compass-native-packaging/`
- `compass-multi-window/`, `compass-stack-cross-process-teardown/`, `compass-gtk4-migration/`
- `compass-tauri-shell.md` (stays flat at the `ui/` root; the corpus's only supersession pointer)

## Re-points (same PR, per CONTRIBUTING §"Moving a record is not a freeze violation")

- **Ledger:** 18 `DECISIONS.md` Record cells `product/<r>` → `ui/<r>`.
- **Supersession pointer:** `compass-tauri-shell.md`'s `Status: Superseded by compass-native-app/design.md` is already the final same-bucket record-relative form; verified it resolves under the generalized `resolveRecordRelative` (no `../` escape) now that both endpoints sit in `ui/`.
- **Cross-bucket citations to the 8:** `infra/ci/compass-agent-image-publish`, `infra/ci/compass-dogfood-e2e`, `infra/release/compass-release-bundling`, `infra/release/compass-distribution`, `infra/runtime/compass-runner-arbitrary-uid`, plus the two moved records' own self/sibling citations.
- **Code/config:** `go/e2e/client_mode_test.go`, `tools/renovate/config.json5`.

## De-govern + delete

- Removed `"product"` from `GOVERNED_ROOTS` in `tools/design-ledger-gate/index.ts` (now `[ui, agent, server, meta, infra, observability, repo]`) and updated its doc comment.
- Deleted the now-empty `docs/designs/product/` directory.
- Dropped CONTRIBUTING §6's `product/ is transitional` subsection and the "plus the transitional `product/` root" clause; the transitional state is over.
- Updated the gate's own test fixtures (`index.test.ts`) that used `product/` as a governed-bucket stand-in to `ui/` (a de-governed bucket would flip their `touchesRecord(...).toBe(true)` assertions).

## Verification

- `design-ledger-gate`: OK — 284 ledger rows, 120 record headers valid, exit 0.
- Gate test suite: 81 pass / 0 fail.
- eng-docs `gather`: 187 docs + index + sidebar, exit 0.
- Relative `.md` link resolution across all 135 design records: 0 dangling.
- The ~108 remaining `docs/designs/product/...` mentions are pre-existing historical prose in frozen records (old `product/DECISIONS.md` ledger path relocated at T2, deleted `compass-0.x` version records, the old `PRODUCT_DIR` gate constant, and the separate `docs/specs/product/` tree) — none reference a moved record, and rewriting frozen records' decision prose is out of scope.

## Coordination note

Two open draft PRs add new NON-native records into `product/` against the pre-T8 flat taxonomy: #601 (`compass-forge-scope-enforcement` → belongs in `server/`) and #662 (`compass-first-run-tour` → belongs in `ui/`). Both must re-point their record to the correct bucket before merge (mis-filed against the dissolved taxonomy regardless). Flagged on both PRs.

Refs RIG-3113. Refs RIG-2577.
Ledger-impact: 18 Record-cell path re-points (product/ → ui/); no decision rows added, flipped, or removed.
Spec-impact: none.

Co-authored-by: Matt Wilkinson <matt@rigel.build>

* fix(design-docs): correct two link citations flagged in review (RIG-3113)

Two backtick-code-span path citations left dangling by the product/->ui/ sweep, both on lines the T10 PR edited:

- infra/ci/compass-dogfood-e2e:50 and ui/compass-native-client-mode:744 — the sweep over-reached into out-of-scope old-ledger-path prose and rewrote it to a nonexistent docs/designs/ui/DECISIONS.md (there is no per-bucket ledger; the single ledger is docs/designs/DECISIONS.md). Reverted both to the frozen base value docs/designs/product/DECISIONS.md to match the untouched sibling records.
- infra/release/compass-distribution:75 — a cross-bucket citation to the moved compass-native-client-only record had a wrong relative depth (../../ui/ resolves to infra/ui/, which does not exist). Corrected to ../../../ui/ so it resolves to docs/designs/ui/compass-native-client-only/design.md.

Co-authored-by: Matt Wilkinson <matt@rigel.build>

Refs RIG-3113

---------

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka

Copy link
Copy Markdown
Contributor Author

Update: #832 merged (4efa6faf, 2026-09-02). docs/designs/product/ is now deleted on main and product is out of GOVERNED_ROOTS — so the re-point to docs/designs/server/compass-forge-scope-enforcement/design.md is now required on this branch's next rebase, not optional. Rebasing will surface the removed dir.

@rigel-mintaka rigel-mintaka changed the title docs(product): forge scope enforcement — API writes + git operations (RIG-2679 B) docs(server): re-point forge scope-enforcement record to the governed server root (RIG-2679 B) Sep 6, 2026
… server root (RIG-2679)

PR 832 dissolved docs/designs/product/ and dropped 'product' from
GOVERNED_ROOTS, so this record's path no longer existed and no rename
carried it. Rebuilt on current main at docs/designs/server/, where the
ledger gate governs it.

Moving into a governed root exposed seven retired SEA-1527 references the
gate could not previously see; rewritten to RIG-1527 (already cited in
three records on main).

Three review rounds against the relocated record found defects the move
had carried unexamined:

- Subscribe/Unsubscribe were described as unimplemented. Both are
  implemented, take a caller, and write to the store, so the write-arm
  exhaustiveness test would have classified two real write arms as out of
  scope and left them ungated.
- account_forge_scopes omitted tenant_id, opting an authorization table
  out of row-level tenant isolation that 26 of 30 tables carry.
- All 119 path:line citations replaced with symbol anchors. Most no
  longer resolved to the quoted text; one cited a range 116 lines past a
  file's end. Line citations drift on every edit above them, so a frozen
  record's anchors rot without the record changing.
- The RLS enrollment instruction split the CREATE TABLE and the
  tenant_tables[] entry across two migrations, which is inert on any
  already-applied database. Both halves now land in 0001_init.sql, and
  the pre-dogfood premise that licenses folding is stated rather than
  assumed, with the branch to take if it lapses.

Also: a stale ledger max that resolved to a taken id, a wrong DL
reference, a nonexistent env var, an incorrect proto field list, and a
dangling migration pointer.
The record is Status: Draft and proposes its ledger row rather than
appending it: the id is assigned at freeze, since any max hardcoded here
goes stale the moment another record merges. The record's own Ledger
delta section carries the proposed row and the DL-200 relationship.

Ledger-impact: none in this PR — the record proposes one row (Comms &
tools) that lands at freeze; DECISIONS.md is untouched here.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka rigel-mintaka changed the title docs(server): re-point forge scope-enforcement record to the governed server root (RIG-2679 B) docs(server): re-point forge scope-enforcement record to the governed server root (RIG-2679) Sep 6, 2026
@rigel-mintaka
rigel-mintaka force-pushed the compass-server/rig-2679-forge-scope-enforcement branch from 40cd38d to b8b4cd7 Compare September 6, 2026 17:34
@rigel-mintaka
rigel-mintaka marked this pull request as ready for review September 6, 2026 17:51
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.

1 participant