feat(org): project remote set and checked, repository editable on Settings (#1873) #1948

Merged
jason.woltje merged 5 commits from project-remote into refactor 2026-11-12 19:51:26 +00:00
Owner

Dev readiness P1-6 and P1-7 (#1873): a project's remote is set and checked, and the Repository URL is editable on the project Settings tab.

  • P1-6: mosaic org project repo init ID PROJECT --remote URL and the Quick and Advanced Setup project step set the checkout's origin and check it with git ls-remote, 15 s, no credentials. The receipt records reachable, needs-credential or unreachable with git's first error line. An ssh result says the host key was not verified.
  • P1-7: new mosaic org project repo set ID PROJECT (--local-only --accept-local-loss-risk | --remote URL) --if-revision N [--json], route GET|POST /api/orgs/ORG/repo/project/PROJECT, and a Repository section on the org project's Settings tab. Same URL rules and check as init; origin follows; nothing is pushed; an org.project.repo.set receipt records it.
  • URL rules (parseRepoRemote): https and ssh only. Refused with a plain message each: a leading -, :: helpers, file://, local paths, other schemes, credentials in userinfo, whitespace or control characters, . and .. segments. The loader applies the same rules to a hand-edited file.
  • The check runs git with an environment built from scratch: no credential helper, askpass, system or global config, agent or identity; redirects off; protocols limited to https and ssh; the URL after --; its own process group, killed at 15 s.
  • F7 (Lead ruling): repo set refuses project-repo-bound while any copy binding naming the project is approved, pending or active, checked before the network check and again under the organization lock. Next step: revoke, set, raise a new binding decision. It reads the validated copyBindings; no binding, launcher or broker change.
  • queue init --remote sets the queue checkout's origin through the same helper. packages/business/src/repo-remote.mjs is the one-line old-path re-export.

Tests: new repo-remote, org-repo-set and project-repo-browser; org-admin extended. Each red on BASE 6d0e9f89b. Live acceptance on a fresh devstack instance for CLI and WebUI. Lane gate 4230/0. Rebased on #1946 (conflicts in org.mjs usage and org-admin.mjs resolved by union) and on CB3 (refusals.mjs, union). Lead check: 263 files on 2ad4b30eb (touched tests, every reader of the touched org, webui, business, bus refusal and repo sources, every refusal-table, redaction and last-values file), 2728/2, 5 skips (federation acceptance runs only under its script; four Quick Setup live tests need a running fixture); both fails are CHAT-01 schema checks (conversation reader and flows), red on refactor 0b8fe202b too: the host Python has no jsonschema module.

Review: build (Opus) changes 7: four fixed with tests, F4 left on the security ruling that no production path reaches the test seam, F3 kept and F7 fixed by Lead ruling. Security (daybreak-blue) changes 1, a P2 on unverified ssh host keys, fixed by labeling; re-check approve. F7 re-check changes 2 (P2): repo init and the first-save remote path can name a project's first repository while a pending binding names it. Lead ruling: accepted as intended, no change. Both paths are owner-only and only the owner approves bindings; each writes once, and boundRepositories refuses when the two fields differ, so neither path moves a grant that already names a repository; and the landed credential-scope-unverified next step tells the owner to do exactly this.

Deferred: queue repo set; ssh host key pinning; F7 does not see a binding decision raised but not yet approved (bus proposal store, CB lanes); agent guard on repo init and repo set (agent-guard follow-up); a 7 px project header overflow at 1440 that is on BASE too.

Dev readiness P1-6 and P1-7 (#1873): a project's remote is set and checked, and the Repository URL is editable on the project Settings tab. - P1-6: `mosaic org project repo init ID PROJECT --remote URL` and the Quick and Advanced Setup project step set the checkout's `origin` and check it with `git ls-remote`, 15 s, no credentials. The receipt records `reachable`, `needs-credential` or `unreachable` with git's first error line. An ssh result says the host key was not verified. - P1-7: new `mosaic org project repo set ID PROJECT (--local-only --accept-local-loss-risk | --remote URL) --if-revision N [--json]`, route `GET|POST /api/orgs/ORG/repo/project/PROJECT`, and a Repository section on the org project's Settings tab. Same URL rules and check as init; origin follows; nothing is pushed; an `org.project.repo.set` receipt records it. - URL rules (`parseRepoRemote`): https and ssh only. Refused with a plain message each: a leading `-`, `::` helpers, `file://`, local paths, other schemes, credentials in userinfo, whitespace or control characters, `.` and `..` segments. The loader applies the same rules to a hand-edited file. - The check runs git with an environment built from scratch: no credential helper, askpass, system or global config, agent or identity; redirects off; protocols limited to https and ssh; the URL after `--`; its own process group, killed at 15 s. - F7 (Lead ruling): `repo set` refuses `project-repo-bound` while any copy binding naming the project is approved, pending or active, checked before the network check and again under the organization lock. Next step: revoke, set, raise a new binding decision. It reads the validated `copyBindings`; no binding, launcher or broker change. - `queue init --remote` sets the queue checkout's origin through the same helper. `packages/business/src/repo-remote.mjs` is the one-line old-path re-export. Tests: new repo-remote, org-repo-set and project-repo-browser; org-admin extended. Each red on BASE 6d0e9f89b. Live acceptance on a fresh devstack instance for CLI and WebUI. Lane gate 4230/0. Rebased on #1946 (conflicts in org.mjs usage and org-admin.mjs resolved by union) and on CB3 (refusals.mjs, union). Lead check: 263 files on 2ad4b30eb (touched tests, every reader of the touched org, webui, business, bus refusal and repo sources, every refusal-table, redaction and last-values file), 2728/2, 5 skips (federation acceptance runs only under its script; four Quick Setup live tests need a running fixture); both fails are CHAT-01 schema checks (conversation reader and flows), red on refactor 0b8fe202b too: the host Python has no jsonschema module. Review: build (Opus) changes 7: four fixed with tests, F4 left on the security ruling that no production path reaches the test seam, F3 kept and F7 fixed by Lead ruling. Security (daybreak-blue) changes 1, a P2 on unverified ssh host keys, fixed by labeling; re-check approve. F7 re-check changes 2 (P2): `repo init` and the first-save remote path can name a project's first repository while a pending binding names it. Lead ruling: accepted as intended, no change. Both paths are owner-only and only the owner approves bindings; each writes once, and `boundRepositories` refuses when the two fields differ, so neither path moves a grant that already names a repository; and the landed `credential-scope-unverified` next step tells the owner to do exactly this. Deferred: `queue repo set`; ssh host key pinning; F7 does not see a binding decision raised but not yet approved (bus proposal store, CB lanes); agent guard on `repo init` and `repo set` (agent-guard follow-up); a 7 px project header overflow at 1440 that is on BASE too.
jason.woltje added 5 commits 2026-11-12 19:51:20 +00:00
Remote mode sets the project checkout's origin and checks it once with
git ls-remote, with no credential, no prompt, a protocol allowlist of
https and ssh and a 15 second limit. The receipt reports reachable,
needs-credential or unreachable with git's error summarized. Nothing is
pushed. URLs are https or ssh only; ext:: and other helpers, file://,
local paths, option-like values and credential-bearing URLs are refused.

`mosaic org project repo set` changes a project's repository later, both
ways between remote and local only, through the same rules and check,
with a revision guard and an org.project.repo.set receipt. The Console's
organization project Settings tab edits it through
/api/orgs/ID/repo/project/PROJECT; Quick Setup accepts ssh and shows the
check.
The needs-credential line no longer promises that a binding will supply
one; it says the check sends none. A recorded check is shown only for
the URL it checked. The Settings section says when the git origin differs
from the recorded repository, and a save repairs it. The URL rule message
names what is refused. Tests cover a document write that fails after
origin is set, the drift line and its repair, and Quick Setup in remote
mode.
The check's ssh client accepts any host key, so an answer over ssh shows
only that something at that address speaks git. Security review P2: a
reachable or needs-credential ssh outcome now adds "the ssh host key was
not verified"; https and unreachable outcomes are unchanged.
The rename r2 B golden pins one packages/business/src re-export per
packages/organization/src module; repo-remote.mjs had none.
A copy binding grants the repositories its projects' remotes name when a
credential is issued, and its decision records only project names. repo set
(CLI, route, Settings tab) is now refused with project-repo-bound while a
pending or bound copy binding names the project, before any check, origin
change or write, and again under the lock. The message names the copies and
the next step: revoke the binding, set the repository, raise a new binding
decision. An unbound binding, or none, behaves as before. Bindings are read
from the loader-validated document; binding, launcher and broker code are
unchanged. New refusal-table entry for project-repo-bound.
jason.woltje merged commit 2ad4b30ebd into refactor 2026-11-12 19:51:26 +00:00
Sign in to join this conversation.