af6e139d30
short-ship and long-ship share one ship-id record under .pouch/ship/<id>/. specifier writes PRDs into spec/; delivery-loop appends evidence pointers. Old names stay as deprecation stubs so existing installs keep working.
75 lines
1.8 KiB
Python
75 lines
1.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Shared ship-id and YAML helpers. Zero third-party deps."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
_REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
if str(_REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(_REPO_ROOT))
|
|
|
|
from pouch.yaml_io import safe_dump, safe_load # noqa: E402
|
|
|
|
SHIP_ID_RE = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")
|
|
DATE_ID_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$|^\d{8}$")
|
|
KIND = frozenset({"short", "long"})
|
|
SHIP_STATUS = frozenset(
|
|
{
|
|
"draft",
|
|
"spec_review",
|
|
"approved",
|
|
"in_progress",
|
|
"blocked",
|
|
"done",
|
|
"parked",
|
|
"superseded",
|
|
}
|
|
)
|
|
TASK_STATUS = frozenset(
|
|
{"open", "in_progress", "blocked", "verified", "merged", "done"}
|
|
)
|
|
DELIVERY_STATE = frozenset(
|
|
{
|
|
"change_ready",
|
|
"verified",
|
|
"pr_opened",
|
|
"merged",
|
|
"regression_passed",
|
|
"artifact_built",
|
|
"published",
|
|
"deployed",
|
|
}
|
|
)
|
|
SOURCE_KIND = frozenset({"user", "hunt", "ack", "pr", "feishu"})
|
|
COMMIT_ROLE = frozenset({"baseline", "merged", "head"})
|
|
|
|
|
|
def skill_dir() -> Path:
|
|
return Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def load_yaml(path: Path) -> Any:
|
|
return safe_load(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def dump_yaml(path: Path, data: Any) -> None:
|
|
path.write_text(safe_dump(data), encoding="utf-8")
|
|
|
|
|
|
def check_ship_id(ship_id: str) -> str | None:
|
|
if not ship_id:
|
|
return "shipId 为空"
|
|
if len(ship_id) > 64:
|
|
return f"shipId 超过 64 字符: {ship_id}"
|
|
if DATE_ID_RE.fullmatch(ship_id):
|
|
return f"shipId 不能是日期: {ship_id}"
|
|
if not SHIP_ID_RE.fullmatch(ship_id):
|
|
return (
|
|
f"shipId 无效: {ship_id!r}(小写字母开头,仅小写字母、数字、连字符)"
|
|
)
|
|
return None
|