okstra 0.86.0 → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/docs/kr/architecture.md +27 -27
  2. package/docs/kr/cli.md +21 -21
  3. package/docs/kr/performance-improvement-plan-v2.md +4 -4
  4. package/docs/kr/performance-improvement-plan.md +3 -3
  5. package/docs/project-structure-overview.md +6 -6
  6. package/docs/superpowers/plans/2026-06-17-okstra-error-report.md +724 -0
  7. package/docs/superpowers/specs/2026-06-17-okstra-error-report-design.md +123 -0
  8. package/docs/task-process/error-analysis.md +1 -1
  9. package/docs/task-process/final-verification.md +4 -4
  10. package/docs/task-process/implementation-planning.md +2 -2
  11. package/docs/task-process/implementation.md +3 -3
  12. package/docs/task-process/requirements-discovery.md +2 -2
  13. package/package.json +1 -1
  14. package/runtime/BUILD.json +2 -2
  15. package/runtime/agents/workers/{gemini-worker.md → antigravity-worker.md} +56 -56
  16. package/runtime/agents/workers/claude-worker.md +2 -2
  17. package/runtime/agents/workers/codex-worker.md +1 -1
  18. package/runtime/agents/workers/report-writer-worker.md +2 -2
  19. package/runtime/bin/lib/okstra/cli.sh +3 -3
  20. package/runtime/bin/lib/okstra/globals.sh +8 -8
  21. package/runtime/bin/lib/okstra/interactive.sh +1 -1
  22. package/runtime/bin/lib/okstra/usage.sh +7 -7
  23. package/runtime/bin/lib/okstra-ctl/cmd-reconcile.sh +5 -3
  24. package/runtime/bin/lib/okstra-ctl/prepare.sh +4 -2
  25. package/runtime/bin/{okstra-gemini-exec.sh → okstra-antigravity-exec.sh} +67 -60
  26. package/runtime/bin/okstra-error-log.py +2 -1
  27. package/runtime/bin/okstra-token-usage.py +3 -4
  28. package/runtime/bin/okstra-trace-cleanup.sh +4 -4
  29. package/runtime/bin/okstra-wrapper-status.py +3 -3
  30. package/runtime/bin/okstra.sh +1 -1
  31. package/runtime/prompts/launch.template.md +3 -3
  32. package/runtime/prompts/lead/context-loader.md +1 -1
  33. package/runtime/prompts/lead/convergence.md +8 -8
  34. package/runtime/prompts/lead/okstra-lead-contract.md +15 -15
  35. package/runtime/prompts/lead/report-writer.md +6 -6
  36. package/runtime/prompts/lead/team-contract.md +31 -31
  37. package/runtime/prompts/profiles/_coding-conventions-preflight.md +2 -2
  38. package/runtime/prompts/profiles/_common-contract.md +7 -7
  39. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  40. package/runtime/prompts/profiles/_implementation-executor.md +6 -6
  41. package/runtime/prompts/profiles/_implementation-verifier.md +4 -4
  42. package/runtime/prompts/profiles/_stage-discipline.md +1 -1
  43. package/runtime/prompts/profiles/error-analysis.md +1 -1
  44. package/runtime/prompts/profiles/final-verification.md +1 -1
  45. package/runtime/prompts/profiles/forbidden-actions.json +2 -2
  46. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  47. package/runtime/prompts/profiles/implementation.md +3 -3
  48. package/runtime/prompts/profiles/improvement-discovery.md +3 -3
  49. package/runtime/prompts/profiles/requirements-discovery.md +1 -1
  50. package/runtime/prompts/wizard/prompts.ko.json +9 -5
  51. package/runtime/python/okstra_ctl/__init__.py +2 -0
  52. package/runtime/python/okstra_ctl/analysis_packet.py +1 -1
  53. package/runtime/python/okstra_ctl/clarification_items.py +17 -7
  54. package/runtime/python/okstra_ctl/codex_dispatch.py +2 -2
  55. package/runtime/python/okstra_ctl/context_cost.py +10 -44
  56. package/runtime/python/okstra_ctl/doctor.py +1 -1
  57. package/runtime/python/okstra_ctl/error_report.py +185 -0
  58. package/runtime/python/okstra_ctl/improvement_lenses.py +1 -1
  59. package/runtime/python/okstra_ctl/models.py +10 -10
  60. package/runtime/python/okstra_ctl/paths.py +9 -9
  61. package/runtime/python/okstra_ctl/reconcile.py +74 -11
  62. package/runtime/python/okstra_ctl/render.py +21 -21
  63. package/runtime/python/okstra_ctl/run.py +16 -16
  64. package/runtime/python/okstra_ctl/run_context.py +1 -1
  65. package/runtime/python/okstra_ctl/seeding.py +1 -1
  66. package/runtime/python/okstra_ctl/task_target.py +44 -0
  67. package/runtime/python/okstra_ctl/team.py +1 -1
  68. package/runtime/python/okstra_ctl/wizard.py +55 -30
  69. package/runtime/python/okstra_ctl/workers.py +3 -3
  70. package/runtime/python/okstra_ctl/workflow.py +1 -1
  71. package/runtime/python/okstra_ctl/worktree.py +1 -1
  72. package/runtime/python/okstra_token_usage/__init__.py +2 -3
  73. package/runtime/python/okstra_token_usage/antigravity.py +26 -0
  74. package/runtime/python/okstra_token_usage/blocks.py +4 -0
  75. package/runtime/python/okstra_token_usage/collect.py +20 -15
  76. package/runtime/python/okstra_token_usage/paths.py +0 -1
  77. package/runtime/python/okstra_token_usage/pricing.py +6 -3
  78. package/runtime/schemas/final-report-v1.0.schema.json +1 -1
  79. package/runtime/skills/okstra-brief/SKILL.md +1 -1
  80. package/runtime/skills/okstra-inspect/SKILL.md +85 -21
  81. package/runtime/skills/okstra-run/SKILL.md +3 -3
  82. package/runtime/skills/okstra-setup/SKILL.md +2 -2
  83. package/runtime/templates/prd/brief.template.md +2 -2
  84. package/runtime/templates/reports/i18n/en.json +1 -1
  85. package/runtime/templates/reports/i18n/ko.json +1 -1
  86. package/runtime/templates/reports/settings.template.json +2 -2
  87. package/runtime/templates/reports/task-brief.template.md +2 -2
  88. package/runtime/templates/worker-prompt-preamble.md +7 -7
  89. package/runtime/validators/forbidden_actions.py +1 -1
  90. package/runtime/validators/lib/fixtures.sh +3 -3
  91. package/runtime/validators/lib/validate-assets.sh +1 -1
  92. package/runtime/validators/validate-run.py +8 -8
  93. package/src/cli-registry.mjs +7 -0
  94. package/src/codex-dispatch.mjs +3 -3
  95. package/src/codex-run.mjs +2 -2
  96. package/src/doctor.mjs +1 -1
  97. package/src/error-report.mjs +27 -0
  98. package/src/install.mjs +1 -1
  99. package/src/render-bundle.mjs +1 -1
  100. package/src/uninstall.mjs +2 -2
  101. package/runtime/python/okstra_token_usage/gemini.py +0 -82
