Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 105 additions & 43 deletions .dev-loop/INGEST_REPORT.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,115 @@
# Knowledge consolidation15 open PRs (#17–#40) → one reconciled state
# Knowledge flush3 insight(s): 1 ingested, 2 dropped as in-flight duplicates

The 15 open `knowledge/*` PRs (created 2026-08-04 → 2026-08-05, before the
harvest processed-store dedupe fix in #41) contained 123 file-versions of ~75
unique pages, with the same insight landing at up to 3 different paths across
up to 8 PRs. Per-PR review would re-import those duplicates, so — as with the
#6–#13 consolidation — this branch carries the reconciled end-state and the 15
PRs are closed in its favor.
Queue drained: `~/.dev-loop/queue/f1a3ae46-….jsonl` (3 pending rows). One new page,
no existing page rewritten, one contradiction between two open PRs flagged in `log.md`.

## Verified best-practice

Every adopted page's sources were carried from its originating PR's flush, where
they were live-verified at flush time; no new URLs were introduced during
consolidation (checked mechanically: every `http(s)` URL in every merged page
appears in a source PR's diff; every added body line in amended pages traces to
a source PR hunk — orphan-line verification). Confidence fields were kept as the
originating flushes set them, except client-side-rate-limiting where the union
of provider-doc citations (Okta, Auth0, GitHub, OpenAI, RFC 6585) supports
`verified` for the load-bearing claims. One subagent's fabricated content (12
files matching neither main nor any PR, with invented source URLs) was detected
by the same verification and replaced with true PR content.
### A — ingested: a worker agent's in-band question reaches nobody

**Claim.** When an agent worker runs unattended and raises a question through its own
interactive question UI, no answer arrives. With a TTY (a tmux pane) the chooser waits
indefinitely while every liveness check passes; without a TTY the tool self-resolves with
empty answers and the agent proceeds as if answered. The channel a worker uses to ask its
coordinator for a decision therefore has to be out-of-band and durable.

**Sources checked and what each supports.**

| Source | Supports | Result |
|--------|----------|--------|
| https://github.com/anthropics/claude-code/issues/50728 | no-TTY branch | `AskUserQuestion` "auto-resolves immediately with empty answers", ~37 ms, before a `can_use_tool` callback or `PreToolUse` hook can intervene; agent receives `User has answered your questions: .` Environment: `claude-agent-sdk` 0.1.63, bundled CLI 2.1.114, Docker. **Closed as not planned** — the behaviour is not being changed, which is what makes the workaround durable knowledge |
| https://github.com/anthropics/claude-code/issues/29530 | no-TTY branch, second report | Same tool "does not render any interactive UI (question text, selectable options)" and returns empty (CLI 2.1.63). **Open**, labelled `bug`, `has repro` |
| https://man7.org/linux/man-pages/man1/tmux.1.html | the unblock mechanism | `send-keys` writes key events into a pane; named keys go without `-l` (with it tmux sends the literal characters) |
| dev-loop 1.4.2 `skills/orchestrate/scripts/` (read in this checkout) | the TTY branch + the remedy, as shipped behaviour | `orca-worker-stalled.sh` header records the measurement: three workers held a live PTY on an interactive prompt for **75 minutes** with byte-identical diffs while every alive/dead check passed, and `lastOutputAt` was "measured and rejected" because a TUI repaints its spinner. `ask-coordinator.sh` is the durable channel (atomic `questions/<task>.json`, refuses task names with `/`, `.`, `..`); `watch-status.sh` surfaces it as its own exit code; `send-prompt.sh keys` validates every key against the allowlist `Up Down Left Right Enter Escape Tab Space 0-9 y n` before sending any, one `send-keys` per key. `SKILL.md` supplies the "at 600 s and 900 s both workers proceeded on a conservative assumption" measurement |
| Field observation, this session (`lo-4-qag1`) | the re-send step | 30-minute stall on a numbered chooser; two rounds of number-then-Enter (selection, then confirmation) cleared it; the prompt queued in the input line before the chooser opened was never submitted and the phase advanced only after it was re-sent |

**Confidence: `verified`.** Two published issues with reproductions cover the no-TTY branch;
the TTY branch and the remedy are backed by shipped code plus its own recorded measurements.
The one part resting on this session alone — that answering the chooser leaves a previously
queued prompt unsubmitted — is written as a single `Do this` step and attributed to the field
observation in `Sources`, not asserted as doc-backed.

### B — dropped: `worktree_escape` escalating on read-only cross-worktree access

