fix(push-guard): single reader, whole-word dir tokens, payload fallback, bad-value banner

Run D2 of manual-push mode (BDR-114).

- push-guard sources lib/gitflow.sh once (absolute path) and reads each
  candidate dir through gitflow_push_mode; a missing lib denies.
- Dir tokens are extracted as whole shell words: a fully quoted token
  (inner apostrophe allowed) is resolved, a backslash-escaped space is
  unescaped deterministically, a token mixing quoted and unquoted parts
  is refused (fail closed) instead of resolving to its parent.
- A payload jq cannot parse is scanned as raw text with its JSON escapes
  folded; a push-looking one gets the static deny through the trap.
- The 20-token cap runs before any per-token classification (a flood of
  20 000 tokens is refused in 0.13 s; T58 locks it under 5 s).
- `case "$mode"` has a deny default; missing core tools warn and allow.
- T42 compares the deny list against main (the last release) instead of
  HEAD; literal-true, mixed-token, broken-payload, lib-missing and
  banner-on-bad-value cases added (98 checks).
- session-start banner reads the mode through the verb and shows
  `push : manual (autopush bad)` on an unparseable value.
- tour hints quote "<abs project>".
This commit is contained in:
bchanot
2026-10-07 17:11:56 +02:00
parent 472cccbc52
commit 3c59333fcf
4 changed files with 187 additions and 39 deletions
+4 -4
View File
@@ -240,12 +240,12 @@ order:
5. Push state, only when a branch exists (report-only, skipped or
dirty-tree projects have none: their row keeps `no branch`, no push
column). Two read-only calls, probe first:
`git -C <abs project> remote get-url origin >/dev/null 2>&1 || echo no-origin`
`git -C "<abs project>" remote get-url origin >/dev/null 2>&1 || echo no-origin`
then
`git -C <abs project> rev-list --count <branch> --not --remotes=origin 2>/dev/null || echo unknown`
`git -C "<abs project>" rev-list --count <branch> --not --remotes=origin 2>/dev/null || echo unknown`
(`<branch>` = the name `gitflow start` returned, suffixed `-2`/`-3` on a
same-day re-run — never the bare `chore/tour-<date>`). 0 → `on origin`;
else `local only → ! git -C <abs project> push -u origin <branch>` (probe
else `local only → ! git -C "<abs project>" push -u origin <branch>` (probe
printed `no-origin` → `local only (no origin remote)`).
```markdown
@@ -282,7 +282,7 @@ without that approval — neither this repo's nor any target project's.
never push `main`/`develop`** — "the tour is green" is not a signal.
The gitflow hooks push the chore branch in auto-push mode only; when it
is not on origin (manual push mode, or a `push FAILED` warning) the USER
pushes it — `! git -C <abs project> push -u origin <branch>` — the tour
pushes it — `! git -C "<abs project>" push -u origin <branch>` — the tour
never pushes or retries.
- Scoped pathspecs only; `git add -A` is forbidden.
- Target TODO.md and target `.claude/memory/` are READ-ONLY. Reconcile