@@ -0,0 +1,185 @@
1
+ """Read-side aggregator for okstra-run error logs.
2
+
3
+ task 의 모든 run errors-<type>-<seq>.jsonl 을 모아 phase / worker / errorType
4
+ 별로 집계하고 timestamp 누적 .md 리포트로 렌더한다. task 산출물을 mutate 하지
5
+ 않는다.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import datetime as dt
11
+ import json
12
+ import sys
13
+ from pathlib import Path
14
+
15
+ from okstra_ctl.task_target import resolve_task_root, project_rel
16
+
17
+
18
+ def _glob_error_logs(task_root: Path) -> list[Path]:
19
+ runs = task_root / "runs"
20
+ if not runs.exists():
21
+ return []
22
+ flat = runs.glob("*/logs/errors-*.jsonl")
23
+ staged = runs.glob("*/stage-*/logs/errors-*.jsonl")
24
+ return sorted(set(flat) | set(staged))
25
+
26
+
27
+ def _parse_records(paths: list[Path]) -> tuple[list[dict], int]:
28
+ records: list[dict] = []
29
+ skipped = 0
30
+ for path in paths:
31
+ for line in path.read_text(encoding="utf-8").splitlines():
32
+ line = line.strip()
33
+ if not line:
34
+ continue
35
+ try:
36
+ rec = json.loads(line)
37
+ except Exception:
38
+ skipped += 1
39
+ continue
40
+ if isinstance(rec, dict):
41
+ rec["_sourceLog"] = str(path)
42
+ records.append(rec)
43
+ else:
44
+ skipped += 1
45
+ return records, skipped
46
+
47
+
48
+ def _tally(records: list[dict], key: str) -> dict:
49
+ out: dict[str, int] = {}
50
+ for rec in records:
51
+ out[str(rec.get(key, ""))] = out.get(str(rec.get(key, "")), 0) + 1
52
+ return out
53
+
54
+
55
+ def _rows(counts: dict, label: str) -> list[dict]:
56
+ return [
57
+ {label: name, "count": n}
58
+ for name, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0]))
59
+ ]
60
+
61
+
62
+ def _aggregate(records: list[dict]) -> dict:
63
+ return {
64
+ "errorCount": len(records),
65
+ "runCount": len({rec.get("_sourceLog", "") for rec in records}),
66
+ "byErrorType": _tally(records, "errorType"),
67
+ "bySource": _tally(records, "source"),
68
+ "byPhase": _rows(_tally(records, "phase"), "phase"),
69
+ "byAgent": _rows(_tally(records, "agent"), "agent"),
70
+ }
71
+
72
+
73
+ def _escape_cell(value: object) -> str:
74
+ return str(value).replace("|", "\\|").replace("\n", " ")
75
+
76
+
77
+ def _md_table(header: list[str], rows: list[list[str]]) -> str:
78
+ line = "| " + " | ".join(_escape_cell(h) for h in header) + " |"
79
+ sep = "| " + " | ".join("---" for _ in header) + " |"
80
+ if not rows:
81
+ empty = "| " + " | ".join(["_없음_"] + [""] * (len(header) - 1)) + " |"
82
+ return "\n".join([line, sep, empty])
83
+ body = ["| " + " | ".join(_escape_cell(c) for c in cells) + " |" for cells in rows]
84
+ return "\n".join([line, sep, *body])
85
+
86
+
87
+ def render_markdown(*, task_key, records, agg, parse_skipped, generated_at) -> str:
88
+ by_type = ", ".join(f"{k}: {v}" for k, v in sorted(agg["byErrorType"].items())) or "_없음_"
89
+ by_src = ", ".join(f"{k}: {v}" for k, v in sorted(agg["bySource"].items())) or "_없음_"
90
+ parts = [
91
+ f"# okstra 에러 리포트 — {task_key}",
92
+ "",
93
+ "## 요약",
94
+ "",
95
+ f"- 총 에러: {agg['errorCount']}건",
96
+ f"- 집계된 로그(run) 수: {agg['runCount']}",
97
+ f"- errorType 별: {by_type}",
98
+ f"- source 별: {by_src}",
99
+ f"- 파싱 건너뛴 줄: {parse_skipped}",
100
+ f"- 생성 시각: {generated_at}",
101
+ "",
102
+ "## phase 별 집계",
103
+ "",
104
+ _md_table(["Phase", "Count"],
105
+ [[r["phase"] or "(없음)", str(r["count"])] for r in agg["byPhase"]]),
106
+ "",
107
+ "## worker(agent) 별 집계",
108
+ "",
109
+ _md_table(["Agent", "Count"],
110
+ [[r["agent"] or "(없음)", str(r["count"])] for r in agg["byAgent"]]),
111
+ "",
112
+ "## 개별 에러 이벤트",
113
+ "",
114
+ _md_table(
115
+ ["ts", "phase", "agent", "errorType", "command", "exitCode", "message"],
116
+ [[
117
+ str(r.get("ts", "")), str(r.get("phase", "")), str(r.get("agent", "")),
118
+ str(r.get("errorType", "")), f"`{r.get('command', '')}`",
119
+ str(r.get("exitCode", "")),
120
+ str(r.get("message", "")),
121
+ ] for r in sorted(records, key=lambda x: str(x.get("ts", "")))],
122
+ ),
123
+ "",
124
+ ]
125
+ return "\n".join(parts)
126
+
127
+
128
+ def _timestamp_segment(now: dt.datetime) -> str:
129
+ return now.strftime("%Y-%m-%d_%H-%M-%S")
130
+
131
+
132
+ def build_and_write(task_root: Path, project_root: Path, now: dt.datetime) -> dict:
133
+ manifest_path = task_root / "task-manifest.json"
134
+ task_key = ""
135
+ if manifest_path.is_file():
136
+ try:
137
+ task_key = json.loads(manifest_path.read_text(encoding="utf-8")).get("taskKey", "")
138
+ except Exception:
139
+ task_key = ""
140
+ paths = _glob_error_logs(task_root)
141
+ records, skipped = _parse_records(paths)
142
+ agg = _aggregate(records)
143
+ report_rel = ""
144
+ if paths:
145
+ md = render_markdown(
146
+ task_key=task_key, records=records, agg=agg,
147
+ parse_skipped=skipped, generated_at=now.isoformat(),
148
+ )
149
+ out_dir = task_root / "error-reports"
150
+ out_dir.mkdir(parents=True, exist_ok=True)
151
+ out_path = out_dir / f"error-report-{_timestamp_segment(now)}.md"
152
+ out_path.write_text(md, encoding="utf-8")
153
+ report_rel = project_rel(out_path, project_root)
154
+ return {
155
+ "taskKey": task_key,
156
+ "reportPath": report_rel,
157
+ "parseSkipped": skipped,
158
+ "totals": {
159
+ "errorCount": agg["errorCount"],
160
+ "runCount": agg["runCount"],
161
+ "byErrorType": agg["byErrorType"],
162
+ "bySource": agg["bySource"],
163
+ },
164
+ "byPhase": agg["byPhase"],
165
+ "byAgent": agg["byAgent"],
166
+ }
167
+
168
+
169
+ def main(argv: list[str] | None = None) -> int:
170
+ parser = argparse.ArgumentParser(
171
+ description="Aggregate okstra-run error logs into a task-level report."
172
+ )
173
+ parser.add_argument("target", help="task root path or task-key")
174
+ parser.add_argument("--project-root", default="", help="project root for task-key lookup")
175
+ parser.add_argument("--cwd", default=".", help="cwd for project root resolution")
176
+ args = parser.parse_args(argv)
177
+
178
+ task_root, project_root = resolve_task_root(args.target, args.project_root, args.cwd)
179
+ result = build_and_write(task_root, project_root, dt.datetime.now(dt.timezone.utc))
180
+ print(json.dumps(result, ensure_ascii=False, indent=2))
181
+ return 0
182
+
183
+
184
+ if __name__ == "__main__":
185
+ raise SystemExit(main(sys.argv[1:]))
@@ -24,7 +24,7 @@ MAX_PRIORITY_LENSES = 4
24
24
 
25
25
  # report-writer is the AUTHOR of the final report; it never produces source
26
26
  # findings. validators reject `report-writer:<id>` entries in Source workers.
27
- SOURCE_WORKERS: tuple[str, ...] = ("claude", "codex", "gemini")
27
+ SOURCE_WORKERS: tuple[str, ...] = ("claude", "codex", "antigravity")
28
28
 
29
29
 
30
30
  def is_valid_lens(value: str) -> bool:
@@ -26,14 +26,14 @@ CLAUDE_MAPPING = {
26
26
  "claude-haiku-4-5": ("haiku-4-5", "claude-haiku-4-5"),
27
27
  "claude-haiku-4-5-20251001": ("haiku-4-5", "claude-haiku-4-5-20251001"),
28
28
  }
29
- GEMINI_MAPPING = {
30
- "auto": ("auto", "auto"),
31
- "gemini auto": ("auto", "auto"),
32
- "pro": ("pro", "pro"),
33
- "gemini pro": ("pro", "pro"),
34
- "gemini 3 flash preview": ("Gemini 3 Flash Preview", "gemini-3-flash-preview"),
35
- "gemini-3-flash-preview": ("Gemini 3 Flash Preview", "gemini-3-flash-preview"),
36
- "gemini-3-pro-preview": ("Gemini 3 Pro Preview", "gemini-3-pro-preview"),
29
+ # Antigravity worker runs the `agy` CLI, which executes Gemini-family models.
30
+ # The execution values stay `gemini-3.x` (agy accepts those slugs); only the
31
+ # okstra provider/worker identity is "antigravity".
32
+ ANTIGRAVITY_MAPPING = {
33
+ "gemini 3.1 pro": ("Gemini 3.1 Pro", "gemini-3.1-pro"),
34
+ "gemini-3.1-pro": ("Gemini 3.1 Pro", "gemini-3.1-pro"),
35
+ "gemini 3.5 flash": ("Gemini 3.5 Flash", "gemini-3.5-flash"),
36
+ "gemini-3.5-flash": ("Gemini 3.5 Flash", "gemini-3.5-flash"),
37
37
  }
38
38
  CODEX_MAPPING = {
39
39
  "gpt-5.5": ("gpt-5.5", "gpt-5.5"),
@@ -43,7 +43,7 @@ CODEX_MAPPING = {
43
43
  "gpt-5.2": ("gpt-5.2", "gpt-5.2"),
44
44
  "codex-auto-review": ("codex-auto-review", "codex-auto-review"),
45
45
  }
46
- PROVIDER_MAPPINGS = {"claude": CLAUDE_MAPPING, "gemini": GEMINI_MAPPING, "codex": CODEX_MAPPING}
46
+ PROVIDER_MAPPINGS = {"claude": CLAUDE_MAPPING, "antigravity": ANTIGRAVITY_MAPPING, "codex": CODEX_MAPPING}
47
47
 
48
48
 
49
49
  @dataclass
@@ -61,7 +61,7 @@ def resolve_model_metadata(
61
61
  ) -> ModelAssignment:
62
62
  """alias → (display, execution_value).
