返回 ppt-master
workflow_log.py
根目录 / skills / ppt-master / scripts / workflow_log.py
1 #!/usr/bin/env python3
2 """
3 PPT Master - Workflow Log
4
5 Append one manually selected important event to a project's cold audit log.
6
7 Usage:
8 python3 scripts/workflow_log.py <project_path> <message>
9
10 Examples:
11 python3 scripts/workflow_log.py projects/demo "Strategist handoff complete"
12 python3 scripts/workflow_log.py projects/demo "Reworked P07 after source-label mismatch"
13
14 Dependencies:
15 None (standard library only)
16 """
17
18 from __future__ import annotations
19
20 import argparse
21 import sys
22 import uuid
23 from datetime import datetime, timezone
24 from pathlib import Path
25
26 _SCRIPTS_DIR = Path(__file__).resolve().parent
27 if str(_SCRIPTS_DIR) not in sys.path:
28 sys.path.insert(0, str(_SCRIPTS_DIR))
29
30 from console_encoding import configure_utf8_stdio # noqa: E402
31
32 WORKFLOW_LOG_RELATIVE_PATH = Path("validation/workflow.log")
33
34
35 def _utc_timestamp() -> str:
36 """Return a compact UTC timestamp."""
37 return (
38 datetime.now(timezone.utc)
39 .isoformat(timespec="milliseconds")
40 .replace("+00:00", "Z")
41 )
42
43
44 def workflow_log_path(project_path: str | Path) -> Path:
45 """Return the canonical workflow-log path for a project."""
46 return Path(project_path) / WORKFLOW_LOG_RELATIVE_PATH
47
48
49 def append_note(project_path: str | Path, message: str) -> Path:
50 """Append one important event without interpreting workflow state."""
51 log_path = workflow_log_path(project_path)
52 log_path.parent.mkdir(parents=True, exist_ok=True)
53 record = (
54 f"\n=== {_utc_timestamp()} NOTE id={uuid.uuid4().hex[:12]} ===\n"
55 f"{message.rstrip()}\n"
56 )
57 with log_path.open(
58 "a",
59 encoding="utf-8",
60 errors="replace",
61 newline="",
62 ) as handle:
63 handle.write(record)
64 handle.flush()
65 return log_path
66
67
68 def build_parser() -> argparse.ArgumentParser:
69 """Build the command-line parser."""
70 parser = argparse.ArgumentParser(
71 description="Append one important event to validation/workflow.log.",
72 formatter_class=argparse.RawDescriptionHelpFormatter,
73 )
74 parser.add_argument("project_path", help="Existing project directory")
75 parser.add_argument("message", help="Concise important event")
76 return parser
77
78
79 def main(argv: list[str] | None = None) -> int:
80 """Run the CLI entry point."""
81 configure_utf8_stdio()
82 parser = build_parser()
83 args = parser.parse_args(argv)
84 project_path = Path(args.project_path)
85 if not project_path.is_dir():
86 parser.error(f"project path does not exist: {project_path}")
87
88 message = args.message.strip()
89 if not message:
90 parser.error("message must not be empty")
91 try:
92 log_path = append_note(project_path, message)
93 except OSError as exc:
94 print(
95 f"[ERROR] Could not append workflow event to "
96 f"{workflow_log_path(project_path)}: {exc}",
97 file=sys.stderr,
98 )
99 return 1
100 print(f"[OK] Workflow event appended: {log_path}", file=sys.stderr)
101 return 0
102
103
104 if __name__ == "__main__":
105 raise SystemExit(main())
106
106 lines PYTHON