Phase 4 — Git / workflow hardening (rides 0.19.0)#318
Merged
Conversation
Sibling site to the clone fix: the conventions read-clone refresh ran 'git fetch --tags <https://TOKEN@host> <branch>', exposing the PAT in the fetch argv on the orchestrator host. Mirrors the clone site's per-call '-c http.extraheader=Authorization: Basic …' prefix + bare URL. SSH URLs and tokenless public repos unchanged.
H8 inserted a 'git status --porcelain' dirty-tree gate at the top of rebase_onto_base (mirroring pull). The 3 rebase control-flow tests mocked _run_git with a side_effect list matching the OLD call sequence (no leading status call), so every call shifted by one and the assertions missed. Prepend a clean-status result to each list so the gate passes and the fetch/checkout/reset/rebase/diff/abort/push sequence aligns. Verified: 16 passed (was 3 failed/13 passed post-H8, 16 passed pre-H8).
pr_target and _workspace_for_branch still documented the actor → assigned_to → created_by fallback chain that L1 removed from _resolve_workspace_agent_id. Update both to the post-L1 actor → assigned_to → None resolver (project.workspace_path as the final fallback) so a future reader doesn't rely on a fallback that no longer exists.
The opus whole-branch review flagged the M37 concurrency tests as
~50% flaky: both asserted caller A wins the FOR UPDATE race, but
which caller wins the lock is non-deterministic. When B won, the
'assert a_row.merged_by == a_merger' branch flipped false even
though the production code (M37) was correct — exactly one merger
recorded, audit trail intact. Assert the invariant instead: both
rows COMPLETED, both report the same merged_by, value in
{a_merger, b_merger}. Applied to both the unit test and the e2e
twin. Also documents the H8 e2e skip in the module docstring (the
report claimed it was documented there but it wasn't) and drops
the internal 'Phase 4' label from the docstring header in favor of
the public '0.19.0' version anchor.
|
Thanks for opening your first pull request on RoboCo! Quick checklist before review (most of these are enforced by CI, but worth a glance):
See CONTRIBUTING.md for the full workflow and the Code of Conduct for the community standards we follow. Welcome aboard — a maintainer will review shortly. |
…l with no live HoM spawn
…d index (migration 066)
…ince split maxsplit 2
…the GrokCliProvider
…reflects grok agents
…ep so live cost reflects Anthropic cache spend
…ud 429 parks instead of crash-respawning
…ens roll into usage/cost summaries
…r's revisit counter survives a restart
…rates after 2026-08-31
…ilently falling back to a zero-usage env id
…ken-sweep test signatures)
…ck stays reachable
… tokens are bounded
…ry so a stolen cookie's exp is fixed
…t revokes the current jti
…w + jti revocation
[phase9] Auth hardening: bound agent tokens (iat/exp) + JWT jti revocation
[scan-fix] Phase 8: LLM providers / usage / billing
[Phase 7] schemas/conventions/MegaTask/API fixes (15 findings, scan-fix program)
Phase 6 — Panel frontend hardening (rides 0.19.0)
Phase 5 — Background-engine hardening (rides 0.19.0)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 4 — Git / workflow hardening (rides 0.19.0)
Fourth phase of the 2026-07-06 full-codebase-scan fix program. Eight verified git/workflow-hardening findings + an e2e smoke module, all under the existing
## [0.19.0]CHANGELOG section. Stacks on Phase 3 (fix/scan-data-integrity, PR #317) — base isfix/scan-data-integrity; merge #317 first.Security
a265fe59,dcac700c,71484358) — the clone/fetch/ls-remote/push PAT is injected viagit -c http.extraheader=Authorization: Basic <base64(x-access-token:token)>instead of URL-embedding (https://TOKEN@host/...). The raw PAT no longer appears in argv, so/proc/<pid>/cmdlinecan't expose it. Applied acrossworkspace.py(clone + read-clone fetch) andrelease_executor.py(clone + push + ls-remote); the root-cause helper_inject_token_into_urlis deleted. SSH URLs and tokenless public repos are byte-for-byte unchanged.bc714180,9445fb7b) —rebase_onto_basegates on a clean tree (git status --porcelainempty elseValidationError(DIRTY_WORKSPACE)), mirroringpull, so a rebase never silently discards uncommitted agent edits.rebase --abortstays valid recovery because the rebase never starts on a dirty tree. Test mocks updated for the new leading status call.43c52193) —_link_commit_to_taskcallsflush()notcommit(), so it no longer commits the verb runner's savepoint / drags in pending orchestrator state out-of-band.Fixed
104532df) —merge_prlocks the work_session rowFOR UPDATE(with_for_update(of=WorkSessionTable)) before thestatus != ACTIVEidempotency check, so two concurrentmerge_prcalls (retried CEO double-click racing PM auto-complete) serialize — only one writesmerged_by/pr_merged_at. Theof=scoping is load-bearing:WorkSessionTable.project/.agentarelazy="joined", so without it Postgres rejectsFOR UPDATEon the nullable join side withFeatureNotSupportedError(mirrors the Phase 2task.py:2757precedent).45dd94b3) —_pr_is_mergedreturnsNone(not False) onhttpx.HTTPError; callers treatNoneas "assume merged" and fall through to the merged-cleanup path, so an indeterminateghcall doesn't re-spawn the PM against an already-merged PR.is_pr_merged_for_taskkeeps aboolreturn viaTrue if merged is None else bool(merged).43be8384) —_cherry_unmerged_entry's marker grep is anchored to^\[{id[:8]}\](leading^+ trailing space), so a commit that merely references the child (e.g. "Reverts [CHILDID8]") no longer matches → a child's unmerged commits are no longer hidden from assembled-PR integrity.a97ebe3c,6a65553f) —actor_agent_idis threaded throughupdate_pr_for_task→_link_commit_to_task, and_resolve_workspace_agent_idis narrowed toactor → assigned_to → None(thecreated_byfallback is removed). The post-handoff Main-PM path still works becausepr_merge's three choreographer callers thread the realactor_agent_id;pr_targethas zero production callers (test-only);is_pr_merged_for_task'sNonepath is a pre-existing fragility the oldcreated_bypath shared. Two stale docstrings refreshed to drop the removedcreated_byfallback.33bcba3b) —push(force=True)uses--force-with-lease(not bare--force), so a concurrent remote advance fails fast instead of being silently clobbered.e2e smoke
tests/e2e_smoke/test_git_workflow.py(46347633) — 4 cross-layer scenarios: H11 (PAT not in argv,http.extraheaderpresent), M37 (two concurrentmerge_prcalls serialize — exactly one merger), M38×2 (_pr_is_mergedreturns None onhttpx.HTTPError;_merge_with_retryfalls through without raisingMergeConflictError). H8 skipped (a real-worktree clean-tree gate is infeasible under the harness's session-scoped workspace contamination; H8 is unit-covered bytest_git_rebase.py). Deviations documented in the module docstring.Verification
uv run ruff format+ruff check— clean on all Phase 4 changed files.uv run mypy roboco/services/{git,work_session,workspace,release_executor}.py— clean.test_git.py+test_git_rebase.py(65 passed),test_work_session.py(13 passed),test_workspace_read_clone.py+test_workspace_clone_pat_leak_cleanup.py(16 passed).make e2e-smoke— every Phase 4 scenario passes in isolation (4 passed). The full-suite session-scoped workspace contamination across e2e files is a pre-existing harness limitation, documented + out of scope.FOR UPDATErace, but which caller wins is non-deterministic; production M37 code was correct) is fixed in7347e29cby asserting the invariant (both rows COMPLETED, both report the samemerged_by, value ∈ {a_merger, b_merger}) in both the unit and e2e tests. Two Task-9 Minors folded into the same commit (H8 e2e skip now documented in the module docstring; internal "Phase 4" label dropped from the docstring header in favor of the public "0.19.0" version anchor). No production code changed in the fix commit. The other 7.5 fixes confirmed clean: H11 (zero dangling_inject_token_into_urlrefs), H8, H9, M38 (None handled at all 3 callers), M39, L1 (no caller depends on the removedcreated_byfallback; the oneNone-actor site is a strict improvement), L2, M37. All 13 commits signed, no co-author, no new# type:ignore/# noqa, no new migration/subsystem, AGPL-3.0 only.Model note
Implementers ran on sonnet; whole-branch review on opus. kimi-k2.7-code:cloud is not routable in this harness.
🤖 Generated with Claude Code