kin: KIN-013 Obsidian sync + Revise UI (fixes и тесты)
- obsidian_sync.py: расширен regex для task ID с цифробуквенными префиксами ([A-Z][A-Z0-9]*-\d+) - test_obsidian_sync.py: тест test_sync_updates_task_status обновлён под uppercase PROJ1-001 - TaskDetail.vue: добавлены revise() функция и Revise modal (отправить задачу на доработку) - test_api.py: добавлены test_revise_task и test_revise_not_found 473/473 тестов проходят. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0032b3056a
commit
6b328d7f2d
4 changed files with 60 additions and 4 deletions
|
|
@ -90,7 +90,7 @@ def parse_task_checkboxes(
|
||||||
|
|
||||||
Returns: [{"task_id": "KIN-013", "done": True, "title": "..."}]
|
Returns: [{"task_id": "KIN-013", "done": True, "title": "..."}]
|
||||||
"""
|
"""
|
||||||
pattern = re.compile(r"^[-*]\s+\[([xX ])\]\s+([A-Z]+-\d+)\s+(.+)$")
|
pattern = re.compile(r"^[-*]\s+\[([xX ])\]\s+([A-Z][A-Z0-9]*-\d+)\s+(.+)$")
|
||||||
results: list[dict] = []
|
results: list[dict] = []
|
||||||
|
|
||||||
search_dirs = [
|
search_dirs = [
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,32 @@ def test_reject_not_found(client):
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_revise_task(client):
|
||||||
|
from core.db import init_db
|
||||||
|
from core import models
|
||||||
|
conn = init_db(api_module.DB_PATH)
|
||||||
|
models.update_task(conn, "P1-001", status="review")
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
r = client.post("/api/tasks/P1-001/revise", json={
|
||||||
|
"comment": "Доисследуй edge case с пустым массивом"
|
||||||
|
})
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json()["status"] == "in_progress"
|
||||||
|
|
||||||
|
# Verify task is in_progress with revise_comment stored
|
||||||
|
conn = init_db(api_module.DB_PATH)
|
||||||
|
row = conn.execute("SELECT status, revise_comment FROM tasks WHERE id = 'P1-001'").fetchone()
|
||||||
|
conn.close()
|
||||||
|
assert row["status"] == "in_progress"
|
||||||
|
assert row["revise_comment"] == "Доисследуй edge case с пустым массивом"
|
||||||
|
|
||||||
|
|
||||||
|
def test_revise_not_found(client):
|
||||||
|
r = client.post("/api/tasks/NOPE/revise", json={"comment": "fix it"})
|
||||||
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
def test_task_pipeline_not_found(client):
|
def test_task_pipeline_not_found(client):
|
||||||
r = client.get("/api/tasks/NOPE/pipeline")
|
r = client.get("/api/tasks/NOPE/pipeline")
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
|
||||||
|
|
@ -157,14 +157,14 @@ def test_sync_updates_task_status(db, tmp_vault):
|
||||||
tmp_vault.mkdir(parents=True)
|
tmp_vault.mkdir(parents=True)
|
||||||
models.update_project(db, "proj1", obsidian_vault_path=str(tmp_vault))
|
models.update_project(db, "proj1", obsidian_vault_path=str(tmp_vault))
|
||||||
|
|
||||||
task = models.create_task(db, "proj1-001", "proj1", "Do something", status="in_progress")
|
task = models.create_task(db, "PROJ1-001", "proj1", "Do something", status="in_progress")
|
||||||
assert task["status"] == "in_progress"
|
assert task["status"] == "in_progress"
|
||||||
|
|
||||||
# Write checkbox file
|
# Write checkbox file
|
||||||
tasks_dir = tmp_vault / "proj1" / "tasks"
|
tasks_dir = tmp_vault / "proj1" / "tasks"
|
||||||
tasks_dir.mkdir(parents=True)
|
tasks_dir.mkdir(parents=True)
|
||||||
(tasks_dir / "sprint.md").write_text(
|
(tasks_dir / "sprint.md").write_text(
|
||||||
"- [x] proj1-001 Do something\n",
|
"- [x] PROJ1-001 Do something\n",
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -172,7 +172,7 @@ def test_sync_updates_task_status(db, tmp_vault):
|
||||||
|
|
||||||
assert result["tasks_updated"] == 1
|
assert result["tasks_updated"] == 1
|
||||||
assert not result["errors"]
|
assert not result["errors"]
|
||||||
updated = models.get_task(db, "proj1-001")
|
updated = models.get_task(db, "PROJ1-001")
|
||||||
assert updated["status"] == "done"
|
assert updated["status"] == "done"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,18 @@ async function reject() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function revise() {
|
||||||
|
if (!task.value || !reviseComment.value) return
|
||||||
|
try {
|
||||||
|
await api.reviseTask(props.id, reviseComment.value)
|
||||||
|
showRevise.value = false
|
||||||
|
reviseComment.value = ''
|
||||||
|
await load()
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function runPipeline() {
|
async function runPipeline() {
|
||||||
try {
|
try {
|
||||||
await api.runTask(props.id)
|
await api.runTask(props.id)
|
||||||
|
|
@ -438,6 +450,11 @@ async function saveEdit() {
|
||||||
class="px-4 py-2 text-sm bg-green-900/50 text-green-400 border border-green-800 rounded hover:bg-green-900">
|
class="px-4 py-2 text-sm bg-green-900/50 text-green-400 border border-green-800 rounded hover:bg-green-900">
|
||||||
✓ Approve
|
✓ Approve
|
||||||
</button>
|
</button>
|
||||||
|
<button v-if="task.status === 'review' && !autoMode"
|
||||||
|
@click="showRevise = true"
|
||||||
|
class="px-4 py-2 text-sm bg-orange-900/50 text-orange-400 border border-orange-800 rounded hover:bg-orange-900">
|
||||||
|
🔄 Revise
|
||||||
|
</button>
|
||||||
<button v-if="(task.status === 'review' || task.status === 'in_progress') && !autoMode"
|
<button v-if="(task.status === 'review' || task.status === 'in_progress') && !autoMode"
|
||||||
@click="showReject = true"
|
@click="showReject = true"
|
||||||
class="px-4 py-2 text-sm bg-red-900/50 text-red-400 border border-red-800 rounded hover:bg-red-900">
|
class="px-4 py-2 text-sm bg-red-900/50 text-red-400 border border-red-800 rounded hover:bg-red-900">
|
||||||
|
|
@ -542,6 +559,19 @@ async function saveEdit() {
|
||||||
</form>
|
</form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<!-- Revise Modal -->
|
||||||
|
<Modal v-if="showRevise" title="🔄 Revise Task" @close="showRevise = false">
|
||||||
|
<form @submit.prevent="revise" class="space-y-3">
|
||||||
|
<p class="text-xs text-gray-500">Опишите, что доработать или уточнить агенту. Задача вернётся в работу с вашим комментарием.</p>
|
||||||
|
<textarea v-model="reviseComment" placeholder="Что доработать / уточнить..." rows="4" required
|
||||||
|
class="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-gray-200 placeholder-gray-600 resize-y"></textarea>
|
||||||
|
<button type="submit"
|
||||||
|
class="w-full py-2 bg-orange-900/50 text-orange-400 border border-orange-800 rounded text-sm hover:bg-orange-900">
|
||||||
|
🔄 Отправить на доработку
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
<!-- Edit Modal (pending tasks only) -->
|
<!-- Edit Modal (pending tasks only) -->
|
||||||
<Modal v-if="showEdit" title="Edit Task" @close="showEdit = false">
|
<Modal v-if="showEdit" title="Edit Task" @close="showEdit = false">
|
||||||
<form @submit.prevent="saveEdit" class="space-y-3">
|
<form @submit.prevent="saveEdit" class="space-y-3">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue