Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ Safest local verification sequence after non-trivial changes:
**Repo Config Trust Boundary (security)**

- The daemon runs `commands.*` from `.no-mistakes.yaml` verbatim via `sh -c`, and `agent` selects which process launches with the maintainer's credentials. The code-executing selection fields (`commands.{test,lint,format}` and `agent`) are therefore loaded from the trusted default branch at a **pinned SHA** resolved by a fresh fetch, never from the pushed SHA. The run aborts when the trusted commit or its present config cannot be read and parsed; a readable tree with no config is valid. See `internal/daemon/manager.go` `startRun`, `loadTrustedRepoConfig`, and `assertGateTrustedConfigReadable`.
- `document.instructions` (the repo's documentation placement policy), `review.path_instructions` (path-scoped review guidance appended to the review prompt), `disable_project_settings` (the gate-agent project-instruction opt-out), `no_ci` (positive declaration that the repository intentionally has no CI), and `ci.rerun_transient` (how many times a transiently failed check may be re-run) are also trusted-only, regardless of `allow_repo_commands`: a pushed branch must not weaken any of those boundaries, self-declare no-CI to bypass checks, or steer its own review; enabling the commands opt-in must not drop the maintainer's own trusted values; and every re-run `ci.rerun_transient` authorizes bills another provider-side workflow run to the repository, so a contributor must not be able to raise it (the operator's own global `ci.rerun_transient` is a separate, non-contributor surface that the trusted repo value still overrides). When the opt-out is enabled, only adapters with verified effective suppression may launch. Other non-executing fields (`ignore_patterns`, `auto_fix`, `commit`, `intent`, `test`) are still read from the pushed branch.
- `document.instructions` (the repo's documentation placement policy), `review.path_instructions` (path-scoped review guidance appended to the review prompt), `disable_project_settings` (the gate-agent project-instruction opt-out), `no_ci` (positive declaration that the repository intentionally has no CI), `ci.rerun_transient` (how many times a transiently failed check may be re-run), and `pre_push_check` (the veto run before a push moves an already-existing remote branch, see the Rebase Base & Force-Push Safety section below) are also trusted-only, regardless of `allow_repo_commands`: a pushed branch must not weaken any of those boundaries, self-declare no-CI to bypass checks, steer its own review, or inject or delete its own pre-push guard; enabling the commands opt-in must not drop the maintainer's own trusted values; and every re-run `ci.rerun_transient` authorizes bills another provider-side workflow run to the repository, so a contributor must not be able to raise it (the operator's own global `ci.rerun_transient` is a separate, non-contributor surface that the trusted repo value still overrides). When the opt-out is enabled, only adapters with verified effective suppression may launch. Other non-executing fields (`ignore_patterns`, `auto_fix`, `commit`, `intent`, `test`) are still read from the pushed branch.
- Selecting which trusted config applies to a run must never depend on a pushed-branch field. `review.path_instructions` is matched against the COMPLETE changed-file set, never the `ignore_patterns`-filtered subset, because filtering there lets a contributor suppress a maintainer's rule from their own review by ignoring its glob. `reviewablePaths` (`internal/pipeline/steps/common_diff.go`) answers only "does this run have anything to work on".
- `pr.base_branch` (the PR, rebase, and CI-merge-conflict-auto-fix integration branch, falling back to `Repo.DefaultBranch` when unset) is trusted-default-branch-only, but unlike the fields in the bullet above it is the deliberate exception that also honors the `allow_repo_commands: true` opt-in, since it controls where an already-maintainer-authorized PR lands rather than what executes. Once a PR already exists, its actual forge base branch (read live via `scm.PRBaseBranchReader`) is authoritative for CI merge-conflict repair and base-branch tip monitoring over a since-changed `pr.base_branch`, and PR lookup matches the existing PR by branch alone, never filtered by base, so a later config change updates that PR instead of opening a duplicate against the new base. Full semantics are owned by `docs/src/content/docs/reference/repo-config.md` (`pr.base_branch`). Regressions: `TestEffectiveRepoConfig_PRBaseBranchTrustedOnly`, `TestEffectiveRepoConfig_PRBaseBranchOptInUsesPushedValue`, `TestEffectiveRepoConfig_PRBaseBranchOptInWithNoTrustedCopyUsesPushedValue`, `TestLoadRepoConfig_PRBaseBranchRejectsInvalidBranchName`, `TestLoadRepoConfig_PRBaseBranchEmptyIsValid`, `TestPRStep_UsesConfiguredBaseBranch`, `TestRebaseStep_UsesConfiguredPRBaseBranch`, `TestCIStep_AutoFixUsesExistingPRBaseAfterConfigChanges`, `TestPRStep_ExistingPRAgainstDifferentBaseIsUpdatedNotDuplicated`.
- `allow_repo_commands` is per-repo, read only from the trusted default-branch copy, and defaults `false`; a contributor cannot self-enable it from a pushed branch. The e2e harness models a trusted single-developer environment and commits `allow_repo_commands: true` via `SetupOpts.AllowRepoCommands`; security tests pass `false`.
Expand Down Expand Up @@ -331,7 +331,8 @@ The rationale lives in the `resolveRun` doc comment and the status-rendering com
- Rebase bases come from the freshly fetched authoritative remote refs, never local or stale state; and a branch built on unpushed local-default-branch commits parks with `NeedsApproval` + `AutoFixable=false` instead of silently widening the PR (`detectBundledLocalDefaultCommits`, #283).
- Every force-push routes through `resolveForcePushDecision`, which re-reads the live remote head and allows the push only for a new branch, an already-equal remote, an unchanged `lastSeenSHA`, or remote commits already incorporated by patch-id (excluding `^baseSHA` history the run knowingly rewrites). Anything else refuses, and a failed ls-remote/fetch fails closed; never degrade to a bare `--force`/`--force-with-lease` without an explicit anchor.
- `lastSeenSHA` must stay the head the run last **observed** (from run/prior-run push provenance or the remote-tracking ref), never the live remote tip: the rebase step refreshes `origin/<branch>` only on a normal push, NOT on a force push. CI repairs commit locally and restart validation at Review; the later Push step owns their remote update and force-push safety. Anchoring a lease to a SHA read immediately before pushing is the original #281 bug (it always passes and protects nothing); always-fetching the branch on force push recreates it. Never reintroduce either.
- Regressions: `TestPushStep_RefusesToClobberAdvancedUpstreamBranch` (#305), `TestForcePushRun_RefusesToClobberOutOfBandBranchCommit`, `TestRebaseStep_DetectsUnpushedLocalDefaultBranchCommits` (#283), `TestResolveForcePushDecision_*`, `TestExecutor_CIRestartRevalidatesBeforePush`, `TestPushStep_AllowsForcePushAfterMidRunRebaseOverPriorPushedGeneration` (#837), `TestPushStep_AllowsForcePushOnRerunOverPriorRunPushedGeneration` (#837).
- `pre_push_check` (`internal/pipeline/steps/prepush.go`) is the repository's veto on a push that would move an ALREADY-EXISTING remote branch, i.e. the only push that can land under an open PR an external merge process owns. It never runs for a new-branch creation, an already-equal remote, or an unset field; a non-zero exit refuses before any object moves. Its command and the PR identity it receives are resolved best-effort, but the guard itself is trusted-default-branch-only regardless of `allow_repo_commands` (`EffectiveRepoConfig`): a pushed branch must be able neither to inject it nor to delete it. Unlike `commands.{test,lint,format}`, the command runs with `StepContext.AppRoot` (the app root, `paths.Root()`) as its cwd, never `WorkDir`: those fields legitimately validate the pushed content, but this field is a security veto, so a repository-relative script must not resolve against contributor-pushed content the same trusted command string could otherwise be pointed at forever. `prePushBaseBranch` seeds `BaseBranch` from `effectivePRBaseBranch`, then `livePRBaseBranch` overrides it with the open PR's live forge base when a `scm.PRBaseBranchReader` is available, matching the CI step's precedence for an existing PR over a since-changed `pr.base_branch`.
- Regressions: `TestPushStep_RefusesToClobberAdvancedUpstreamBranch` (#305), `TestForcePushRun_RefusesToClobberOutOfBandBranchCommit`, `TestRebaseStep_DetectsUnpushedLocalDefaultBranchCommits` (#283), `TestResolveForcePushDecision_*`, `TestExecutor_CIRestartRevalidatesBeforePush`, `TestPushStep_AllowsForcePushAfterMidRunRebaseOverPriorPushedGeneration` (#837), `TestPushStep_AllowsForcePushOnRerunOverPriorRunPushedGeneration` (#837), `TestPushStep_PrePushCheck*`, `TestRunConfiguredPrePushCheck_SkipMatrix`, `TestRepoConfig_PrePushCheck`, `TestRunConfiguredPrePushCheck_RunsOutsideWorktree`, `TestRunConfiguredPrePushCheck_UsesLivePRBaseBranch`.

**macOS Release Signing (permanent identity)**

Expand Down
4 changes: 4 additions & 0 deletions docs/src/content/docs/reference/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ Disable telemetry collection.

When set to a disabling value, telemetry stays off even if a runtime or embedded website ID is available.

## Variables no-mistakes sets for your commands

The `NO_MISTAKES_*` variables above are read by no-mistakes. One repository setting goes the other way and hands variables **to** a command you configure: [`pre_push_check`](/no-mistakes/reference/repo-config/#pre_push_check) receives the pull request and commit identity of the push it is being asked to approve. That contract is documented with the field.

## Environment the daemon sees

When the daemon runs through a managed service (launchd, systemd user service, Task Scheduler), the macOS and Linux service definitions include a default `PATH` with common user and system binary directories. They also bake in any proxy variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, `ALL_PROXY`) that were set when you installed or refreshed the service, so the daemon and the agents it spawns can reach the network through your proxy even when the login-shell probe is unavailable. Once baked in, the values are preserved across later service refreshes and restarts even when the proxy variables are not exported in that shell, so a routine `daemon restart` or a binary upgrade will not strip them; export the variables again only when you need to change or remove them. Both the upper- and lower-case spellings are forwarded exactly as you set them, because tooling is inconsistent about which it reads (curl, for example, honors only the lower-case `http_proxy` for plain-HTTP requests). Because a proxy URL can embed credentials (for example `http://user:pass@host`), the generated service file is restricted to owner-only `0600` permissions whenever proxy values are forwarded into it. When no proxy variables are set, the generated definition is unchanged and keeps the conventional `0644` mode. Windows Task Scheduler inherits your logon environment and needs no forwarding. At daemon startup, the daemon resolves environment from your login shell on macOS and Linux, preserves your shell `PATH` order, and appends any missing well-known directories such as `~/.local/bin`, `~/go/bin`, `~/.cargo/bin`, `~/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, and `/bin`. If login-shell resolution fails or returns no entries, the daemon logs a warning and uses an augmented process-environment fallback that may omit version-manager directories such as nvm, fnm, or volta. On Windows it reuses the current process environment.
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/pipeline-steps.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ Pushes the validated branch to the configured push target.
- Immediately before remote mutation, reloads the durable review-approved commit and refuses to push when that binding is missing, malformed, or unreachable
- Requires the commit proposed for push to equal or descend from the review-approved commit, allowing commits made by later pipeline steps without authorizing unrelated history
- Re-reads the push target via `git ls-remote` before pushing
- For a push that would move an **already-existing** remote branch, runs the repository's [`pre_push_check`](/no-mistakes/reference/repo-config/#pre_push_check) when one is configured, and refuses the push on a non-zero exit. Creating a branch for the first time, and a remote already at the pushed head, are not gated
- For existing branches, refuses to force-push when the live remote carries commits the pipeline has not incorporated by patch-id
- Fails closed when the remote safety check cannot verify whether the push would discard existing remote work
- Uses `--force-with-lease=<ref>:<sha>` with an explicit SHA anchor for allowed existing-branch rewrites
Expand Down
Loading
Loading