diff --git a/docker/agent/bin/push-work b/docker/agent/bin/push-work index 74ac8a2..0978143 100755 --- a/docker/agent/bin/push-work +++ b/docker/agent/bin/push-work @@ -16,7 +16,12 @@ # `sylph-agent`). They are never printed, never logged, and never passed on a # command line. # -# push-work push the current branch +# push-work push the current branch, and any annotated tags on it +# +# --follow-tags publishes ANNOTATED tags reachable from the pushed commits. That +# is what makes a pinned decoder state durable: the port depends on commits of +# ours by revision, and a commit reachable only from a topic branch is orphaned +# by a squash-merge. Lightweight tags are deliberately not pushed. # push-work --dry-run say what it would do set -euo pipefail @@ -50,7 +55,17 @@ if [ ! -s "$HOME/.git-credentials" ]; then fi # `store` reads the file we mounted; nothing is written back (it is read-only). -git config --local credential.helper "store --file=$HOME/.git-credentials" +# Applied to THIS COMMAND ONLY, via `-c`, never `git config --local`. +# +# Writing it to --local config persists it in the repository, and this repo is a +# bind mount the host also uses -- so the host's git inherited +# `store --file=/sylph-home/re/.git-credentials`, a path that exists only inside +# the container, and every host push then failed with +# `unable to get credential storage lock: No such file or directory`. +# +# A tool that configures a shared repository to suit itself breaks every other +# user of that repository. Keep it to the invocation. +CRED_HELPER="store --file=$HOME/.git-credentials" ahead=$(git rev-list --count "origin/$branch..$branch" 2>/dev/null || git rev-list --count HEAD) echo "push-work: $branch — $ahead commit(s) to publish" @@ -63,5 +78,5 @@ fi # --force-with-lease is deliberately NOT offered. If this is rejected as # non-fast-forward, someone else moved the branch: fetch and merge, do not # overwrite. -git push --set-upstream origin "$branch" +git -c "credential.helper=$CRED_HELPER" push --follow-tags --set-upstream origin "$branch" echo "push-work: pushed $branch" diff --git a/docs/port/MISSION.md b/docs/port/MISSION.md index 2a9572f..432c8b0 100644 --- a/docs/port/MISSION.md +++ b/docs/port/MISSION.md @@ -271,6 +271,35 @@ answer is no-go by definition, not "try harder". **Then stop and write the go/no-go.** Do not start Ready Room work on your own authority. +## Publishing a decoder state the port pins + +The port's exporter depends on `sylpheed-formats` **by revision**, so a commit of +yours becomes part of its build. That creates an obligation that is easy to miss: + +⚠️ **A commit reachable only from an `auto/*` branch is not safe to pin.** If the +branch is later deleted, or — worse — **squash-merged**, the object is orphaned. +Squash creates *new* commits, so `main` appears to contain the work while the +pinned sha becomes unreachable, and the port stops building for anyone doing a +fresh checkout. Silently, at their build, long after the moment of breakage. + +**So when you land something the port needs, tag it:** + +```bash +git tag -a formats-pin-$(date +%F) -m "what the port gets from this state" +push-work # --follow-tags publishes annotated tags with the branch +``` + +A tag is a permanent ref. It survives branch deletion and squash-merge, it is +self-documenting in the port's `Cargo.toml`, and it fails loudly at *fetch* if it +ever goes missing rather than silently at build. + +Then tell the port over the message channel that a new pin exists. It bumps +deliberately, as its own commit — that is how it stays current without floating, +which would only give it staleness it cannot see. + +`formats-pin-2026-08-29` at `7eeae30` is the first, created because the port had +already pinned a commit that lived on one topic branch and nowhere else. + ## Handing it over [`HANDOFF.md`](HANDOFF.md) is the single page the port agent reads. Keep it