release: v0.20 Codex-ready skill-driven core
This commit is contained in:
+170
-15
@@ -30,11 +30,12 @@ from scripts.runtime.assembly import build_chapter_briefs, build_compressed_find
|
||||
from scripts.runtime.orchestrator import create_phase2_task_cards, write_placeholder_packets
|
||||
from scripts.runtime.methods import ResearchMethodRegistry
|
||||
from scripts.runtime.phase1 import create_project, render_framework, write_material_brief
|
||||
from scripts.runtime.review import build_phase3_critique
|
||||
from scripts.runtime.review import build_phase3_critique, build_phase3_model_critique
|
||||
from scripts.runtime.roles import resolve_runtime_profile
|
||||
from scripts.runtime.source_cache import cache_sources
|
||||
from scripts.runtime.sources import rebuild_sources_from_packets
|
||||
from scripts.runtime.skills import SkillRegistry, default_adapter_skill_dirs
|
||||
from scripts.runtime.tasks import TaskCard
|
||||
from scripts.runtime.tasks import TaskCard, load_task_cards, validate_packet, write_task_cards
|
||||
from scripts.runtime.workers import run_packet_workers
|
||||
|
||||
|
||||
@@ -156,7 +157,12 @@ def cmd_frame(args: argparse.Namespace) -> int:
|
||||
print(f"Research method: {method.key}")
|
||||
print(f"Chapters: {args.chapters}")
|
||||
return 0
|
||||
path = render_framework(project_root, method_key=args.method, chapter_count=args.chapters)
|
||||
path = render_framework(
|
||||
project_root,
|
||||
method_key=args.method,
|
||||
chapter_count=args.chapters,
|
||||
preserve_existing_outline=args.preserve_existing_outline,
|
||||
)
|
||||
print(f"Project: {project_root.name}")
|
||||
print(f"Wrote: {path.relative_to(project_root)}")
|
||||
print("Pause: review and approve the framework before Phase 2.")
|
||||
@@ -222,12 +228,62 @@ def cmd_research(args: argparse.Namespace) -> int:
|
||||
"Phase 1 is not approved. Review phase1/material_brief.md and phase1/framework.md, "
|
||||
"then run `uv run python scripts/dr.py approve <project>` or pass --force."
|
||||
)
|
||||
runtime = resolve_runtime_profile(profile=args.profile)
|
||||
card_dicts = create_phase2_task_cards(
|
||||
project_root,
|
||||
axes=args.axis,
|
||||
dry_run=args.dry_run,
|
||||
if args.profile == "codex_native" and (args.execute_packets or args.assemble_chapters):
|
||||
raise SystemExit(
|
||||
"`codex_native` cannot be used for Python-core model execution: scripts/dr.py currently calls external "
|
||||
"API clients, not Codex App built-in models. Use a clearly external profile such as `medium`, or run "
|
||||
"Codex-native execution through the surface adapter/manual task workflow."
|
||||
)
|
||||
runtime = resolve_runtime_profile(
|
||||
profile=args.profile,
|
||||
overrides=parse_model_overrides(args.model_override),
|
||||
)
|
||||
if args.append_task_cards:
|
||||
generated = create_phase2_task_cards(
|
||||
project_root,
|
||||
axes=args.axis,
|
||||
dry_run=True,
|
||||
)
|
||||
existing_path = project_root / "phase2" / "task_cards.json"
|
||||
existing_cards = load_task_cards(existing_path) if existing_path.exists() else []
|
||||
seen = {card.task_id for card in existing_cards}
|
||||
appended_cards = [TaskCard(**item) for item in generated if item["task_id"] not in seen]
|
||||
runnable_existing_cards: list[TaskCard] = []
|
||||
if args.execute_packets and args.axis:
|
||||
axis_set = set(args.axis)
|
||||
for card in existing_cards:
|
||||
if card.topic_axis not in axis_set:
|
||||
continue
|
||||
packet_path = project_root / card.output_packet
|
||||
try:
|
||||
validate_packet(json.loads(packet_path.read_text(encoding="utf-8")))
|
||||
except Exception:
|
||||
runnable_existing_cards.append(card)
|
||||
merged_cards = [*existing_cards, *appended_cards]
|
||||
if not args.dry_run:
|
||||
write_task_cards(existing_path, merged_cards)
|
||||
phase2 = manifest.setdefault("phase2", {})
|
||||
phase2.update(
|
||||
{
|
||||
"status": "in_progress",
|
||||
"runtime": "python-core-v0.20",
|
||||
"task_cards_path": "phase2/task_cards.json",
|
||||
"task_cards_total": len(merged_cards),
|
||||
"task_cards_appended": len(appended_cards),
|
||||
"updated_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
|
||||
}
|
||||
)
|
||||
(project_root / "manifest.json").write_text(
|
||||
json.dumps(manifest, ensure_ascii=False, indent=2) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
card_dicts = [card.to_dict() for card in [*appended_cards, *runnable_existing_cards]]
|
||||
else:
|
||||
card_dicts = create_phase2_task_cards(
|
||||
project_root,
|
||||
axes=args.axis,
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
if args.execute_packets and args.dry_run:
|
||||
raise SystemExit("--execute-packets cannot be combined with --dry-run")
|
||||
if args.assemble_chapters and args.dry_run:
|
||||
@@ -350,7 +406,9 @@ def cmd_run(args: argparse.Namespace) -> int:
|
||||
workers=args.workers,
|
||||
axis=None,
|
||||
profile=args.profile,
|
||||
model_override=[],
|
||||
execute_packets=False,
|
||||
append_task_cards=False,
|
||||
allow_search_fallback=False,
|
||||
build_briefs=False,
|
||||
assemble_chapters=False,
|
||||
@@ -362,7 +420,26 @@ def cmd_run(args: argparse.Namespace) -> int:
|
||||
|
||||
def cmd_review(args: argparse.Namespace) -> int:
|
||||
project_root = resolve_project(args.project)
|
||||
path = build_phase3_critique(project_root)
|
||||
if args.model_review:
|
||||
if args.dry_run:
|
||||
print(f"Project: {project_root.name}")
|
||||
print("Phase 3 model review plan:")
|
||||
print(f" model: {args.model}")
|
||||
print(" context: phase3/review_context_opus_4_7.md")
|
||||
print(" output: phase3/critique.md")
|
||||
return 0
|
||||
from scripts.lib.zenmux_client import ZenMuxClient, load_secrets
|
||||
|
||||
load_secrets()
|
||||
with ZenMuxClient(log_file=project_root / "phase3" / "logs" / "review.jsonl") as client:
|
||||
path = build_phase3_model_critique(
|
||||
project_root,
|
||||
client=client,
|
||||
model=args.model,
|
||||
max_context_chars=args.max_context_chars,
|
||||
)
|
||||
else:
|
||||
path = build_phase3_critique(project_root)
|
||||
print(f"Project: {project_root.name}")
|
||||
print(f"Wrote: {path.relative_to(project_root)}")
|
||||
print("Pause: review critique before Phase 4.")
|
||||
@@ -482,12 +559,31 @@ def cmd_finalize(args: argparse.Namespace) -> int:
|
||||
roles = resolved["roles"]
|
||||
|
||||
if not args.legacy_translate:
|
||||
final_input = args.input
|
||||
if args.number_citations and not args.dry_run:
|
||||
rc = run_cmd(
|
||||
[
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "scripts" / "number_citations.py"),
|
||||
str(project_root),
|
||||
"--input",
|
||||
args.input,
|
||||
"--output",
|
||||
args.numbered_output,
|
||||
],
|
||||
dry_run=False,
|
||||
)
|
||||
if rc != 0:
|
||||
return rc
|
||||
final_input = args.numbered_output
|
||||
elif args.number_citations:
|
||||
final_input = args.numbered_output
|
||||
cmd: list[str] = [
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "scripts" / "build_report.py"),
|
||||
str(project_root),
|
||||
"--input",
|
||||
args.input,
|
||||
final_input,
|
||||
]
|
||||
if args.report_engine:
|
||||
cmd += ["--engine", args.report_engine]
|
||||
@@ -497,6 +593,21 @@ def cmd_finalize(args: argparse.Namespace) -> int:
|
||||
cmd.append("--no-pdf")
|
||||
if args.dry_run:
|
||||
print("Chinese-native finalize plan:")
|
||||
if args.number_citations:
|
||||
print(
|
||||
"$ "
|
||||
+ " ".join(
|
||||
[
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "scripts" / "number_citations.py"),
|
||||
str(project_root),
|
||||
"--input",
|
||||
args.input,
|
||||
"--output",
|
||||
args.numbered_output,
|
||||
]
|
||||
)
|
||||
)
|
||||
print("$ " + " ".join(cmd))
|
||||
if args.polish:
|
||||
print(
|
||||
@@ -506,8 +617,8 @@ def cmd_finalize(args: argparse.Namespace) -> int:
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "scripts" / "polish.py"),
|
||||
str(project_root),
|
||||
"--input",
|
||||
args.input,
|
||||
"--source",
|
||||
final_input,
|
||||
"--workers",
|
||||
str(args.polish_workers),
|
||||
"--model",
|
||||
@@ -522,8 +633,8 @@ def cmd_finalize(args: argparse.Namespace) -> int:
|
||||
sys.executable,
|
||||
str(REPO_ROOT / "scripts" / "polish.py"),
|
||||
str(project_root),
|
||||
"--input",
|
||||
args.input,
|
||||
"--source",
|
||||
final_input,
|
||||
"--workers",
|
||||
str(args.polish_workers),
|
||||
"--model",
|
||||
@@ -633,6 +744,30 @@ def cmd_models(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_sources(args: argparse.Namespace) -> int:
|
||||
project_root = resolve_project(args.project)
|
||||
if args.sources_cmd == "cache":
|
||||
if args.dry_run:
|
||||
print(f"Project: {project_root.name}")
|
||||
print(f"Would cache sources from: {args.sources}")
|
||||
print(f"Important only: {not args.all}")
|
||||
print(f"Limit: {args.limit}")
|
||||
return 0
|
||||
results = cache_sources(
|
||||
project_root,
|
||||
sources_rel=args.sources,
|
||||
important_only=not args.all,
|
||||
limit=args.limit,
|
||||
force=args.force,
|
||||
)
|
||||
print(f"Project: {project_root.name}")
|
||||
print(f"Cached source snapshots: {len(results)}")
|
||||
print("Wrote: phase2/source_cache/md/*.md")
|
||||
print("Updated: phase2/sources.jsonl")
|
||||
return 0
|
||||
raise SystemExit(f"unknown sources command: {args.sources_cmd}")
|
||||
|
||||
|
||||
def cmd_apply_models(args: argparse.Namespace) -> int:
|
||||
cmd = [
|
||||
sys.executable,
|
||||
@@ -672,6 +807,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
frame.add_argument("project", help="Project slug or path")
|
||||
frame.add_argument("--method", help="Override research method key")
|
||||
frame.add_argument("--chapters", type=int, default=10)
|
||||
frame.add_argument("--preserve-existing-outline", action="store_true", help="Keep current framework chapter titles and enrich Phase 1 planning")
|
||||
frame.add_argument("--dry-run", action="store_true")
|
||||
frame.set_defaults(func=cmd_frame)
|
||||
|
||||
@@ -694,7 +830,9 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
research.add_argument("--workers", type=int, default=6)
|
||||
research.add_argument("--axis", action="append", help="Restrict generated task axes; repeatable")
|
||||
research.add_argument("--profile", help="Model profile name from configs/models.yaml")
|
||||
research.add_argument("--model-override", action="append", default=[], metavar="ROLE=MODEL", help="Override a role model for this run; repeatable")
|
||||
research.add_argument("--execute-packets", action="store_true", help="Call model workers to fill evidence packets")
|
||||
research.add_argument("--append-task-cards", action="store_true", help="Append newly generated task cards instead of replacing phase2/task_cards.json")
|
||||
research.add_argument("--allow-search-fallback", action="store_true", help="Allow generic search fallback for specialized routes")
|
||||
research.add_argument("--build-briefs", action="store_true", help="Aggregate packets into chapter briefs")
|
||||
research.add_argument("--assemble-chapters", action="store_true", help="Call model workers to write Chinese chapter drafts")
|
||||
@@ -721,8 +859,12 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
status.add_argument("project", nargs="?", help="Project slug or path")
|
||||
status.set_defaults(func=cmd_status)
|
||||
|
||||
review = sub.add_parser("review", help="Run deterministic Phase 3 review")
|
||||
review = sub.add_parser("review", help="Run Phase 3 review")
|
||||
review.add_argument("project", help="Project slug or path")
|
||||
review.add_argument("--model-review", action="store_true", help="Run independent model-based Phase 3 review")
|
||||
review.add_argument("--model", default="zenmux-anthropic/claude-opus-4-7", help="Model for --model-review")
|
||||
review.add_argument("--max-context-chars", type=int, default=650_000, help="Bounded context size for model review")
|
||||
review.add_argument("--dry-run", action="store_true")
|
||||
review.set_defaults(func=cmd_review)
|
||||
|
||||
prompt = sub.add_parser("prompt", help="Print a Codex command prompt template")
|
||||
@@ -745,6 +887,8 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
finalize.add_argument("--input", default="phase4/final_zh.md", help="Chinese Markdown source for default v0.20 finalization")
|
||||
finalize.add_argument("--legacy-translate", action="store_true", help="Use legacy final_en -> translate -> polish pipeline")
|
||||
finalize.add_argument("--polish", action="store_true", help="Run optional Chinese polish step before rendering")
|
||||
finalize.add_argument("--number-citations", action="store_true", help="Convert [src_xxx] citations to numeric references before rendering")
|
||||
finalize.add_argument("--numbered-output", default="phase4/final_zh_numbered.md", help="Output path for numeric citation Markdown")
|
||||
finalize.add_argument("--report-engine", choices=["reportlab", "quarto"], default=None)
|
||||
finalize.add_argument("--no-docx", action="store_true")
|
||||
finalize.add_argument("--no-pdf", action="store_true")
|
||||
@@ -781,6 +925,17 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
models.add_argument("--json", action="store_true", help="Emit JSON")
|
||||
models.set_defaults(func=cmd_models)
|
||||
|
||||
sources = sub.add_parser("sources", help="Manage source snapshots and source registry")
|
||||
sources_sub = sources.add_subparsers(dest="sources_cmd", required=True)
|
||||
sources_cache = sources_sub.add_parser("cache", help="Cache important sources as local Markdown snapshots")
|
||||
sources_cache.add_argument("project", help="Project slug or path")
|
||||
sources_cache.add_argument("--sources", default="phase2/sources.jsonl", help="Source registry path relative to project")
|
||||
sources_cache.add_argument("--all", action="store_true", help="Cache all remote sources, not only important official/Tier 1 sources")
|
||||
sources_cache.add_argument("--limit", type=int, help="Maximum sources to cache in this run")
|
||||
sources_cache.add_argument("--force", action="store_true", help="Refetch even if cached_text_path already exists")
|
||||
sources_cache.add_argument("--dry-run", action="store_true")
|
||||
sources_cache.set_defaults(func=cmd_sources)
|
||||
|
||||
apply_models = sub.add_parser("apply-models", help="Apply profile to agent files")
|
||||
apply_models.add_argument("--profile", required=True, help="Profile name from configs/models.yaml")
|
||||
apply_models.add_argument("--target", choices=["opencode", "codex", "both"], default="both")
|
||||
|
||||
Reference in New Issue
Block a user