Block handoffs from a dirty working tree

Unrelated tracked changes could ride along with a handoff. Refuse to send when
git status --porcelain is non-empty, list the offending files, and keep the
draft so it can be fixed and resent. SWARMFORGE_ALLOW_DIRTY=1 overrides.

By specifier.
This commit is contained in:
Chris Troutner
2026-09-15 18:28:45 -07:00
parent a5f8967a71
commit 0942b2ef40
2 changed files with 23 additions and 7 deletions
+8 -7
View File
@@ -459,13 +459,14 @@ At the end of each session, update this file:
- Note the current `master` HEAD commit.
- State the next feature to work on.
Current `master` HEAD: `c8ceb82` (merged architect's thread-query-bounds job —
`GET /posts/:txid/thread` now counts likes only for the thread's txids via
`countLikesForTxids` and prefix-scans `postChildren` per parent through a new
`PostQuery.listChildTxids` seam, so its work is proportional to the thread
rather than the database. Spec:
`psf-memo-db/specs/thread-query-performance.feature`. Verified psf-memo-db unit
(357) + property (44) + acceptance (all 11 suites) + lint all passing).
Current `master` HEAD: `a5f8967` (thread-query-bounds merged at `c8ceb82`, then
a process-improvement pass on this branch: the architect now always notifies
the specifier; acceptance LevelDB temp dirs are cleaned up; the handoff daemon
is self-healing; APS is single-sourced at `tmp/aps`; acceptance generation is
incremental; `verify.sh` emits a machine-readable verification record;
`mutate-file.sh` guards differential under-selection; and `clean-builds.sh`
keeps mutation worker copies small. Run `swarmforge/scripts/state.sh` to refresh
these HEAD lines.)
Next action: **TBD** — current direction is front-end improvements to
`psf-memo-client` (UI/UX polish, accessibility, performance, responsiveness,
state handling, error surfacing). See `specs/feature-backlog.md`.
+15
View File
@@ -302,6 +302,20 @@
(fs/move tmp-file outbox-file)
outbox-file))
(defn dirty-files []
(->> (:out (command "." "git" "status" "--porcelain"))
str/split-lines
(remove str/blank?)))
(defn guard-clean-tree! []
(let [dirty (dirty-files)]
(when (and (seq dirty) (not (System/getenv "SWARMFORGE_ALLOW_DIRTY")))
(binding [*out* *err*]
(println "HANDOFF BLOCKED: working tree has uncommitted changes.")
(doseq [line dirty] (println " " line))
(println "Commit or stash them, or set SWARMFORGE_ALLOW_DIRTY=1 to send anyway."))
(System/exit 3))))
(defn error-report [draft errors]
(binding [*out* *err*]
(println "HANDOFF INVALID:" (str draft))
@@ -328,6 +342,7 @@
(when (seq all-errors)
(error-report draft all-errors)
(System/exit 2))
(guard-clean-tree!)
(let [outbox-file (write-handoff! {:headers headers
:recipients (:recipients validation)
:canonical-commit (:canonical-commit validation)