idle-draft: add sample_override config flag to bypass cold-start throttle

Optional config key (default false). When true, research candidates are
offered even at max_unreviewed_research_per_dossier; research_sampled
bookkeeping, mark sampled, and status flags are unchanged so unsampled
items stay visible for later human verification.

Claude-Session: https://claude.ai/code/session_01Lgv4Qn82boNFC1jn8QXSNw
This commit is contained in:
Paul O'Reilly
2026-08-03 07:26:41 +12:00
parent 29618f4319
commit bdd10cbbc1
3 changed files with 21 additions and 1 deletions

View File

@@ -683,6 +683,7 @@ def count_open_topic_proposals(repo: Path, dossier: str) -> int:
def build_ready_queue(repo: Path, config: dict, state: dict, in_flight: set) -> list[dict]:
candidates: list[dict] = []
dossiers = config["dossiers"]
sample_override = bool(config.get("sample_override", False))
for idx, dossier in enumerate(dossiers):
unreviewed = count_unreviewed_research(repo, dossier, state)
max_unreviewed = config["max_unreviewed_research_per_dossier"]
@@ -693,7 +694,7 @@ def build_ready_queue(repo: Path, config: dict, state: dict, in_flight: set) ->
files = item_files(repo, dossier, f"{nn}-{slug}")
entry = get_item_state(state, key)
wtype, reason = next_work_type_for_item(files, entry)
if wtype == "research" and unreviewed >= max_unreviewed:
if wtype == "research" and unreviewed >= max_unreviewed and not sample_override:
continue
if wtype is None:
continue

View File

@@ -86,6 +86,12 @@ Parse `--config` as JSON. Required top-level keys: `parallel`, `providers`, `wor
verbatim. Missing required keys or malformed structure → log loudly, exit **2**.
`--parallel` on the CLI overrides the config value.
Optional key `sample_override` (bool, default `false`): when `true`, the cold-start
throttle (§ dispatch, below) is bypassed — `research` candidates are offered even when
the dossier is at `max_unreviewed_research_per_dossier`. Nothing else changes:
`research_sampled` bookkeeping, `mark … sampled`, and the `status` flags are unaffected,
so unsampled research remains visible and awaiting human verification.
### 4. State load and validation
Read `<repo>/idle-draft.state.json`. Missing file is not an error — treat as
@@ -184,6 +190,8 @@ from consideration for further dispatch until the in-flight task completes.
whose state entry does **not** have `research_sampled: true`. Once that count reaches
`max_unreviewed_research_per_dossier`, no further `research` candidates are offered for
that dossier this cycle (draft/review candidates in that dossier are unaffected).
If config `sample_override` is `true`, this throttle is skipped entirely; the unsampled
counts and `research_sampled` state are still maintained exactly as above.
**`topic_ideas`:** dossier-level (not tied to an `NN-slug`), considered only when the
combined candidate list above (across all dossiers) is empty. For each dossier, eligible

View File

@@ -570,6 +570,17 @@ draft_candidates = [c["slug"] for c in tqueue if c["work_type"] == "draft"]
check("cold-start throttle: draft candidates for 01/02 unaffected by the research throttle",
set(draft_candidates) == {"01-a", "02-b"}, draft_candidates)
# sample_override: throttle bypassed, sampling state untouched
override_config = dict(throttle_config)
override_config["sample_override"] = True
oqueue = m.build_ready_queue(throttle_repo, override_config, throttle_state, set())
override_research = [c["slug"] for c in oqueue if c["work_type"] == "research"]
check("sample_override: item 03's research is offered despite the dossier being at the cap",
"03-c" in override_research, override_research)
check("sample_override: research_sampled state is not mutated by queue building",
not any(m.get_item_state(throttle_state, f"ai/{s}")["research_sampled"]
for s in ("01-a", "02-b", "03-c")), throttle_state)
# topic_ideas: only offered when nothing else is eligible
idle_repo = tmpdir / "idle-repo"
(idle_repo / "ai").mkdir(parents=True, exist_ok=True)