63
63
 
64
- provider: "claude" | "codex" | "gemini" (그 외는 mapping 미적용)
64
+ provider: "claude" | "codex" | "antigravity" (그 외는 mapping 미적용)
65
65
 
66
66
  Mapping 이 정의된 provider 에 대해 사용자가 빈 값이 아닌 raw_value 를 줬는데
67
67
  mapping 에 없는 경우 `UnknownModelError` 를 발생시킨다. 이는 manifest 에
@@ -175,7 +175,7 @@ def compute_run_paths(
175
175
  run_prompt_snapshot = run_prompts / f"claude-execution-prompt{suffixes['prompts']}.md"
176
176
  claude_worker_prompt = run_prompts / f"claude-worker-prompt{suffixes['prompts']}.md"
177
177
  codex_worker_prompt = run_prompts / f"codex-worker-prompt{suffixes['prompts']}.md"
178
- gemini_worker_prompt = run_prompts / f"gemini-worker-prompt{suffixes['prompts']}.md"
178
+ antigravity_worker_prompt = run_prompts / f"antigravity-worker-prompt{suffixes['prompts']}.md"
179
179
  report_writer_worker_prompt = run_prompts / f"report-writer-worker-prompt{suffixes['prompts']}.md"
180
180
  final_report = run_reports / f"final-report{suffixes['reports']}.md"
181
181
  final_status = run_status / f"final{suffixes['status']}.status"
@@ -190,7 +190,7 @@ def compute_run_paths(
190
190
  task_catalog_file = discovery_dir / "task-catalog.json"
191
191
  claude_worker_result = worker_results / f"claude-worker{suffixes['worker_results']}.md"
192
192
  codex_worker_result = worker_results / f"codex-worker{suffixes['worker_results']}.md"
193
- gemini_worker_result = worker_results / f"gemini-worker{suffixes['worker_results']}.md"
193
+ antigravity_worker_result = worker_results / f"antigravity-worker{suffixes['worker_results']}.md"
194
194
  report_writer_worker_result = worker_results / f"report-writer-worker{suffixes['worker_results']}.md"
195
195
 
196
196
  run_errors_log = run_logs / f"errors-{task_type_segment}-{seqs['state']}.jsonl"
@@ -198,7 +198,7 @@ def compute_run_paths(
198
198
 
199
199
  claude_worker_errors_sidecar = worker_results / f"claude-worker-errors{suffixes['worker_results']}.json"
200
200
  codex_worker_errors_sidecar = worker_results / f"codex-worker-errors{suffixes['worker_results']}.json"
201
- gemini_worker_errors_sidecar = worker_results / f"gemini-worker-errors{suffixes['worker_results']}.json"
201
+ antigravity_worker_errors_sidecar = worker_results / f"antigravity-worker-errors{suffixes['worker_results']}.json"
202
202
  report_writer_worker_errors_sidecar = worker_results / f"report-writer-worker-errors{suffixes['worker_results']}.json"
203
203
 
204
204
  run_validator_script = workspace_root / "validators" / "validate-run.py"
@@ -262,7 +262,7 @@ def compute_run_paths(
262
262
  "RUN_PROMPT_SNAPSHOT_FILE": str(run_prompt_snapshot),
263
263
  "CLAUDE_WORKER_PROMPT_FILE": str(claude_worker_prompt),
264
264
  "CODEX_WORKER_PROMPT_FILE": str(codex_worker_prompt),
265
- "GEMINI_WORKER_PROMPT_FILE": str(gemini_worker_prompt),
265
+ "ANTIGRAVITY_WORKER_PROMPT_FILE": str(antigravity_worker_prompt),
266
266
  "REPORT_WRITER_WORKER_PROMPT_FILE": str(report_writer_worker_prompt),
267
267
  "FINAL_REPORT_PATH": str(final_report),
268
268
  "FINAL_STATUS_PATH": str(final_status),
@@ -277,12 +277,12 @@ def compute_run_paths(
277
277
  "OKSTRA_TASK_CATALOG_FILE": str(task_catalog_file),
278
278
  "CLAUDE_WORKER_RESULT_FILE": str(claude_worker_result),
279
279
  "CODEX_WORKER_RESULT_FILE": str(codex_worker_result),
280
- "GEMINI_WORKER_RESULT_FILE": str(gemini_worker_result),
280
+ "ANTIGRAVITY_WORKER_RESULT_FILE": str(antigravity_worker_result),
281
281
  "REPORT_WRITER_WORKER_RESULT_FILE": str(report_writer_worker_result),
282
282
  "RUN_ERRORS_LOG_PATH": str(run_errors_log),
283
283
  "CLAUDE_WORKER_ERRORS_SIDECAR_PATH": str(claude_worker_errors_sidecar),
284
284
  "CODEX_WORKER_ERRORS_SIDECAR_PATH": str(codex_worker_errors_sidecar),
285
- "GEMINI_WORKER_ERRORS_SIDECAR_PATH": str(gemini_worker_errors_sidecar),
285
+ "ANTIGRAVITY_WORKER_ERRORS_SIDECAR_PATH": str(antigravity_worker_errors_sidecar),
286
286
  "REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH": str(report_writer_worker_errors_sidecar),
287
287
  "RUN_VALIDATOR_PATH": str(run_validator_script),
288
288
  "RUN_MANIFEST_FILENAME": run_manifest_file.name,
@@ -323,7 +323,7 @@ def compute_run_paths(
323
323
  ("RUN_PROMPT_SNAPSHOT_RELATIVE_PATH", run_prompt_snapshot),
324
324
  ("CLAUDE_WORKER_PROMPT_RELATIVE_PATH", claude_worker_prompt),
325
325
  ("CODEX_WORKER_PROMPT_RELATIVE_PATH", codex_worker_prompt),
326
- ("GEMINI_WORKER_PROMPT_RELATIVE_PATH", gemini_worker_prompt),
326
+ ("ANTIGRAVITY_WORKER_PROMPT_RELATIVE_PATH", antigravity_worker_prompt),
327
327
  ("REPORT_WRITER_WORKER_PROMPT_RELATIVE_PATH", report_writer_worker_prompt),
328
328
  ("FINAL_REPORT_RELATIVE_PATH", final_report),
329
329
  ("FINAL_STATUS_RELATIVE_PATH", final_status),
@@ -339,12 +339,12 @@ def compute_run_paths(
339
339
  ("RUN_VALIDATOR_RELATIVE_PATH", run_validator_script),
340
340
  ("CLAUDE_WORKER_RESULT_RELATIVE_PATH", claude_worker_result),
341
341
  ("CODEX_WORKER_RESULT_RELATIVE_PATH", codex_worker_result),
342
- ("GEMINI_WORKER_RESULT_RELATIVE_PATH", gemini_worker_result),
342
+ ("ANTIGRAVITY_WORKER_RESULT_RELATIVE_PATH", antigravity_worker_result),
343
343
  ("REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", report_writer_worker_result),
344
344
  ("RUN_ERRORS_LOG_RELATIVE_PATH", run_errors_log),
345
345
  ("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", claude_worker_errors_sidecar),
346
346
  ("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", codex_worker_errors_sidecar),
347
- ("GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", gemini_worker_errors_sidecar),
347
+ ("ANTIGRAVITY_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", antigravity_worker_errors_sidecar),
348
348
  ("REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", report_writer_worker_errors_sidecar),
349
349
  ("LATEST_RUN_RELATIVE_PATH", run_dir),
350
350
  ]
@@ -14,6 +14,10 @@ from .project_meta import upsert_project_meta
14
14
 
15
15
  DEFAULT_ABORT_AFTER_SECONDS = 12 * 3600 # 12시간 무진척이면 aborted 로 간주
16
16
 
17
+ # recent.jsonl 에서 디스크 manifest 로 재조정 대상이 되는 비종결 상태.
18
+ # completed/failed/aborted 는 이미 종결이라 건드리지 않는다.
19
+ NON_TERMINAL_RECENT_STATUSES = {"prepared", "running", "in-progress"}
20
+
17
21
 
18
22
  def _now_iso() -> str:
19
23
  return datetime.now(timezone.utc).replace(tzinfo=None).strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -74,6 +78,26 @@ def _read_run_manifest_validation(project_root: Path, run_dir_rel: str,
74
78
  return "not-run"
75
79
 
76
80
 
81
+ def _sync_project_index(home: Path, project_id: str, updated_rows: list) -> None:
82
+ """updated_rows 의 (status/finishedAt/validation) 를 프로젝트 index.jsonl 의
83
+ 동일 runId 행에 반영한다. reconcile_active(promote) 와 reconcile_recent
84
+ (in-place refresh) 가 공유하는 단일 인덱스 갱신 경로."""
85
+ proj_index = home / "projects" / project_id / "index.jsonl"
86
+ if not proj_index.is_file():
87
+ return
88
+ by_id = {r["runId"]: r for r in updated_rows}
89
+ existing = read_jsonl(proj_index)
90
+ for er in existing:
91
+ src = by_id.get(er.get("runId"))
92
+ if src:
93
+ er.update({k: src[k] for k in ("status", "finishedAt", "validation")})
94
+ tmp = proj_index.with_suffix(".jsonl.tmp")
95
+ with tmp.open("w") as f:
96
+ for er in existing:
97
+ f.write(json.dumps(er, separators=(",", ":")) + "\n")
98
+ os.replace(tmp, proj_index)
99
+
100
+
77
101
  def reconcile_active(home: Path, *,
78
102
  abort_after_seconds: int = DEFAULT_ABORT_AFTER_SECONDS,
79
103
  project: Optional[str] = None) -> dict:
@@ -148,19 +172,58 @@ def _reconcile_active_locked(home: Path, *,
148
172
  os.replace(tmp, active)
149
173
  for row in promoted:
150
174
  append_jsonl(home / "recent.jsonl", row)
151
- proj_index = home / "projects" / row["projectId"] / "index.jsonl"
152
- if proj_index.is_file():
153
- existing = read_jsonl(proj_index)
154
- for er in existing:
155
- if er.get("runId") == row["runId"]:
156
- er.update({k: row[k] for k in ("status", "finishedAt", "validation")})
157
- tmp = proj_index.with_suffix(".jsonl.tmp")
158
- with tmp.open("w") as f:
159
- for er in existing:
160
- f.write(json.dumps(er, separators=(",", ":")) + "\n")
161
- os.replace(tmp, proj_index)
175
+ _sync_project_index(home, row["projectId"], [row])
162
176
  upsert_project_meta(home, row["projectId"],
163
177
  project_root=row["projectRoot"],
164
178
  when=row["finishedAt"], finished=True)
165
179
  rotate_recent_if_needed(home)
166
180
  return summary
181
+
182
+
183
+ def reconcile_recent(home: Path, *, project: Optional[str] = None) -> dict:
184
+ """recent.jsonl 의 비종결(prepared 등) 행을 디스크 run-manifest 로 in-place 갱신.
185
+
186
+ `--render-only` 로 prepared 로 박힌 행은 record_start 가 terminal 로 취급해
187
+ recent 에 직접 쓰므로 reconcile_active(active.jsonl 전용) 의 promote 경로를
188
+ 타지 않고, backfill 도 이미 인덱싱된 runId 라 skip 한다. 실행이 끝나
189
+ manifest 가 terminal(passed/failed) 로 바뀌어도 recent 의 status 가 prepared
190
+ 로 영영 고착되는 갭을 메운다. 중앙 락 안에서 read-modify-write 한다."""
191
+ with central_lock(home):
192
+ return _reconcile_recent_locked(home, project=project)
193
+
194
+
195
+ def _reconcile_recent_locked(home: Path, *, project: Optional[str] = None) -> dict:
196
+ recent = home / "recent.jsonl"
197
+ summary = {"completed": 0, "failed": 0, "unchanged": 0}
198
+ rows = read_jsonl(recent)
199
+ updated_by_project: dict = {}
200
+ for row in rows:
201
+ if (project and row.get("projectId") != project) or \
202
+ row.get("status") not in NON_TERMINAL_RECENT_STATUSES:
203
+ summary["unchanged"] += 1
204
+ continue
205
+ validation_status = _read_run_manifest_validation(
206
+ Path(row.get("projectRoot", "")), row.get("runDirRel", ""),
207
+ row.get("taskType", ""), int(row.get("runSeq", 0) or 0))
208
+ if not validation_status or validation_status == "not-run":
209
+ summary["unchanged"] += 1
210
+ continue
211
+ new_status = normalize_reconciled_report_status(validation_status)
212
+ if new_status in NON_TERMINAL_RECENT_STATUSES:
213
+ summary["unchanged"] += 1
214
+ continue
215
+ row["status"] = new_status
216
+ row["finishedAt"] = row.get("finishedAt") or _now_iso()
217
+ row["validation"] = validation_status
218
+ updated_by_project.setdefault(row["projectId"], []).append(row)
219
+ summary["completed" if new_status == "completed" else "failed"] += 1
220
+ if not updated_by_project:
221
+ return summary
222
+ tmp = recent.with_suffix(".jsonl.tmp")
223
+ with tmp.open("w") as f:
224
+ for row in rows:
225
+ f.write(json.dumps(row, separators=(",", ":")) + "\n")
226
+ os.replace(tmp, recent)
227
+ for pid, urows in updated_by_project.items():
228
+ _sync_project_index(home, pid, urows)
229
+ return summary
@@ -256,15 +256,15 @@ def _worker_catalog(ctx: dict) -> dict:
256
256
  "resultPath": ctx.get("CODEX_WORKER_RESULT_RELATIVE_PATH", ""),
257
257
  "promptPath": ctx.get("CODEX_WORKER_PROMPT_RELATIVE_PATH", ""),
258
258
  },
259
- "gemini": {
260
- "workerId": "gemini",
261
- "role": "Gemini worker",
262
- "agent": "gemini",
263
- "agentLabel": "Gemini",
264
- "model": ctx.get("GEMINI_WORKER_MODEL", ""),
265
- "modelExecutionValue": ctx.get("GEMINI_WORKER_MODEL_EXECUTION_VALUE", ""),
266
- "resultPath": ctx.get("GEMINI_WORKER_RESULT_RELATIVE_PATH", ""),
267
- "promptPath": ctx.get("GEMINI_WORKER_PROMPT_RELATIVE_PATH", ""),
259
+ "antigravity": {
260
+ "workerId": "antigravity",
261
+ "role": "Antigravity worker",
262
+ "agent": "antigravity",
263
+ "agentLabel": "Antigravity",
264
+ "model": ctx.get("ANTIGRAVITY_WORKER_MODEL", ""),
265
+ "modelExecutionValue": ctx.get("ANTIGRAVITY_WORKER_MODEL_EXECUTION_VALUE", ""),
266
+ "resultPath": ctx.get("ANTIGRAVITY_WORKER_RESULT_RELATIVE_PATH", ""),
267
+ "promptPath": ctx.get("ANTIGRAVITY_WORKER_PROMPT_RELATIVE_PATH", ""),
268
268
  },
269
269
  "report-writer": {
270
270
  "workerId": "report-writer",
@@ -364,7 +364,7 @@ def _active_error_logs(ctx: dict) -> dict:
364
364
  "sidecarsByWorkerId": {
365
365
  "claude": ctx.get("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
366
366
  "codex": ctx.get("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
367
- "gemini": ctx.get("GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
367
+ "antigravity": ctx.get("ANTIGRAVITY_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
368
368
  "report-writer": ctx.get("REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
369
369
  },
370
370
  }
@@ -1057,7 +1057,7 @@ def render_task_manifest(manifest_path: str, ctx: dict) -> None:
1057
1057
  "requiredAgentStatusEntries": required_agent_status_entries,
1058
1058
  "requireDistinctLeadFromClaudeWorker": True,
1059
1059
  "requireAllRequiredWorkerAttempts": True,
1060
- "requireGeminiWorkerAttempt": "gemini" in reviewers,
1060
+ "requireAntigravityWorkerAttempt": "antigravity" in reviewers,
1061
1061
  "requireCollectedWorkerStatusesBeforeFinalVerdict": True,
1062
1062
  "disallowLeadSoloAnalysisAsWorkerResult": True,
1063
1063
  "disallowGenericParallelOnlyExecution": True,
@@ -1127,7 +1127,7 @@ def _build_convergence_block(ctx: dict) -> dict:
1127
1127
  ctx knobs honoured:
1128
1128
  - `OKSTRA_PLAN_VERIFICATION`: "true" | "false" | "" (empty → default True).
1129
1129
  Wired from CLI `--no-plan-verification` (sets "false").
1130
- - `CRITIC_CHOICE`: "" | "off" | "claude" | "codex" | "gemini" — critic
1130
+ - `CRITIC_CHOICE`: "" | "off" | "claude" | "codex" | "antigravity" — critic
1131
1131
  backing provider (enabled only for requirements-discovery / error-analysis /
1132
1132
  implementation-planning / final-verification); model taken from that
1133
1133
  provider's execution value.
@@ -1145,7 +1145,7 @@ def _build_convergence_block(ctx: dict) -> dict:
1145
1145
  critic_exec_key = {
1146
1146
  "claude": "CLAUDE_WORKER_MODEL_EXECUTION_VALUE",
1147
1147
  "codex": "CODEX_WORKER_MODEL_EXECUTION_VALUE",
1148
- "gemini": "GEMINI_WORKER_MODEL_EXECUTION_VALUE",
1148
+ "antigravity": "ANTIGRAVITY_WORKER_MODEL_EXECUTION_VALUE",
1149
1149
  }
1150
1150
  critic_enabled = critic_choice in critic_exec_key and task_type in critic_phases
1151
1151
  critic_block = {
@@ -1287,7 +1287,7 @@ def render_run_manifest(run_manifest_path: str, ctx: dict) -> None:
1287
1287
  + [catalog[item]["role"] for item in reviewers],
1288
1288
  "requireDistinctLeadFromClaudeWorker": True,
1289
1289
  "requireAllRequiredWorkerAttempts": True,
1290
- "requireGeminiWorkerAttempt": "gemini" in reviewers,
1290
+ "requireAntigravityWorkerAttempt": "antigravity" in reviewers,
1291
1291
  "requireCollectedWorkerStatusesBeforeFinalVerdict": True,
1292
1292
  "disallowLeadSoloAnalysisAsWorkerResult": True,
1293
1293
  "disallowGenericParallelOnlyExecution": True,
@@ -1329,7 +1329,7 @@ def render_timeline(timeline_path: str, ctx: dict) -> None:
1329
1329
  worker_prompt_paths = {
1330
1330
  "claude": ctx.get("CLAUDE_WORKER_PROMPT_RELATIVE_PATH", ""),
1331
1331
  "codex": ctx.get("CODEX_WORKER_PROMPT_RELATIVE_PATH", ""),
1332
- "gemini": ctx.get("GEMINI_WORKER_PROMPT_RELATIVE_PATH", ""),
1332
+ "antigravity": ctx.get("ANTIGRAVITY_WORKER_PROMPT_RELATIVE_PATH", ""),
1333
1333
  "report-writer": ctx.get("REPORT_WRITER_WORKER_PROMPT_RELATIVE_PATH", ""),
1334
1334
  }
1335
1335
  path = Path(timeline_path)
@@ -1527,7 +1527,7 @@ def render_task_index(template_path: str, output_path: str, ctx: dict) -> None:
1527
1527
  f"- `{lead_role}`: `{rc.get('leadModel', ctx.get('LEAD_MODEL', ''))}`",
1528
1528
  f"- `Claude worker`: `{ctx.get('CLAUDE_WORKER_MODEL', '')}`",
1529
1529
  f"- `Codex worker`: `{ctx.get('CODEX_WORKER_MODEL', '')}`",
1530
- f"- `Gemini worker`: `{ctx.get('GEMINI_WORKER_MODEL', '')}`",
1530
+ f"- `Antigravity worker`: `{ctx.get('ANTIGRAVITY_WORKER_MODEL', '')}`",
1531
1531
  f"- `Report writer worker`: `{ctx.get('REPORT_WRITER_MODEL', '')}`",
1532
1532
  ]
1533
1533
  ),
@@ -1759,9 +1759,9 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
1759
1759
  worker_role_sentence = "- No worker roles were selected for this run."
1760
1760
  preferred_results_sentence = "- No worker results are expected for this run."
1761
1761
  worker_attempt_sentence = (
1762
- "- `Gemini worker` is mandatory to attempt for this workflow."
1763
- if "gemini" in selected
1764
- else "- `Gemini worker` is not selected for this run, so no Gemini attempt is required."
1762
+ "- `Antigravity worker` is mandatory to attempt for this workflow."
1763
+ if "antigravity" in selected
1764
+ else "- `Antigravity worker` is not selected for this run, so no Antigravity attempt is required."
1765
1765
  )
1766
1766
 
1767
1767
  # Team creation gate block: phase-conditional.
@@ -1998,7 +1998,7 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
1998
1998
  "\n"
1999
1999
  "- The `runs/<phase>/prompts/<worker>-worker-prompt-*.md` files referenced in `task-manifest.json → artifacts.workerPromptPathByWorkerId` are **NOT** rendered by the okstra runtime. Those paths are the *assigned* locations where the prompt history MUST be written at dispatch time.\n"
2000
2000
  "- Therefore: at the start of every phase the prompts/ directory is normally empty (or contains only previously-dispatched workers' files). This is expected. Do NOT narrate it as \"missing\", \"누락\", or \"not yet rendered\" — it just means dispatch has not happened yet.\n"
2001
- "- Before dispatching any required worker, **you (the lead) construct the worker prompt and persist it to the assigned absolute path using `Write`** (per `team-contract` rule 6). Only after persisting do you call the worker subagent (Agent tool / Codex / Gemini wrapper). The wrapper subagents will also re-write the same file on their end; the double-write is intentional and idempotent.\n"
2001
+ "- Before dispatching any required worker, **you (the lead) construct the worker prompt and persist it to the assigned absolute path using `Write`** (per `team-contract` rule 6). Only after persisting do you call the worker subagent (Agent tool / Codex / Antigravity wrapper). The wrapper subagents will also re-write the same file on their end; the double-write is intentional and idempotent.\n"
2002
2002
  "- Do not \"check if the file exists and skip dispatch\" — file presence is not a signal to skip. Worker selection and skipping rules come from team-state, never from prompts/ directory contents.\n"
2003
2003
  f"- **Worker Preamble Path (single anchor — replaces inlined `[Required reading]` and `[Error reporting]` blocks).** Every worker prompt you construct MUST include the anchor header `**Worker Preamble Path:** {ctx.get('WORKER_PROMPT_PREAMBLE_PATH', str(okstra_home() / 'templates' / 'worker-prompt-preamble.md'))}`. The file at that path is the canonical SSOT for Required Reading + Error Reporting + Output sections; workers Read it end-to-end before producing output. Do NOT re-inline those blocks into worker prompt bodies — that is the legacy ~80-line dispatch boilerplate that this anchor is designed to replace."
2004
2004
  )
@@ -2014,7 +2014,7 @@ def inject_lead_prompt_computed_tokens(ctx: dict) -> None:
2014
2014
  ctx["MODEL_ASSIGNMENT_LINES"] = "\n".join(model_assignment_lines)
2015
2015
  ctx["TEAM_ROLE_LINES"] = "\n".join(team_role_lines)
2016
2016
  ctx["REQUIRED_WORKER_ROLE_SENTENCE"] = worker_role_sentence
2017
- ctx["GEMINI_ATTEMPT_SENTENCE"] = worker_attempt_sentence
2017
+ ctx["ANTIGRAVITY_ATTEMPT_SENTENCE"] = worker_attempt_sentence
2018
2018
  ctx["PREFERRED_WORKER_RESULTS_SENTENCE"] = preferred_results_sentence
2019
2019
  ctx["EXECUTION_STATUS_EXACT_ENTRIES"] = ", ".join(execution_status_entries)
2020
2020
  ctx["EXECUTION_STATUS_TABLE_ROWS"] = "\n".join(execution_status_table_lines)
@@ -294,7 +294,7 @@ class PrepareInputs:
294
294
  lead_model: str = ""
295
295
  claude_model: str = ""
296
296
  codex_model: str = ""
297
- gemini_model: str = ""
297
+ antigravity_model: str = ""
298
298
  report_writer_model: str = ""
299
299
  lead_runtime: str = "claude-code"
300
300
  lead_runtime_request: str = ""
@@ -989,7 +989,7 @@ def _canonical_argv(inp: PrepareInputs, ctx: dict) -> list[str]:
989
989
  ("--lead-model", inp.lead_model or ctx.get("LEAD_MODEL", "")),
990
990
  ("--claude-model", inp.claude_model or ctx.get("CLAUDE_WORKER_MODEL", "")),
991
991
  ("--codex-model", inp.codex_model or ctx.get("CODEX_WORKER_MODEL", "")),
992
- ("--gemini-model", inp.gemini_model or ctx.get("GEMINI_WORKER_MODEL", "")),
992
+ ("--antigravity-model", inp.antigravity_model or ctx.get("ANTIGRAVITY_WORKER_MODEL", "")),
993
993
  ("--report-writer-model", inp.report_writer_model or ctx.get("REPORT_WRITER_MODEL", "")),
994
994
  ("--lead-runtime", inp.lead_runtime if inp.lead_runtime != "claude-code" else ""),
995
995
  ("--executor", inp.executor or ctx.get("EXECUTOR_PROVIDER", "")),
@@ -1441,11 +1441,11 @@ class _ModelBindings:
1441
1441
 
1442
1442
 
1443
1443
  def _resolve_worker_models(inp: PrepareInputs) -> dict:
1444
- """lead/claude/codex/gemini/report-writer 모델을 alias → (display, execution) 로 해소."""
1444
+ """lead/claude/codex/antigravity/report-writer 모델을 alias → (display, execution) 로 해소."""
1445
1445
  lead_default = _default("OKSTRA_DEFAULT_LEAD_MODEL", "opus")
1446
1446
  claude_default = _default("OKSTRA_DEFAULT_CLAUDE_MODEL", "opus")
1447
1447
  codex_default = _default("OKSTRA_DEFAULT_CODEX_MODEL", "gpt-5.5")
1448
- gemini_default = _default("OKSTRA_DEFAULT_GEMINI_MODEL", "auto")
1448
+ antigravity_default = _default("OKSTRA_DEFAULT_ANTIGRAVITY_MODEL", "auto")
1449
1449
  report_writer_default = _default("OKSTRA_DEFAULT_REPORT_WRITER_MODEL", lead_default)
1450
1450
  return {
1451
1451
  "lead": resolve_model_metadata(
@@ -1461,8 +1461,8 @@ def _resolve_worker_models(inp: PrepareInputs) -> dict:
1461
1461
  default_display=codex_default, default_execution=codex_default,
1462
1462
  ),
1463
1463
  "ge": resolve_model_metadata(
1464
- provider="gemini", raw_value=inp.gemini_model,
1465
- default_display=gemini_default, default_execution=gemini_default,
1464
+ provider="antigravity", raw_value=inp.antigravity_model,
1465
+ default_display=antigravity_default, default_execution=antigravity_default,
1466
1466
  ),
1467
1467
  "rw": resolve_model_metadata(
1468
1468
  provider="claude", raw_value=inp.report_writer_model,
@@ -1475,15 +1475,15 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
1475
1475
  """worker 모델 + critic 선택 + executor 바인딩을 한 묶음으로 해소·검증한다."""
1476
1476
  m = _resolve_worker_models(inp)
1477
1477
  critic_choice = (inp.critic or "").strip().lower()
1478
- if critic_choice not in ("", "off", "claude", "codex", "gemini"):
1478
+ if critic_choice not in ("", "off", "claude", "codex", "antigravity"):
1479
1479
  raise PrepareError(
1480
- f"--critic must be one of: off, claude, codex, gemini (got: {critic_choice!r})"
1480
+ f"--critic must be one of: off, claude, codex, antigravity (got: {critic_choice!r})"
1481
1481
  )
1482
1482
  executor_default = _default("OKSTRA_DEFAULT_EXECUTOR", "claude")
1483
1483
  executor_provider = (inp.executor or executor_default).strip().lower()
1484
- if executor_provider not in ("claude", "codex", "gemini"):
1484
+ if executor_provider not in ("claude", "codex", "antigravity"):
1485
1485
  raise PrepareError(
1486
- f"--executor must be one of: claude, codex, gemini (got: {executor_provider!r})"
1486
+ f"--executor must be one of: claude, codex, antigravity (got: {executor_provider!r})"
1487
1487
  )
1488
1488
  if inp.task_type == "implementation" and executor_provider not in workers:
1489
1489
  raise PrepareError(
@@ -1494,7 +1494,7 @@ def _resolve_model_bindings(inp: PrepareInputs, workers: list[str]) -> _ModelBin
1494
1494
  provider_to_meta = {
1495
1495
  "claude": ("Claude executor", "claude-worker", m["cw"]),
1496
1496
  "codex": ("Codex executor", "codex-worker", m["co"]),
1497
- "gemini": ("Gemini executor", "gemini-worker", m["ge"]),
1497
+ "antigravity": ("Antigravity executor", "antigravity-worker", m["ge"]),
1498
1498
  }
1499
1499
  display_name, worker_agent, model_meta = provider_to_meta[executor_provider]
1500
1500
  return _ModelBindings(
@@ -1916,7 +1916,7 @@ def _persist_run_inputs(
1916
1916
  "leadModel": models.lead.display,
1917
1917
  "claudeModel": models.cw.display,
1918
1918
  "codexModel": models.co.display,
1919
- "geminiModel": models.ge.display,
1919
+ "antigravityModel": models.ge.display,
1920
1920
  "reportWriterModel": models.rw.display,
1921
1921
  "executor": models.executor_provider,
1922
1922
  "relatedTasks": inp.related_tasks_raw,
@@ -2273,8 +2273,8 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
2273
2273
  "CLAUDE_WORKER_MODEL_EXECUTION_VALUE": cw.execution,
2274
2274
  "CODEX_WORKER_MODEL": co.display,
2275
2275
  "CODEX_WORKER_MODEL_EXECUTION_VALUE": co.execution,
2276
- "GEMINI_WORKER_MODEL": ge.display,
2277
- "GEMINI_WORKER_MODEL_EXECUTION_VALUE": ge.execution,
2276
+ "ANTIGRAVITY_WORKER_MODEL": ge.display,
2277
+ "ANTIGRAVITY_WORKER_MODEL_EXECUTION_VALUE": ge.execution,
2278
2278
  "REPORT_WRITER_MODEL": rw.display,
2279
2279
  "REPORT_WRITER_MODEL_EXECUTION_VALUE": rw.execution,
2280
2280
  "EXECUTOR_PROVIDER": executor_provider,
@@ -2423,7 +2423,7 @@ def main(argv: list[str]) -> int:
2423
2423
  p.add_argument("--lead-model", default="")
2424
2424
  p.add_argument("--claude-model", default="")
2425
2425
  p.add_argument("--codex-model", default="")
2426
- p.add_argument("--gemini-model", default="")
2426
+ p.add_argument("--antigravity-model", default="")
2427
2427
  p.add_argument("--report-writer-model", default="")
2428
2428
  p.add_argument(
2429
2429
  "--lead-runtime",
@@ -2582,7 +2582,7 @@ def main(argv: list[str]) -> int:
2582
2582
  lead_model=args.lead_model,
2583
2583
  claude_model=args.claude_model,
2584
2584
  codex_model=args.codex_model,
2585
- gemini_model=args.gemini_model,
2585
+ antigravity_model=args.antigravity_model,
2586
2586
  report_writer_model=args.report_writer_model,
2587
2587
  lead_runtime=args.lead_runtime,
2588
2588
  lead_runtime_request=args.lead_runtime_request,