diff --git a/skills/.curated/self-improving-operator/LICENSE.txt b/skills/.curated/self-improving-operator/LICENSE.txt new file mode 100644 index 00000000..4d990d08 --- /dev/null +++ b/skills/.curated/self-improving-operator/LICENSE.txt @@ -0,0 +1,201 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf of + any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets.) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/skills/.curated/self-improving-operator/SKILL.md b/skills/.curated/self-improving-operator/SKILL.md new file mode 100644 index 00000000..35239546 --- /dev/null +++ b/skills/.curated/self-improving-operator/SKILL.md @@ -0,0 +1,97 @@ +--- +name: "self-improving-operator" +description: "Use when the user wants Codex to keep iterating on a project, maintain `.operator/` state, keep finding the next safe task, and stop only on a real stop reason." +--- + +Use this skill when the user wants Codex to keep driving a project forward without stopping after one narrow edit. + +Typical triggers: + +- `continue pushing this project forward` +- `keep improving this until it feels production-ready` +- `take ownership and keep iterating` +- `do not stop after the first fix` +- `proactively improve this repository` +- `bring this closer to enterprise standard` +- `keep finding the next safe high-leverage task` + +## Mission + +Operate a repository as an actively improving system that can keep finding safe, high-leverage work until it reaches a real stop reason. + +The operator must use persistent repo-local state under `.operator/` so a new thread can resume without rediscovering the whole situation. + +## Required loop + +1. Infer or load the mission from `.operator/mission.md`. +2. Refresh repo and GitHub signals into `.operator/backlog.json`. +3. Pick the next safe, high-leverage work item. +4. Implement one bounded improvement. +5. Verify with direct evidence. +6. Write a checkpoint into `.operator/checkpoints/` and update `.operator/state.json`. +7. Re-scan and continue until a real stop reason exists. + +Planning is not a stopping point. If a broad plan is needed, decompose it into backlog items and keep going. + +## Persistent files + +- `.operator/mission.md`: mission, scope, work sources, stop reasons, and publish mode. +- `.operator/backlog.json`: prioritized work items discovered from repo and GitHub signals. +- `.operator/state.json`: current item, verification state, last stop reason, and `next_action`. +- `.operator/checkpoints/*.md`: durable checkpoints written after verified work. + +## Work sources + +- `local_tests` +- `ci_failures` +- `runtime_failures` +- `todo_fixme` +- `docs_handoff_gaps` +- `github_issues` +- `github_pr_reviews` +- `github_discussions` + +Only actionable signals should become backlog items. Ignore explanatory prose, string literals, generated outputs, and any stale scan-derived item that no longer appears in the current refresh. + +## Priority order + +1. broken runtime or ci +2. github blockers +3. existing backlog commitments +4. tests diagnostics onboarding docs +5. cleanup and polish + +## Stop reasons + +- `needs_user_decision` +- `external_blocker` +- `risk_budget_exceeded` +- `no_safe_work` +- `mission_complete` + +Return to the user only when one of the stop reasons is true, and always write the stop reason into `.operator/state.json`. + +## Runtime commands + +Use the bundled script `scripts/operator_runtime.py` for deterministic state updates. + +- `python3 scripts/operator_runtime.py bootstrap --repo /path/to/repo --goal "Ship onboarding reliably"` +- `python3 scripts/operator_runtime.py scan --repo /path/to/repo` +- `python3 scripts/operator_runtime.py next --repo /path/to/repo` +- `python3 scripts/operator_runtime.py ingest-plan --repo /path/to/repo --plan-file /path/to/plan.md` +- `python3 scripts/operator_runtime.py checkpoint --repo /path/to/repo --item-id --summary "..." --verification-status passed --verification-summary "..." --publish-checkpoint` +- `python3 scripts/operator_runtime.py status --repo /path/to/repo` + +## Guardrails + +- Continue automatically only on safe, in-scope, bounded work. +- Do not invent new product lines, marketing tracks, or unrelated missions unless the mission explicitly includes them. +- Default publish mode is checkpoint branch/commit, not auto-PR. +- If a task needs a different skill for one step, use it, then return to this operator loop. +- Prefer tests over confidence, runtime probes over guesses, and backlog updates over memory. + +## Handoff discipline + +- Always save `next_action` before stopping. +- Always record what changed, what was verified, and what remains blocked. +- Make the next thread faster by leaving a clean `.operator` state, not just prose in chat. diff --git a/skills/.curated/self-improving-operator/agents/openai.yaml b/skills/.curated/self-improving-operator/agents/openai.yaml new file mode 100644 index 00000000..99686b7b --- /dev/null +++ b/skills/.curated/self-improving-operator/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Self-Improving Operator" + short_description: "Persistent autonomy kernel with backlog, checkpoints, and next-task continuation" + default_prompt: "Use this skill to load or create `.operator/` state, refresh the backlog, execute one bounded high-leverage task, checkpoint it, and keep going until a real stop reason is reached." diff --git a/skills/.curated/self-improving-operator/references/autonomy-kernel.md b/skills/.curated/self-improving-operator/references/autonomy-kernel.md new file mode 100644 index 00000000..999f6f83 --- /dev/null +++ b/skills/.curated/self-improving-operator/references/autonomy-kernel.md @@ -0,0 +1,41 @@ +# Autonomy Kernel + +The self-improving operator is not just a style guideline. It is a persistent execution loop with a durable backlog. + +## Required state files + +- `.operator/mission.md`: mission, scope, work sources, stop reasons, and publish mode. +- `.operator/backlog.json`: prioritized work items discovered from repo and GitHub signals. +- `.operator/state.json`: current item, verification state, last stop reason, and `next_action`. +- `.operator/checkpoints/*.md`: durable checkpoints written after verified work. + +## Default work sources + +- `local_tests` +- `ci_failures` +- `runtime_failures` +- `todo_fixme` +- `docs_handoff_gaps` +- `github_issues` +- `github_pr_reviews` +- `github_discussions` + +## Priority order + +1. broken runtime or ci +2. github blockers +3. existing backlog commitments +4. tests diagnostics onboarding docs +5. cleanup and polish + +## Stop reasons + +- `needs_user_decision` +- `external_blocker` +- `risk_budget_exceeded` +- `no_safe_work` +- `mission_complete` + +## Default publishing rule + +The default publish mode is `checkpoint_commit`: create a checkpoint branch/commit when work reaches a meaningful verified checkpoint, but do not auto-open a PR. diff --git a/skills/.curated/self-improving-operator/references/runtime-commands.md b/skills/.curated/self-improving-operator/references/runtime-commands.md new file mode 100644 index 00000000..e6a5b556 --- /dev/null +++ b/skills/.curated/self-improving-operator/references/runtime-commands.md @@ -0,0 +1,17 @@ +# Runtime Commands + +Use `scripts/operator_runtime.py` to make the operator loop deterministic. + +- `python3 scripts/operator_runtime.py bootstrap --repo /path/to/repo --goal "Ship onboarding reliably"` +- `python3 scripts/operator_runtime.py scan --repo /path/to/repo` +- `python3 scripts/operator_runtime.py next --repo /path/to/repo` +- `python3 scripts/operator_runtime.py ingest-plan --repo /path/to/repo --plan-file /path/to/plan.md` +- `python3 scripts/operator_runtime.py checkpoint --repo /path/to/repo --item-id --summary "..." --verification-status passed --verification-summary "..." --publish-checkpoint` +- `python3 scripts/operator_runtime.py status --repo /path/to/repo` + +## Notes + +- `scan` refreshes backlog candidates from repo and GitHub signals. +- `next` selects and records the current highest-priority pending item. +- `checkpoint` writes a durable checkpoint and updates `next_action`. +- `ingest-plan` converts a broad plan into multiple executable backlog items. diff --git a/skills/.curated/self-improving-operator/scripts/operator_runtime.py b/skills/.curated/self-improving-operator/scripts/operator_runtime.py new file mode 100644 index 00000000..e5ba209a --- /dev/null +++ b/skills/.curated/self-improving-operator/scripts/operator_runtime.py @@ -0,0 +1,898 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import hashlib +import json +import os +import re +import shutil +import subprocess +import sys +from dataclasses import dataclass +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +STATE_SCHEMA_VERSION = 2 +DEFAULT_WORK_SOURCES = [ + "local_tests", + "ci_failures", + "runtime_failures", + "todo_fixme", + "docs_handoff_gaps", + "github_issues", + "github_pr_reviews", + "github_discussions", +] +DEFAULT_PRIORITY_BUCKETS = { + "ci_failure": 1, + "runtime_failure": 1, + "verification_failure": 1, + "github_pr_review": 2, + "github_issue": 2, + "github_discussion": 2, + "carryover": 3, + "plan_decomposition": 3, + "todo_fixme": 4, + "docs_handoff_gap": 4, + "cleanup": 5, +} +PERSISTENT_PENDING_SOURCES = { + "carryover", + "plan_decomposition", + "runtime_failure", + "verification_failure", +} +DEFAULT_STOP_REASONS = { + "needs_user_decision", + "external_blocker", + "risk_budget_exceeded", + "no_safe_work", + "mission_complete", +} +SKIP_DIR_NAMES = { + ".git", + ".hg", + ".svn", + ".venv", + "node_modules", + "dist", + "build", + "generated", + ".next", + ".turbo", + "__pycache__", + ".operator", +} +TEXT_SIGNAL_EXTENSIONS = {".md", ".mdx", ".txt", ".rst", ".adoc"} +COMMENT_TOKENS = { + ".c": ("//", "/*", "*"), + ".cc": ("//", "/*", "*"), + ".cpp": ("//", "/*", "*"), + ".cs": ("//", "/*", "*"), + ".go": ("//", "/*", "*"), + ".java": ("//", "/*", "*"), + ".js": ("//", "/*", "*"), + ".jsx": ("//", "/*", "*"), + ".kt": ("//", "/*", "*"), + ".lua": ("--",), + ".py": ("#",), + ".rb": ("#",), + ".rs": ("//", "/*", "*"), + ".sh": ("#",), + ".sql": ("--",), + ".swift": ("//", "/*", "*"), + ".ts": ("//", "/*", "*"), + ".tsx": ("//", "/*", "*"), + ".yaml": ("#",), + ".yml": ("#",), +} +ACTIONABLE_TODO_PATTERN = re.compile(r"^(TODO|FIXME|HACK|XXX)\b(?::|\s+)", re.IGNORECASE) +ACTIONABLE_HANDOFF_PATTERN = re.compile( + r"^(next step|next steps|follow[- ]up|known blocker|known blockers|remaining work)\b", + re.IGNORECASE, +) + + +@dataclass +class SignalItem: + source: str + title: str + evidence: list[str] + impact: str + effort: str + risk: str + next_action: str + fingerprint: str + + +def utc_now() -> str: + return datetime.now(timezone.utc).replace(microsecond=0).isoformat() + + +def slugify(value: str) -> str: + slug = re.sub(r"[^a-zA-Z0-9]+", "-", value.strip().lower()).strip("-") + return slug or "item" + + +def read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def write_text(path: Path, content: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + + +def read_json(path: Path, default: Any) -> Any: + if not path.exists(): + return default + return json.loads(path.read_text(encoding="utf-8")) + + +def write_json(path: Path, payload: Any) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8") + + +def parse_frontmatter(text: str) -> tuple[dict[str, Any], str]: + if not text.startswith("---\n"): + return {}, text + + match = re.match(r"^---\n(.*?)\n---\n?", text, re.DOTALL) + if not match: + return {}, text + + data: dict[str, Any] = {} + current_key: str | None = None + for raw_line in match.group(1).splitlines(): + if raw_line.startswith(" - ") and current_key: + data.setdefault(current_key, []) + data[current_key].append(raw_line[4:].strip()) + continue + if ":" not in raw_line: + current_key = None + continue + key, value = raw_line.split(":", 1) + key = key.strip() + value = value.strip().strip('"') + if value == "": + data[key] = [] + else: + data[key] = value + current_key = key + + return data, text[match.end() :] + + +def render_mission(repo_name: str, goal: str, github_repo: str | None) -> str: + github_line = github_repo or "unknown" + return ( + "---\n" + f"title: Improve {repo_name}\n" + f"goal: {goal}\n" + "autonomy_mode: aggressive_in_scope\n" + "continuity: cross_thread_cross_day\n" + "publish_mode: checkpoint_commit\n" + "risk_budget: medium\n" + f"github_repo: {github_line}\n" + "work_sources:\n" + " - local_tests\n" + " - ci_failures\n" + " - runtime_failures\n" + " - todo_fixme\n" + " - docs_handoff_gaps\n" + " - github_issues\n" + " - github_pr_reviews\n" + " - github_discussions\n" + "stop_reasons:\n" + " - needs_user_decision\n" + " - external_blocker\n" + " - risk_budget_exceeded\n" + " - no_safe_work\n" + " - mission_complete\n" + "---\n\n" + "# Mission\n\n" + "## In Scope\n\n" + "- Repository code, tests, docs, CI, and backlog items directly related to the current project.\n" + "- Follow-up work discovered through repo signals or GitHub signals.\n\n" + "## Out Of Scope\n\n" + "- New product lines or marketing tracks not implied by the current repository.\n" + "- Large irreversible architecture shifts without evidence.\n\n" + "## Success Signals\n\n" + "- The current mission is moving through verified checkpoints.\n" + "- `next_action` always points at the next safe, high-leverage task.\n" + ) + + +def run_command(args: list[str], cwd: Path | None = None) -> tuple[int, str, str]: + result = subprocess.run( + args, + cwd=str(cwd) if cwd else None, + capture_output=True, + text=True, + check=False, + ) + return result.returncode, result.stdout, result.stderr + + +def infer_goal(repo_path: Path) -> str: + readme = repo_path / "README.md" + if not readme.exists(): + return f"Improve {repo_path.name} through verified high-leverage iterations." + + text = read_text(readme) + lines = [line.strip() for line in text.splitlines() if line.strip()] + for line in lines: + if line.startswith("#"): + continue + return line + return f"Improve {repo_path.name} through verified high-leverage iterations." + + +def infer_github_repo(repo_path: Path) -> str | None: + code, stdout, _ = run_command(["git", "-C", str(repo_path), "remote", "get-url", "origin"]) + if code != 0: + return None + + remote = stdout.strip() + https_match = re.search(r"github\.com[:/](?P[^/]+)/(?P[^/.]+)(?:\.git)?$", remote) + if not https_match: + return None + return f"{https_match.group('owner')}/{https_match.group('repo')}" + + +def operator_paths(repo_path: Path) -> dict[str, Path]: + root = repo_path / ".operator" + return { + "root": root, + "mission": root / "mission.md", + "backlog": root / "backlog.json", + "state": root / "state.json", + "checkpoints": root / "checkpoints", + } + + +def default_state(github_repo: str | None = None) -> dict[str, Any]: + return { + "schema_version": STATE_SCHEMA_VERSION, + "status": "active", + "current_item_id": None, + "next_action": "Run scan, choose the next item, and continue executing.", + "last_scan_at": None, + "last_checkpoint_at": None, + "last_stop_reason": None, + "last_completed_item_id": None, + "verification": { + "status": "unknown", + "summary": None, + "updated_at": None, + }, + "github_repo": github_repo, + "publish_mode": "checkpoint_commit", + } + + +def bootstrap_state(repo_path: Path, goal: str | None = None, github_repo: str | None = None) -> dict[str, Any]: + paths = operator_paths(repo_path) + paths["checkpoints"].mkdir(parents=True, exist_ok=True) + github_repo = github_repo or infer_github_repo(repo_path) + if not paths["mission"].exists(): + mission_goal = goal or infer_goal(repo_path) + write_text(paths["mission"], render_mission(repo_path.name, mission_goal, github_repo)) + if not paths["backlog"].exists(): + write_json(paths["backlog"], {"schema_version": STATE_SCHEMA_VERSION, "items": []}) + if not paths["state"].exists(): + write_json(paths["state"], default_state(github_repo)) + return load_state(repo_path) + + +def load_mission(repo_path: Path) -> dict[str, Any]: + mission_path = operator_paths(repo_path)["mission"] + if not mission_path.exists(): + bootstrap_state(repo_path) + frontmatter, body = parse_frontmatter(read_text(mission_path)) + frontmatter["body"] = body.strip() + return frontmatter + + +def load_state(repo_path: Path) -> dict[str, Any]: + return read_json(operator_paths(repo_path)["state"], default_state()) + + +def load_backlog(repo_path: Path) -> dict[str, Any]: + return read_json(operator_paths(repo_path)["backlog"], {"schema_version": STATE_SCHEMA_VERSION, "items": []}) + + +def signal_to_item(signal: SignalItem) -> dict[str, Any]: + bucket = DEFAULT_PRIORITY_BUCKETS.get(signal.source, 5) + digest = hashlib.sha1(f"{signal.source}:{signal.fingerprint}".encode("utf-8")).hexdigest()[:10] + return { + "id": f"{slugify(signal.source)}-{digest}", + "title": signal.title, + "source": signal.source, + "evidence": signal.evidence, + "impact": signal.impact, + "effort": signal.effort, + "risk": signal.risk, + "priority_bucket": bucket, + "status": "pending", + "next_action": signal.next_action, + "updated_at": utc_now(), + } + + +def existing_item_map(items: list[dict[str, Any]]) -> dict[str, dict[str, Any]]: + return {item["id"]: item for item in items} + + +def merge_items(existing: list[dict[str, Any]], incoming: list[dict[str, Any]]) -> list[dict[str, Any]]: + merged = existing_item_map(existing) + for item in incoming: + current = merged.get(item["id"]) + if current and current.get("status") == "completed": + continue + if current: + current.update(item) + current.setdefault("status", "pending") + else: + merged[item["id"]] = item + items = list(merged.values()) + items.sort(key=lambda item: (item["priority_bucket"], item["title"])) + return items + + +def iter_candidate_files(repo_path: Path) -> list[Path]: + files: list[Path] = [] + for root, dirs, filenames in os.walk(repo_path): + dirs[:] = [name for name in dirs if name not in SKIP_DIR_NAMES] + for filename in filenames: + path = Path(root) / filename + if path.is_file(): + files.append(path) + return files + + +def strip_doc_prefix(line: str) -> str: + return re.sub(r"^\s*(?:[#>*-]+|\d+[.)])\s*", "", line).strip() + + +def extract_comment_text(path: Path, line: str) -> str | None: + stripped = line.strip() + tokens = COMMENT_TOKENS.get(path.suffix.lower(), ()) + for token in tokens: + index = line.find(token) + if index == -1: + continue + prefix = line[:index].strip() + if prefix: + continue + return line[index + len(token) :].strip() + if path.suffix.lower() in TEXT_SIGNAL_EXTENSIONS: + return strip_doc_prefix(stripped) + return None + + +def actionable_todo_text(path: Path, line: str) -> str | None: + candidate = extract_comment_text(path, line) + if candidate and ACTIONABLE_TODO_PATTERN.search(candidate): + return candidate + return None + + +def actionable_handoff_text(path: Path, line: str) -> str | None: + if path.suffix.lower() not in TEXT_SIGNAL_EXTENSIONS: + return None + candidate = strip_doc_prefix(line) + if candidate and ACTIONABLE_HANDOFF_PATTERN.search(candidate): + return candidate + return None + + +def scan_todo_signals(repo_path: Path) -> list[SignalItem]: + signals: list[SignalItem] = [] + for path in iter_candidate_files(repo_path): + if path.suffix.lower() in {".png", ".jpg", ".jpeg", ".gif", ".ico", ".pdf", ".zip"}: + continue + try: + text = read_text(path) + except UnicodeDecodeError: + continue + for index, line in enumerate(text.splitlines(), start=1): + candidate = actionable_todo_text(path, line) + if candidate: + relpath = str(path.relative_to(repo_path)) + fingerprint = f"{relpath}:{index}:{candidate}" + signals.append( + SignalItem( + source="todo_fixme", + title=f"Resolve TODO in {relpath}:{index}", + evidence=[f"{relpath}:{index}: {candidate}"], + impact="medium", + effort="small", + risk="low", + next_action=f"Inspect {relpath}:{index} and decide whether to implement, remove, or formalize the follow-up.", + fingerprint=fingerprint, + ) + ) + return signals + + +def scan_handoff_signals(repo_path: Path) -> list[SignalItem]: + signals: list[SignalItem] = [] + for path in iter_candidate_files(repo_path): + if path.suffix.lower() not in {".md", ".txt"}: + continue + try: + text = read_text(path) + except UnicodeDecodeError: + continue + for index, line in enumerate(text.splitlines(), start=1): + candidate = actionable_handoff_text(path, line) + if candidate: + relpath = str(path.relative_to(repo_path)) + fingerprint = f"{relpath}:{index}:{candidate}" + signals.append( + SignalItem( + source="docs_handoff_gap", + title=f"Review follow-up note in {relpath}:{index}", + evidence=[f"{relpath}:{index}: {candidate}"], + impact="medium", + effort="small", + risk="low", + next_action=f"Confirm whether the follow-up in {relpath}:{index} is still true and either execute it or update the doc.", + fingerprint=fingerprint, + ) + ) + return signals + + +def command_available(name: str) -> bool: + return shutil.which(name) is not None + + +def github_json(repo: str, args: list[str]) -> Any | None: + if not command_available("gh"): + return None + code, stdout, _ = run_command(["gh", *args, "-R", repo]) + if code != 0 or not stdout.strip(): + return None + return json.loads(stdout) + + +def scan_github_issue_signals(github_repo: str | None) -> list[SignalItem]: + if not github_repo: + return [] + payload = github_json(github_repo, ["issue", "list", "--state", "open", "--limit", "20", "--json", "number,title,url,labels"]) + if not payload: + return [] + + signals: list[SignalItem] = [] + for issue in payload: + fingerprint = f"issue:{issue['number']}" + labels = ", ".join(label["name"] for label in issue.get("labels", [])) or "no labels" + signals.append( + SignalItem( + source="github_issue", + title=f"Resolve GitHub issue #{issue['number']}: {issue['title']}", + evidence=[issue["url"], f"labels: {labels}"], + impact="high", + effort="medium", + risk="medium", + next_action=f"Inspect GitHub issue #{issue['number']} and decide whether it should become the next active work item.", + fingerprint=fingerprint, + ) + ) + return signals + + +def scan_github_pr_review_signals(github_repo: str | None) -> list[SignalItem]: + if not github_repo: + return [] + payload = github_json( + github_repo, + ["pr", "list", "--state", "open", "--limit", "20", "--json", "number,title,url,reviewDecision,isDraft"], + ) + if not payload: + return [] + + signals: list[SignalItem] = [] + for pr in payload: + decision = pr.get("reviewDecision") or "UNKNOWN" + if decision not in {"CHANGES_REQUESTED", "REVIEW_REQUIRED"}: + continue + fingerprint = f"pr:{pr['number']}:{decision}" + signals.append( + SignalItem( + source="github_pr_review", + title=f"Address PR #{pr['number']} review state: {pr['title']}", + evidence=[pr["url"], f"reviewDecision: {decision}"], + impact="high", + effort="medium", + risk="medium", + next_action=f"Inspect PR #{pr['number']} and resolve requested review work before lower-priority tasks.", + fingerprint=fingerprint, + ) + ) + return signals + + +def scan_github_run_signals(github_repo: str | None) -> list[SignalItem]: + if not github_repo: + return [] + payload = github_json( + github_repo, + ["run", "list", "--limit", "20", "--json", "databaseId,workflowName,displayTitle,conclusion,url"], + ) + if not payload: + return [] + + signals: list[SignalItem] = [] + for run in payload: + if run.get("conclusion") != "failure": + continue + fingerprint = f"run:{run['databaseId']}" + title = run.get("displayTitle") or run.get("workflowName") or "Failing workflow" + signals.append( + SignalItem( + source="ci_failure", + title=f"Fix failing CI run: {title}", + evidence=[run["url"], f"workflow: {run.get('workflowName') or 'unknown'}"], + impact="high", + effort="medium", + risk="medium", + next_action=f"Inspect the failing run for {title} and turn it into the next active repair task.", + fingerprint=fingerprint, + ) + ) + return signals + + +def scan_github_discussion_signals(github_repo: str | None) -> list[SignalItem]: + if not github_repo or not command_available("gh"): + return [] + code, stdout, _ = run_command(["gh", "api", f"repos/{github_repo}/discussions?per_page=10"]) + if code != 0 or not stdout.strip(): + return [] + try: + payload = json.loads(stdout) + except json.JSONDecodeError: + return [] + + signals: list[SignalItem] = [] + for discussion in payload: + fingerprint = f"discussion:{discussion.get('number')}" + title = discussion.get("title") or "GitHub discussion" + signals.append( + SignalItem( + source="github_discussion", + title=f"Review actionable discussion #{discussion.get('number')}: {title}", + evidence=[discussion.get("html_url", ""), f"category: {discussion.get('category', {}).get('name', 'unknown')}"], + impact="medium", + effort="medium", + risk="low", + next_action=f"Check whether discussion #{discussion.get('number')} implies a concrete backlog item.", + fingerprint=fingerprint, + ) + ) + return signals + + +def carry_over_items(backlog: dict[str, Any]) -> list[dict[str, Any]]: + items = [] + for item in backlog.get("items", []): + status = item.get("status") + source = item.get("source", "carryover") + keep_pending = status == "pending" and source in PERSISTENT_PENDING_SOURCES + if status in {"in_progress", "blocked"} or keep_pending: + item = dict(item) + item["source"] = source + item["priority_bucket"] = DEFAULT_PRIORITY_BUCKETS.get(source, item.get("priority_bucket", 5)) + items.append(item) + return items + + +def refresh_backlog(repo_path: Path) -> dict[str, Any]: + bootstrap_state(repo_path) + mission = load_mission(repo_path) + state = load_state(repo_path) + existing_backlog = load_backlog(repo_path) + github_repo = mission.get("github_repo") or state.get("github_repo") or infer_github_repo(repo_path) + + incoming: list[dict[str, Any]] = carry_over_items(existing_backlog) + for scanner in ( + scan_todo_signals, + scan_handoff_signals, + ): + incoming.extend(signal_to_item(item) for item in scanner(repo_path)) + + for scanner in ( + scan_github_run_signals, + scan_github_pr_review_signals, + scan_github_issue_signals, + scan_github_discussion_signals, + ): + incoming.extend(signal_to_item(item) for item in scanner(github_repo)) + + completed_items = [item for item in existing_backlog.get("items", []) if item.get("status") == "completed"] + merged = merge_items(completed_items, incoming) + payload = {"schema_version": STATE_SCHEMA_VERSION, "items": merged} + write_json(operator_paths(repo_path)["backlog"], payload) + state["last_scan_at"] = utc_now() + state["github_repo"] = github_repo + write_json(operator_paths(repo_path)["state"], state) + return payload + + +def choose_next_item(repo_path: Path) -> dict[str, Any] | None: + backlog = load_backlog(repo_path) + state = load_state(repo_path) + pending = [item for item in backlog.get("items", []) if item.get("status") == "pending"] + pending.sort(key=lambda item: (item.get("priority_bucket", 99), item.get("title", ""))) + next_item = pending[0] if pending else None + if next_item: + next_item["status"] = "in_progress" + state["current_item_id"] = next_item["id"] + state["next_action"] = next_item["next_action"] + state["last_stop_reason"] = None + else: + state["current_item_id"] = None + state["next_action"] = "No safe pending work found. Stop only after recording `no_safe_work` or a more specific stop reason." + state["last_stop_reason"] = "no_safe_work" + write_json(operator_paths(repo_path)["backlog"], backlog) + write_json(operator_paths(repo_path)["state"], state) + return next_item + + +def parse_plan_items(plan_text: str) -> list[str]: + items: list[str] = [] + for raw_line in plan_text.splitlines(): + match = re.match(r"^\s*(?:[-*]|\d+\.)\s+(.*)$", raw_line) + if match: + text = match.group(1).strip() + if text: + items.append(text) + return items + + +def ingest_plan(repo_path: Path, plan_text: str) -> dict[str, Any]: + backlog = refresh_backlog(repo_path) + new_items = [] + for line in parse_plan_items(plan_text): + signal = SignalItem( + source="plan_decomposition", + title=line, + evidence=["decomposed from a broad plan"], + impact="high", + effort="medium", + risk="low", + next_action=f"Execute the planned step: {line}", + fingerprint=line, + ) + new_items.append(signal_to_item(signal)) + payload = {"schema_version": STATE_SCHEMA_VERSION, "items": merge_items(backlog["items"], new_items)} + write_json(operator_paths(repo_path)["backlog"], payload) + return payload + + +def current_branch(repo_path: Path) -> str | None: + code, stdout, _ = run_command(["git", "-C", str(repo_path), "branch", "--show-current"]) + return stdout.strip() if code == 0 and stdout.strip() else None + + +def default_branch(repo_path: Path) -> str | None: + for branch_name in ("main", "master"): + code, _, _ = run_command(["git", "-C", str(repo_path), "rev-parse", "--verify", branch_name]) + if code == 0: + return branch_name + return None + + +def ensure_checkpoint_branch(repo_path: Path, item: dict[str, Any]) -> None: + branch = current_branch(repo_path) + default = default_branch(repo_path) + if branch and default and branch == default: + checkpoint_branch = f"codex/checkpoint/{datetime.now().strftime('%Y%m%d-%H%M%S')}-{slugify(item['title'])[:32]}" + run_command(["git", "-C", str(repo_path), "checkout", "-b", checkpoint_branch]) + + +def maybe_commit_checkpoint(repo_path: Path, item: dict[str, Any]) -> None: + code, stdout, _ = run_command(["git", "-C", str(repo_path), "status", "--short"]) + if code != 0 or not stdout.strip(): + return + ensure_checkpoint_branch(repo_path, item) + run_command(["git", "-C", str(repo_path), "add", "-A"]) + run_command(["git", "-C", str(repo_path), "commit", "-m", f"checkpoint: {item['title']}"]) + + +def write_checkpoint( + repo_path: Path, + item: dict[str, Any], + summary: str, + verification_status: str, + verification_summary: str, + stop_reason: str | None, +) -> Path: + timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") + checkpoint_path = operator_paths(repo_path)["checkpoints"] / f"{timestamp}-{slugify(item['title'])}.md" + content = ( + f"# Checkpoint: {item['title']}\n\n" + f"- Time: {utc_now()}\n" + f"- Item ID: {item['id']}\n" + f"- Verification: {verification_status}\n" + f"- Stop reason: {stop_reason or 'none'}\n\n" + "## Summary\n\n" + f"{summary.strip()}\n\n" + "## Verification\n\n" + f"{verification_summary.strip()}\n" + ) + write_text(checkpoint_path, content) + return checkpoint_path + + +def apply_checkpoint( + repo_path: Path, + item_id: str, + summary: str, + verification_status: str, + verification_summary: str, + stop_reason: str | None, + publish_checkpoint: bool, +) -> dict[str, Any]: + if stop_reason and stop_reason not in DEFAULT_STOP_REASONS: + raise SystemExit(f"Unsupported stop reason: {stop_reason}") + + backlog = load_backlog(repo_path) + state = load_state(repo_path) + item = next((candidate for candidate in backlog["items"] if candidate["id"] == item_id), None) + if not item: + raise SystemExit(f"Unknown backlog item: {item_id}") + + checkpoint_path = write_checkpoint(repo_path, item, summary, verification_status, verification_summary, stop_reason) + if verification_status == "passed": + item["status"] = "completed" + state["last_completed_item_id"] = item_id + elif verification_status == "failed": + item["status"] = "blocked" + failure_signal = signal_to_item( + SignalItem( + source="verification_failure", + title=f"Repair failed verification for {item['title']}", + evidence=[verification_summary.strip()], + impact="high", + effort="medium", + risk="medium", + next_action=f"Fix the failed verification for {item['title']} and rerun validation.", + fingerprint=f"verification:{item_id}:{verification_summary.strip()}", + ) + ) + backlog["items"] = merge_items(backlog["items"], [failure_signal]) + else: + item["status"] = "completed" + state["last_completed_item_id"] = item_id + + state["verification"] = { + "status": verification_status, + "summary": verification_summary.strip(), + "updated_at": utc_now(), + } + state["last_checkpoint_at"] = utc_now() + state["last_stop_reason"] = stop_reason + state["current_item_id"] = None + write_json(operator_paths(repo_path)["backlog"], backlog) + write_json(operator_paths(repo_path)["state"], state) + + if publish_checkpoint: + maybe_commit_checkpoint(repo_path, item) + + refresh_backlog(repo_path) + next_item = choose_next_item(repo_path) + state = load_state(repo_path) + state["next_action"] = next_item["next_action"] if next_item else "Wait for a user decision or a new safe signal before continuing." + write_json(operator_paths(repo_path)["state"], state) + return { + "checkpoint": str(checkpoint_path), + "next_item": next_item, + } + + +def status_payload(repo_path: Path) -> dict[str, Any]: + mission = load_mission(repo_path) + backlog = load_backlog(repo_path) + state = load_state(repo_path) + counts: dict[str, int] = {} + for item in backlog.get("items", []): + status = item.get("status", "pending") + counts[status] = counts.get(status, 0) + 1 + return { + "mission_title": mission.get("title"), + "goal": mission.get("goal"), + "github_repo": state.get("github_repo"), + "current_item_id": state.get("current_item_id"), + "next_action": state.get("next_action"), + "last_stop_reason": state.get("last_stop_reason"), + "counts": counts, + } + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Persistent runtime for the self-improving operator.") + subparsers = parser.add_subparsers(dest="command", required=True) + + bootstrap_parser = subparsers.add_parser("bootstrap") + bootstrap_parser.add_argument("--repo", required=True) + bootstrap_parser.add_argument("--goal") + bootstrap_parser.add_argument("--github-repo") + + scan_parser = subparsers.add_parser("scan") + scan_parser.add_argument("--repo", required=True) + + next_parser = subparsers.add_parser("next") + next_parser.add_argument("--repo", required=True) + + status_parser = subparsers.add_parser("status") + status_parser.add_argument("--repo", required=True) + + ingest_parser = subparsers.add_parser("ingest-plan") + ingest_parser.add_argument("--repo", required=True) + ingest_parser.add_argument("--plan-file") + ingest_parser.add_argument("--plan-text") + + checkpoint_parser = subparsers.add_parser("checkpoint") + checkpoint_parser.add_argument("--repo", required=True) + checkpoint_parser.add_argument("--item-id", required=True) + checkpoint_parser.add_argument("--summary", required=True) + checkpoint_parser.add_argument("--verification-status", choices=["passed", "failed", "unknown"], required=True) + checkpoint_parser.add_argument("--verification-summary", required=True) + checkpoint_parser.add_argument("--stop-reason") + checkpoint_parser.add_argument("--publish-checkpoint", action="store_true") + + return parser.parse_args() + + +def main() -> int: + args = parse_args() + repo_path = Path(getattr(args, "repo")).resolve() + + if args.command == "bootstrap": + state = bootstrap_state(repo_path, goal=args.goal, github_repo=args.github_repo) + print(json.dumps(state, indent=2)) + return 0 + if args.command == "scan": + payload = refresh_backlog(repo_path) + print(json.dumps(payload, indent=2)) + return 0 + if args.command == "next": + next_item = choose_next_item(repo_path) + print(json.dumps(next_item or {"stop_reason": "no_safe_work"}, indent=2)) + return 0 + if args.command == "status": + print(json.dumps(status_payload(repo_path), indent=2)) + return 0 + if args.command == "ingest-plan": + if not args.plan_file and not args.plan_text: + raise SystemExit("Provide --plan-file or --plan-text.") + plan_text = args.plan_text or read_text(Path(args.plan_file)) + payload = ingest_plan(repo_path, plan_text) + print(json.dumps(payload, indent=2)) + return 0 + if args.command == "checkpoint": + payload = apply_checkpoint( + repo_path=repo_path, + item_id=args.item_id, + summary=args.summary, + verification_status=args.verification_status, + verification_summary=args.verification_summary, + stop_reason=args.stop_reason, + publish_checkpoint=args.publish_checkpoint, + ) + print(json.dumps(payload, indent=2)) + return 0 + return 0 + + +if __name__ == "__main__": + sys.exit(main())