Not ingested, and the candidate's framing is **wrong**. It claimed the rule "fires `ask` even on
read-only access". Read against the rule source (`plugins/guardrails/hooks/bash-guard.sh`,
`worktree_escape`, groundwork checkout): after stripping the worker's own worktree path and any
configured `allowPaths`, a surviving `$main_root/` mention fires **only if** a write verb
(`rm|mv|cp|tee|mkdir|touch|install|dd`) or a redirect to an absolute path also matches — and the
two tests are matched independently over the whole command string. A bare `grep`/`awk`/`git status`
read does not fire; a read sharing a command line with any write verb does. That is exactly what
open PR #51 already documents, so the candidate adds nothing and would re-introduce a wrong mechanism.

### C — dropped: Orca dispatch-binding failure taxonomy

Not ingested. The candidate's content (check for the idle prompt before binding; `runtime_unavailable`
= the turn's tail still occupies the terminal, wait and bind a *new* unit; `agent_unconfigured` = the
agent process is dead, close the terminal and create a new worker-mode agent; always pass the worktree
with the terminal) is carried by open PRs #51 and #47 in equal or better form, with the same field
evidence. Verified only far enough to confirm the overlap.

## Existing-layer check

- Merged-main near-dup scan before consolidation: pairwise Jaccard over
title + "When this applies" across all 141 merged pages → **0 flagged pairs**;
previously merged content carries no duplication.
- Cross-PR dedup during consolidation: 10 duplicate clusters collapsed to one
canonical page each (rate limiting 8→1, call-site enumeration 7→folded into
the canonical merged in #20, stderr/exit-0 diagnostics 4→1, sysroot 2→1,
env-off-switch 2→1, completion predicates 2→1, robots.txt 2→1,
harness-mediated results 2→1, leaked artifacts 2→1, orchestration category
naming unified). Three near-pairs kept distinct after trigger comparison,
with mutual `related:` links (differential setup vs interpretation; expansion
semantics vs off-switch design; import-time tactics vs level choice).
- 24 existing pages received union-merged amendments; additions already present
in main (from #16/#20) were skipped, and all non-canonical `related:` ids
were remapped to canonical page ids (post-merge broken-link scan: 0).
Routed via `INDEX.md` → `infrastructure` (its route line already names multi-agent orchestration:
worker liveness signals, shared run state, tmux pane delivery, completion gates, worktree-isolated
workers) → `wiki/infrastructure/index.md` → the `agent-orchestration` category. Every page in that
category whose "load when" line could overlap was opened in full, plus the one cross-domain page the
no-TTY branch touches.

Pages read: infrastructure-agent-orchestration-worktree-isolated-workers, infrastructure-agent-orchestration-pane-delivery-confirmation, infrastructure-agent-orchestration-control-signals-vs-primary-artifacts, infrastructure-agent-orchestration-shared-run-state, platforms-processes-non-interactive-cli-invocation

| Page read | Overlap with insight A | Verdict |
|-----------|------------------------|---------|
| `…-pane-delivery-confirmation` | Owns "did the keystroke land" — busy indicator vs pane diff, serialising sends. Says nothing about *why* the target is waiting or who is supposed to answer | Adjacent, not a home. Linked both ways; step 4's "busy indicator → keep waiting" row defers to it |
| `…-control-signals-vs-primary-artifacts` | Owns done/alive/dead verdicts and has a stalled-third-state row (fresh heartbeat + unchanged artifact) | Adjacent. The wedged-on-a-question case is one cause of that state, referenced from the new page's edge cases; linked both ways |
| `…-shared-run-state` | Owns the layout of the run's shared directories (`status`, `briefs`, `escalations`) and run-id namespacing | Adjacent — the `questions/` record is a sibling of those. The new page defers path layout to it rather than restating; linked both ways |
| `…-worktree-isolated-workers` | Brief/output contract and guardrail direction. No question-channel content | No overlap |
| `platforms-processes-non-interactive-cli-invocation` | Covers a prompt-capable CLI being *invoked* from a script (detach fd 0, close the prompt channel, bracketed-paste submit). Its concern is the harness's invocation; ours is a question raised mid-turn by a worker already running | No overlap; one-way `related:` link added from the new page |

**Merge-before-create judgement.** No existing page carries the trigger "a worker asked a question
and nobody is there". The three adjacent pages each own one slice (delivery, liveness verdict, state
layout) and splitting this insight across all three would violate one-case-per-page in each of them.
New page created, all three linked bidirectionally.

**Conflicts flagged.** One, and it is between two *open PRs*, not with merged content: #47's
`worktree-isolated-workers` row says the guardrail's read/write asymmetry is version-dependent and
that "a conservative rule treats any cross-worktree path reference … reads included"; #51's row says
reads still pass alone and fire only alongside a write verb or absolute redirect. Reading the rule
source settles it in #51's favour (see "B — dropped" above). Recorded in `log.md` as a
`contradiction` entry for resolution at merge time; nothing overwritten here.

