Improve dashboard loading and task sync

This commit is contained in:
2026-05-19 02:32:22 +02:00
parent cc21c5869e
commit c7150bae5f
4 changed files with 194 additions and 63 deletions

View File

@@ -24,6 +24,7 @@ Checks:
C-17 repo-ahead-push-failed WARN No Local repo has unpushed commits and push failed — writes skipped to prevent runaway divergence
C-19 workstream-planning-drift WARN Yes planning_priority/planning_order differs between file and DB
C-20 workstream-dependency-missing WARN Yes Workplan dependency frontmatter missing from DB graph
C-22 task-description-drift WARN Yes Task description/content differs between file and DB
Usage:
python scripts/consistency_check.py --repo SLUG [--fix] [--no-writeback] [--json] [--api-base URL]
@@ -83,7 +84,7 @@ except ImportError:
# ---------------------------------------------------------------------------
_TASK_BLOCK_RE = re.compile(r"```task\s*\n(.*?)\n```", re.DOTALL)
_HEADING_RE = re.compile(r"^#{1,4}\s+(.+?)$", re.MULTILINE)
_HEADING_RE = re.compile(r"^(#{1,4})\s+(.+?)$", re.MULTILINE)
_ARCHIVED_WP_RE = re.compile(r"^\d{6}-(.+\.md)$")
VALID_WP_STATUSES = set(CANONICAL_WORKSTREAM_STATUSES)
SUPPORTED_WP_STATUSES = set(SUPPORTED_WORKSTREAM_STATUSES)
@@ -236,16 +237,64 @@ def parse_frontmatter(text: str) -> tuple[dict, str]:
return meta, parts[2]
def _clean_task_description(raw: str) -> str | None:
"""Trim section chrome while preserving task markdown content."""
lines = raw.splitlines()
while lines and not lines[0].strip():
lines.pop(0)
while lines and not lines[-1].strip():
lines.pop()
while lines and lines[-1].strip() in {"---", "***", "___"}:
lines.pop()
while lines and not lines[-1].strip():
lines.pop()
while lines and lines[0].strip() in {"---", "***", "___"}:
lines.pop(0)
while lines and not lines[0].strip():
lines.pop(0)
text = "\n".join(lines).strip()
return text or None
def parse_task_blocks(body: str) -> list[dict]:
"""Extract task blocks, injecting title from the nearest preceding ### heading."""
headings = [(m.start(), m.group(1).strip()) for m in _HEADING_RE.finditer(body)]
"""Extract task blocks, injecting title and markdown content from the task section."""
headings = [
(m.start(), len(m.group(1)), m.group(2).strip())
for m in _HEADING_RE.finditer(body)
]
task_matches = list(_TASK_BLOCK_RE.finditer(body))
results = []
for m in _TASK_BLOCK_RE.finditer(body):
for idx, m in enumerate(task_matches):
task = _parse_yaml_block(m.group(1).strip())
prev = [(pos, level, text) for pos, level, text in headings if pos < m.start()]
prev_heading = prev[-1] if prev else None
if "title" not in task:
prev = [(pos, text) for pos, text in headings if pos < m.start()]
if prev:
task["title"] = prev[-1][1]
if prev_heading:
task["title"] = prev_heading[2]
content_end = len(body)
if idx + 1 < len(task_matches):
content_end = min(content_end, task_matches[idx + 1].start())
if prev_heading:
current_level = prev_heading[1]
next_peer_heading = next(
(
pos
for pos, level, _text in headings
if pos > m.end() and level <= current_level
),
None,
)
if next_peer_heading is not None:
content_end = min(content_end, next_peer_heading)
else:
next_heading = next((pos for pos, _level, _text in headings if pos > m.end()), None)
if next_heading is not None:
content_end = min(content_end, next_heading)
description = _clean_task_description(body[m.end():content_end])
if description:
task["description"] = description
results.append(task)
return results
@@ -900,6 +949,27 @@ def check_repo(api_base: str, repo_slug: str, repo_path_override: str | None = N
fixable=True,
_fix_context={"task_id": t_sh_id, "status": t_status},
)
file_description = task.get("description")
if isinstance(file_description, str):
file_description = file_description.strip() or None
else:
file_description = None
db_description = db_task.get("description")
if isinstance(db_description, str):
db_description = db_description.strip() or None
else:
db_description = None
if file_description and file_description != db_description:
report.add(
severity="WARN", check_id="C-22",
message=f"Task description drift '{t_id}': file content differs from DB (file wins)",
file_path=f"{fname}#{t_id}",
db_id=t_sh_id,
file_value=file_description[:120],
db_value=(db_description or "")[:120],
fixable=True,
_fix_context={"task_id": t_sh_id, "description": file_description},
)
elif t_id:
# C-11: task exists in file but not linked to DB
ws_status = ws.get("status", "")
@@ -1478,6 +1548,7 @@ def fix_repo(
t_data = _api_post(api_base, "/tasks", {
"workstream_id": new_ws_id,
"title": str(task.get("title", t_id)).strip() or t_id,
"description": task.get("description") or None,
"status": t_status,
"priority": t_priority,
"assignee": task.get("assignee") or None,
@@ -1526,10 +1597,14 @@ def fix_repo(
status = ctx["status"]
result = _api_patch(api_base, f"/tasks/{task_id}",
{"status": status})
if result is not None:
if result is not None and "_error" not in result:
report.fixes_applied.append(
f"C-10 fixed: task {task_id[:8]}… status → {status!r}"
)
elif result is not None:
report.fixes_applied.append(
f"C-10 FAILED: task {task_id[:8]}… status → {status!r}: {result['_error']}"
)
elif issue.check_id == "C-11":
ws_id = ctx["ws_id"]
@@ -1555,6 +1630,7 @@ def fix_repo(
t_data = _api_post(api_base, "/tasks", {
"workstream_id": ws_id,
"title": str(task.get("title", t_id)).strip() or t_id,
"description": task.get("description") or None,
"status": t_status,
"priority": t_priority,
"assignee": task.get("assignee") or None,
@@ -1579,6 +1655,19 @@ def fix_repo(
f"C-12 fixed: orphan task {task_id[:8]}… cancelled (workstream finished)"
)
elif issue.check_id == "C-22":
task_id = ctx["task_id"]
description = ctx["description"]
result = _api_patch(api_base, f"/tasks/{task_id}", {"description": description})
if result is not None and "_error" not in result:
report.fixes_applied.append(
f"C-22 fixed: task {task_id[:8]}… description updated"
)
elif result is not None:
report.fixes_applied.append(
f"C-22 FAILED: task {task_id[:8]}… description update: {result['_error']}"
)
elif issue.check_id == "C-15":
# T03 — writeback: DB is ahead of file — patch file to match DB.
if no_writeback: