specsmither-lifecycle 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. specsmither_lifecycle-0.1.0/.gitignore +38 -0
  2. specsmither_lifecycle-0.1.0/CHANGELOG.md +40 -0
  3. specsmither_lifecycle-0.1.0/PKG-INFO +42 -0
  4. specsmither_lifecycle-0.1.0/README.md +18 -0
  5. specsmither_lifecycle-0.1.0/pyproject.toml +44 -0
  6. specsmither_lifecycle-0.1.0/src/specsmither/domain/__init__.py +6 -0
  7. specsmither_lifecycle-0.1.0/src/specsmither/domain/enums.py +266 -0
  8. specsmither_lifecycle-0.1.0/src/specsmither/domain/records.py +489 -0
  9. specsmither_lifecycle-0.1.0/src/specsmither/domain/status.py +102 -0
  10. specsmither_lifecycle-0.1.0/src/specsmither/ids.py +53 -0
  11. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/__init__.py +6 -0
  12. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/audit/__init__.py +26 -0
  13. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/audit/action_builder.py +219 -0
  14. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/audit/operation_mapper.py +128 -0
  15. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/audit/transition_builder.py +69 -0
  16. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/config.py +134 -0
  17. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/dispatch.py +100 -0
  18. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/gate.py +418 -0
  19. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/__init__.py +9 -0
  20. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/cross_validation.yaml +87 -0
  21. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/epic_decomposition.yaml +177 -0
  22. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/epic_expansion.yaml +197 -0
  23. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/planning_spec.yaml +247 -0
  24. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/ticket_decomposition.yaml +137 -0
  25. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/catalogs/ticket_expansion.yaml +221 -0
  26. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/compose.py +624 -0
  27. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/field_instructions.py +275 -0
  28. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/guidance/types.py +130 -0
  29. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/operations_projector.py +631 -0
  30. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/operations_registry.py +471 -0
  31. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/ports.py +383 -0
  32. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/__init__.py +82 -0
  33. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/blueprint_epic_ratio.py +70 -0
  34. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/blueprint_link_refs_exist.py +68 -0
  35. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/cascade_rules.py +70 -0
  36. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/content_shape_valid.py +120 -0
  37. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/count_bounds.py +134 -0
  38. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/cross_cut_references.py +62 -0
  39. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/dependencies_batch.py +223 -0
  40. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/entity_refs_exist.py +96 -0
  41. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/enum_field_guards.py +112 -0
  42. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/field_shape_soft_deny.py +73 -0
  43. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/gate_currently_passing.py +154 -0
  44. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/operation_allowed.py +110 -0
  45. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/result.py +75 -0
  46. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/schema_validate.py +173 -0
  47. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/prechecks/spec_status_check.py +89 -0
  48. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/session_record.py +82 -0
  49. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/state_machine.py +90 -0
  50. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/__init__.py +13 -0
  51. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/_common.py +74 -0
  52. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/action.py +472 -0
  53. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/approve.py +191 -0
  54. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/complete.py +215 -0
  55. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/inspect.py +106 -0
  56. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/reject.py +88 -0
  57. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/reject_with_feedback.py +102 -0
  58. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/start.py +271 -0
  59. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/support.py +208 -0
  60. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/verbs/types.py +118 -0
  61. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/write_plan.py +1146 -0
  62. specsmither_lifecycle-0.1.0/src/specsmither/lifecycle/write_plan_types.py +225 -0
  63. specsmither_lifecycle-0.1.0/src/specsmither/py.typed +0 -0
