idle-draft: per-work-type allowed_tools; research must web-search for stories
Research and topic_ideas runs get --allowedTools WebSearch,WebFetch; research template mandates >=1 web search for related stories with full-URL + access-date citations under the dossier's status labels. Claude-Session: https://claude.ai/code/session_01YQDoWNM7XPPii28khFWoMc
This commit is contained in:
@@ -88,8 +88,8 @@ cat > "$FIXTURE_CONFIG" <<EOF
|
||||
"work_types": {
|
||||
"review": { "providers": ["anthropic"] },
|
||||
"draft": { "providers": ["anthropic"] },
|
||||
"research": { "providers": ["anthropic", "minimax"] },
|
||||
"topic_ideas": { "providers": ["anthropic", "minimax"] }
|
||||
"research": { "providers": ["anthropic", "minimax"], "allowed_tools": ["WebSearch", "WebFetch"] },
|
||||
"topic_ideas": { "providers": ["anthropic", "minimax"], "allowed_tools": ["WebSearch", "WebFetch"] }
|
||||
},
|
||||
"dossiers": ["ai"],
|
||||
"review_score_threshold": 11,
|
||||
@@ -135,6 +135,7 @@ assert_contains "$output" "item: ai/03-topic-three" "dryrun picks item 03 (
|
||||
assert_contains "$output" "work_type: review" "dryrun names work_type review"
|
||||
assert_contains "$output" "provider: anthropic" "dryrun names provider anthropic"
|
||||
assert_not_contains "$output" "would run" "dryrun does not execute claude"
|
||||
assert_not_contains "$output" "--allowedTools" "dryrun argv for review (no allowed_tools configured) omits the flag"
|
||||
|
||||
echo "-- dryrun: exact resolved claude argv for a research item --"
|
||||
# Force only the research-ready item to be eligible by pointing --repo at a
|
||||
@@ -151,6 +152,8 @@ output=$("$SCRIPT" --config "$FIXTURE_CONFIG" --repo "$SOLO" --probe-json "$FIXT
|
||||
assert_contains "$output" "work_type: research" "dryrun (solo fixture) picks research"
|
||||
assert_contains "$output" "argv: ['claude', '-p', '--max-turns', '25', '--add-dir'" "dryrun prints resolved claude argv with --add-dir for research"
|
||||
assert_contains "$output" "$TMPDIR/evidence" "dryrun argv includes configured evidence dir"
|
||||
assert_contains "$output" "--allowedTools" "dryrun argv for research includes --allowedTools (allowed_tools configured)"
|
||||
assert_contains "$output" "WebSearch,WebFetch" "dryrun argv --allowedTools value is the comma-joined tool list"
|
||||
|
||||
echo "-- dryrun: no mutation --"
|
||||
before_hash=$(cd "$FIXTURE_REPO" && git rev-parse HEAD)
|
||||
@@ -182,6 +185,31 @@ output=$("$SCRIPT" --config "$BADCONFIG" --repo "$FIXTURE_REPO" --probe-json "$F
|
||||
code=$?
|
||||
assert_exit_code "$code" 2 "config missing required keys exits 2"
|
||||
|
||||
BADALLOWED="$TMPDIR/bad-allowed.config.json"
|
||||
cat > "$BADALLOWED" <<EOF
|
||||
{
|
||||
"parallel": 2,
|
||||
"providers": {
|
||||
"anthropic": { "profile": "$PROFILE_ANTHROPIC", "threshold_pct": 80, "five_hour_ceiling": 50, "min_idle": 5 }
|
||||
},
|
||||
"work_types": {
|
||||
"review": { "providers": ["anthropic"] },
|
||||
"draft": { "providers": ["anthropic"] },
|
||||
"research": { "providers": ["anthropic"], "allowed_tools": "WebSearch" },
|
||||
"topic_ideas": { "providers": ["anthropic"] }
|
||||
},
|
||||
"dossiers": ["ai"],
|
||||
"review_score_threshold": 11,
|
||||
"max_unreviewed_research_per_dossier": 3,
|
||||
"max_open_topic_proposals": 6,
|
||||
"evidence_dirs": ["$TMPDIR/evidence"]
|
||||
}
|
||||
EOF
|
||||
output=$("$SCRIPT" --config "$BADALLOWED" --repo "$FIXTURE_REPO" --probe-json "$FIXTURE_PROBE" --dryrun 2>&1)
|
||||
code=$?
|
||||
assert_exit_code "$code" 2 "allowed_tools not a list of strings exits 2"
|
||||
assert_contains "$output" "allowed_tools" "allowed_tools config error names the offending key"
|
||||
|
||||
echo "-- mark subcommand round-trip --"
|
||||
MARKREPO="$TMPDIR/mark-repo"
|
||||
mkdir -p "$MARKREPO/ai"
|
||||
@@ -398,6 +426,55 @@ check("build_child_env: profile without provider.env gets only CLAUDE_CONFIG_DIR
|
||||
"ANTHROPIC_BASE_URL" not in env2 and "ANTHROPIC_API_KEY" not in env2 and env2["CLAUDE_CONFIG_DIR"] == str(no_provider_env_dir),
|
||||
env2)
|
||||
|
||||
# --- allowed_tools / --allowedTools argv ---
|
||||
argv = m.build_claude_argv("model-x", 25, ["/tmp/ev"], ["WebSearch", "WebFetch"])
|
||||
check("build_claude_argv: appends --allowedTools with comma-joined list when provided",
|
||||
"--allowedTools" in argv and argv[argv.index("--allowedTools") + 1] == "WebSearch,WebFetch", argv)
|
||||
|
||||
argv_empty = m.build_claude_argv("model-x", 25, ["/tmp/ev"], [])
|
||||
check("build_claude_argv: omits --allowedTools when allowed_tools is an empty list", "--allowedTools" not in argv_empty, argv_empty)
|
||||
|
||||
argv_none = m.build_claude_argv("model-x", 25, ["/tmp/ev"], None)
|
||||
check("build_claude_argv: omits --allowedTools when allowed_tools is None", "--allowedTools" not in argv_none, argv_none)
|
||||
|
||||
cfg_at = {
|
||||
"work_types": {
|
||||
"research": {"providers": ["anthropic"], "allowed_tools": ["WebSearch", "WebFetch"]},
|
||||
"draft": {"providers": ["anthropic"]},
|
||||
}
|
||||
}
|
||||
check("resolve_allowed_tools: returns configured list for research",
|
||||
m.resolve_allowed_tools(cfg_at, "research") == ["WebSearch", "WebFetch"], m.resolve_allowed_tools(cfg_at, "research"))
|
||||
check("resolve_allowed_tools: returns [] when work_type has no allowed_tools key",
|
||||
m.resolve_allowed_tools(cfg_at, "draft") == [], m.resolve_allowed_tools(cfg_at, "draft"))
|
||||
check("resolve_allowed_tools: returns [] for a work_type not present in config at all",
|
||||
m.resolve_allowed_tools(cfg_at, "review") == [], m.resolve_allowed_tools(cfg_at, "review"))
|
||||
|
||||
bad_allowed_cfg_path = tmpdir / "bad-allowed-direct.config.json"
|
||||
bad_allowed_cfg = json.loads((tmpdir / "idle-draft.config.json").read_text())
|
||||
bad_allowed_cfg["work_types"]["research"]["allowed_tools"] = "WebSearch"
|
||||
bad_allowed_cfg_path.write_text(json.dumps(bad_allowed_cfg))
|
||||
try:
|
||||
m.load_config(bad_allowed_cfg_path)
|
||||
check("load_config: allowed_tools as a bare string (not a list) raises ConfigError", False)
|
||||
except m.ConfigError as e:
|
||||
check("load_config: allowed_tools as a bare string (not a list) raises ConfigError", "allowed_tools" in str(e), str(e))
|
||||
|
||||
bad_allowed_cfg2_path = tmpdir / "bad-allowed-direct2.config.json"
|
||||
bad_allowed_cfg2 = json.loads((tmpdir / "idle-draft.config.json").read_text())
|
||||
bad_allowed_cfg2["work_types"]["research"]["allowed_tools"] = ["WebSearch", 5]
|
||||
bad_allowed_cfg2_path.write_text(json.dumps(bad_allowed_cfg2))
|
||||
try:
|
||||
m.load_config(bad_allowed_cfg2_path)
|
||||
check("load_config: allowed_tools list containing a non-string raises ConfigError", False)
|
||||
except m.ConfigError as e:
|
||||
check("load_config: allowed_tools list containing a non-string raises ConfigError", "allowed_tools" in str(e), str(e))
|
||||
|
||||
good_allowed_cfg = m.load_config(tmpdir / "idle-draft.config.json")
|
||||
check("load_config: well-formed allowed_tools (list of strings) loads without error",
|
||||
good_allowed_cfg["work_types"]["research"]["allowed_tools"] == ["WebSearch", "WebFetch"],
|
||||
good_allowed_cfg["work_types"]["research"])
|
||||
|
||||
# --- Citation validation ---
|
||||
real_path = cred_repo / "exists.txt"
|
||||
real_path.write_text("x")
|
||||
|
||||
Reference in New Issue
Block a user