Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
284b2e3c23 | ||
|
|
69efad2f9a | ||
|
|
587cb19641 |
@@ -23,10 +23,10 @@
|
||||
| RI-2-001 | done | RI-N2 (Forge): remove stub-executor false success; `--simulate` typed `simulated` results that satisfy nothing; literal-`true` gates and echo-review replaced with real gates or typed waiting-for-authority | #1275 | pi-glm-5.3 | mosaicstack/stack | fix/ri-050-forge-fail-closed | RI-0-001 | 20K | Independent review APPROVED 2026-08-17 (Gitea review 172 on PR #1278, head 99b8f6ea; reviewing seat fargo — recorded under shared host principal mos-dt-0, provenance correction posted by fred; wrapper gap filed by fred). Executed at head: forge tests 116/116, lint green, typecheck green after building macp dist (minimal-install artifact, not a defect), workspace typecheck 45/45, no external type consumers of the changed interfaces. CI red = known lane-wide fleet-test failure only, carries no information about this change (fred, log-content analysis, pipelines 2456-2458). Non-blocking finding: README L141-143 + skills/mosaic-forge/SKILL.md document bare forge run/resume, which now fails closed — fast-follow docs touch. Merge queued behind #1270. UPDATE 2026-08-18: #1270 merged; CI GREEN at head 4917df1f via serialized retry (pipeline 2477) - root cause of prior reds was CI-agent contention (web SPA timeouts under concurrent pipelines), superseding the fleet-test-failure theory. |
|
||||
| RI-2-002 | done | RI-N2 (MACP): gate runner fails closed on empty commands, stub executors, and unimplemented CI-provider gates unless explicit simulate; typed capability failures | #1275 | pi-glm-5.3 | mosaicstack/stack | fix/ri-050-macp-fail-closed | RI-0-001 | 15K | PR #1293 (head 2097379e): CI green (pipeline 2465), independent review APPROVED (Gitea review 173, jarvis seat, 2026-08-17) - macp 109/109 verified at head. Merge queued behind #1276/#1277/#1278. |
|
||||
| RI-3-001 | done | RI-N4: complete probe inventory mapping every TS and shell quality-rail check to one canonical check with disposition (preserve/strengthen/retire, each named) | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-qr-probe-inventory | RI-0-001 | 12K | PR #1302 (head e06a47fac591): CI green (2484), independent review APPROVED (Gitea review 187, fargo seat, 2026-08-18) — 54 rows / 21 canonical checks / dispositions 43-2-9-0 verified by row-count and code spot-checks. Merged by fargo at pinned head. |
|
||||
| RI-3-002 | done | RI-N4: TS evaluator absorbs effective shell probes; typed results (passed/failed/blocked/error/not-applicable) with versioned digested check definitions; shell commands become thin adapters; contract/parity/negative-control tests | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-qr-evaluator | RI-3-001 | 30K | PR #1308 (head 68279d61): CI green (2506), independent review APPROVED (Gitea review 188, fred, seven mutations incl. vacuous-pass + stage-removal). Merged by fargo at pinned head → next @ 245e0c4. Follow-up #1309 (digest wording). |
|
||||
| RI-4-001 | done | RI-N3: one PRD application service — `mission --plan` persists mission↔PRD linkage (ids/versions/selected requirements); `mosaic prdy` routes through the service or becomes a named import/export adapter; Markdown is a labeled generated view; explicit conflict-aware import | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-prd-authority | RI-0-001 | 35K | PR #1294 (head 8d258e1d): CI green (pipeline 2466), independent review APPROVED (Gitea review 174, jarvis seat, 2026-08-17) - prdy 20/20 + command specs 9/9 at head. Merge queued behind #1276/#1277/#1278. PR #1294 (head 8d258e1d): CI green (2466), review 174. Merged 2026-08-18 overnight wave → next @ d92de53. |
|
||||
| RI-3-002 | not-started | RI-N4: TS evaluator absorbs effective shell probes; typed results (passed/failed/blocked/error/not-applicable) with versioned digested check definitions; shell commands become thin adapters; contract/parity/negative-control tests | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-qr-evaluator | RI-3-001 | 30K | |
|
||||
| RI-4-001 | in-progress | RI-N3: one PRD application service — `mission --plan` persists mission↔PRD linkage (ids/versions/selected requirements); `mosaic prdy` routes through the service or becomes a named import/export adapter; Markdown is a labeled generated view; explicit conflict-aware import | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-prd-authority | RI-0-001 | 35K | PR #1294 (head 8d258e1d): CI green (pipeline 2466), independent review APPROVED (Gitea review 174, jarvis seat, 2026-08-17) - prdy 20/20 + command specs 9/9 at head. Merge queued behind #1276/#1277/#1278. |
|
||||
| RI-5-001 | done | RI-N5: typed freshness states (current/stale/partial/unknown/unavailable); no failed-fetch-renders-empty; stale derived verdicts → unknown; mutations disabled when stale; failure-matrix tests | #1275 | pi-glm-5.3 | mosaicstack/stack | feat/ri-050-web-stale-safety | RI-0-001 | 25K | |
|
||||
| RI-V-001 | in-progress | Final verification + release evidence: all cards verified merged, negative controls demonstrated, real `next` publish run green on exact commit, evidence pack recorded | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-release-evidence | RI-1-002, RI-2-001, RI-2-002, RI-3-002, RI-4-001, RI-5-001 | 10K | Evidence pack live on branch docs/ri-050-release-evidence — all five requirements evidenced; registry credential fixed (jarvis, #1275 c23239) and PROVEN green: pipeline 2517 (retry of 2512, identical commit) all steps green incl. build-gateway; pack PR next, then topher review + merge, close #1275. |
|
||||
| RI-V-001 | not-started | Final verification + release evidence: all cards verified merged, negative controls demonstrated, real `next` publish run green on exact commit, evidence pack recorded | #1275 | pi-glm-5.3 | mosaicstack/stack | docs/ri-050-release-evidence | RI-1-002, RI-2-001, RI-2-002, RI-3-002, RI-4-001, RI-5-001 | 10K | |
|
||||
|
||||
## Dispatch waves (max 2 parallel workers)
|
||||
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
# RI-050 Release Evidence Pack (alpha 0.0.50 release-integrity floor)
|
||||
|
||||
> Status: **DRAFT — proof complete, awaiting review + merge**. All five normative requirements (RI-N1..N5) merged to `next` behind the live gate. Registry credential fixed 2026-08-18 23:47Z and **proven end-to-end**: push pipeline **2517** (retry of failed 2512 at the identical commit d4d32a8, only the secret changed between runs) — all steps green including `build-gateway`. Remaining for closure: this pack PR reviewed (topher), merged to `next`, its own push pipeline green, #1275 closed. Last updated 2026-08-19 by fargo (day-takeover orchestrator).
|
||||
> Card: RI-V-001. All sections marked ⏳ pending their card's merge. Normative source:
|
||||
> `docs/PRD.md` § Release Integrity Workstream (#1275).
|
||||
|
||||
## RI-N1 — Canonical terminal verification + exact-commit publish gate
|
||||
|
||||
| exhibit | evidence | where |
|
||||
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------- |
|
||||
| Gate live, fail-closed | Push pipeline **2486**: `verify` ran at exact commit, FAILED on a real latent defect (gateway cross-user-isolation cleanup on the no-DB path), and `build` / `publish-npm` / `build-gateway` were all **skipped**. First push in repo history that did not publish ungated (prior ungated publishes beside failing builds: 2439, 2462, 2482). | Woodpecker repo 47 pipeline 2486 |
|
||||
| Gate-caught defect fixed | PR **#1304** (afterAll honors `dbAvailable`; both paths verified: dead-port 28 skipped + file passes; live-5433 28 passed). Review 180 (fred). | PR #1304 |
|
||||
| First gated green npm publish | Push pipeline **2488** (post-#1304): `verify` GREEN → `build` GREEN → `publish-next-npm` GREEN, all publish effects behind the gate. | Woodpecker pipeline 2488 |
|
||||
| Negative controls | PR **#1305**: structural DAG tests (S1 missing edge, S2 renamed effect incl. command-based npm/kaniko detection, S3 detach, S4 failure:ignore/success override, S5 when-filter, S6 HEAD-mover between verify and publish with legitimate-recheckout positive control, S7 removal) + subset-stage composition control in verify-release.test.mjs. Mutation-verified by the dispatching seat in both directions (true bypass → S1 assertion fires; non-bypass edit → correctly green). Scripts tests 20/20, CI 2490 green. | PR #1305 |
|
||||
| ✅ Canonical command | `scripts/verify-release.mjs` (stage table pinned to ci.yml by checked-in test). Merged with #1277; now also invokes the RI-N4 evaluator via its `quality-rails` stage (#1308). | `scripts/verify-release.mjs` |
|
||||
|
||||
## RI-N2 — Forge + MACP fail-closed (typed explicit simulation)
|
||||
|
||||
- ✅ Forge: PR **#1278** merged (head 4917df1f; CI 2477; review 184 fred at pinned head — prior review 172 dismissed by rebase, correctly re-taken).
|
||||
- ✅ MACP: PR **#1293** merged (head 2097379e; CI 2465; review 173).
|
||||
- ✅ Post-merge behavior docs: PR **#1299** merged (head 8a405b14; CI 2497; review 186 fargo at pinned head — legitimate independent seat; merged 2026-08-18 with --expect-head pin, content-verified on next @ ff45f7b).
|
||||
|
||||
## RI-N3 — PRD authority
|
||||
|
||||
- ✅ PR **#1294** merged (head 8d258e1d; CI 2466; review 174).
|
||||
|
||||
## RI-N4 — Quality-rails evaluator
|
||||
|
||||
- ✅ Probe inventory: PR **#1302** merged (head e06a47fac59; CI 2484; review 187 fargo at pinned head; 54 rows / 21 canonical checks / dispositions 43-2-9-0 row-count-verified; merged 2026-08-18, content-verified on next @ 6435089).
|
||||
- ✅ TS evaluator absorbs shell probes: PR **#1308** merged (head 68279d61; CI 2506; review 188 fred at pinned head — seven targeted mutations, seven detections, incl. the vacuous-pass hole M1 and stage-removal M7). Evaluator: typed fail-closed verdicts, digested versioned definitions, per-subject sets; QC-19 absorbed (verbatim-list parity oracle), QC-20 as thin adapter (verify.sh unmodified); verify-release `quality-rails` stage wired (RI-N1 consumes the evaluator). Worker-produced, independently verified by the dispatching seat (quality-rails 40/40 incl. sabotage control 6-failed/34-passed restored sha-verified; root build 25/25; typecheck 45/45).
|
||||
|
||||
## RI-N5 — Consequence-aware stale UI
|
||||
|
||||
- ✅ PR **#1300** merged (head a337d787; CI 2481; review 179). Web suite 199 → 281 tests (failure matrix + negative controls), independently re-run by the dispatching seat before merge.
|
||||
|
||||
## Known-open infrastructure item (not a card)
|
||||
|
||||
Gateway/ci-base **image** pushes fail on registry credentials: Woodpecker repo
|
||||
secrets `REGISTRY_USERNAME`/`REGISTRY_PASSWORD` are rejected by the Gitea
|
||||
container registry (explicit `UNAUTHORIZED` at `/v2/token`; pipeline 2494 after
|
||||
PR #1306 corrected the secret references — previously masked as an ambiguous
|
||||
push-permission error since at least 2439). Requires a package-scoped token
|
||||
(Jason). The npm publish path is green and gated; this item tracks image pushes
|
||||
only and predates the RI-050 floor.
|
||||
|
||||
**Update 2026-08-18 (fargo):** Jason set new secret values ~17:25Z; pipeline
|
||||
**2507** (the #1308 merge push, first after the update, 18:0xZ) still fails
|
||||
`build-gateway` with the identical `UNAUTHORIZED`. Read-only isolation (no
|
||||
secrets read, no CI retries): the registry endpoint and auth mechanism are
|
||||
HEALTHY — a valid Gitea token via basic-auth mints a JWT at `/v2/token` (200),
|
||||
bad credentials 401 cleanly. Therefore the failure is isolated to the secret
|
||||
VALUES, not the endpoint or pipeline. Most likely shape error (labeled guess):
|
||||
the registry authenticates username + **API token with package scope**, not
|
||||
username + login password; if REGISTRY_PASSWORD holds a login password rather
|
||||
than a minted token value, `/v2/token` 401s exactly as observed. npm publishes
|
||||
remained green in 2507; every publish step except the image push is gated and
|
||||
green.
|
||||
|
||||
**Resolution 2026-08-18 23:47Z — FIXED on the Gitea server (jarvis, #1275
|
||||
comment 23239).** Root cause was neither scope nor a missing token:
|
||||
`REGISTRY_USERNAME` held `mosaic`, the **pre-rename org name**. Gitea's rename
|
||||
redirect covers API/web paths but not Basic-auth username lookup, and
|
||||
`mosaicstack` is an organization, which has no password — the pair could never
|
||||
authenticate. Fix: `REGISTRY_USERNAME`=`woodpecker` (the existing service
|
||||
account, Gitea user 41, already in `ci-publish`) and `REGISTRY_PASSWORD`= a
|
||||
newly minted `write:package`-only token (`gitea admin user generate-access-token`
|
||||
in the Gitea container; minting with a token is forbidden server-side). Events
|
||||
`[push, tag]` preserved. Verified **without a pipeline run**:
|
||||
`POST /v2/<pkg>/blobs/uploads/` opened then cancelled a session — **202** on
|
||||
all four kaniko destinations (gateway, appservice, web, ci-base), anonymous
|
||||
control **401**, wrong-owner control **401**. The earlier "Requires a
|
||||
package-scoped token (Jason)" expectation is superseded: the defect was a
|
||||
stale value from the org rename, not a scope grant Jason owed.
|
||||
|
||||
**Proof 2026-08-19 ~00:2xZ (fargo): pipeline 2517 green at build-gateway.**
|
||||
Woodpecker retry of 2512 — identical commit d4d32a8, identical pipeline
|
||||
config, only the server-side secret changed between runs — went green on
|
||||
every step (clone, install, verify, build, publish-next-npm,
|
||||
**build-gateway**). A/B at the same commit isolates the credential as the
|
||||
variable; the stored value is byte-intact. Retry was serialized (sole run in
|
||||
flight; merge-purpose CI queue guard had blocked on 2512's terminal failure
|
||||
at the `next` head, which this retry also clears). The item is closed.
|
||||
|
||||
**Timing caveat (recorded so the pack does not outlive the memory of what the
|
||||
pin was).** Every pipeline this pack cites — including headline 2517 —
|
||||
finished by 2026-08-19 00:34Z, which is BEFORE the registry credential pin
|
||||
landed at 2026-08-19 23:42:05Z. The A/B above remains sound regardless: it
|
||||
compares identical commits (d4d32a8) with only the secret differing, so it
|
||||
proves the credential value, not anything about the later pin. No pipeline
|
||||
cited in this pack exercises the post-pin registry state; a green trunk
|
||||
publish after the pin is a separate fact that this pack does not claim.
|
||||
|
||||
## Process record (audit trail)
|
||||
|
||||
- Merges executed under the jarvis principal (topher seat; identity provisioning
|
||||
pending) via the Gitea API replicating `pr-merge.sh` semantics (head-pin +
|
||||
squash + keep branch): `pr-merge.sh` hard-codes `main`-only targets and cannot
|
||||
express this repo's `next` trunk — wrapper gap captured to OpenBrain
|
||||
(id 9db7a95a) and to the framework queue.
|
||||
- Reviews tonight: 175/178 (zane's #1298, both heads, by topher); 176/177/179/
|
||||
180/181/182 (fred) — cross-review rule (producer ≠ reviewer) held on every
|
||||
merge: producers were pi workers / zane; reviewers were the other seat.
|
||||
- CI contention note: concurrent PR pipelines on the single CI agent can time
|
||||
out the web SPA suite (measured 2470/2472 vs serialized 2475/2476/2477);
|
||||
serialize retries when the queue is busy.
|
||||
|
||||
## Process record — 2026-08-18 day takeover (fargo)
|
||||
|
||||
- Takeover directive: Jason (via jarvis router + both seats' handoff documents,
|
||||
relayed verbatim over comms). First-move conflict between the two handoffs
|
||||
(zane: doctor PR first; topher: review-queue first) resolved on dependency
|
||||
grounds per jarvis's read — topher's order won; zane's finding-2 doctor PR
|
||||
(upgraded by fred's measurement) remains queued, nothing depends on it.
|
||||
- Reviews 186 (#1299) + 187 (#1302): fargo, at pinned heads, as the legitimate
|
||||
independent seat (topher dispatched both producers; cross-review rule held).
|
||||
Both merged with --expect-head pinning via the REPO-COPY pr-merge.sh
|
||||
(allows next; the installed copy still lags — zane's route, not the raw-API
|
||||
break-glass), each preceded by ci-queue-wait -B next -R mosaicstack/stack.
|
||||
CI green at both heads (2497, 2484). Merges content-verified on the shipping
|
||||
ref (TASKS anchors at ff45f7b / 6435089).
|
||||
- RI-3-002: one pi worker (zai/glm-5.3:high), independently verified by the
|
||||
dispatching seat before push; PR #1308 reviewed by fred (188, seven
|
||||
mutations incl. vacuous-pass and stage-removal) and merged head-pinned at
|
||||
68279d61 → next @ 245e0c4.
|
||||
- Registry-credential isolation measurement (above) performed read-only; no
|
||||
secret values read, no retry-pushes against CI.
|
||||
- One reviewer-scope disclosure (fred, review 188): fred's approval explicitly
|
||||
did NOT re-run root build/typecheck/mosaic-vitest — those remain the
|
||||
dispatching seat's numbers. The changed-package suites, verify-release
|
||||
suite, and seven mutations were fred's own.
|
||||
@@ -65,19 +65,6 @@ Each of these produced a wrong conclusion before it was written down.
|
||||
conclusion drawn from it describes the wrong tree. Confirm `git rev-parse --show-toplevel`
|
||||
is the tree you think it is before trusting any git output.
|
||||
|
||||
13. **Run the repository's PINNED tool version.** `npx <tool>` resolves a local `node_modules`
|
||||
install when one is present and fetches the latest release when one is not, so the same
|
||||
command answers differently depending on where it ran. A reviewer measuring in a fresh clone
|
||||
or a detached worktree — which is exactly where reviewers measure — has no `node_modules` and
|
||||
silently gets the latest release instead of the pinned one. Measured on mosaicstack#1313: the
|
||||
lockfile pins prettier 3.8.1, under which three guides pass; a version-less `npx` in a
|
||||
worktree resolved 3.9.6, under which the same three fail; and 3.0.0, the floor of the declared
|
||||
`^3.0.0` range, fails a different one. Three versions, three verdicts, identical bytes. Use
|
||||
`node_modules/.bin/<tool>`, or name the version the lockfile pins.
|
||||
14. **A formatter or linter declared as a range is a dated verdict, not a fact.** If a lockfile
|
||||
pins it, the gate is reproducible today and will disagree with itself the day the pin moves.
|
||||
Report a formatting failure with the version that produced it, always.
|
||||
|
||||
### Feedback Categories
|
||||
|
||||
- **Blocker**: must fix before merge (security, bugs, test failures)
|
||||
|
||||
@@ -53,21 +53,23 @@ sends, it does not auto-reply.
|
||||
|
||||
### Exit codes
|
||||
|
||||
| rc | Meaning |
|
||||
| --- | ---------------------------------------------- |
|
||||
| 0 | delivered or queued |
|
||||
| 1 | target session not found |
|
||||
| 2 | text reached the pane but is **still a draft** |
|
||||
| 3 | usage error (bad class, missing `-s`) |
|
||||
| rc | Meaning |
|
||||
| --- | -------------------------------------------------------------------------------------------- |
|
||||
| 0 | delivered or queued |
|
||||
| 1 | target session not found |
|
||||
| 2 | submission unconfirmed: draft still on the input line, or no positive evidence of submission |
|
||||
| 3 | usage error (bad class, missing `-s`) |
|
||||
|
||||
**Never retry on rc=2.** The message is in the target pane; retrying double-sends it. Confirm
|
||||
instead:
|
||||
**Never retry on rc=2.** The message may be in the target pane, and a retry can double-send it.
|
||||
Confirm instead:
|
||||
|
||||
```bash
|
||||
tmux capture-pane -p -t <session>:0.0 | tail -20
|
||||
```
|
||||
|
||||
rc=2 is the normal result when the target is an idle pi seat.
|
||||
rc=0 is the normal result for both idle and busy pi seats (submission confirmed by draft
|
||||
transition, not by prompt glyph). rc=2 on a healthy seat is exceptional — treat it as a real
|
||||
report and investigate the pane.
|
||||
|
||||
## Durable comms
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ packages/mosaic/framework/tools/git/test-lane-brief-pr-linkage.sh | unmeasured i
|
||||
# --- tools/tmux: require a live tmux server ---
|
||||
packages/mosaic/framework/tools/tmux/test-send-message-socket.sh | requires a real tmux server on a throwaway socket; CI image ships no tmux; #1017 burndown (needs tmux in image or a signed permanent exclusion)
|
||||
packages/mosaic/framework/tools/tmux/test-send-message-verdict.sh | requires real tmux-pane fixtures on a throwaway socket; CI image ships no tmux; #1017 burndown (same condition as its sibling)
|
||||
packages/mosaic/framework/tools/tmux/test-send-message-glyph-agnostic.sh | requires real tmux-pane fixtures on a throwaway socket; CI image ships no tmux; #1017 burndown (same condition as its siblings) — signed at adoption of #1262 (rev-code-02 F5), red-first verified on sb-it-1-dt
|
||||
|
||||
# --- single-suite directories: unmeasured in CI ---
|
||||
|
||||
|
||||
@@ -97,13 +97,34 @@ printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b "$BUF" -
|
||||
# would otherwise accumulate forever.
|
||||
sleep 0.5
|
||||
|
||||
# 2) Submit, then POSITIVELY confirm submission; flush with another Enter if it is
|
||||
# still a draft. Success requires positive evidence — the queued banner, OR the
|
||||
# REPL input box located AND clear of our message tail. The historical bug was
|
||||
# treating ABSENCE of a draft as delivery: if the prompt glyph was never matched
|
||||
# (wrong pane / prompt-glyph drift), an unsubmitted message read as "delivered"
|
||||
# and worker->lead relays stalled silently. We now default to UNCONFIRMED and only
|
||||
# upgrade to delivered on positive evidence; anything we cannot confirm fails loud.
|
||||
# 2) Submit, then POSITIVELY confirm submission by DRAFT TRANSITION, not by prompt
|
||||
# glyph. The historical bug was treating ABSENCE of a draft as delivery; the
|
||||
# 2026-08 fix over-corrected to glyph inference (grep '❯|^>|│ >'), which locates
|
||||
# only Claude Code's box and false-NEGATIVES every glyphless REPL (pi renders a
|
||||
# U+2500 rule, no glyph) — a delivered message reported "UNDELIVERED", driving a
|
||||
# retry that duplicates it. Runtime-agnostic evidence: our message tail sits on
|
||||
# the INPUT line (located by the cursor row, not a glyph) BEFORE Enter, and has
|
||||
# LEFT it AFTER — that transition is positive proof of submission and needs no
|
||||
# glyph. Absence alone still never means delivered: if we never saw our draft on
|
||||
# the input line we stay UNCONFIRMED (wrong/dead pane), and a draft that never
|
||||
# leaves the input line stays a DRAFT (exit 2), preserving both historical guards.
|
||||
_cursor_line() { # echo the pane's current input (cursor) line, glyph-free
|
||||
local cy line
|
||||
cy=$("${tmux_cmd[@]}" display-message -p -t "$EFFECTIVE_TARGET" -F '#{cursor_y}' 2>/dev/null) || return 1
|
||||
[ -n "$cy" ] || return 1
|
||||
"${tmux_cmd[@]}" capture-pane -t "$EFFECTIVE_TARGET" -p 2>/dev/null | sed -n "$((cy + 1))p"
|
||||
}
|
||||
_draft_on_input() { # true iff our message tail is sitting on the input line now
|
||||
[ -n "$snippet" ] || return 1
|
||||
grep -qF "$snippet" <<<"$(_cursor_line)"
|
||||
}
|
||||
|
||||
# Baseline: after the paste, our draft must be on the input line. This is positive
|
||||
# proof we are on the right pane and the paste landed — the anchor the transition
|
||||
# check measures against.
|
||||
saw_draft=0
|
||||
_draft_on_input && saw_draft=1
|
||||
|
||||
status="unconfirmed"
|
||||
for attempt in $(seq 1 $((RETRIES + 1))); do
|
||||
"${tmux_cmd[@]}" send-keys -t "$EFFECTIVE_TARGET" Enter
|
||||
@@ -113,20 +134,26 @@ for attempt in $(seq 1 $((RETRIES + 1))); do
|
||||
if grep -qF "$QUEUED_RE" <<<"$pane"; then
|
||||
status="queued"; break
|
||||
fi
|
||||
# Locate the REPL input box (prompt glyph). If we cannot see it, we have NO
|
||||
# evidence of submission state — stay UNCONFIRMED and retry; never infer delivery.
|
||||
# POSITIVE draft evidence from a located prompt box, when one exists. This is the
|
||||
# cursor-row check's blind spot: a pane in COOKED mode (a plain shell whose
|
||||
# foreground process never reads stdin) echoes our paste via the kernel line
|
||||
# discipline and moves the cursor off it on Enter, which is indistinguishable from
|
||||
# a real submit by cursor row alone. If a prompt box IS locatable and still carries
|
||||
# our tail, that is affirmative proof the message was not consumed. Absence of a
|
||||
# glyph is still never used for anything — that inference is the original E7 bug.
|
||||
promptline=$(printf '%s' "$pane" | grep -E '❯|^>|│ >' | tail -1)
|
||||
if [ -z "$promptline" ]; then
|
||||
status="unconfirmed"; continue
|
||||
fi
|
||||
# Input box located AND still carrying our tail => unsubmitted draft. Flush + retry.
|
||||
# (Submitted messages scroll up into history; a draft stays on the ❯ line.)
|
||||
if [ -n "$snippet" ] && grep -qF "$snippet" <<<"$promptline"; then
|
||||
if [ -n "$promptline" ] && [ -n "$snippet" ] && grep -qF "$snippet" <<<"$promptline"; then
|
||||
status="draft"; continue
|
||||
fi
|
||||
# Input box located AND clear of our tail => positively submitted. This is the
|
||||
# only path to success besides the queued banner.
|
||||
status="delivered"; break
|
||||
if [ "$saw_draft" = 1 ]; then
|
||||
if _draft_on_input; then
|
||||
status="draft"; continue # still on the input line => not submitted; flush + retry
|
||||
fi
|
||||
status="delivered"; break # left the input line => positively submitted
|
||||
fi
|
||||
# No confirmed baseline yet: try to (re)acquire it; never infer delivery from absence.
|
||||
if _draft_on_input; then saw_draft=1; status="draft"; continue; fi
|
||||
status="unconfirmed"; continue
|
||||
done
|
||||
|
||||
[ "$VERBOSE" = 1 ] && { echo "--- pane tail ($TARGET) ---"; printf '%s\n' "$pane" | tail -4; echo "---"; }
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
# Red-first regression test for E7 (#1017 task 2): the confirm-check must bind
|
||||
# "delivered" to WHETHER THE MESSAGE WAS SUBMITTED, not to which runtime's prompt
|
||||
# glyph is present. A pi seat renders a U+2500 rule input box with no ❯/^>/│ >
|
||||
# glyph; send-message.sh:118 locates the box only by glyph, so a genuinely
|
||||
# delivered message on a glyphless REPL falsely reports exit 2 "may be UNDELIVERED",
|
||||
# and the operator's rc=2-driven retry duplicates it.
|
||||
#
|
||||
# Parameterized on $SEND: RED against the shipping blob (B and D fail), GREEN
|
||||
# against a candidate patch. No pi; no fake HOME; hermetic throwaway socket.
|
||||
#
|
||||
# Submission counting is EXACT and terminal-echo-independent: the fixture message
|
||||
# is `echo <tok> >>SINK`; each real submission appends one line. wc -l SINK ==
|
||||
# number of times the REPL actually executed the send. This does not depend on how
|
||||
# many times the marker string is painted on screen.
|
||||
set -u
|
||||
SEND="${SEND:?set SEND=/path/to/send-message.sh}"
|
||||
SOCKET="glyphagnostic-$$"
|
||||
TMP="$(mktemp -d)"
|
||||
tmux() { command tmux -L "$SOCKET" "$@"; }
|
||||
cleanup() { command tmux -L "$SOCKET" kill-server 2>/dev/null; rm -rf "$TMP"; }
|
||||
trap cleanup EXIT
|
||||
pass=0; fail=0
|
||||
ok() { printf 'ok %s\n' "$1"; pass=$((pass+1)); }
|
||||
no() { printf 'FAIL %s -- %s\n' "$1" "$2"; fail=$((fail+1)); }
|
||||
|
||||
mk() { tmux new-session -d -s "$1" -x 120 -y 40 -c "$TMP" "PS1='$2' exec bash --noprofile --norc -i"; sleep 0.5; }
|
||||
subs() { [ -f "$1" ] && wc -l <"$1" | tr -d ' ' || echo 0; } # exact submission count
|
||||
|
||||
echo "SEND=$SEND tmux $(command tmux -V | awk '{print $2}')"
|
||||
|
||||
# --- A (control): glyph box (❯) that submits => exit 0, exactly one submission.
|
||||
mk ctl '❯ '
|
||||
SINK="$TMP/sink.ctl"
|
||||
out=$("$SEND" -L "$SOCKET" -t ctl -m "echo x >>'$SINK'" 2>"$TMP/e.ctl"); rc=$?; sleep 0.4
|
||||
if [ "$rc" = 0 ] && [ "$(subs "$SINK")" = 1 ]; then
|
||||
ok "control: ❯-box submits => exit 0, exactly one submission"
|
||||
else no "control: ❯-box submits => exit 0, one submission" "rc=$rc subs=$(subs "$SINK") err=[$(cat "$TMP/e.ctl")]"; fi
|
||||
|
||||
# --- B (THE false-rc regression): glyphless U+2500 box that SUBMITS. Message lands
|
||||
# (subs==1) yet shipping reports exit 2. Must be exit 0.
|
||||
mk sub $'──────── \n'
|
||||
SINK="$TMP/sink.sub"
|
||||
out=$("$SEND" -L "$SOCKET" -t sub -m "echo x >>'$SINK'" 2>"$TMP/e.sub"); rc=$?; sleep 0.4
|
||||
if [ "$rc" = 0 ] && [ "$(subs "$SINK")" = 1 ]; then
|
||||
ok "glyphless: U+2500 box that submits => exit 0 (delivered, not 'UNDELIVERED')"
|
||||
else no "glyphless: U+2500 box that submits => exit 0" \
|
||||
"rc=$rc subs=$(subs "$SINK")(delivered=$([ "$(subs "$SINK")" -ge 1 ] && echo yes||echo no)) err=[$(cat "$TMP/e.sub")]"; fi
|
||||
|
||||
# --- D (duplicate arm): operator follows the rc=2 stderr and retries once. On the
|
||||
# glyphless box, shipping => two submissions (the reported duplicate). The
|
||||
# property: one logical send => exactly one submission. Same fix closes it.
|
||||
mk dup $'──────── \n'
|
||||
SINK="$TMP/sink.dup"
|
||||
tries=0
|
||||
for attempt in 1 2; do
|
||||
tries=$((tries+1))
|
||||
out=$("$SEND" -L "$SOCKET" -t dup -m "echo x >>'$SINK'" 2>/dev/null); rc=$?
|
||||
sleep 0.4
|
||||
[ "$rc" = 0 ] && break # operator stops retrying only when told delivered
|
||||
done
|
||||
if [ "$(subs "$SINK")" = 1 ]; then
|
||||
ok "duplicate: one logical send (rc-driven retry) => exactly one submission (tries=$tries)"
|
||||
else no "duplicate: one logical send => exactly one submission" "submissions=$(subs "$SINK") tries=$tries"; fi
|
||||
|
||||
# --- E (faithful hung managed TUI, NOT a cooked shell): raw/no-echo, paints nothing.
|
||||
# A cooked `sleep infinity` echoes the paste via the kernel line discipline and
|
||||
# false-passes a cursor-row fix that is correct on real seats (measured). So: raw.
|
||||
mk_rawstuck() { tmux new-session -d -s "$1" -x 120 -y 40 -c "$TMP" \
|
||||
"bash --noprofile --norc -c 'stty -echo -icanon min 1 time 0 2>/dev/null; exec sleep infinity'"; sleep 0.5; }
|
||||
mk_rawstuck estuck
|
||||
SINK="$TMP/sink.estuck"
|
||||
out=$("$SEND" -L "$SOCKET" -t estuck -r 1 -m "this stuck draft was never submitted" 2>/dev/null); rc=$?
|
||||
sleep 0.3
|
||||
if [ "$rc" != 0 ] && [ "$(subs "$SINK")" = 0 ]; then
|
||||
ok "raw/no-echo stuck TUI (not submitted) => non-zero (no false delivered)"
|
||||
else no "raw stuck TUI must NOT report delivered" "rc=$rc subs=$(subs "$SINK")"; fi
|
||||
|
||||
# --- F (busy/queued branch, your BUSY-not-runtime finding): glyphless pane rendering the
|
||||
# queued banner, never consuming. QUEUED_RE :113 fires before the glyph grep => rc=0.
|
||||
mk_busy() { tmux new-session -d -s "$1" -x 120 -y 40 -c "$TMP" \
|
||||
"bash --noprofile --norc -c 'printf \"Press up to edit queued messages\n\"; exec sleep infinity'"; sleep 0.5; }
|
||||
mk_busy ebusy
|
||||
SINK="$TMP/sink.ebusy"
|
||||
out=$("$SEND" -L "$SOCKET" -t ebusy -m "echo x >>'$SINK'" 2>/dev/null); rc=$?; sleep 0.3
|
||||
if [ "$rc" = 0 ]; then
|
||||
ok "busy/queued-banner glyphless => exit 0 (queued is delivery; runtime owns custody)"
|
||||
else no "busy/queued-banner must report delivered" "rc=$rc"; fi
|
||||
|
||||
# --- C (historical-bug guard): unresolvable target. No pane ever carried our draft
|
||||
# => must fail, never infer delivered from absence of a glyph/snippet.
|
||||
if out=$("$SEND" -L "$SOCKET" -t "nonexistent-$$" -m "echo x >>'$TMP/sink.wrong'" 2>/dev/null); then
|
||||
no "wrong-pane: unresolvable target must NOT report success" "expected non-zero, got 0"
|
||||
else ok "wrong-pane: unresolvable target => non-zero (no false delivered)"; fi
|
||||
|
||||
echo "---"; echo "pass=$pass fail=$fail"
|
||||
[ "$fail" = 0 ]
|
||||
@@ -4,16 +4,19 @@
|
||||
#
|
||||
# 1. DELIVERED — a REPL that renders a `❯ ` input box and submits on Enter
|
||||
# (text scrolls to history, box clears) => exit 0 "✓ delivered".
|
||||
# 2. UNCONFIRMED — a pane with NO locatable prompt glyph. This is the exact
|
||||
# historical FALSE POSITIVE: pre-patch it printed "✓ delivered"
|
||||
# exit 0; post-patch it MUST fail loud (exit 2, stderr
|
||||
# "could not confirm submission").
|
||||
# 2. DELIVERED — a pane with NO prompt glyph that DOES submit => exit 0. A pi
|
||||
# seat is this fixture (U+2500 rule, no glyph). Reshaped for
|
||||
# #1257; see the note at the fixture for why the old exit-2
|
||||
# assertion was wrong.
|
||||
# 2b. UNCONFIRMED— a glyphless pane that never submits (raw/no-echo hung TUI)
|
||||
# => must fail loud. This carries the historical
|
||||
# false-positive guard that fixture 2 used to be credited with.
|
||||
# 3. DRAFT — a `❯ `-prompt pane that never submits (message stays on the
|
||||
# input line) => exit 2, stderr "unsubmitted draft".
|
||||
set -uo pipefail
|
||||
|
||||
HERE=$(cd -- "$(dirname -- "$0")" && pwd)
|
||||
SEND="$HERE/send-message.sh"
|
||||
SEND="${SEND:-$HERE/send-message.sh}"
|
||||
SOCKET="verdict-test-$RANDOM-$$"
|
||||
TMP=$(mktemp -d)
|
||||
trap 'tmux -L "$SOCKET" kill-server >/dev/null 2>&1 || true; rm -rf "$TMP"' EXIT
|
||||
@@ -37,19 +40,44 @@ else
|
||||
no "delivered: ❯-prompt REPL that submits => exit 0 ✓ delivered" "rc=$rc out=[$out] err=[$(cat "$TMP/e1")]"
|
||||
fi
|
||||
|
||||
# --- Fixture 2: NO prompt glyph (default bash PS1). THE regression: pre-patch this
|
||||
# was a silent false-positive "delivered"; post-patch it must be unconfirmed→exit 2.
|
||||
# --- Fixture 2: NO prompt glyph, and the pane DOES submit (interactive bash).
|
||||
# RESHAPED 2026-08-16 (#1257), deliberately. This fixture previously asserted
|
||||
# exit 2 here and was labelled "false-positive FIXED". That assertion was wrong,
|
||||
# and locking it in is what kept E7 alive: the pane submits, so "delivered" is
|
||||
# the truth, and a pi seat — whose input box is a bare U+2500 rule with no glyph
|
||||
# — IS this fixture. Reporting exit 2 for it told operators a delivered message
|
||||
# may be undelivered, and the retry that advice invites is the duplicate.
|
||||
#
|
||||
# The guard this fixture was reaching for is real and is NOT dropped: "never
|
||||
# infer delivered from absence" is now enforced positively by fixture 2b below
|
||||
# (glyphless AND not submitting => must fail) and by fixture 3 (locatable box
|
||||
# still carrying our tail => draft). Absence alone decides nothing either way.
|
||||
tmux -L "$SOCKET" new-session -d -s noglyph -c "$TMP" \
|
||||
'PS1="sh-noglyph$ " exec bash --noprofile --norc -i'
|
||||
sleep 0.3
|
||||
if out=$("$SEND" -L "$SOCKET" -t "=noglyph" -m "verdict fixture two must fail loud" 2>"$TMP/e2"); then
|
||||
no "unconfirmed: glyphless pane must NOT report success" "expected exit 2, got 0 (out=[$out])"
|
||||
out=$("$SEND" -L "$SOCKET" -t "=noglyph" -m "verdict fixture two must fail loud" 2>"$TMP/e2"); rc=$?
|
||||
if [ "$rc" -eq 0 ] && grep -qF "✓ delivered" <<<"$out"; then
|
||||
ok "delivered: glyphless pane that submits => exit 0 (runtime-agnostic, E7 FIXED)"
|
||||
else
|
||||
no "delivered: glyphless pane that submits => exit 0" "rc=$rc out=[$out] err=[$(cat "$TMP/e2")]"
|
||||
fi
|
||||
|
||||
# --- Fixture 2b: NO prompt glyph AND never submits — a hung managed TUI holding the
|
||||
# terminal in raw/no-echo, which is what a stuck agent seat actually is (measured
|
||||
# on live pi: stty -echo -icanon). Nothing is echoed, nothing is consumed, so
|
||||
# there is no positive evidence of submission and the tool MUST fail loud. This
|
||||
# is the historical false-positive guard, kept as a positive test.
|
||||
tmux -L "$SOCKET" new-session -d -s rawstuck -c "$TMP" \
|
||||
'bash --noprofile --norc -c "stty -echo -icanon min 1 time 0 2>/dev/null; exec sleep infinity"'
|
||||
sleep 0.3
|
||||
if out=$("$SEND" -L "$SOCKET" -t "=rawstuck" -r 1 -m "verdict fixture two-b never submitted" 2>"$TMP/e2b"); then
|
||||
no "unconfirmed: glyphless hung TUI must NOT report success" "expected non-zero, got 0 (out=[$out])"
|
||||
else
|
||||
rc=$?
|
||||
if [ "$rc" -eq 2 ] && grep -qF "could not confirm submission" "$TMP/e2"; then
|
||||
ok "unconfirmed: glyphless pane => exit 2 + 'could not confirm submission' (false-positive FIXED)"
|
||||
if [ "$rc" -ne 0 ] && grep -qF "could not confirm submission" "$TMP/e2b"; then
|
||||
ok "unconfirmed: glyphless hung TUI (raw/no-echo) => non-zero + 'could not confirm submission'"
|
||||
else
|
||||
no "unconfirmed: glyphless pane => exit 2 + stderr" "rc=$rc err=[$(cat "$TMP/e2")]"
|
||||
no "unconfirmed: glyphless hung TUI => non-zero + stderr" "rc=$rc err=[$(cat "$TMP/e2b")]"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user