You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Context: This repo keeps feature plans as markdown under docs/plans/, each carrying an ## Open Questions section for decisions only the human owner can make. Today those questions are answered by typing, either in a local Claude Code session or through the Telegram poll channel shipped in #2701. This issue adds a spoken channel: the agent pre-records each question as an audio clip, plays them back one at a time, and writes the recorded answers into the plan docs that asked them. The target user is a remote architect who would rather talk for fifteen minutes than read and type.
This feature is per-project and per-machine by design. Each project in projects.json is owned by exactly one machine (single-machine ownership), and the tooling built here runs against one repo on the machine that owns it, with its own calling link tied to that project. It ships to other machines the way every global capability does, and each instance harvests only its own repo. Cross-project harvesting is an explicit non-goal.
Problem
Unanswered architect questions are the current throttle on delivery velocity. Work sits waiting on a decision only the owner can make, and the channel for getting those decisions is narrow enough that the queue drains slower than it fills. The intended cadence for this feature is a call a few times per week, which is also why per-session preparation cost is acceptable and why clip reuse matters.
Open questions in this repo are answered by typing, and only when the owner happens to be at a keyboard with enough context loaded to answer well. Two things are broken.
First, the corpus is not machine-readable, so nobody knows what is actually outstanding. There is no tool that enumerates open questions across plan docs and issues. The one extractor that exists (bridge/message_drafter.py:100_extract_open_questions) reads agent stdout, not files, and would misfire on the real corpus (see Recon Summary). Answering questions therefore requires a human to remember which plan asked what.
Second, there is no spoken channel at all. Outbound audio exists (tools/tts/, /do-debrief) and inbound transcription exists (tools/transcribe/), but they have never been joined into a question-and-answer loop, and every synthesized clip is deleted immediately after send.
Current behavior:
No command answers "what open questions exist right now, and which ones block the most work?" Verified: nothing in tools/, scripts/, or the skills directories walks docs/plans/ or gh issue list for questions.
Answering happens through /ask-me, which drafts its blocker list from model context per invocation. Nothing is harvested from disk, so the list is not reproducible between runs.
tools/tts.synthesize() writes a fresh OGG/Opus file per call and the documented send path passes --cleanup-after-send, so bridge/telegram_relay.py:690 unlinks the audio after delivery. There is no clip library and no content-keyed audio cache anywhere in the repo.
Answers arrive as chat prose. Getting them back into the plan doc that asked the question is manual.
Desired outcome:
python -m tools.open_questions list prints every genuinely open question across docs/plans/*.md and open issues, each with a stable ID, its source location, and an unblock-leverage score.
The owner receives questions as Telegram voice notes, one at a time, highest-leverage first, and answers by replying with a voice note. Answering one question can prune others that its answer moots, so the session ends early rather than reading the whole list.
Recorded answers land in the ## Open Questions section of the plan doc that asked them, attributed and dated, committed to main.
Re-running the harvester after a session shows the answered questions resolved and does not re-ask them.
Definitions
Term
Definition
Reference
Plan doc
A markdown feature plan under docs/plans/, with YAML frontmatter including status, type, appetite, and tracking (the GitHub issue URL).
A pre-computed DAG over harvested questions. Each node is one question; each edge says which other questions a given answer disposition moots. Traversal order is decided before the session, not during it.
New in this issue
Pruning edge
An edge asserting "if the answer to Q1 is disposition A, then Q4 and Q7 no longer need asking."
New in this issue
Unblock leverage
How much work a question's answer releases: count of plan docs and open issues that cannot proceed until it is answered, plus questions it would prune.
New in this issue
Clip
A persistent OGG/Opus audio file of one question's spoken text, content-addressed by the question's hash so it is reused rather than re-synthesized.
New in this issue
Project isolation
Each project is owned by one machine, and its knowledge sources partition by project key. Context does not cross project boundaries.
An item under ## Open Questions that is explicitly not an ask: it records a decision already made with a stated default, overturned only by an owner objection. "Silence keeps the default."
Plan-doc writes in the shared main checkout have no single-writer protocol #2650: plan-doc writes in the shared main checkout have no single-writer protocol. Closed, but the protocol was never built. Catalogues seven collision shapes with SHAs, including an unrecoverable one on 2026-08-13 where a plan-doc agent's git add -A swallowed another lane's staged code onto main under an unrelated issue reference. This is the governing hazard for the writeback step.
Direction is settled: pre-record the questions, so the delivery agent only plays clips and captures answers. All intelligence runs before the session starts, where it has full context and no latency budget. Spending generously on preparation is the intended trade.
Scope boundary, load-bearing. Everything below operates on a single repo: this one, on this machine. The harvester reads the local working tree and this repo's own issues. It must not reach into another project's checkout, another repo's issues, or a remote repo's contents API. Other machines get the same capability by the normal global-tooling sync and harvest their own project independently, each with its own calling link.
Four components.
1. Harvester. A tool that enumerates open questions from docs/plans/*.md (excluding tools/plan_doc_scope.py's NON_LANE_PLANS) and open issues, emitting records with a content-hash ID, source path and line, and the owning plan's tracking: issue. It must distinguish genuinely open questions from resolved ones and from defaulted ones. The corpus uses at least seven mutually incompatible resolution conventions, so this is the hard part and the part with standalone value.
2. Preparation pass. Score each question by unblock leverage; for each, enumerate plausible answer dispositions and compute which other questions each disposition moots; pick the root that maximizes expected pruning; emit a tree bounded by a target session length. For each node, author the spoken question text, a deeper-context clip for "what's this about?", a readback line, and a draft of the writeback that each anticipated disposition would produce. Synthesize every clip and store it content-addressed so re-preparation only re-cuts what changed.
3. Delivery. One clip per outbound voice note; the owner's reply voice note is the answer to the clip that was just sent. The binding is structural per message, which is why the first transport should be Telegram rather than a live call: no timestamp alignment is needed. After each answer, follow the pre-computed pruning edges to select the next node. Off-script answers stop the traversal and are recorded verbatim for the writeback pass.
4. Writeback. Per plan doc, draft the resolution and any issue edits the answer implies. Drafting fans out in parallel; applying and committing is strictly sequential and single-writer. See Pre-requisites: concurrent plan-doc writes on the shared main checkout have already caused unrecoverable damage, and no lock exists. Pre-drafted writebacks are re-verified against current main before being applied, because they were authored against an older tree.
Constraints discovered in recon that the plan must honor:
Plan-doc edits use the Edit tool, never Write. Four PostToolUse validators match Write on docs/plans with a propagating exit policy (.claude/hooks/manifest.toml:133).
Spoken text must not recite multi-digit identifiers, per the /do-voice-recording prosody rules. Clips reference work by name, not by issue number.
Synthesis rejects text over 4096 characters, which bounds per-clip length.
Clips must not be sent with --cleanup-after-send; the relay unlinks the file on success and that would destroy the library.
In a headless bridge session, valor-ask-poll renders and AskUserQuestion must be the turn's final act to fire the needs_human edge (.claude/skill-context/ask-me.md). A voice session waits the same way rather than inventing a mechanism.
Recon Summary
Four parallel Explore agents. Findings that changed the issue are marked.
Confirmed: 6 items
No aggregator exists. Nothing in tools/, scripts/, or the skills directories harvests questions from plan docs or issues. No duplication risk.
## Open Questions is a real convention with 79% coverage: 19 of 24 top-level plan docs carry a question-or-decision section. PLAN_TEMPLATE.md:495 is its source.
A reusable starting point exists: bridge/message_drafter.py:100_extract_open_questions, with tests at tests/unit/test_open_question_gate.py. Its heading regex and placeholder filter are sound; its input is stdout, not files.
Audio primitives are sufficient for the Telegram leg. Outbound: tools/tts.synthesize() returns OGG/Opus, never raises, local Kokoro ONNX with OpenAI tts-1 fallback. Inbound: tools/transcribe.transcribe() via SuperWhisper with an OpenAI whisper-1 fallback, and bridge/media.py:461 already transcribes inbound voice notes into the agent's message text automatically.
Delivery has a supported non-bridge producer path: valor-telegram send --voice-note, or RPUSH the payload directly. reflections/pm_briefings/delivery.py is a working in-process precedent.
Commit rules for plan-only commits on main are as described in the Solution Sketch. core.hooksPath is set to .githooks on this machine, so both validation legs are live.
Revised: 4 items
The in-repo corpus is 15 genuinely unresolved questions today, and that is a floor set by the bottleneck, not a ceiling on demand. A full pass over the 24 top-level plan docs found 15 genuinely unresolved questions in 6 documents (13 under a strict reading), against 46 already resolved and 11 "defaulted" items that are explicitly not asks. Of 127 open issues, only 16 have any question-shaped section. Owner ruling: the low count reflects velocity already throttled by waiting on architect answers, so draining the queue raises velocity and generates more questions. The count is therefore not a gate on building, and the original framing of this bullet as a reason to reconsider scope is withdrawn.
Resolution marking is not machine-readable. Seven incompatible conventions in active use: section-level "None.", heading rename to ## Resolved Questions, a blockquote RESOLVED banner over retained original text, per-item inline markers with varied wording, strikethrough, "all open questions resolved" prose, and RETIRED as a disposition distinct from resolved. Nine distinct heading strings, one of them an H3. The naive extractor would emit 9 false positives from the defaulted items in improvement-controller-lane-3 and lane-5 alone.
Issues are effectively not harvestable as a structured source. 16 of 126 carry a question section, across 14 distinct heading strings, varying in case, heading level, and plurality. Treat issues as a best-effort secondary source, not a peer of plan docs.
Writeback must be sequential, not a parallel fan-out. This reverses the original framing of one subagent per plan doc writing concurrently. Plan-doc writes in the shared main checkout have no single-writer protocol #2650 documents seven real collision shapes on the shared main checkout, including an unrecoverable 2026-08-13 incident. No lock exists. Parallel drafting is safe and retained; parallel writing is not.
Pre-requisites: 3 items
No single-writer protocol for plan docs.docs/archive/plans-completed/plan-doc-single-writer-lease.md still reads status: Ready and was moved into the completed-plans archive by a content-free rename (433c166a9), apparently swept up by PR Move completed plans to docs/archive/plans-completed/ #2942's bulk move. It was archived as completed without being built. This issue does not depend on it being built, because sequential single-writer writeback sidesteps it, but that discrepancy should be filed separately.
A resolution-marking convention needs choosing before the harvester can be reliable. Either normalize the seven existing shapes into one (a PLAN_TEMPLATE.md and /do-plan change, touching existing docs) or teach the harvester all seven. The defaulted-question shape is the specific blocker: it lives under ## Open Questions but must not be asked, and no marker distinguishes it mechanically.
/do-plan removes the ## Open Questions section on finalize, which means a finalized plan has no place to record the answer. The writeback target needs deciding for plans already past that point.
Dropped: 3 items
The live phone call, and the Tel-Agent dependency, are out of scope.Tel-Agent is AGPL-3.0 with network-service copyleft, speaks only SIP so it needs a PBX reachable on the LAN, and does not document pre-recorded clip playback (it is built around streaming TTS). Independently, this repo has no streaming STT: tools/transcribe is batch and file-based, driving a macOS GUI app over the filesystem. A live duplex leg is greenfield across licensing, infrastructure, and transport. It should be its own issue, taken only if the Telegram leg proves the pipeline.
The live 5-way intent classifier. Only needed for duplex traversal. Also: the nearest measured precedent, run_typed_local on granite4.1:3b, runs ~1.1s median and ~1.4s p95 per config/settings.py:383, which is marginal inside a call's turn-taking loop. Deferred with the call itself.
Cross-project harvesting. An earlier draft of this issue considered aggregating questions across the projects in projects.json, including reading plan docs from repos with no local checkout via the GitHub contents API. Rejected by owner ruling: client projects are deliberately separate and each is hosted on its own machine, so a cross-repo harvest would both violate project isolation and put one machine's calling link in front of another project's questions. Each machine harvests its own repo only. The probe that motivated the idea is recorded here so it is not retried: 4 of the 9 configured projects are not checked out on this machine, and 2 of those have no discoverable GitHub repo under either org.
Timestamped transcript alignment. Unnecessary on the Telegram transport, where one inbound voice note is structurally the answer to one outbound clip. It would also force the cloud STT path, since the local SuperWhisper backend returns no timestamps and no detected language.
Acceptance Criteria
A CLI lists every genuinely open question across docs/plans/*.md and open issues, each with a stable content-hash ID, source path and line, and owning tracking: issue.
The harvester correctly classifies all seven resolution conventions found in the corpus, and excludes defaulted questions. Verified against the audited counts: 15 genuinely unresolved (or 13 under the documented strict reading), 11 defaulted, 46 resolved.
The harvester excludes NON_LANE_PLANS and does not crash on the known frontmatter irregularities (tracking: null, last_comment_id: none, bare last_comment_id:, the two plans with no frontmatter).
A preparation pass emits a question tree: ordered nodes, pruning edges, and a leverage score per node, bounded by a target session length.
Every node has a synthesized OGG/Opus clip, content-addressed by question hash, persisted outside the send-and-delete path, and reused on re-preparation when the hash is unchanged.
Spoken clip text contains no multi-digit identifiers and no clip exceeds the 4096-character synthesis limit.
A session delivers clips as Telegram voice notes one at a time, and an inbound voice-note reply is bound to the clip it answers without timestamp alignment.
Answering a question follows its pre-computed pruning edges, and a session that prunes its remaining nodes ends without asking them.
An off-script answer stops traversal, is recorded verbatim, and does not produce a fabricated resolution.
Writeback drafts in parallel and applies sequentially: one writer at a time, Edit only on plan docs, staging and commit in one atomic invocation with explicit paths, no git add -A.
Writeback commits touching only docs/plans/ carry no closing keyword and pass scripts/check_issue_disposition.py.
Every pre-drafted writeback is re-verified against current main before being applied; a drifted question is reported, not silently overwritten.
An answer that does not resolve its question leaves the question open and records the partial steer.
The harvester reads only this repo's working tree and this repo's issues. It has no code path that reads another project's checkout, another repo's issues, or a remote repo's contents API.
Re-running the harvester after a session shows the answered questions resolved and does not re-ask them.
Open Questions
Normalize the resolution convention, or teach the harvester all seven shapes? Normalizing means editing existing plan docs and changing PLAN_TEMPLATE.md, and would make every future harvest cheap. Teaching seven shapes is contained but permanently fragile.
How are defaulted questions marked going forward? They live under ## Open Questions but must never be asked. A distinct heading, an explicit per-item marker, or a frontmatter key.
Should this share the /ask-me transport or sit beside it? Polls already ship, /ask-me already ranks and merges questions, and Poll feature: deferrals carried out of PR #3092's review, with no tracking issue #3095 is open backlog on that channel. A voice transport inside /ask-me reuses the ranking and the needs_human edge; a separate skill keeps the poll channel from growing a second mode.
Where does the answer go when /do-plan has already removed the plan's ## Open Questions section? The tracking: issue, a new section, or reinstating the section.
Does the delivery half belong in .claude/skills-global/ from the start? Every machine will need it, but the harvester's handling of resolution conventions may turn out to be repo-specific, which is what .claude/skill-context/ exists for.
Downstream
This issue feeds into /do-plan, which will produce a plan document at docs/plans/{slug}.md, then executed by /do-build.
Constraints for the planner:
No new external vendor dependency. The Telegram leg needs nothing beyond what ships today. Telephony, streaming STT, and hosted voice vendors are out of scope per the Recon Summary, and the repo declares no STT or TTS vendor key beyond OPENAI_API_KEY.
Do not build a plan-doc lock. Sequential single-writer writeback is the chosen mitigation. If the plan concludes a lock is required, that is a scope change to raise, not to absorb.
Single project, single repo. No cross-project harvesting, no reading repos that are not checked out here. The corpus is this repo's docs/plans/ and this repo's issues.
Assume replication. Other machines will run this against their own projects, so avoid hardcoding anything specific to this repo's plan-doc dialect in a place that cannot be overridden per repo.
Triage 2026-09-15
Status: planned feature work, not a defect. Not subject to the hotfix/complexity questions applied to the rest of today's batch.
Classification: net-new capability with a written plan (docs/plans/voice-interview-open-questions.md). Largest single scope opened in the last three days.
Triage note: this is the one issue in the batch that adds surface area rather than repairing it. Every other open issue from this window is a guard, ledger, or fixture that drifted from reality. Worth sequencing this behind the pipeline-reliability cluster (#3312, #3303, #3301) — a new per-project, per-machine channel inherits whatever SDLC and lane-identity weaknesses are still unfixed when it lands.
Plan: https://github.com/tomcounsell/ai/blob/main/docs/plans/voice-interview-open-questions.md
Problem
Unanswered architect questions are the current throttle on delivery velocity. Work sits waiting on a decision only the owner can make, and the channel for getting those decisions is narrow enough that the queue drains slower than it fills. The intended cadence for this feature is a call a few times per week, which is also why per-session preparation cost is acceptable and why clip reuse matters.
Open questions in this repo are answered by typing, and only when the owner happens to be at a keyboard with enough context loaded to answer well. Two things are broken.
First, the corpus is not machine-readable, so nobody knows what is actually outstanding. There is no tool that enumerates open questions across plan docs and issues. The one extractor that exists (
bridge/message_drafter.py:100_extract_open_questions) reads agent stdout, not files, and would misfire on the real corpus (see Recon Summary). Answering questions therefore requires a human to remember which plan asked what.Second, there is no spoken channel at all. Outbound audio exists (
tools/tts/,/do-debrief) and inbound transcription exists (tools/transcribe/), but they have never been joined into a question-and-answer loop, and every synthesized clip is deleted immediately after send.Current behavior:
tools/,scripts/, or the skills directories walksdocs/plans/orgh issue listfor questions./ask-me, which drafts its blocker list from model context per invocation. Nothing is harvested from disk, so the list is not reproducible between runs.tools/tts.synthesize()writes a fresh OGG/Opus file per call and the documented send path passes--cleanup-after-send, sobridge/telegram_relay.py:690unlinks the audio after delivery. There is no clip library and no content-keyed audio cache anywhere in the repo.Desired outcome:
python -m tools.open_questions listprints every genuinely open question acrossdocs/plans/*.mdand open issues, each with a stable ID, its source location, and an unblock-leverage score.## Open Questionssection of the plan doc that asked them, attributed and dated, committed tomain.Definitions
docs/plans/, with YAML frontmatter includingstatus,type,appetite, andtracking(the GitHub issue URL).docs/plans/,PLAN_TEMPLATE.md## Open Questions/do-planremoves it on finalize.PLAN_TEMPLATE.md:495docs/features/single-machine-ownership.md,CLAUDE.mdvalor-telegram send --voice-note, which enqueues to the Redis outbox the relay drains.tools/valor_telegram.py:1379## Open Questionsthat is explicitly not an ask: it records a decision already made with a stated default, overturned only by an owner objection. "Silence keeps the default."docs/plans/improvement-controller-lane-3-control-journal-fenced-dispatch.md:787Prior Context
/ask-mequestions as native Telegram polls. Shipped. This is the existing asynchronous decision channel and the direct sibling of this work. Feature doc:docs/features/telegram-poll-questions.md. The voice channel described here is a second transport over the same need, not a replacement.git add -Aswallowed another lane's staged code ontomainunder an unrelated issue reference. This is the governing hazard for the writeback step.tts-1as fallback, not a hosted voice vendor.AskUserQuestionPreToolUse edge and thepause_open_questionrouter verdict that stops a session being nudged past its own outstanding question. The voice session must use this same edge rather than inventing a wait.Solution Sketch
Direction is settled: pre-record the questions, so the delivery agent only plays clips and captures answers. All intelligence runs before the session starts, where it has full context and no latency budget. Spending generously on preparation is the intended trade.
Scope boundary, load-bearing. Everything below operates on a single repo: this one, on this machine. The harvester reads the local working tree and this repo's own issues. It must not reach into another project's checkout, another repo's issues, or a remote repo's contents API. Other machines get the same capability by the normal global-tooling sync and harvest their own project independently, each with its own calling link.
Four components.
1. Harvester. A tool that enumerates open questions from
docs/plans/*.md(excludingtools/plan_doc_scope.py'sNON_LANE_PLANS) and open issues, emitting records with a content-hash ID, source path and line, and the owning plan'stracking:issue. It must distinguish genuinely open questions from resolved ones and from defaulted ones. The corpus uses at least seven mutually incompatible resolution conventions, so this is the hard part and the part with standalone value.2. Preparation pass. Score each question by unblock leverage; for each, enumerate plausible answer dispositions and compute which other questions each disposition moots; pick the root that maximizes expected pruning; emit a tree bounded by a target session length. For each node, author the spoken question text, a deeper-context clip for "what's this about?", a readback line, and a draft of the writeback that each anticipated disposition would produce. Synthesize every clip and store it content-addressed so re-preparation only re-cuts what changed.
3. Delivery. One clip per outbound voice note; the owner's reply voice note is the answer to the clip that was just sent. The binding is structural per message, which is why the first transport should be Telegram rather than a live call: no timestamp alignment is needed. After each answer, follow the pre-computed pruning edges to select the next node. Off-script answers stop the traversal and are recorded verbatim for the writeback pass.
4. Writeback. Per plan doc, draft the resolution and any issue edits the answer implies. Drafting fans out in parallel; applying and committing is strictly sequential and single-writer. See Pre-requisites: concurrent plan-doc writes on the shared
maincheckout have already caused unrecoverable damage, and no lock exists. Pre-drafted writebacks are re-verified against currentmainbefore being applied, because they were authored against an older tree.Constraints discovered in recon that the plan must honor:
Edittool, neverWrite. Four PostToolUse validators matchWriteondocs/planswith a propagating exit policy (.claude/hooks/manifest.toml:133).git add -A. This is Plan-doc writes in the shared main checkout have no single-writer protocol #2650's stated mitigation.docs/plans/need no issue disposition, and must not carry a closing keyword:scripts/check_issue_disposition.pyblocksCloses|Fixes|Resolves #Non a plan-only commit (Plan-only commits are exempt from the disposition gate, so a closing keyword quoted from plan prose closes a live issue #2890). Any commit reaching outside that prefix needsCloses #N,Refs #N, orNo-issue: <reason>./do-voice-recordingprosody rules. Clips reference work by name, not by issue number.--cleanup-after-send; the relay unlinks the file on success and that would destroy the library.valor-ask-pollrenders andAskUserQuestionmust be the turn's final act to fire theneeds_humanedge (.claude/skill-context/ask-me.md). A voice session waits the same way rather than inventing a mechanism.Recon Summary
Four parallel Explore agents. Findings that changed the issue are marked.
Confirmed: 6 items
tools/,scripts/, or the skills directories harvests questions from plan docs or issues. No duplication risk.## Open Questionsis a real convention with 79% coverage: 19 of 24 top-level plan docs carry a question-or-decision section.PLAN_TEMPLATE.md:495is its source.bridge/message_drafter.py:100_extract_open_questions, with tests attests/unit/test_open_question_gate.py. Its heading regex and placeholder filter are sound; its input is stdout, not files.tools/tts.synthesize()returns OGG/Opus, never raises, local Kokoro ONNX with OpenAItts-1fallback. Inbound:tools/transcribe.transcribe()via SuperWhisper with an OpenAIwhisper-1fallback, andbridge/media.py:461already transcribes inbound voice notes into the agent's message text automatically.valor-telegram send --voice-note, or RPUSH the payload directly.reflections/pm_briefings/delivery.pyis a working in-process precedent.mainare as described in the Solution Sketch.core.hooksPathis set to.githookson this machine, so both validation legs are live.Revised: 4 items
The in-repo corpus is 15 genuinely unresolved questions today, and that is a floor set by the bottleneck, not a ceiling on demand. A full pass over the 24 top-level plan docs found 15 genuinely unresolved questions in 6 documents (13 under a strict reading), against 46 already resolved and 11 "defaulted" items that are explicitly not asks. Of 127 open issues, only 16 have any question-shaped section. Owner ruling: the low count reflects velocity already throttled by waiting on architect answers, so draining the queue raises velocity and generates more questions. The count is therefore not a gate on building, and the original framing of this bullet as a reason to reconsider scope is withdrawn.
Resolution marking is not machine-readable. Seven incompatible conventions in active use: section-level "None.", heading rename to
## Resolved Questions, a blockquote RESOLVED banner over retained original text, per-item inline markers with varied wording, strikethrough, "all open questions resolved" prose, andRETIREDas a disposition distinct from resolved. Nine distinct heading strings, one of them an H3. The naive extractor would emit 9 false positives from the defaulted items inimprovement-controller-lane-3andlane-5alone.Issues are effectively not harvestable as a structured source. 16 of 126 carry a question section, across 14 distinct heading strings, varying in case, heading level, and plurality. Treat issues as a best-effort secondary source, not a peer of plan docs.
Writeback must be sequential, not a parallel fan-out. This reverses the original framing of one subagent per plan doc writing concurrently. Plan-doc writes in the shared main checkout have no single-writer protocol #2650 documents seven real collision shapes on the shared
maincheckout, including an unrecoverable 2026-08-13 incident. No lock exists. Parallel drafting is safe and retained; parallel writing is not.Pre-requisites: 3 items
docs/archive/plans-completed/plan-doc-single-writer-lease.mdstill readsstatus: Readyand was moved into the completed-plans archive by a content-free rename (433c166a9), apparently swept up by PR Move completed plans to docs/archive/plans-completed/ #2942's bulk move. It was archived as completed without being built. This issue does not depend on it being built, because sequential single-writer writeback sidesteps it, but that discrepancy should be filed separately.PLAN_TEMPLATE.mdand/do-planchange, touching existing docs) or teach the harvester all seven. The defaulted-question shape is the specific blocker: it lives under## Open Questionsbut must not be asked, and no marker distinguishes it mechanically./do-planremoves the## Open Questionssection on finalize, which means a finalized plan has no place to record the answer. The writeback target needs deciding for plans already past that point.Dropped: 3 items
tools/transcribeis batch and file-based, driving a macOS GUI app over the filesystem. A live duplex leg is greenfield across licensing, infrastructure, and transport. It should be its own issue, taken only if the Telegram leg proves the pipeline.run_typed_localongranite4.1:3b, runs ~1.1s median and ~1.4s p95 perconfig/settings.py:383, which is marginal inside a call's turn-taking loop. Deferred with the call itself.projects.json, including reading plan docs from repos with no local checkout via the GitHub contents API. Rejected by owner ruling: client projects are deliberately separate and each is hosted on its own machine, so a cross-repo harvest would both violate project isolation and put one machine's calling link in front of another project's questions. Each machine harvests its own repo only. The probe that motivated the idea is recorded here so it is not retried: 4 of the 9 configured projects are not checked out on this machine, and 2 of those have no discoverable GitHub repo under either org.Acceptance Criteria
docs/plans/*.mdand open issues, each with a stable content-hash ID, source path and line, and owningtracking:issue.NON_LANE_PLANSand does not crash on the known frontmatter irregularities (tracking: null,last_comment_id: none, barelast_comment_id:, the two plans with no frontmatter).Editonly on plan docs, staging and commit in one atomic invocation with explicit paths, nogit add -A.docs/plans/carry no closing keyword and passscripts/check_issue_disposition.py.mainbefore being applied; a drifted question is reported, not silently overwritten.Open Questions
PLAN_TEMPLATE.md, and would make every future harvest cheap. Teaching seven shapes is contained but permanently fragile.## Open Questionsbut must never be asked. A distinct heading, an explicit per-item marker, or a frontmatter key./ask-metransport or sit beside it? Polls already ship,/ask-mealready ranks and merges questions, and Poll feature: deferrals carried out of PR #3092's review, with no tracking issue #3095 is open backlog on that channel. A voice transport inside/ask-mereuses the ranking and theneeds_humanedge; a separate skill keeps the poll channel from growing a second mode./do-planhas already removed the plan's## Open Questionssection? Thetracking:issue, a new section, or reinstating the section..claude/skills-global/from the start? Every machine will need it, but the harvester's handling of resolution conventions may turn out to be repo-specific, which is what.claude/skill-context/exists for.Downstream
This issue feeds into
/do-plan, which will produce a plan document atdocs/plans/{slug}.md, then executed by/do-build.Constraints for the planner:
OPENAI_API_KEY.docs/plans/and this repo's issues.Triage 2026-09-15
Status: planned feature work, not a defect. Not subject to the hotfix/complexity questions applied to the rest of today's batch.
Classification: net-new capability with a written plan (
docs/plans/voice-interview-open-questions.md). Largest single scope opened in the last three days.Triage note: this is the one issue in the batch that adds surface area rather than repairing it. Every other open issue from this window is a guard, ledger, or fixture that drifted from reality. Worth sequencing this behind the pipeline-reliability cluster (#3312, #3303, #3301) — a new per-project, per-machine channel inherits whatever SDLC and lane-identity weaknesses are still unfixed when it lands.