**Links added.** `related:` now bidirectional between the new page and `…-pane-delivery-confirmation`,
`…-control-signals-vs-primary-artifacts`, `…-shared-run-state`; one-way to
`…-session-completion-gates` and `platforms-processes-non-interactive-cli-invocation`.

## Open-PR check

`gh pr list` is unusable here (the ambient `gh` token returns HTTP 401 on the GraphQL API), so open
PRs were listed through the GitHub MCP REST endpoint and every knowledge head was fetched and diffed
locally: `git fetch origin <head>` then `git diff origin/main FETCH_HEAD -- wiki/`.

Open `knowledge/*` heads at flush time (11): #62, #61, #58, #57, #56, #55, #52, #51, #50, #49, #47.
(#63 is `feat/role-model-selection`, not a knowledge PR.)

| Candidate | Overlapping open head(s) | Verdict |
|-----------|--------------------------|---------|
| A — unattended worker questions | none. Every open head's `wiki/` diff was grepped for `menu` / `interactive` / `send-keys` / `prompt-capable`; the only hits are `related:` id lists and an unrelated job-control row in #57 | **new** — ingested |
| B — `worktree_escape` on read-only access | **#47** (`worktree-isolated-workers` edge row + a field-evidence source line with the same Wave-2 `awk`/`grep`/`git status` evidence) and **#51** (same page, mechanism-accurate rows plus a local reproduction) | **drop** — pending duplicate, twice over, and the candidate's own wording is the mechanism #51 corrects. Nothing unique to fold; the correction that *is* new (the rule source settles #47 vs #51) is recorded in `log.md` rather than pushed into either branch |
| C — Orca dispatch-binding taxonomy | **#51** (`pane-delivery-confirmation`: four new edge rows — bind only at the idle prompt, `runtime_unavailable` → wait and bind a fresh unit, `agent_unconfigured` → replace the agent, pass the worktree with the terminal — plus an `Instead of` row) and **#47** (`control-signals`: the done-signal-is-not-release-time row) | **drop** — pending duplicate with nothing new |

No sibling duplicate PR was opened, and nothing was pushed to another contributor's branch.

## Routing decision

- New categories: `infrastructure/agent-orchestration` (5 pages; unified the
competing `orchestration`/`agent-orchestration` names), `databases/data-survey`
(1), `qa/deliverables` (1). All other pages route into existing categories.
- Canonical-path decisions: rate limiting → `backend/common/reliability/`
(sits beside timeouts-and-retries; 6 of 8 variants chose it); stderr
diagnostics → `platforms/processes/` (concern spans beyond shells); leaked
artifacts → `testing/data/artifact-leakage-from-a-suite`; call-site
enumeration → the existing `backend/common/change-impact/` page.
- All 38 new pages listed in their domain indexes (nearest-index rule; backend
routes via its python sub-index for bytecode-cache-staleness); INDEX.md domain
summaries updated for infrastructure/qa/databases. Full-wiki lint: frontmatter,
ids, related-links, index coverage, size, qualifiers, staleness → 0 findings.
| Insight | Target | New category? |
|---------|--------|---------------|
| A | `infrastructure` / `agent-orchestration` / **new page** `unattended-worker-questions.md` (id `infrastructure-agent-orchestration-unattended-worker-questions`) | No — `agent-orchestration` already exists and its scope line in `INDEX.md` covers worker liveness signals and tmux pane delivery. This is a sixth page in it |
| B | none (retired) | — |
| C | none (retired) | — |

`applies_to: [tmux, general]` — the detect/unblock half is multiplexer-specific, the
channel-design half is not. Domain index gained a "load when" row for the new page; `log.md`
gained one `ingest` entry and one `contradiction` entry.

Files changed: `wiki/infrastructure/agent-orchestration/unattended-worker-questions.md` (new, 76 body
lines), `wiki/infrastructure/index.md`, three `related:` back-links, `log.md`.
2 changes: 2 additions & 0 deletions log.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ Append-only. Format: `## [YYYY-MM-DD] <ingest|revise|lint|gap|contradiction|drif
## [2026-08-06] revise | Union-merged amendments from the same 15 PRs into 24 existing pages (largest: tests-that-cannot-fail — 7 PRs: per-assertion mutation granularity, restore-mechanism-by-commit-state, suite-total-drop detection; portable-shell-scripts — 8 PRs: POSIX inline set-- reordering, ${VAR:-} vs ${VAR-} colon semantics, quote-by-what-the-text-is; test-data-and-isolation — 6 PRs: env-derived write paths, absent-variable cases, exec-bit fixtures, leak attribution; change-impact/call-site-enumeration — 8 unique additions from 7 path-variants: rename-in-place, same-type reorder, *args forwarding, two-searches-agree fallacy, codemod, coverage completion check). All non-canonical cross-page ids remapped to canonical pages; every added line traced to its source PR hunk (fabricated worker output was detected by orphan-line verification and replaced with true PR content).
## [2026-08-06] dedup | Cross-PR duplicate clusters collapsed: client-side rate limiting (8 PR versions at 3 paths → 1), call-site enumeration (7 versions at 7 paths → additions folded into the already-merged canonical page), stderr/exit-0 diagnostics (4 versions at 4 paths → 1), macOS sysroot (2→1), env-var off switch (2→1), completion predicates (2→1), robots.txt (2→1), harness-mediated results (2→1), leaked test artifacts (2→1), orchestration category naming (orchestration vs agent-orchestration → agent-orchestration). Kept distinct after trigger comparison: differential-testing vs differential-run-agreement (setup vs interpretation), unset-versus-empty-parameters vs env-var-off-switches (semantics vs design, cross-linked), import-time-side-effects vs test-level-choice edge (tactics vs level choice, cross-linked). Merged-main near-dup scan (Jaccard over title+trigger, 141 pages): 0 pairs — no duplication among previously merged content.
## [2026-08-06] ingest | Reconciled post-consolidation flush PRs #42–#43 (created against pre-#44 main). Folded unique content: platforms/filesystems/permissions-and-exec-bits (+`sh "$SCRIPT"` interpreter-invocation rows — stubs need no exec bit, avoids EDR chmod+x heuristics), platforms/shells/command-text-inspected-before-execution (+gate-AUTHOR step 8: parse all three POSIX quoting forms, expand only ~/$HOME/${HOME}; red-then-green bats evidence), testing/quality/checks-that-cannot-pass (+vacuously-green pre-implementation test row), platforms/processes/tool-diagnostics-without-a-failing-exit-code (+repeat-suppression edge, +clang reproduction source), infrastructure/ci-cd/secrets-handling (+independent credential channels row: gh token vs git SSH vs API token, git push --dry-run / author:@me probes), testing/mocking/what-to-mock (+ESM read-only-bindings DI rows, +PATH-emptied tripwire negative-proof). Dropped as duplicates of the #44 state: #42's rate-limit row (already in timeouts-and-retries + client-side-rate-limiting), #42's warning-only-diagnostics page (same trigger as tool-diagnostics-without-a-failing-exit-code), #42's leak-attribution row (already in test-data-and-isolation).
## [2026-08-08] ingest | knowledge-flush of 3 queued insights → 1 ingested, 2 dropped as in-flight duplicates. New: infrastructure/agent-orchestration/unattended-worker-questions (a worker agent's in-band question reaches nobody — with a TTY the chooser waits indefinitely, without one it self-answers empty in ~37 ms; classify a live-terminal stall from the terminal tail before restarting, unblock with an allowlisted key sequence validated whole and re-driven until the idle prompt returns, re-send the prompt the chooser swallowed, and prevent recurrence with a durable out-of-band question record the watcher wakes on). Sources: anthropics/claude-code#50728 (closed as not planned) and #29530 (open), tmux(1), plus the shipped dev-loop 1.4.2 orchestrate scripts (ask-coordinator.sh, watch-status.sh, send-prompt.sh keys, orca-worker-stalled.sh's 75-minute measurement) and a field observation on worker lo-4-qag1. Dropped: the worktree_escape read-only-escalation candidate (already in flight twice — PR #47 and PR #51) and the Orca dispatch-binding taxonomy candidate (already in flight — PR #51's pane-delivery-confirmation rows and PR #47's control-signals row).
## [2026-08-08] contradiction | PRs #47 and #51 give opposite mechanisms for the same guardrail. #47's worktree-isolated-workers row claims "a conservative rule treats any cross-worktree path reference in command text as a potential write, reads included" and that the read/write asymmetry is version-dependent; #51's row claims reads still pass alone and fire only when a write verb or an absolute redirect appears anywhere in the same command string. Read against groundwork guardrails `plugins/guardrails/hooks/bash-guard.sh` (worktree_escape), #51 is correct: after stripping the worker's own worktree path and any configured allowPaths, a surviving `$main_root/` mention fires only if `match "(^|[[:space:];&|(])(rm|mv|cp|tee|mkdir|touch|install|dd)[[:space:]]"` or `match "(>|>>)[[:space:]]*[\"']?/"` also matches — the two tests are independent, which is why a read sharing a command line with any write verb escalates. Resolve in favour of #51's wording when merging.
Loading
Loading