@@ -0,0 +1,38 @@
1
+ # --- Local-only planning docs (milestone breakdowns; not published) ---
2
+ planner/
3
+
4
+ # --- Throwaway CLI/TUI prototype (milestone 02 mock; not published) ---
5
+ playground/
6
+
7
+ # Python
8
+ __pycache__/
9
+ *.py[cod]
10
+ .venv/
11
+ venv/
12
+ *.egg-info/
13
+ dist/
14
+ build/
15
+ .mypy_cache/
16
+ .ruff_cache/
17
+ .pytest_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # Local SQLite databases / config
22
+ *.db
23
+ *.sqlite3
24
+ *.sqlite3-wal
25
+ *.sqlite3-shm
26
+ .specsmither/
27
+
28
+ # Dev-only uv overrides (e.g. an editable ../crucible source) — kept local so
29
+ # CI + published builds resolve crucible-forge from PyPI.
30
+ uv.toml
31
+
32
+ # Node (only used by tools/gen_*_golden.mjs to regenerate golden fixtures)
33
+ node_modules/
34
+
35
+ # Editor / OS
36
+ .DS_Store
37
+ .idea/
38
+ .vscode/
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to `specsmither-lifecycle` are documented here.
4
+ The format follows [Keep a Changelog](https://keepachangelog.com/); this project
5
+ adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## 0.1.0
8
+
9
+ First release — the pure planning-lifecycle core, extracted from the `specsmither`
10
+ product into its own publishable distribution with **no persistence and no UI/server
11
+ dependency** (no SQLAlchemy, no Textual, no MCP). A host embeds the planning state
12
+ machine in-process and provides its own persistence through the `WritePlan` seam.
13
+
14
+ ### Added
15
+
16
+ - **Pure verb surface** — `start_planning_session`, `action_planning_session`,
17
+ `complete_planning_session`, `inspect_planning_session`, and the handover verbs
18
+ (`approve_handover` / `reject_handover` / `reject_handover_with_feedback`). Each is
19
+ a pure `(payload, ports) -> {response, WritePlan}` function: it reads state through
20
+ injected ports and returns the mutation as a `WritePlan` — it performs no I/O.
21
+ - **`LifecyclePorts`** — the injection contract: the `PlanningSessionStore` /
22
+ `SpecStore` / `ConfigStore` read ports, the `Validator` (crucible), the pure
23
+ operations projector, and the optional `persist_write_plan` / `clock` /
24
+ `id_generator`. The host implements the stores over its own database.
25
+ - **`WritePlan` data contract** (`lifecycle.write_plan_types`) — an ordered list of
26
+ serializable item kinds (`specMutation`, `sessionUpdate`, `actionAppend`,
27
+ score/transition datapoints, …). Any executor — the product's SQLite one or an
28
+ external Postgres one — consumes it as plain data.
29
+ - **`PlanningSessionRecord`** — a pure, DB-free frozen record for the session state
30
+ the verbs read/return (replaces the ORM row on the verb boundary).
31
+ - **In-memory operations projector** — computes post-mutation state for the
32
+ validator/gate without touching a database.
33
+ - **Phase-gate evaluator + guidance composer + prechecks** — the deterministic gate
34
+ and the agent-facing guidance, driven entirely by `crucible.validate`.
35
+
36
+ ### Dependencies
37
+
38
+ `crucible-forge`, `pydantic`, `python-ulid`, `pyyaml` only. This distribution shares
39
+ the `specsmither` PEP 420 namespace with the `specsmither` product distribution; both
40
+ build and publish independently from the same repository.
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: specsmither-lifecycle
3
+ Version: 0.1.0
4
+ Summary: The PURE planning-lifecycle core of SpecSmither — the crucible-gated verb surface (start/action/complete/inspect + handovers), the in-memory operations projector, the WritePlan data contract, and the domain records. No SQLAlchemy, no Textual, no MCP.
5
+ Project-URL: Homepage, https://github.com/blacksmithers/specsmither
6
+ Project-URL: Repository, https://github.com/blacksmithers/specsmither
7
+ Project-URL: Issues, https://github.com/blacksmithers/specsmither/issues
8
+ Author-email: Gabriel Augusto Gonçalves <gab.augustog@gmail.com>
9
+ License: Apache-2.0
10
+ Keywords: crucible,lifecycle,planning,spec,specforge,specsmither
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: crucible-forge>=0.2.0
20
+ Requires-Dist: pydantic>=2.6
21
+ Requires-Dist: python-ulid>=2.0
22
+ Requires-Dist: pyyaml>=6.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # specsmither-lifecycle
26
+
27
+ The **pure** planning-lifecycle core of [SpecSmither](https://github.com/blacksmithers/specsmither).
28
+
29
+ This distribution contributes the `specsmither.lifecycle`, `specsmither.domain`, and
30
+ `specsmither.ids` modules to the shared `specsmither` PEP 420 namespace. It is the
31
+ crucible-gated verb surface — `start` / `action` / `complete` / `inspect` plus the
32
+ handover verbs — together with the in-memory operations projector, the `WritePlan`
33
+ data contract, and the domain records/enums.
34
+
35
+ It is deliberately **dependency-light**: `crucible-forge`, `pydantic`, `python-ulid`,
36
+ and `pyyaml` only. It pulls in **no** SQLAlchemy, Textual, or MCP — those live in the
37
+ `specsmither` product distribution, which depends on this package and wires it to a
38
+ concrete SQLite store, TUI, and MCP server.
39
+
40
+ The verb surface satisfies a pure `(payload, ports) -> {response, WritePlan}` contract:
41
+ a host embeds it by providing the `LifecyclePorts` seam (stores, validator, operations
42
+ projector, clock, id generator) and consuming the returned `WritePlan`.
@@ -0,0 +1,18 @@
1
+ # specsmither-lifecycle
2
+
3
+ The **pure** planning-lifecycle core of [SpecSmither](https://github.com/blacksmithers/specsmither).
4
+
5
+ This distribution contributes the `specsmither.lifecycle`, `specsmither.domain`, and
6
+ `specsmither.ids` modules to the shared `specsmither` PEP 420 namespace. It is the
7
+ crucible-gated verb surface — `start` / `action` / `complete` / `inspect` plus the
8
+ handover verbs — together with the in-memory operations projector, the `WritePlan`
9
+ data contract, and the domain records/enums.
10
+
11
+ It is deliberately **dependency-light**: `crucible-forge`, `pydantic`, `python-ulid`,
12
+ and `pyyaml` only. It pulls in **no** SQLAlchemy, Textual, or MCP — those live in the
13
+ `specsmither` product distribution, which depends on this package and wires it to a
14
+ concrete SQLite store, TUI, and MCP server.
15
+
16
+ The verb surface satisfies a pure `(payload, ports) -> {response, WritePlan}` contract:
17
+ a host embeds it by providing the `LifecyclePorts` seam (stores, validator, operations
18
+ projector, clock, id generator) and consuming the returned `WritePlan`.
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "specsmither-lifecycle"
7
+ version = "0.1.0"
8
+ description = "The PURE planning-lifecycle core of SpecSmither — the crucible-gated verb surface (start/action/complete/inspect + handovers), the in-memory operations projector, the WritePlan data contract, and the domain records. No SQLAlchemy, no Textual, no MCP."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Gabriel Augusto Gonçalves", email = "gab.augustog@gmail.com" }]
13
+ keywords = ["spec", "specforge", "specsmither", "planning", "lifecycle", "crucible"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: Apache Software License",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Typing :: Typed",
22
+ ]
23
+ # The PURE core deps ONLY — the whole point of the split. NO sqlalchemy/textual/mcp.
24
+ dependencies = [
25
+ "crucible-forge>=0.2.0",
26
+ "pydantic>=2.6",
27
+ "python-ulid>=2.0",
28
+ "pyyaml>=6.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/blacksmithers/specsmither"
33
+ Repository = "https://github.com/blacksmithers/specsmither"
34
+ Issues = "https://github.com/blacksmithers/specsmither/issues"
35
+
36
+ # PEP 420 namespace package: this dist contributes ``specsmither.lifecycle`` /
37
+ # ``specsmither.domain`` / ``specsmither.ids`` to the shared ``specsmither`` namespace
38
+ # (there is intentionally NO src/specsmither/__init__.py). The product dist
39
+ # ``specsmither`` contributes the rest of the namespace.
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/specsmither"]
42
+
43
+ [tool.hatch.build.targets.sdist]
44
+ include = ["src/specsmither", "README.md", "CHANGELOG.md"]
@@ -0,0 +1,6 @@
1
+ """Domain layer: enums, status transitions, and runtime DB-row records.
2
+
3
+ Pure (no SQLAlchemy, no I/O). Defines the type vocabulary the whole engine
4
+ shares. ``records.py`` rebuilds crucible's ``Specification`` shape from DB rows
5
+ (``SpecFull``) so the planning gate can score a persisted spec.
6
+ """
@@ -0,0 +1,266 @@
1
+ """Runtime / lifecycle / status enum vocabulary for SpecSmither.
2
+
3
+ These are the enums that crucible does NOT provide. The OpenSpec *authoring*
4
+ vocabulary (TicketType, Complexity, EpicCategory, TestType, …) is the single
5
+ source of truth in ``crucible.models.enums`` and is reused from there — it is
6
+ deliberately NOT redefined in this module.
7
+
8
+ Ground truth (ported verbatim so values round-trip with the TS/JSON wire):
9
+ - ``session-types/src/runtime/enums.ts`` (lifecycle / planning / cross-val vocab)
10
+ - ``api-types/src/runtime/status.ts`` (the 4-value ticket status surface)
11
+ - ``api-types/src/runtime/ticket-record.ts`` (ticket file-change ``kind``)
12
+
13
+ All members are :class:`enum.StrEnum`, so a member compares equal to and is
14
+ usable anywhere its verbatim string value is expected.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from enum import StrEnum
20
+
21
+ __all__ = [
22
+ "ActorType",
23
+ "ActualAction",
24
+ "DatapointTrigger",
25
+ "EpicStatus",
26
+ "ExpectedAction",
27
+ "FieldState",
28
+ "FileChangeKind",
29
+ "FileChangeStatus",
30
+ "FindingCategory",
31
+ "GateResult",
32
+ "GuidanceVariant",
33
+ "JustificationApproved",
34
+ "Outcome",
35
+ "PlanningPhase",
36
+ "PlanningSessionStatus",
37
+ "SpecStatus",
38
+ "TicketStatus",
39
+ "TransitionTrigger",
40
+ "WorkSessionStatus",
41
+ ]
42
+
43
+
44
+ class TicketStatus(StrEnum):
45
+ """Canonical 4-value ticket status (api-types ``TICKET_STATUSES``).
46
+
47
+ "Blocked" is not a status — it is signalled via ``block_reason`` on a
48
+ ``pending`` ticket.
49
+ """
50
+
51
+ PENDING = "pending"
52
+ READY = "ready"
53
+ ACTIVE = "active"
54
+ DONE = "done"
55
+
56
+
57
+ class SpecStatus(StrEnum):
58
+ """Spec lifecycle (session-types ``SPEC_STATUSES``) — all 8 values verbatim.
59
+
60
+ SpecSmither has NO review lifecycle, so ``ready_for_review`` / ``in_review``
61
+ / ``reviewed`` are UNREACHABLE here. They are kept because the M0 CRUD freeze
62
+ guards (#18) reference them: ``reviewed`` is treated as read-only and
63
+ ``in_review`` blocks create. Do not drop them.
64
+ """
65
+
66
+ DRAFT = "draft"
67
+ PLANNING = "planning"
68
+ READY = "ready"
69
+ IN_PROGRESS = "in_progress"
70
+ # Unreachable in SpecSmither (no review lifecycle) — retained for the
71
+ # CRUD-freeze guards (#18): `reviewed` = read-only, `in_review` = blocks create.
72
+ READY_FOR_REVIEW = "ready_for_review"
73
+ IN_REVIEW = "in_review"
74
+ REVIEWED = "reviewed"
75
+ DONE = "done"
76
+
77
+
78
+ class PlanningSessionStatus(StrEnum):
79
+ """Planning-session status (session-types ``PLANNING_SESSION_STATUSES``)."""
80
+
81
+ ACTIVE = "active"
82
+ AWAITING_HUMAN_REVIEW = "awaiting_human_review"
83
+ CLOSED = "closed"
84
+
85
+
86
+ class WorkSessionStatus(StrEnum):
87
+ """Work-session status (session-types ``WORK_SESSION_STATUSES``)."""
88
+
89
+ ACTIVE = "active"
90
+ COMPLETED = "completed"
91
+ CLOSED = "closed"
92
+
93
+
94
+ class PlanningPhase(StrEnum):
95
+ """Planning phase machine (session-types ``PLANNING_PHASES``)."""
96
+
97
+ PLANNING_SPEC = "planning_spec"
98
+ EPIC_DECOMPOSITION = "epic_decomposition"
99
+ EPIC_EXPANSION = "epic_expansion"
100
+ TICKET_DECOMPOSITION = "ticket_decomposition"
101
+ TICKET_EXPANSION = "ticket_expansion"
102
+ CROSS_VALIDATION = "cross_validation"
103
+ PLANNED = "planned"
104
+
105
+
106
+ class TransitionTrigger(StrEnum):
107
+ """Phase-transition trigger (session-types ``TRANSITION_TRIGGERS``)."""
108
+
109
+ AUTO_INITIAL = "auto_initial"
110
+ AI_AGENT = "ai_agent"
111
+ HUMAN_APPROVE = "human_approve"
112
+ HUMAN_COMPLETE = "human_complete"
113
+ HUMAN_REJECT_WITH_FEEDBACK = "human_reject_with_feedback"
114
+ HUMAN_REJECT_NO_FEEDBACK = "human_reject_no_feedback"
115
+
116
+
117
+ class Outcome(StrEnum):
118
+ """Audited action outcome (session-types ``OUTCOMES``)."""
119
+
120
+ SUCCESS = "success"
121
+ DENIED = "denied"
122
+
123
+
124
+ class DatapointTrigger(StrEnum):
125
+ """Score-datapoint trigger (session-types ``DATAPOINT_TRIGGERS``)."""
126
+
127
+ CREATED = "created"
128
+ METADATA_UPDATED = "metadata_updated"
129
+ EPIC_FIELD_UPDATED = "epic_field_updated"
130
+ TICKET_FIELD_UPDATED = "ticket_field_updated"
131
+ BLUEPRINT_FIELD_UPDATED = "blueprint_field_updated"
132
+ DEPENDENCY_ADDED = "dependency_added"
133
+ DEPENDENCY_REMOVED = "dependency_removed"
134
+ BLUEPRINT_LINKED = "blueprint_linked"
135
+ BLUEPRINT_UNLINKED = "blueprint_unlinked"
136
+ CROSS_VAL_RECOMPUTE = "cross_val_recompute"
137
+
138
+
139
+ class GuidanceVariant(StrEnum):
140
+ """Guidance-message variant (session-types ``GUIDANCE_VARIANTS``).
141
+
142
+ Includes the six M6.6 additions consumed exclusively by
143
+ ``get_planning_status``.
144
+ """
145
+
146
+ DENIED = "denied"
147
+ GATE_FAILED = "gate_failed"
148
+ GATE_PASSED = "gate_passed"
149
+ HUMAN_HANDOVER = "human_handover"
150
+ HUMAN_FEEDBACK = "human_feedback"
151
+ PHASE_ADVANCE = "phase_advance"
152
+ PHASE_ROLLBACK = "phase_rollback"
153
+ PHASE_COMPLETE = "phase_complete"
154
+ # M6.6 additions.
155
+ PHASE_STATUS_REPORT = "phase_status_report"
156
+ AWAITING_HUMAN_REVIEW_HANDOVER = "awaiting_human_review_handover"
157
+ HUMAN_FEEDBACK_RECEIVED = "human_feedback_received"
158
+ HUMAN_REJECTED_NO_FEEDBACK = "human_rejected_no_feedback"
159
+ PHASE_ADVANCED_AFTER_APPROVE = "phase_advanced_after_approve"
160
+ SESSION_CLOSED = "session_closed"
161
+
162
+
163
+ class FindingCategory(StrEnum):
164
+ """Cross-validation finding category (session-types ``FINDING_CATEGORIES``)."""
165
+
166
+ RUBRIC = "rubric"
167
+ COUNT = "count"
168
+ CROSS_CUT = "cross-cut"
169
+ CASCADE = "cascade"
170
+ CROSS_VALIDATION = "cross-validation"
171
+ SCHEMA = "schema"
172
+
173
+
174
+ class GateResult(StrEnum):
175
+ """Gate result (session-types ``GATE_RESULTS``)."""
176
+
177
+ PASS = "pass"
178
+ FAIL = "fail"
179
+
180
+
181
+ class FieldState(StrEnum):
182
+ """Field-presence state (session-types ``FIELD_STATES``)."""
183
+
184
+ EMPTY = "empty"
185
+ PARTIAL = "partial"
186
+ FILLED = "filled"
187
+ NA = "na"
188
+
189
+
190
+ class ExpectedAction(StrEnum):
191
+ """Planned file action (session-types ``EXPECTED_ACTIONS``)."""
192
+
193
+ CREATE = "create"
194
+ MODIFY = "modify"
195
+ DELETE = "delete"
196
+ REFERENCE = "reference"
197
+
198
+
199
+ class ActualAction(StrEnum):
200
+ """Observed file action (session-types ``ACTUAL_ACTIONS``)."""
201
+
202
+ CREATED = "created"
203
+ MODIFIED = "modified"
204
+ DELETED = "deleted"
205
+ REFERENCED = "referenced"
206
+ ABSENT = "absent"
207
+
208
+
209
+ class FileChangeStatus(StrEnum):
210
+ """Expected-vs-actual reconciliation (session-types ``FILE_CHANGE_STATUSES``)."""
211
+
212
+ MATCHED = "matched"
213
+ MISSING = "missing"
214
+ MISMATCHED = "mismatched"
215
+ EXTRA = "extra"
216
+
217
+
218
+ class JustificationApproved(StrEnum):
219
+ """Justification approval state (session-types ``JUSTIFICATION_APPROVED_VALUES``)."""
220
+
221
+ PENDING = "pending"
222
+ APPROVED = "approved"
223
+ REJECTED = "rejected"
224
+
225
+
226
+ class FileChangeKind(StrEnum):
227
+ """Ticket PLAN-side file-change kind (api-types ``TicketFileChangeKind``).
228
+
229
+ Drives ``ticket_file_changes.kind`` (schema §3). Distinct from the work-side
230
+ :class:`ExpectedAction` / :class:`ActualAction` vocabulary — these are the
231
+ ``toBeX`` camelCase literals from ``api-types/runtime/ticket-record.ts``.
232
+ """
233
+
234
+ TO_BE_CREATED = "toBeCreated"
235
+ TO_BE_MODIFIED = "toBeModified"
236
+ TO_BE_DELETED = "toBeDeleted"
237
+ TO_BE_REFERENCED = "toBeReferenced"
238
+
239
+
240
+ class EpicStatus(StrEnum):
241
+ """Epic lifecycle status.
242
+
243
+ A DISTINCT enum exists in the TS (``types/src/schema/epic.ts`` →
244
+ ``'todo' | 'in_progress' | 'completed'``), and the planning verbs that
245
+ SpecSmither ports create epics with ``status: 'todo'`` and explicitly comment
246
+ that this is "a constrained EpicStatus enum (todo|in_progress|completed)"
247
+ (``lifecycle/.../action-planning-session.ts``). So epics do NOT share the
248
+ ticket vocabulary — these three values are ported verbatim.
249
+ """
250
+
251
+ TODO = "todo"
252
+ IN_PROGRESS = "in_progress"
253
+ COMPLETED = "completed"
254
+
255
+
256
+ class ActorType(StrEnum):
257
+ """Audit actor identity (§6 actor model).
258
+
259
+ Confirmed against the TS: ``actor: 'agent' | 'human'`` throughout the
260
+ planning verbs / ``action-builder.ts``. There is no named ``ACTOR_TYPES``
261
+ array — the union is inlined — so the two members are defined here. MCP tool
262
+ calls resolve to ``agent``; explicit CLI handover/approve commands to ``human``.
263
+ """
264
+
265
+ AGENT = "agent"
266
+ HUMAN = "human"