Shared size limits and status vocabulary for model-visible goal state.
Lifecycle status of a TUI-owned goal.
active and blocked are unfinished working states, paused preserves the goal
without driving work, and complete is terminal. A blocked goal is still
considered actionable by the goal-state notice, whereas a paused goal is
unfinished but not actionable.
Declared in this leaf module, not beside the state schema, so both normalizers
can share one vocabulary: resume_state.coerce_goal_status and
goal_state_notice.project_goal_state. The notice path deliberately avoids
resume_state's heavy deepagents import, and this module depends only on
typing, so it is reachable from either side at no startup cost.
Every recognized GoalStatus value, derived so it cannot drift.
Maximum characters in one persisted goal objective.
Maximum characters in one rubric or goal acceptance-criteria value.
Maximum combined raw characters in an accepted objective and its criteria.
Published to the criteria model and enforced per field on raw text, so it stays
comparable to the per-field max_length values the schema publishes. The
rendered budget — what an accepted pair is allowed to occupy after HTML
escaping — is GOAL_NOTICE_TEXT_CHAR_LIMIT, which is wider.
Maximum characters in a completion-evidence or blocker note.
Maximum combined rendered text embedded in one goal-state notice.
Derived rather than written out, because the acceptance checks share it: an
accepted objective-and-criteria pair is validated against this exact value, so
a hand-written literal could drift apart from what _accept_goal_rubric
enforces and let an accepted goal fail its own notice. The acceptance checks
reuse it so a newly accepted goal always keeps the whole status-note budget in
reserve: accepting an application can never produce a goal whose next status
update is already unwritable, no matter how the note is escaped.
The application budget covers objective and criteria, so for ordinary text the
aggregate check here is only reachable via prior_blocker or an oversized
note. HTML escaping is what makes it live for any section: it expands embedded
text up to fivefold, and nothing limits a status note's escaped length, so the
aggregate is the only check that bounds what update_goal can leave embedded.
Raise this deliberately if a new section is ever embedded in a notice.
Every value a GoalStateSizeError can name.
A closed set rather than free text: the label reaches both the user and the model verbatim, so a consumer that wants to branch on which budget was exceeded can do so exhaustively instead of matching strings.
The labels that can name the status-note budget.
Narrower than GoalStateSizeLabel so validate_goal_status_note cannot be asked
to report a note overflow under a budget that has nothing to do with notes — the
message reaches the model verbatim, naming a field the notice does not carry.
Reject a goal objective that cannot fit its persistent context budget.
This checks raw length only. HTML escaping can still expand the text
fivefold, so a caller about to run criteria generation should also call
validate_goal_objective_rendered: an objective that passes here can still
leave no room for any criteria in the rendered notice.
Reject criteria that cannot fit their persistent context budget.
Validate an objective and its generated criteria as one application.
Validate only the combined budget, not either field's own limit.
Split out because the two kinds of overshoot need opposite handling in the
criteria agent's structured-output loop: a per-field overshoot is a model
mistake against a limit the schema publishes, so it is worth retrying, while
the combined budget is not in the schema at all and half of it is the user's
own objective. See _raise_terminal_goal_state_size_error.
Validate the escaped size an accepted pair will occupy in the notice.
Split from validate_goal_application_total because the two budgets answer
different questions. The raw combined check shares its budget with the
criteria model: the schema's per-field max_length values and the system
prompt's combined limit are all raw counts the model can count against
while drafting. This rendered check is acceptance-side only — the model has
no way to predict HTML expansion, so a rendered overshoot is reported to
the user, not retried.
The budget is the aggregate notice limit rather than the raw application
limit. Escaping has no meaningful worst case below fivefold expansion, and
any budget tighter than GOAL_NOTICE_TEXT_CHAR_LIMIT would reject pairs
whose notices fit. The wider budget is safe because acceptance clears the
status note on create, and the note budget stays in reserve otherwise.
Reject an objective whose escaped text alone cannot fit a notice.
Reject a goal status note that cannot fit its persistent context budget.
Validate every user-controlled section of a goal-state notice.