diff --git a/src/sdlc_hub/modules/jira/api.py b/src/sdlc_hub/modules/jira/api.py index 0ac3983..c001c6d 100644 --- a/src/sdlc_hub/modules/jira/api.py +++ b/src/sdlc_hub/modules/jira/api.py @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) MIME_RE = re.compile(r"[\w!#$&^.+-]+/[\w!#$&^.+-]+", re.ASCII) # тип/подтип без параметров, пробелов и не-ASCII: заголовок не внушить DOWNLOADS_EVERY = 24 * 3600 # уборка downloads/ не чаще раза в сутки -ISSUE_FILTERS = ("sprint", "assignee", "work_kind", "fact_status", "status", "label", "epic", "neighbor", "q", "hide_closed", "project") +ISSUE_FILTERS = ("sprint", "assignee", "work_kind", "status", "label", "epic", "neighbor", "q", "hide_closed", "project") _OFFSET = re.compile(r"(Z|[+-]\d{2}:?\d{2})$") diff --git a/src/sdlc_hub/modules/jira/importer.py b/src/sdlc_hub/modules/jira/importer.py index a94bc07..0764a48 100644 --- a/src/sdlc_hub/modules/jira/importer.py +++ b/src/sdlc_hub/modules/jira/importer.py @@ -42,7 +42,7 @@ def _import(conn, folder: Path) -> dict: if not key: continue _note(conn, key, { - "fact_status": _clean(r.get("Статус по факту")), "work_kind": _clean(r.get("Вид работы")), + "work_kind": _clean(r.get("Вид работы")), "done_text": _clean(r.get("Что сделано по факту (внутр.)")), "rest_text": _clean(r.get("Что осталось (внутр.)")), "owner_note": _clean(r.get("Комментарий актуализации"))}) notes += 1 @@ -56,7 +56,7 @@ def _import(conn, folder: Path) -> dict: new_keys[f"new:{i}"] = real csv_keys.append(real) _note(conn, key, { - "fact_status": _clean(r.get("Статус по факту")), "work_kind": _clean(r.get("Вид работы")), + "work_kind": _clean(r.get("Вид работы")), "rest_text": _clean(r.get("Основание (внутр.)")), "owner_note": None if real else _clean(r.get("Тема"))}) notes += 1 diff --git a/src/sdlc_hub/modules/jira/mcp.py b/src/sdlc_hub/modules/jira/mcp.py index ca51fc1..c07bb17 100644 --- a/src/sdlc_hub/modules/jira/mcp.py +++ b/src/sdlc_hub/modules/jira/mcp.py @@ -26,10 +26,10 @@ fmt_agent = core.fmt_agent def fmt_issues(issues) -> str: if not issues: return "задач нет" - lines = ["ключ | тема | статус | срок | исполнитель | спринт | статус по факту"] + lines = ["ключ | тема | статус | срок | исполнитель | спринт"] for i in issues: lines.append(" | ".join([i["key"], core.cut(i.get("summary")), core.value(i.get("status")), core.value(i.get("duedate")), - core.value(i.get("assignee")), core.value(i.get("sprint")), core.value(i.get("fact_status"))])) + core.value(i.get("assignee")), core.value(i.get("sprint"))])) return "\n".join(lines) @@ -73,7 +73,7 @@ def fmt_issue(d) -> str: links = [f" → {l['target_key']} ({l['link_type']})" for l in d.get("links_out", [])] + \ [f" ← {l['source_key']} ({l['link_type']})" for l in d.get("links_in", [])] note = d.get("note") or {} - notes = [f" {k}: {note[k]}" for k in ("fact_status", "work_kind", "done_text", "rest_text", "owner_note") if note.get(k)] + notes = [f" {k}: {note[k]}" for k in ("work_kind", "done_text", "rest_text", "owner_note") if note.get(k)] cmds = [f" #{c['id']} {c['type']} {c['status']} {c.get('finished_at') or ''} {c.get('error') or ''}".rstrip() for c in d.get("commands", [])] comments = d.get("comments") or [] if comments: @@ -180,11 +180,11 @@ def fmt_batch(b) -> str: @mcp.tool() @core.guard @core.scoped(True) -def issues(sprint: str = "", assignee: str = "", work_kind: str = "", fact_status: str = "", status: str = "", +def issues(sprint: str = "", assignee: str = "", work_kind: str = "", status: str = "", label: str = "", epic: str = "", q: str = "", neighbor: str = "", limit: int = 50) -> str: - """Задачи зеркала по фильтрам (спринт, исполнитель, вид работы, статус по факту, статус Jira, метка, эпик, + """Задачи зеркала по фильтрам (спринт, исполнитель, вид работы, статус Jira, метка, эпик, подстрока темы/ключа, neighbor=1 — только соседние). Компактная таблица.""" - data = core.client().get("/issues", project=core.PROJECT, sprint=sprint, assignee=assignee, work_kind=work_kind, fact_status=fact_status, + data = core.client().get("/issues", project=core.PROJECT, sprint=sprint, assignee=assignee, work_kind=work_kind, status=status, label=label, epic=epic, q=q, neighbor=neighbor, limit=limit) return fmt_issues(data["issues"]) @@ -297,17 +297,17 @@ def changes(hours: float | None = None, since: str = "", until: str = "", kinds: @mcp.tool() @core.guard @core.scoped(True) -def notes_set(key: str, fact_status: str | None = None, work_kind: str | None = None, done_text: str | None = None, +def notes_set(key: str, work_kind: str | None = None, done_text: str | None = None, rest_text: str | None = None, owner_note: str | None = None) -> str: """Правка служебных заметок задачи (только переданные поля).""" - fields = {k: v for k, v in dict(fact_status=fact_status, work_kind=work_kind, done_text=done_text, + fields = {k: v for k, v in dict(work_kind=work_kind, done_text=done_text, rest_text=rest_text, owner_note=owner_note).items() if v is not None} fields["updated_by"] = core.AUTHOR if not is_temp(key): # новая задача пакета проекта — своя по построению if core.PROJECT not in (core.client().get(f"/issues/{key}").get("labels") or []): return f"{key} не в проекте {core.PROJECT}" note = core.client().put(f"/issues/{key}/note", fields) - return "заметки " + key + ": " + "; ".join(f"{k}: {note.get(k)}" for k in ("fact_status", "work_kind", "done_text", "rest_text", "owner_note") if note.get(k)) + return "заметки " + key + ": " + "; ".join(f"{k}: {note.get(k)}" for k in ("work_kind", "done_text", "rest_text", "owner_note") if note.get(k)) def _read_local(path) -> bytes | str: diff --git a/src/sdlc_hub/modules/jira/mirror.py b/src/sdlc_hub/modules/jira/mirror.py index b90912c..be3cd08 100644 --- a/src/sdlc_hub/modules/jira/mirror.py +++ b/src/sdlc_hub/modules/jira/mirror.py @@ -7,7 +7,7 @@ from ...core import db ISSUE_COLUMNS = ["key", "id", "issue_type", "summary", "description", "status", "priority", "assignee", "reporter", "duedate", "planned_start", "planned_end", "epic_key", "labels", "sprint", "created", "jira_updated"] -NOTE_FIELDS = ["fact_status", "work_kind", "done_text", "rest_text", "owner_note"] +NOTE_FIELDS = ["work_kind", "done_text", "rest_text", "owner_note"] def check_comments(issues) -> None: """Форма поля comments у задач состояния (агент v10+); поле необязательно. Нарушение — ValueError с ключом и полем.""" @@ -54,7 +54,7 @@ def check_attachments(issues) -> None: _FILTERS = {"sprint": "i.sprint = ?", "assignee": "i.assignee = ?", "status": "i.status = ?", "epic": "i.epic_key = ?", - "work_kind": "n.work_kind = ?", "fact_status": "n.fact_status = ?"} + "work_kind": "n.work_kind = ?"} def apply_state(conn, issues, links, neighbor_keys=(), fetched_at=None, authoritative=True) -> dict: @@ -155,11 +155,11 @@ def list_issues(conn, filters=None, limit=50, offset=0, closed_statuses=()) -> l where.append("(i.summary LIKE ? OR i.key LIKE ?)") args += [f"%{filters['q']}%", f"%{filters['q']}%"] if project: - sql = (SCOPE_CTE + "SELECT i.*, s.nb AS scope_nb, n.fact_status, n.work_kind FROM issue i " + sql = (SCOPE_CTE + "SELECT i.*, s.nb AS scope_nb, n.work_kind FROM issue i " "JOIN scope s ON s.key = i.key LEFT JOIN issue_note n ON n.key = i.key") args = [project] + args else: - sql = "SELECT i.*, n.fact_status, n.work_kind FROM issue i LEFT JOIN issue_note n ON n.key = i.key" + sql = "SELECT i.*, n.work_kind FROM issue i LEFT JOIN issue_note n ON n.key = i.key" sql = (sql + (" WHERE " + " AND ".join(where) if where else "") + " ORDER BY i.duedate IS NULL, i.duedate, i.key LIMIT ? OFFSET ?") @@ -223,7 +223,7 @@ BOARD_FIELDS = ["key", "summary", "issue_type", "status", "priority", "assignee" def board(conn, base_jql, project: str | None = None) -> dict: - """Снимок зеркала для доски связей (GET /board): все задачи с заметками fact_status/work_kind, все связи и последний + """Снимок зеркала для доски связей (GET /board): все задачи с work_kind из заметок, все связи и последний успешный полный прогон (refresh по базовой JQL, команда done). Данные без преобразований — перевод в модель доски делает интерфейс. С project — свои задачи проекта и соседи (neighbor относительно проекта), связи — те, у которых source или target — своя задача; last_sync — как без проекта. Чтение в одной транзакции: задачи, связи и last_sync — из одного состояния базы.""" @@ -234,7 +234,7 @@ def board(conn, base_jql, project: str | None = None) -> dict: if project: cols = ", ".join("i." + c for c in BOARD_FIELDS if c != "neighbor") issues = [_issue_dict(r) for r in conn.execute( - SCOPE_CTE + f"SELECT {cols}, s.nb AS neighbor, n.fact_status, n.work_kind FROM issue i " + SCOPE_CTE + f"SELECT {cols}, s.nb AS neighbor, n.work_kind FROM issue i " "JOIN scope s ON s.key = i.key LEFT JOIN issue_note n ON n.key = i.key ORDER BY i.key", (project,))] links = [dict(r) for r in conn.execute( SCOPE_CTE + "SELECT source_key AS source, target_key AS target, link_type FROM issue_link " @@ -242,7 +242,7 @@ def board(conn, base_jql, project: str | None = None) -> dict: "ORDER BY source_key, target_key, link_type", (project,))] else: issues = [_issue_dict(r) for r in conn.execute( - f"SELECT {', '.join('i.' + c for c in BOARD_FIELDS)}, n.fact_status, n.work_kind " + f"SELECT {', '.join('i.' + c for c in BOARD_FIELDS)}, n.work_kind " "FROM issue i LEFT JOIN issue_note n ON n.key = i.key ORDER BY i.key")] links = [dict(r) for r in conn.execute( "SELECT source_key AS source, target_key AS target, link_type FROM issue_link " diff --git a/tests/fixtures/baseline/mcp_tools.expected.json b/tests/fixtures/baseline/mcp_tools.expected.json index cc7a33b..dd6551e 100644 --- a/tests/fixtures/baseline/mcp_tools.expected.json +++ b/tests/fixtures/baseline/mcp_tools.expected.json @@ -1,6 +1,6 @@ [ { - "description": "Задачи зеркала по фильтрам (спринт, исполнитель, вид работы, статус по факту, статус Jira, метка, эпик,\nподстрока темы/ключа, neighbor=1 — только соседние). Компактная таблица.", + "description": "Задачи зеркала по фильтрам (спринт, исполнитель, вид работы, статус Jira, метка, эпик,\nподстрока темы/ключа, neighbor=1 — только соседние). Компактная таблица.", "inputSchema": { "properties": { "assignee": { @@ -13,11 +13,6 @@ "title": "Epic", "type": "string" }, - "fact_status": { - "default": "", - "title": "Fact Status", - "type": "string" - }, "label": { "default": "", "title": "Label", @@ -183,18 +178,6 @@ "default": null, "title": "Done Text" }, - "fact_status": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Fact Status" - }, "key": { "title": "Key", "type": "string" diff --git a/tests/test_api.py b/tests/test_api.py index 7b64479..6688986 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -112,8 +112,14 @@ def test_interface_routes(hub, add_project): hub["agent"].post("/agent/state", {"issues": [{"key": "A-1", "summary": "Т", "sprint": "Спринт 2", "labels": []}, {"key": "A-2", "summary": "Д", "sprint": "Спринт 1", "labels": []}], "links": []}) assert [i["key"] for i in c.get("/issues", sprint="Спринт 2")["issues"]] == ["A-1"] - note = c.put("/issues/A-1/note", {"fact_status": "В работе", "updated_by": "owner"}) - assert note["fact_status"] == "В работе" and note["updated_by"] == "owner" + assert [i["key"] for i in c.get("/issues", fact_status="В работе")["issues"]] == ["A-1", "A-2"] # параметр игнорируется + assert all("fact_status" not in i for i in c.get("/issues")["issues"]) + with pytest.raises(HubError) as e: # статуса по факту нет: поле — неизвестное (fact-status-removal) + c.put("/issues/A-1/note", {"fact_status": "В работе", "work_kind": "Аналитика", "updated_by": "owner"}) + assert e.value.status == 422 and "неизвестные поля заметки: fact_status" in e.value.detail + assert c.get("/issues/A-1")["note"] is None # и work_kind не записан + note = c.put("/issues/A-1/note", {"work_kind": "Аналитика", "updated_by": "owner"}) + assert note["work_kind"] == "Аналитика" and note["updated_by"] == "owner" and note.get("fact_status") is None with pytest.raises(HubError) as e: c.put("/issues/A-1/note", {"summary": "x"}) assert e.value.status == 422 diff --git a/tests/test_e2e_hub.py b/tests/test_e2e_hub.py index 0a2ddb6..7ef47f2 100644 --- a/tests/test_e2e_hub.py +++ b/tests/test_e2e_hub.py @@ -27,7 +27,7 @@ def live(tmp_path, add_project): def test_create_link_sprint_flow(live): agent, client = live - client.put("/issues/new:60/note", {"fact_status": "Ожидание разработки", "work_kind": "разработка", "updated_by": "import"}) + client.put("/issues/new:60/note", {"rest_text": "Ожидание разработки", "work_kind": "разработка", "updated_by": "import"}) b = client.post("/batches", {"title": "новая задача в спринт", "author": "claude", "commands": [ {"type": "ensure_issue", "params": {"temp_key": "new:60", "issue_type": "task", "summary": "Новая", "duedate": "2026-10-23", "labels": ["AWPCA"]}}, diff --git a/tests/test_importer.py b/tests/test_importer.py index f26b5ac..634ce12 100644 --- a/tests/test_importer.py +++ b/tests/test_importer.py @@ -48,7 +48,7 @@ def test_import_notes_and_report(conn, tmp_path): rep = importer.import_package(conn, tmp_path) assert rep["notes"] == 6 n = mirror.get_note(conn, "AURORA-1533") - assert n["fact_status"] == "Ожидание разработки" and n["work_kind"] == "тестирование" + assert n["work_kind"] == "тестирование" and n.get("fact_status") is None # колонка CSV пропускается (fact-status-removal) assert n["rest_text"] == "Прогнать на стенде" and n["owner_note"] == "Актуализация 10.10.2026. Порядок" and n["updated_by"] == "import" assert mirror.get_note(conn, "AURORA-1416")["done_text"] == "Сделан список" assert mirror.get_note(conn, "AURORA-1586")["rest_text"] == "решение 11.10" diff --git a/tests/test_mcp.py b/tests/test_mcp.py index 35cfaeb..42ee818 100644 --- a/tests/test_mcp.py +++ b/tests/test_mcp.py @@ -1,3 +1,4 @@ +import inspect import asyncio import hashlib import json @@ -47,9 +48,9 @@ def test_tools_roundtrip(live): "assignee": "ivanov", "sprint": "Спринт 2", "labels": ["AWPCA"]}], "links": []}) out = jmcp.issues(sprint="Спринт 2") assert "A-1" in out and "Тема раз" in out and "ivanov" in out - assert "В работе" in jmcp.notes_set("A-1", fact_status="В работе") + assert "Аналитика" in jmcp.notes_set("A-1", work_kind="Аналитика") detail = jmcp.issue("A-1") - assert "fact_status: В работе" in detail and "Готово" in detail + assert "work_kind: Аналитика" in detail and "Готово" in detail created = jmcp.batch_create("срок", [{"type": "ensure_fields", "params": {"key": "A-1", "duedate": "2026-10-24"}}]) batch_id = int(created.split("#")[1].split()[0]) assert "2 команд" in created and "мин" in created @@ -73,11 +74,19 @@ def test_tools_roundtrip(live): assert jmcp.settings_set("tokens", '"x"').startswith("ошибка хаба 422") +def test_no_fact_status_params(): + for tool in (jmcp.issues, jmcp.notes_set): + assert "fact_status" not in inspect.signature(tool).parameters + + def test_formatters(): assert jmcp.fmt_issues([]) == "задач нет" text = jmcp.fmt_issues([{"key": "A-1", "summary": "x" * 100, "status": "Готово", "duedate": None, "assignee": None, - "sprint": None, "fact_status": None}]) + "sprint": None, "fact_status": "В работе"}]) assert "x" * 79 + "…" in text and "x" * 80 not in text and "—" in text + head, row = text.splitlines() + assert head == "ключ | тема | статус | срок | исполнитель | спринт" # статуса по факту нет (fact-status-removal) + assert len(row.split(" | ")) == 6 and "В работе" not in row assert "очередь" in jmcp.fmt_agent({"online": False, "agent_version": None, "current_command_id": None, "rate_limited_24h": 0, "redelivered": [], "warning": None, "last_seen_at": None, "settings_version": None}) diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 96c2aff..6baec01 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -60,14 +60,16 @@ def test_partial_load_never_lowers_neighbor(conn): def test_note_survives_refresh_and_renames(conn): - mirror.set_note(conn, "new:7", {"fact_status": "В работе", "work_kind": "разработка"}, "import") + mirror.set_note(conn, "new:7", {"rest_text": "В работе", "work_kind": "разработка"}, "import") mirror.rename_note(conn, "new:7", "A-5") mirror.apply_state(conn, [issue("A-5")], []) got = mirror.get_issue(conn, "A-5") - assert got["note"]["fact_status"] == "В работе" and got["note"]["updated_by"] == "import" + assert got["note"]["rest_text"] == "В работе" and got["note"]["updated_by"] == "import" assert mirror.get_note(conn, "new:7") is None with pytest.raises(ValueError): mirror.set_note(conn, "A-5", {"summary": "нельзя"}, "x") + with pytest.raises(ValueError, match="неизвестные поля заметки: fact_status"): # fact-status-removal + mirror.set_note(conn, "A-5", {"fact_status": "В работе"}, "x") def test_list_issues_filters(conn): @@ -86,9 +88,9 @@ def test_list_issues_filters(conn): def test_rename_note_same_key_keeps_note(conn): mirror.apply_state(conn, [issue("A-1")], []) - mirror.set_note(conn, "A-1", {"fact_status": "В работе"}, "x") + mirror.set_note(conn, "A-1", {"rest_text": "В работе"}, "x") mirror.rename_note(conn, "A-1", "A-1") - assert mirror.get_note(conn, "A-1")["fact_status"] == "В работе" + assert mirror.get_note(conn, "A-1")["rest_text"] == "В работе" def test_rename_note_missing_is_noop(conn): @@ -99,32 +101,32 @@ def test_rename_note_missing_is_noop(conn): def test_set_note_partial_keeps_other_fields(conn): mirror.set_note(conn, "A-1", {"work_kind": "разработка", "done_text": "сделано"}, "x") - got = mirror.set_note(conn, "A-1", {"fact_status": "Готово"}, "y") + got = mirror.set_note(conn, "A-1", {"rest_text": "Готово"}, "y") assert got["work_kind"] == "разработка" and got["done_text"] == "сделано" - assert got["fact_status"] == "Готово" and got["updated_by"] == "y" + assert got["rest_text"] == "Готово" and got["updated_by"] == "y" def test_rename_note_merges_with_existing(conn): mirror.set_note(conn, "A-5", {"owner_note": "спросить ИБ", "work_kind": "Старое"}, "human") - mirror.set_note(conn, "new:5", {"work_kind": "Аналитика", "fact_status": "В работе"}, "import") + mirror.set_note(conn, "new:5", {"work_kind": "Аналитика", "rest_text": "В работе"}, "import") mirror.rename_note(conn, "new:5", "A-5") note = mirror.get_note(conn, "A-5") assert note["work_kind"] == "Аналитика" # непустое поле временной заметки побеждает assert note["owner_note"] == "спросить ИБ" # пустое у временной — остаётся под ключом - assert note["fact_status"] == "В работе" + assert note["rest_text"] == "В работе" assert note["updated_by"] == "import" # временная заметка записана позже assert mirror.get_note(conn, "new:5") is None def test_rename_note_keeps_newer_real_key_stamp(conn): - mirror.set_note(conn, "new:6", {"fact_status": "В работе"}, "import") + mirror.set_note(conn, "new:6", {"rest_text": "В работе"}, "import") mirror.set_note(conn, "A-6", {"owner_note": "позже"}, "human") conn.execute("UPDATE issue_note SET updated_at = '2020-01-01T00:00:00+00:00' WHERE key = 'new:6'") conn.execute("UPDATE issue_note SET updated_at = '2030-01-01T00:00:00+00:00' WHERE key = 'A-6'") mirror.rename_note(conn, "new:6", "A-6") note = mirror.get_note(conn, "A-6") assert note["updated_by"] == "human" and note["updated_at"] == "2030-01-01T00:00:00+00:00" - assert note["fact_status"] == "В работе" and note["owner_note"] == "позже" + assert note["rest_text"] == "В работе" and note["owner_note"] == "позже" def test_rename_note_empty_string_does_not_overwrite(conn): @@ -160,9 +162,9 @@ def test_board_snapshot_raw_mirror_data(conn): assert [i["key"] for i in snap["issues"]] == ["AURORA-1351", "AURORA-1620", "AURORA-1621"] a = snap["issues"][1] assert set(a) == {"key", "summary", "issue_type", "status", "priority", "assignee", "sprint", "duedate", - "planned_start", "planned_end", "epic_key", "labels", "neighbor", "fact_status", "work_kind"} + "planned_start", "planned_end", "epic_key", "labels", "neighbor", "work_kind"} assert a["labels"] == ["kind-dev"] and a["epic_key"] == "AURORA-1351" - assert a["work_kind"] == "разработка" and a["fact_status"] is None and a["neighbor"] is False + assert a["work_kind"] == "разработка" and a["neighbor"] is False assert snap["issues"][2]["neighbor"] is True and snap["issues"][2]["work_kind"] is None assert snap["links"] == [{"source": "AURORA-1620", "target": "AURORA-1621", "link_type": "ASUPR_Блокер_Начала"}] assert snap["last_sync"] is None and snap["generated_at"] diff --git a/tests/test_queue.py b/tests/test_queue.py index e587a36..779d5d0 100644 --- a/tests/test_queue.py +++ b/tests/test_queue.py @@ -101,7 +101,7 @@ def test_backup_failure_does_not_block_claim(conn, caplog): def test_temp_key_substitution_and_skip(conn, add_project): add_project(conn) - mirror.set_note(conn, "new:60", {"fact_status": "В работе"}, "import") + mirror.set_note(conn, "new:60", {"rest_text": "В работе"}, "import") queue.create_batch(conn, "создать", "claude", [ {"type": "ensure_issue", "params": {"temp_key": "new:60", "issue_type": "task", "summary": "Новая"}}, {"type": "ensure_link", "params": {"source": "new:60", "target": "A-1", "link_type": "связанные"}}, @@ -111,7 +111,7 @@ def test_temp_key_substitution_and_skip(conn, add_project): ]) c1 = queue.claim(conn, "jira") queue.report_result(conn, c1["id"], "done", result={"key": "A-90"}) - assert mirror.get_note(conn, "A-90")["fact_status"] == "В работе" and mirror.get_note(conn, "new:60") is None + assert mirror.get_note(conn, "A-90")["rest_text"] == "В работе" and mirror.get_note(conn, "new:60") is None c2 = queue.claim(conn, "jira") assert c2["type"] == "ensure_link" and c2["params"]["source"] == "A-90" queue.report_result(conn, c2["id"], "done") @@ -362,7 +362,7 @@ def test_report_for_queued_command_rejected(conn): def test_late_report_after_force_cancel_keeps_issue_key(conn): - mirror.set_note(conn, "new:7", {"fact_status": "В работе"}, "import") + mirror.set_note(conn, "new:7", {"rest_text": "В работе"}, "import") queue.create_batch(conn, "x", "claude", [ {"type": "ensure_issue", "params": {"temp_key": "new:7", "issue_type": "task", "summary": "Тема"}}]) c = queue.claim(conn, "jira") @@ -370,7 +370,7 @@ def test_late_report_after_force_cancel_keeps_issue_key(conn): late = queue.report_result(conn, c["id"], "done", result={"key": "A-20", "created": True}, jira_calls=3) assert late["status"] == "cancelled" and late["result"] == {"key": "A-20", "created": True} and late["jira_calls"] == 3 assert late["finished_at"] == cancelled["finished_at"] - assert mirror.get_note(conn, "A-20")["fact_status"] == "В работе" and mirror.get_note(conn, "new:7") is None + assert mirror.get_note(conn, "A-20")["rest_text"] == "В работе" and mirror.get_note(conn, "new:7") is None again = queue.report_result(conn, c["id"], "done", result={"key": "A-21"}) assert again["result"] == {"key": "A-20", "created": True} # второй поздний отчёт ничего не меняет @@ -399,7 +399,7 @@ def test_error_is_trimmed_to_1500(conn): def test_late_done_after_delivery_limit_keeps_issue_key(conn): - mirror.set_note(conn, "new:8", {"fact_status": "План"}, "import") + mirror.set_note(conn, "new:8", {"rest_text": "План"}, "import") queue.create_batch(conn, "x", "claude", [ {"type": "ensure_issue", "params": {"temp_key": "new:8", "issue_type": "task", "summary": "Тема"}}]) c = queue.claim(conn, "jira") @@ -407,7 +407,7 @@ def test_late_done_after_delivery_limit_keeps_issue_key(conn): late = queue.report_result(conn, c["id"], "done", result={"key": "A-30", "created": True}, jira_calls=2) assert late["status"] == "failed" and late["result"] == {"key": "A-30", "created": True} assert late["error"] == "агент не отчитался после 3 выдач" - assert mirror.get_note(conn, "A-30")["fact_status"] == "План" and mirror.get_note(conn, "new:8") is None + assert mirror.get_note(conn, "A-30")["rest_text"] == "План" and mirror.get_note(conn, "new:8") is None again = queue.report_result(conn, c["id"], "done", result={"key": "A-31"}) assert again["result"] == {"key": "A-30", "created": True} # повтор ничего не меняет