@@ -58,6 +58,12 @@ class WorkflowTimelineEntryKind(StrEnum): |
| 58 | 58 | CLARIFY_EXIT = "clarify_exit" |
| 59 | 59 | PLAN_REFRESH = "plan_refresh" |
| 60 | 60 | VERIFY_SKIP = "verify_skip" |
| 61 | + COMPLETION_CHECK = "completion_check" |
| 62 | + COMPLETION_CONTINUE = "completion_continue" |
| 63 | + COMPLETION_COMPLETE = "completion_complete" |
| 64 | + COMPLETION_FINALIZE = "completion_finalize" |
| 65 | + REPAIR_RETRY = "repair_retry" |
| 66 | + REPAIR_FAIL = "repair_fail" |
| 61 | 67 | |
| 62 | 68 | |
| 63 | 69 | @dataclass(slots=True) |
@@ -291,6 +297,8 @@ class WorkflowTimelineEntry: |
| 291 | 297 | clarify_pressure_kind: str | None = None |
| 292 | 298 | pressure_pass_complete: bool = False |
| 293 | 299 | missing_readiness_gates: list[str] = field(default_factory=list) |
| 300 | + policy_stage: str | None = None |
| 301 | + policy_outcome: str | None = None |
| 294 | 302 | prompt_format: str | None = None |
| 295 | 303 | prompt_sections: list[str] = field(default_factory=list) |
| 296 | 304 | artifact_paths: list[str] = field(default_factory=list) |
@@ -314,6 +322,8 @@ class WorkflowTimelineEntry: |
| 314 | 322 | "clarify_pressure_kind": self.clarify_pressure_kind, |
| 315 | 323 | "pressure_pass_complete": self.pressure_pass_complete, |
| 316 | 324 | "missing_readiness_gates": list(self.missing_readiness_gates), |
| 325 | + "policy_stage": self.policy_stage, |
| 326 | + "policy_outcome": self.policy_outcome, |
| 317 | 327 | "prompt_format": self.prompt_format, |
| 318 | 328 | "prompt_sections": list(self.prompt_sections), |
| 319 | 329 | "artifact_paths": list(self.artifact_paths), |
@@ -339,6 +349,8 @@ class WorkflowTimelineEntry: |
| 339 | 349 | clarify_pressure_kind=_optional_text(data.get("clarify_pressure_kind")), |
| 340 | 350 | pressure_pass_complete=bool(data.get("pressure_pass_complete", False)), |
| 341 | 351 | missing_readiness_gates=_string_list(data.get("missing_readiness_gates")), |
| 352 | + policy_stage=_optional_text(data.get("policy_stage")), |
| 353 | + policy_outcome=_optional_text(data.get("policy_outcome")), |
| 342 | 354 | prompt_format=_optional_text(data.get("prompt_format")), |
| 343 | 355 | prompt_sections=_string_list(data.get("prompt_sections")), |
| 344 | 356 | artifact_paths=_string_list(data.get("artifact_paths")), |
@@ -386,6 +398,48 @@ class WorkflowTimelineEntry: |
| 386 | 398 | artifact_paths=list(artifact_paths or []), |
| 387 | 399 | ) |
| 388 | 400 | |
| 401 | + @classmethod |
| 402 | + def accountability( |
| 403 | + cls, |
| 404 | + *, |
| 405 | + kind: WorkflowTimelineEntryKind, |
| 406 | + mode: WorkflowMode | str, |
| 407 | + reason_code: str, |
| 408 | + summary: str, |
| 409 | + policy_stage: str | None = None, |
| 410 | + policy_outcome: str | None = None, |
| 411 | + decision_kind: WorkflowDecisionKind | str | None = WorkflowDecisionKind.FORCED, |
| 412 | + prompt_format: str | None = None, |
| 413 | + prompt_sections: list[str] | None = None, |
| 414 | + signal_summary: list[str] | None = None, |
| 415 | + evidence_summary: list[str] | None = None, |
| 416 | + artifact_paths: list[str] | None = None, |
| 417 | + ) -> WorkflowTimelineEntry: |
| 418 | + """Build one typed non-routing accountability entry.""" |
| 419 | + |
| 420 | + resolved_mode = mode.value if isinstance(mode, WorkflowMode) else str(mode) |
| 421 | + if isinstance(decision_kind, WorkflowDecisionKind): |
| 422 | + resolved_decision_kind = decision_kind.value |
| 423 | + elif decision_kind is None: |
| 424 | + resolved_decision_kind = None |
| 425 | + else: |
| 426 | + resolved_decision_kind = str(decision_kind) |
| 427 | + return cls( |
| 428 | + timestamp=_utc_now(), |
| 429 | + kind=kind.value, |
| 430 | + mode=resolved_mode, |
| 431 | + reason_code=reason_code, |
| 432 | + summary=summary, |
| 433 | + decision_kind=resolved_decision_kind, |
| 434 | + signal_summary=list(signal_summary or []), |
| 435 | + evidence_summary=list(evidence_summary or []), |
| 436 | + policy_stage=policy_stage, |
| 437 | + policy_outcome=policy_outcome, |
| 438 | + prompt_format=prompt_format, |
| 439 | + prompt_sections=list(prompt_sections or []), |
| 440 | + artifact_paths=list(artifact_paths or []), |
| 441 | + ) |
| 442 | + |
| 389 | 443 | |
| 390 | 444 | class WorkflowPolicy: |
| 391 | 445 | """Scored workflow-policy engine for route and clarify decisions.""" |