{"repo":"NousResearch/hermes-agent","public_id":"39996","display_label":"Background review fork tool whitelist and read-before-write fixes","summary":"These issues and PRs center on the background/cron review fork's tool whitelist being too restrictive (blocking read_file and other read-only tools) and the resulting curator loops caused by a read-before-write guard not being satisfied. Additional adjacent concerns include narrowing the tool schema exposed to local/weak models and configuring which memory providers the background review can write to.","subsystem":"agent","counts":{"members":37,"open_issues":4,"open_issues_distinct":4,"open_prs":1,"closed_issues":9,"merged_prs":6,"attempt_prs":18},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":55906},"resolution":{"state":"partially_resolved","by":"pr-55906,pr-94530,pr-98254,pr-98278"},"freshness":{"state":"verified","last_verified_at":"2026-08-30T02:10:07+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.049729729729729714},"fix_edges":[{"issue_number":33064,"pr_number":97650,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":39996,"pr_number":98254,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":40003,"pr_number":98254,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":44672,"pr_number":98278,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":45877,"pr_number":98254,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":55647,"pr_number":55906,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":61521,"pr_number":98254,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":62397,"pr_number":94530,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":63964,"pr_number":97451,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":63964,"pr_number":97650,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false},{"issue_number":64926,"pr_number":98254,"tier":"verified","basis":"superseded_ref","confidence":0.9,"recoverable_from_timeline":false}],"members":[{"kind":"issue","number":39996,"title":"[Bug]: background-review fork advertises the full tool schema to LOCAL endpoints, making weak local models thrash the deny-wall","url":null,"state":"closed"},{"kind":"issue","number":40003,"title":"[Bug]: background-review fork can't read an external file — model calls skill_manage(action=\"read_file\") and gets \"Unknown action\"","url":null,"state":"closed"},{"kind":"issue","number":41465,"title":"[Bug]: Background review skill creation fails — tool whitelist blocks read_file","url":null,"state":"closed"},{"kind":"issue","number":44672,"title":"Background self-review: make the tool whitelist configurable (allow MCP/memory-provider writes)","url":null,"state":"closed"},{"kind":"issue","number":45877,"title":"Cron background review blocks read-only tools (read_file, search_files)","url":null,"state":"closed"},{"kind":"issue","number":55647,"title":"background-review: skill_manage patches hallucinate existing skill content; review fork writes without read-before-write invariant","url":null,"state":"closed"},{"kind":"issue","number":58475,"title":"[Bug]: Background review agent repeatedly gets stuck — prompt ambiguity, vague tool whitelist, and incomprehensible error message","url":null,"state":"open"},{"kind":"issue","number":59244,"title":"Bug: background memory review ignores configured memory provider and only writes legacy memory","url":null,"state":"open"},{"kind":"issue","number":61521,"title":"background_review fork advertises the parent's full toolset but only permits skills+memory → per-turn tool-denial storm starves the self-improvement loop","url":null,"state":"closed"},{"kind":"issue","number":62397,"title":"Background review fork can't patch skills: prompt never calls skill_view, guard refuses the write (read-before-write mismatch)","url":null,"state":"closed"},{"kind":"issue","number":63388,"title":"[Bug] Curator/skill_manage fails in background: \"SKILL.md content has not been loaded in this review turn\"","url":null,"state":"closed"},{"kind":"issue","number":63964,"title":"blocker-report: background curator loops on skill_manage patch errors","url":null,"state":"open"},{"kind":"issue","number":64131,"title":"Background curator skill_manage loop reports repeated guard/schema failures","url":null,"state":"open"},{"kind":"pr","number":39997,"title":"fix(agent): narrow background-review tool schema for local providers","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#39996","number":39996},{"kind":"pr","value":"#39997","number":39997},{"kind":"pr","value":"#39997","number":39997},{"kind":"pr","value":"#39996","number":39996},{"kind":"pr","value":"#39997","number":39997},{"kind":"pr","value":"#39997","number":39997}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":40007,"title":"fix(agent): allow background review to read files","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":40060,"title":"fix(background_review): add read_file to review fork tool whitelist","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"1ee30352ca60f79d984bba62ba1ffd2716c36924","number":0},{"kind":"pr","value":"#98254","number":98254},{"kind":"pr","value":"#40003","number":40003}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":41469,"title":"fix: grant background review agent read_file access for skill patching","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"20871c1d941a697723aae24cd8e88afe701793ae","number":0}],"category":"uncategorized","implication":"needs_llm"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":45892,"title":"fix(agent): allow read-only file tools in background review whitelist","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"1ee30352ca60f79d984bba62ba1ffd2716c36924","number":0},{"kind":"pr","value":"#98254","number":98254},{"kind":"pr","value":"#40060","number":40060},{"kind":"pr","value":"#46049","number":46049}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":46049,"title":"fix(agent): allow read-only file tools in cron background review","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"1ee30352ca60f79d984bba62ba1ffd2716c36924","number":0},{"kind":"pr","value":"#98254","number":98254},{"kind":"pr","value":"#40060","number":40060}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":55657,"title":"fix(skills): require review forks to read before writing skills","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"20871c1d9","number":0},{"kind":"pr","value":"#55906","number":55906}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":55665,"title":"fix(tools): guard against catastrophic content loss in background review patches","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#55906","number":55906},{"kind":"pr","value":"#55647","number":55647}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":55906,"title":"fix(skills): require background-review forks to read before writing skills (#55647)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/background_review.py","scripts/release.py","tests/tools/test_skill_manager_tool.py","tools/skill_manager_tool.py","tools/skills_tool.py"],"fix_hunks":[{"file":"agent/background_review.py","hunks":["@@ -739,6 +739,13 @@ def _bg_review_auto_deny(command, description, **kwargs):\n                     \"{tool_name}. Only memory/skill tools are allowed.\"\n                 ),\n             )\n+            try:\n+                from tools.skill_manager_tool import _reset_background_review_read_marks\n+\n+                _reset_background_review_read_marks()\n+            except Exception:\n+                pass\n+\n             try:\n                 # Routed to a different model -> replay a digest (cache is cold\n                 # on that model anyway, so minimise cold-written tokens). Same"]},{"file":"scripts/release.py","hunks":["@@ -271,6 +271,7 @@\n     \"[redacted:email]\": \"zhaoleibd\",\n     \"[redacted:email]\": \"jeffrobodie-glitch\",\n     \"[redacted:email]\": \"kyssta-exe\",\n+    \"[redacted:email]\": \"kyssta-exe\",  # PR #55657 salvage (read-before-write invariant for background-review skill patches; #55647)\n     \"[redacted:email]\": \"ITheEqualizer\",\n     \"[redacted:email]\": \"stremtec\",\n     \"[redacted:email]\": \"solaitken\","]},{"file":"tests/tools/test_skill_manager_tool.py","hunks":["@@ -957,6 +957,9 @@ def test_background_review_unpinned_skill_not_blocked_by_pin_guard(self, tmp_pat\n             _create_skill(\"my-skill\", VALID_SKILL_CONTENT)\n             token = [redacted:secret-assignment]\n             try:\n+                from tools.skill_manager_tool import mark_background_review_skill_read\n+\n+                mark_background_review_skill_read(tmp_path / \"my-skill\" / \"SKILL.md\")\n                 with patch(\n                     \"tools.skill_usage.get_record\",\n                     side_effect=lambda n: {\"pinned\": False},","@@ -1173,6 +1176,7 @@ def _curator_pass(tmp_path, *, monkeypatch):\n     skills_root.mkdir(parents=True, exist_ok=True)\n     monkeypatch.setenv(\"HERMES_HOME\", str(hermes_home))\n     with patch(\"tools.skill_manager_tool.SKILLS_DIR\", skills_root), \\\n+         patch(\"tools.skills_tool.SKILLS_DIR\", skills_root), \\\n          patch(\"agent.skill_utils.get_all_skills_dirs\", return_value=[skills_root]), \\\n          patch(\"tools.skill_provenance.is_background_review\", return_value=True):\n         yield skills_root","@@ -1281,3 +1285,66 @@ def test_dispatcher_preserves_usage_record_on_curator_archive(self, tmp_path, mo\n             rec = skill_usage.get_record(\"narrow\")\n         # Record kept (not forgotten) and marked archived.\n         assert rec.get(\"state\") == skill_usage.STATE_ARCHIVED\n+\n+    def test_background_review_patch_requires_skill_view_first(self, tmp_path, monkeypatch):\n+        from tools.skills_tool import skill_view\n+        from tools.skill_manager_tool import _reset_background_review_read_marks\n+\n+        _reset_background_review_read_marks()\n+        with _curator_pass(tmp_path, monkeypatch=monkeypatch):\n+            _create_skill(\"reviewed\", _skill_content(\"reviewed\"))\n+\n+            blocked = json.loads(skill_manage(\n+                action=\"patch\",\n+                name=\"reviewed\",\n+                old_string=\"Step 1: Do the thing.\",\n+                new_string=\"Step 1: Do the thing safely.\",\n+            ))\n+            assert blocked[\"success\"] is False\n+            assert blocked.get(\"_read_before_write_required\") is True\n+\n+            viewed = json.loads(skill_view(\"reviewed\"))\n+            assert viewed[\"success\"] is True\n+\n+            allowed = json.loads(skill_manage(\n+                action=\"patch\",\n+                name=\"reviewed\",\n+                old_string=\"Step 1: Do the thing.\",\n+                new_string=\"Step 1: Do the thing safely.\",\n+            ))\n+            assert allowed[\"success\"] is True, allowed\n+\n+        _reset_background_review_read_marks()\n+\n+    def test_background_review_support_file_overwrite_requires_that_file_read(self, tmp_path, monkeypatch):\n+        from tools.skills_tool import skill_view\n+        from tools.skill_manager_tool import _reset_background_review_read_marks\n+\n+        _reset_background_review_read_marks()\n+        with _curator_pass(tmp_path, monkeypatch=monkeypatch):\n+            _create_skill(\"reviewed\", _skill_content(\"reviewed\"))\n+            ref = tmp_path / \".hermes\" / \"skills\" / \"reviewed\" / \"references\"\n+            ref.mkdir()\n+            (ref / \"workflow.md\").write_text(\"old workflow\\n\", encoding=\"utf-8\")\n+\n+            # Reading SKILL.md does not authorize overwriting a linked file.\n+            assert json.loads(skill_view(\"reviewed\"))[\"success\"] is True\n+            blocked = json.loads(skill_manage(\n+                action=\"write_file\",\n+                name=\"reviewed\",\n+                file_path=\"references/workflow.md\",\n+                file_content=\"new workflow\\n\",\n+            ))\n+            assert blocked[\"success\"] is False\n+            assert blocked.get(\"_read_before_write_required\") is True\n+\n+            assert json.loads(skill_view(\"reviewed\", \"references/workflow.md\"))[\"success\"] is True\n+            allowed = json.loads(skill_manage(\n+                action=\"write_file\",\n+                name=\"reviewed\",\n+                file_path=\"references/workflow.md\",\n+                file_content=\"new workflow\\n\",\n+            ))\n+            assert allowed[\"success\"] is True, allowed\n+\n+        _reset_background_review_read_marks()"]},{"file":"tools/skill_manager_tool.py","hunks":["@@ -38,15 +38,58 @@\n import re\n import shutil\n import tempfile\n+import contextvars as _ctxvars\n from pathlib import Path\n-from hermes_constants import get_hermes_home, display_hermes_home\n-from typing import Dict, Any, List, Optional, Tuple\n+from typing import Any, Dict, List, Optional, Tuple\n \n+from hermes_constants import get_hermes_home, display_hermes_home\n from utils import atomic_replace, is_truthy_value\n from hermes_cli.config import cfg_get\n \n logger = logging.getLogger(__name__)\n \n+_background_review_read_paths: \"_ctxvars.ContextVar[frozenset[str]]\" = _ctxvars.ContextVar(\n+    \"background_review_read_paths\", default=frozenset()\n+)\n+\n+\n+def mark_background_review_skill_read(path: Path) -> None:\n+    \"\"\"Record that the active background-review fork has read a skill file.\n+\n+    The autonomous review fork is allowed to evolve skills, but it must not\n+    patch or rewrite content it has only inferred from the transcript.  The\n+    skill_view tool calls this after returning file content to the model; write\n+    paths below require the corresponding target path to be present when the\n+    current origin is ``background_review``.\n+    \"\"\"\n+    try:\n+        from tools.skill_provenance import is_background_review\n+        if not is_background_review():\n+            return\n+    except Exception:\n+        return\n+\n+    try:\n+        resolved = str(path.resolve())\n+    except Exception:\n+        resolved = str(path)\n+    current = set(_background_review_read_paths.get())\n+    current.add(resolved)\n+    _background_review_read_paths.set(frozenset(current))\n+\n+\n+def _background_review_has_read(path: Path) -> bool:\n+    try:\n+        resolved = str(path.resolve())\n+    except Exception:\n+        resolved = str(path)\n+    return resolved in _background_review_read_paths.get()\n+\n+\n+def _reset_background_review_read_marks() -> None:\n+    \"\"\"Test helper: clear read-before-write marks for the current context.\"\"\"\n+    _background_review_read_paths.set(frozenset())\n+\n # Import security scanner — external hub installs always get scanned;\n # agent-created skills only get scanned when skills.guard_agent_created is on.\n try:","@@ -320,6 +363,36 @@ def _background_review_write_guard(\n     return None\n \n \n+def _background_review_read_before_write_guard(\n+    name: str,\n+    target: Path,\n+    action: str,\n+    file_label: str,\n+) -> Optional[Dict[str, Any]]:\n+    \"\"\"Require review forks to load the exact target before mutating it.\"\"\"\n+    try:\n+        from tools.skill_provenance import is_background_review\n+        if not is_background_review():\n+            return None\n+    except Exception:\n+        return None\n+\n+    if _background_review_has_read(target):\n+        return None\n+\n+    return {\n+        \"success\": False,\n+        \"error\": (\n+            f\"Refusing background curator {action} for skill '{name}': \"\n+            f\"the current {file_label} content has not been loaded in this \"\n+            \"review turn. Call skill_view(name) for SKILL.md, or \"\n+            \"skill_view(name, file_path=...) for a supporting file, then \"\n+            \"retry the write using the content just returned.\"\n+        ),\n+        \"_read_before_write_required\": True,\n+    }\n+\n+\n def _background_review_preflight(action: str, name: str) -> Optional[Dict[str, Any]]:\n     if action not in {\"edit\", \"patch\", \"delete\", \"write_file\", \"remove_file\"}:\n         return None","@@ -786,6 +859,12 @@ def _edit_skill(name: str, content: str) -> Dict[str, Any]:\n         return guard\n \n     skill_md = existing[\"path\"] / \"SKILL.md\"\n+    read_guard = _background_review_read_before_write_guard(\n+        name, skill_md, \"edit\", \"SKILL.md\"\n+    )\n+    if read_guard:\n+        return read_guard\n+\n     # Back up original content for rollback\n     original_content = skill_md.read_text(encoding=\"utf-8\") if skill_md.exists() else None\n     _atomic_write_text(skill_md, content)","@@ -849,6 +928,7 @@ def _patch_skill(\n         target, err = _resolve_skill_target(skill_dir, file_path)\n         if err:\n             return {\"success\": False, \"error\": err}\n+        assert target is not None\n     else:\n         # Patching SKILL.md\n         target = skill_dir / \"SKILL.md\"","@@ -856,6 +936,15 @@ def _patch_skill(\n     if not target.exists():\n         return {\"success\": False, \"error\": f\"File not found: {target.relative_to(skill_dir)}\"}\n \n+    read_guard = _background_review_read_before_write_guard(\n+        name,\n+        target,\n+        \"patch\",\n+        \"SKILL.md\" if not file_path else file_path,\n+    )\n+    if read_guard:\n+        return read_guard\n+\n     content = target.read_text(encoding=\"utf-8\")\n \n     # Use the same fuzzy matching engine as the file patch tool.","@@ -1057,6 +1146,13 @@ def _write_file(name: str, file_path: str, file_content: str) -> Dict[str, Any]:\n     target, err = _resolve_skill_target(existing[\"path\"], file_path)\n     if err:\n         return {\"success\": False, \"error\": err}\n+    assert target is not None\n+    if target.exists():\n+        read_guard = _background_review_read_before_write_guard(\n+            name, target, \"write_file\", file_path\n+        )\n+        if read_guard:\n+            return read_guard\n     target.parent.mkdir(parents=True, exist_ok=True)\n     # Back up for rollback\n     original_content = target.read_text(encoding=\"utf-8\") if target.exists() else None","@@ -1096,6 +1192,7 @@ def _remove_file(name: str, file_path: str) -> Dict[str, Any]:\n     target, err = _resolve_skill_target(skill_dir, file_path)\n     if err:\n         return {\"success\": False, \"error\": err}\n+    assert target is not None\n     if not target.exists():\n         # List what's actually there for the model to see\n         available = []","@@ -1111,6 +1208,12 @@ def _remove_file(name: str, file_path: str) -> Dict[str, Any]:\n             \"available_files\": available if available else None,\n         }\n \n+    read_guard = _background_review_read_before_write_guard(\n+        name, target, \"remove_file\", file_path\n+    )\n+    if read_guard:\n+        return read_guard\n+\n     target.unlink()\n \n     # Clean up empty subdirectories"]},{"file":"tools/skills_tool.py","hunks":["@@ -1281,6 +1281,17 @@ def _record(sd: Optional[Path], smd: Path) -> None:\n                     ensure_ascii=False,\n                 )\n \n+            try:\n+                from tools.skill_manager_tool import mark_background_review_skill_read\n+\n+                mark_background_review_skill_read(target_file)\n+            except Exception:\n+                logger.debug(\n+                    \"Could not record background-review skill read for %s\",\n+                    target_file,\n+                    exc_info=True,\n+                )\n+\n             return json.dumps(\n                 {\n                     \"success\": True,","@@ -1484,6 +1495,17 @@ def _record(sd: Optional[Path], smd: Path) -> None:\n         if capture_result[\"gateway_setup_hint\"]:\n             result[\"gateway_setup_hint\"] = capture_result[\"gateway_setup_hint\"]\n \n+        try:\n+            from tools.skill_manager_tool import mark_background_review_skill_read\n+\n+            mark_background_review_skill_read(skill_md)\n+        except Exception:\n+            logger.debug(\n+                \"Could not record background-review skill read for %s\",\n+                skill_md,\n+                exc_info=True,\n+            )\n+\n         if setup_needed:\n             missing_items = [\n                 f\"env ${env_name}\" for env_name in remaining_missing_required_envs\n"]}]},{"kind":"pr","number":59175,"title":"[codex] Fix background review skill read-before-write","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#75020","number":75020}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":60331,"title":"fix(review): teach skill-review prompts the v2026.7.1 read-before-write guard","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"a70d2ffce59b58b9bdb36a518ddffd16953b67f2","number":0},{"kind":"pr","value":"#94530","number":94530}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":61529,"title":"fix(agent): use restricted toolset for background review fork","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"1ee30352ca60f79d984bba62ba1ffd2716c36924","number":0},{"kind":"pr","value":"#61521","number":61521},{"kind":"pr","value":"#98254","number":98254},{"kind":"pr","value":"#39997","number":39997}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":62414,"title":"fix(review): teach skill-review prompts the v2026.7.1 read-before-write guard","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"a70d2ffce59b58b9bdb36a518ddffd16953b67f2","number":0},{"kind":"pr","value":"#94530","number":94530},{"kind":"pr","value":"#60331","number":60331}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":62428,"title":"fix(agent): instruct background review fork to call skill_view before patching skills (#62397)","url":null,"state":"closed","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":62664,"title":"fix(agent): document read-before-write handshake in background-review prompt (#62397)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#60331","number":60331},{"kind":"pr","value":"#55906","number":55906},{"kind":"pr","value":"#62397","number":62397},{"kind":"pr","value":"#62428","number":62428},{"kind":"pr","value":"#62414","number":62414},{"kind":"pr","value":"#62400","number":62400},{"kind":"pr","value":"#60331","number":60331}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":63433,"title":"fix(curator): add explicit skill_view-before-skill_manage instruction","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#61024","number":61024}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":63991,"title":"fix(tools): prevent curator infinite loop on skill_manage patch errors (#63964)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#97650","number":97650},{"kind":"pr","value":"#63879","number":63879},{"kind":"pr","value":"#97451","number":97451}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":65803,"title":"fix(agent): steer background review to permitted tools","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"802d74a910bd","number":0},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#29704","number":29704},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#61521","number":61521},{"kind":"pr","value":"#65803","number":65803},{"kind":"pr","value":"#17276","number":17276},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#18973","number":18973},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#20674","number":20674},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#25330","number":25330},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#25427","number":25427},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#25322","number":25322},{"kind":"pr","value":"#26850","number":26850},{"kind":"pr","value":"#27422","number":27422},{"kind":"pr","value":"#27510","number":27510},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#29568","number":29568},{"kind":"pr","value":"#29704","number":29704},{"kind":"pr","value":"#29704","number":29704},{"kind":"pr","value":"#29567","number":29567},{"kind":"pr","value":"#30532","number":30532},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#36967","number":36967},{"kind":"pr","value":"#36995","number":36995},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#40003","number":40003},{"kind":"pr","value":"#40060","number":40060},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#45014","number":45014},{"kind":"pr","value":"#45892","number":45892},{"kind":"pr","value":"#45902","number":45902},{"kind":"pr","value":"#45902","number":45902},{"kind":"pr","value":"#45877","number":45877},{"kind":"pr","value":"#46049","number":46049},{"kind":"pr","value":"#45902","number":45902},{"kind":"pr","value":"#46684","number":46684},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#61529","number":61529},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#65803","number":65803},{"kind":"pr","value":"#61521","number":61521},{"kind":"pr","value":"#61618","number":61618},{"kind":"pr","value":"#17276","number":17276},{"kind":"pr","value":"#25427","number":25427},{"kind":"pr","value":"#25434","number":25434},{"kind":"pr","value":"#29568","number":29568},{"kind":"pr","value":"#29704","number":29704},{"kind":"pr","value":"#18973","number":18973},{"kind":"pr","value":"#20674","number":20674},{"kind":"pr","value":"#27510","number":27510},{"kind":"pr","value":"#30532","number":30532},{"kind":"pr","value":"#36995","number":36995},{"kind":"pr","value":"#64379","number":64379},{"kind":"pr","value":"#27422","number":27422},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#40060","number":40060},{"kind":"pr","value":"#46684","number":46684},{"kind":"pr","value":"#45892","number":45892},{"kind":"pr","value":"#45902","number":45902},{"kind":"pr","value":"#46049","number":46049},{"kind":"pr","value":"#61529","number":61529},{"kind":"pr","value":"#65803","number":65803},{"kind":"pr","value":"#61521","number":61521},{"kind":"pr","value":"#65803","number":65803},{"kind":"pr","value":"#61618","number":61618},{"kind":"pr","value":"#65803","number":65803},{"kind":"pr","value":"#61618","number":61618},{"kind":"pr","value":"#61529","number":61529},{"kind":"pr","value":"#40060","number":40060},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#45892","number":45892},{"kind":"pr","value":"#46049","number":46049},{"kind":"pr","value":"#45902","number":45902},{"kind":"pr","value":"#40007","number":40007},{"kind":"pr","value":"#1","number":1},{"kind":"pr","value":"#1","number":1},{"kind":"pr","value":"#15803","number":15803},{"kind":"pr","value":"#14532","number":14532},{"kind":"pr","value":"#6","number":6},{"kind":"pr","value":"#1","number":1},{"kind":"pr","value":"#9","number":9},{"kind":"pr","value":"#374151","number":374151},{"kind":"pr","value":"#4338","number":4338},{"kind":"pr","value":"#61521","number":61521},{"kind":"pr","value":"#61529","number":61529},{"kind":"pr","value":"#65803","number":65803}],"category":"partial_fix","implication":"needs_llm"},"merged":false},{"kind":"pr","number":75020,"title":"fix: require skill_view before background skill writes","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"a70d2ffce59b","number":0},{"kind":"commit","value":"335c60ecdd76","number":0},{"kind":"pr","value":"#94530","number":94530},{"kind":"pr","value":"#94511","number":94511}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":94530,"title":"fix(background-review): curator no longer refused for skipping the pre-read it was never told about (#62397, direction from #60331)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/background_review.py","tests/run_agent/test_review_prompt_class_first.py"]},{"kind":"pr","number":97451,"title":"fix(skill_manage): batch failures keep the failing op's file_preview — models no longer recover blind","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["tools/skill_manager_tool.py"]},{"kind":"pr","number":97650,"title":"fix(skill_manage): missing-old_string errors teach recovery instead of dead-ending into write_file clobbers (#33064, salvage #63879)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["tests/tools/test_skill_manager_tool.py","tools/skill_manager_tool.py"]},{"kind":"pr","number":98254,"title":"Background review lands skill patches again — read tools whitelisted, cache parity intact (#61521, #39996)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/background_review.py","tests/run_agent/test_background_review_toolset_restriction.py","tools/file_tools.py"]},{"kind":"pr","number":98278,"title":"Background review accepts profile-configured extra tools via opt-in whitelist (closes #44672, salvage #82146)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/background_review.py","contributors/emails/[redacted:email]","tests/run_agent/test_background_review_toolset_restriction.py","website/docs/user-guide/features/memory.md"]}],"trust":{"default_tier":"verified","includes_suggested":false}}