1f70fd9a22
## Thinking Path > - Paperclip orchestrates AI agents across isolated execution workspaces; the local cwd is the only persistence boundary between runs. > - Workspace lifecycle (worktree_prepare → execute → workspace_finalize) and the wake/accept flow are what guarantee that dependent issues see a consistent worktree. > - PAPA-380 / PAPA-431 / PAPA-432 / PAPA-440 surfaced three holes in that contract: silent env reuse across assignees, dependent wakes firing before finalize, and `issue.interaction.accept` advancing before finalize landed. > - PAPA-441 / PAPA-442 then needed to document the "no remote git" contract and prevent future adapter/runtime code from quietly reintroducing `git push` as a backdoor sync. > - This pull request lands those server fixes, the static `check-no-git-push` enforcement, the AUTHORING.md cross-link, and the Cody-review follow-ups on the PAPA-430 thread. > - The benefit is that finalize is a real barrier — board accepts, dependent wakes, and operator-set env all respect it — and adapter code can't bypass it via raw `git push`. ## What Changed - **server (PAPA-380, PAPA-431):** `execution-workspace-policy` refuses silent env reuse when the assignee's resolved env disagrees with the workspace it would inherit. The inheritance protection is now scoped to the actual inheritance signal — explicit issue-level `environmentId` is honored even when the agent's default env is `null`. - **server (PAPA-432):** `heartbeat.ts` gates dependent wakes on `listUnfinalizedExecutionWorkspaceIds`, and writes a `workspace_finalize` row on the succeeded path. Write failures now surface instead of being swallowed so dependents aren't silently stranded behind a missing row. - **server (PAPA-440):** `issue-thread-interactions.acceptInteraction` adds a workspace_finalize precondition for `request_confirmation` (not `suggest_tasks`). Accept returns 409 if finalize hasn't succeeded for the latest workspace operation. - **ci (PAPA-442):** new `scripts/check-no-git-push.mjs` static check scans `packages/adapters/`, `packages/adapter-utils/`, `server/src/`, and `cli/src/` for any `git push` invocation (string or args-array). Wired into the `policy` PR job and `test:release-registry`. Operators can opt in per-call with `// paperclip:allow-git-push: <reason>`. Release scripts are out of scope by design. - **docs (PAPA-441):** `AUTHORING.md` documents the no-remote-git contract and cross-links the static check so adapter authors learn the rule and the enforcement together. - **review follow-up (PAPA-430, Cody):** three fixes — env resolver bug, accept-gate scope (request_confirmation only), and finalize record write on the succeeded path. ## Verification - `pnpm exec vitest run server/src/__tests__/execution-workspace-policy.test.ts server/src/__tests__/issue-thread-interactions-service.test.ts` → 33/33 pass - `node scripts/check-no-git-push.test.mjs` → check covers string form, args-array form, comment exclusions, and per-line allow-comment. - Manual: server compiles; the policy job runs the check in <1s before heavier jobs. ## Risks - **Behavioral shift in accept:** boards accepting `request_confirmation` while finalize is in-flight now get 409s. This is intentional — they can retry — but it changes timing on a hot path. `suggest_tasks` is unaffected. - **Workspace policy:** the env-reuse refusal is a new error path. Issues that previously silently reused an env from a different-assignee workspace will now fail-loud; the resolver still honors explicit issue-level `executionWorkspaceSettings.environmentId`. - **CI rule:** any future legitimate `git push` in scoped dirs must be marked with the allow-comment, which is the intended ergonomic. ## Model Used - Claude Opus 4.7 (`claude-opus-4-7`, extended thinking), via Claude Code in the Paperclip executor adapter. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots (N/A — server/CI/docs only) - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] I will address all Greptile and reviewer comments before requesting merge Closes related issues: PAPA-430, PAPA-380, PAPA-431, PAPA-432, PAPA-440, PAPA-441, PAPA-442 --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
59 lines
3.2 KiB
Markdown
59 lines
3.2 KiB
Markdown
# Adapter Authoring Notes
|
|
|
|
In-repo notes for adapter authors. The user-facing guide lives at
|
|
[`docs/adapters/creating-an-adapter.md`](../../docs/adapters/creating-an-adapter.md);
|
|
this file holds invariants that are easy to violate from inside the adapter
|
|
package itself.
|
|
|
|
## No-remote-git contract (cross-run persistence)
|
|
|
|
The local execution-workspace cwd is the only persistence boundary across
|
|
runs. No adapter may depend on a git remote for cross-run state.
|
|
|
|
Why: Paperclip resolves a local execution workspace (a worktree) for each
|
|
heartbeat. Code state is carried forward by syncing that local cwd to wherever
|
|
the agent actually runs — over ssh, into a sandbox, into a managed runtime —
|
|
and then syncing changes back when the run finishes. Treating a `git remote`
|
|
as the source of truth (`git push` from inside the agent, fetch on the next
|
|
wake) breaks dependent issues that are gated on the local worktree being
|
|
caught up, and breaks isolated execution workspaces that have no remote
|
|
configured at all.
|
|
|
|
How to apply:
|
|
|
|
- Never `git push` from adapter runtime code. Never assume the local worktree
|
|
has any `git remote` configured. If you need data from the previous run,
|
|
read it from the local cwd Paperclip handed you.
|
|
- If your adapter runs the agent on a different host (ssh, sandbox, remote
|
|
container), use the round-trip helpers in `@paperclipai/adapter-utils`:
|
|
[`prepareWorkspaceForSshExecution`](../adapter-utils/src/ssh.ts) bundles the
|
|
local cwd to the remote dir before the run, and
|
|
[`restoreWorkspaceFromSshExecution`](../adapter-utils/src/ssh.ts) syncs
|
|
remote-side changes (including new git commits) back into the local cwd
|
|
after the run. Both run with no `git remote` configured.
|
|
- If your adapter runs the agent locally, you can read and write the cwd
|
|
directly — same invariant applies: changes that future runs need must live
|
|
in the local cwd by the time `execute()` returns.
|
|
- A failed sync-back is a run-level error. The heartbeat records
|
|
`workspace_finalize=failed` on the execution workspace, which gates
|
|
dependent issue wakes until the next successful finalize. Do not swallow
|
|
restore errors.
|
|
|
|
The invariant is pinned by the `no-remote-git contract` case in
|
|
[`packages/adapter-utils/src/ssh-fixture.test.ts`](../adapter-utils/src/ssh-fixture.test.ts),
|
|
which asserts that a remote-only commit propagates to the local worktree
|
|
through `prepareWorkspaceForSshExecution` → `restoreWorkspaceFromSshExecution`
|
|
with no git remote configured at any point.
|
|
|
|
A static check enforces the rule before runtime ever sees it:
|
|
[`scripts/check-no-git-push.mjs`](../../scripts/check-no-git-push.mjs) scans
|
|
adapter and runtime source (`packages/adapters/`, `packages/adapter-utils/`,
|
|
`server/src/`, `cli/src/`) and fails the `policy` CI job if any unapproved
|
|
`git push` invocation is added. If you are building an operator-configured
|
|
path that legitimately must push, add a
|
|
`// paperclip:allow-git-push: <reason>` comment on the line (or the line
|
|
above) so the opt-in shows up in code review.
|
|
|
|
For the architecture-level write-up of cross-run persistence, see
|
|
[`docs/guides/board-operator/execution-workspaces-and-runtime-services.md`](../../docs/guides/board-operator/execution-workspaces-and-runtime-services.md#cross-run-persistence-no-remote-git-contract).
|