Skip to main content

Ticket

Struct Ticket 

Source
pub struct Ticket {
Show 20 fields pub slug: String, pub title: String, pub priority: u8, pub repo_refs: Vec<String>, pub schedule: Schedule, pub max_budget_usd: Option<f64>, pub goal: String, pub context: String, pub scoping_answers: Vec<String>, pub acceptance_hints: Vec<String>, pub blocked_by: Vec<String>, pub task_class: Option<String>, pub review_artifact: Option<String>, pub review_output: Option<String>, pub trigger: Option<String>, pub traced_from_mission: Option<String>, pub defer_until: Option<DateTime<Utc>>, pub lifecycle: Option<TicketLifecycle>, pub state_note: Option<String>, pub raw_body: String,
}
Expand description

A parsed ticket: frontmatter fields plus body sections.

Fields§

§slug: String§title: String§priority: u8§repo_refs: Vec<String>§schedule: Schedule§max_budget_usd: Option<f64>§goal: String§context: String§scoping_answers: Vec<String>§acceptance_hints: Vec<String>§blocked_by: Vec<String>

Slugs of tickets that must reach a Complete mission before this one can be approved (blocked-by: [a, b] frontmatter).

§task_class: Option<String>

Backlog task class (task-class: execution-class frontmatter), used to route the executor to a tier via crate::config::task_class_to_tier.

§review_artifact: Option<String>

Tracked text artifact reviewed by spec-review / incident-review. It is context, never a writable deliverable.

§review_output: Option<String>

Required review deliverable. Review tickets default this to reviews/<slug>.md; non-review tickets carry neither field.

§trigger: Option<String>

External trigger provenance (trigger: ci-failure|pr-comment frontmatter) — set on webhook-drafted tickets (design D-F, crate::hooks); None on human-authored tickets.

§traced_from_mission: Option<String>

Defect→mission link (traced-from-mission: m-xxxx frontmatter) — the ONE data addition of the flight-surgeon console (ticket flight-surgeon-dashboard): a defect ticket traces back to the mission that shipped it. Seeded by kranz draft --from-mission or added by hand; absent means “not a traced defect” (no false positives).

§defer_until: Option<DateTime<Utc>>

Deferral (defer-until: <RFC 3339> frontmatter, D-BW-3 adopted from beads): present but NOT ready until the timestamp passes. Evaluated against the clock at listing/admission time — no scheduler machinery; None means ready now.

§lifecycle: Option<TicketLifecycle>

Operator-declared lifecycle (state: frontmatter; see the module docs). None = the key is absent, so the .status sidecar governs exactly as before this schema existed (backcompat); Some(Open) = an explicit open, which defers to the sidecar the same way.

§state_note: Option<String>

The free-text state-note: frontmatter carried alongside a lifecycle state (e.g. “superseded by the flight-surgeon console”). Never parsed for meaning — notes are discussion, not a second state channel.

§raw_body: String

The full markdown body (everything after the frontmatter block).

Implementations§

Source§

impl Ticket

Source

pub fn tickets_dir(repo_root: &Path) -> PathBuf

Directory holding ticket markdown for a repo: .kranz/tickets/.

Source

pub fn valid_slug(slug: &str) -> bool

A slug is a bare file stem, never a path: reject separators, .., leading dots, and empties BEFORE any join — a slug like ../x must not escape .kranz/tickets/ (review P3).

Source

pub fn ensure_valid_slug(slug: &str) -> Result<()>

valid_slug as an error for write/scaffold paths.

Source

pub fn ticket_template( title: &str, goal: Option<&str>, context: Option<&str>, ) -> String

The scaffolded body of a new ticket. Frontmatter carries the title; the body is the four sections the orchestrator expects (## Goal, ## Context, ## Scoping answers, ## Acceptance hints), pre-seeded with the goal/context when supplied. The result parses back cleanly through Ticket::parse.

Source

pub fn scaffold( repo_root: &Path, slug: &str, title: &str, goal: Option<&str>, context: Option<&str>, ) -> Result<PathBuf>

Scaffold .kranz/tickets/<slug>.md from the template. Shared by the CLI (kranz ticket new) and the REST POST /api/tickets handler so the template lives in exactly one place. EngineError::Config for an invalid slug, EngineError::InvalidState (→ 409 over REST) if a ticket with that slug already exists. Returns the written path.

Source

pub fn create_markdown( repo_root: &Path, slug: &str, body: &str, ) -> Result<PathBuf>

Create a complete ticket without following links or replacing an existing entry. Retain directory capabilities through the write so a concurrent parent rename cannot redirect it outside the ticket tree.

Source

pub fn parse(slug: &str, markdown: &str) -> Result<Ticket>

Parse ticket markdown. slug is supplied by the caller (usually the file stem). Malformed frontmatter is an EngineError::Config.

Source

pub fn load(path: &Path) -> Result<Ticket>

Load and parse a ticket file; the slug is the file stem.

The read is NO-FOLLOW (crate::paths::open_read_nofollow): tickets live in a worker-writable tree and this loader runs unsandboxed in both the CLI and kranz serve, so a symlinked <slug>.md would otherwise hand a reader any file the process can open (audit: server leaf reads follow symlinks).

Source

pub fn list(repo_root: &Path) -> Vec<Ticket>

Parse every *.md under .kranz/tickets/, skipping (with a warning) any file that fails to parse. Sorted by (priority, slug).

Source

pub fn is_ready_at(&self, now: DateTime<Utc>) -> bool

Whether the ticket is ready at now: a defer-until timestamp in the future parks it (D-BW-3); anything else — absent, or past — is ready. Callers supply the clock so the check is explicit at each listing / admission site (there is no scheduler flipping state).

Source

pub fn mission_goal(&self) -> String

Fold the whole ticket into one readable-markdown message: the goal plus a compact appendix carrying scoping answers, acceptance hints, context, and (when set) the task class — enough for a draft driver to seed the orchestrator in one go, and the one channel that carries the task class into crate::orchestrator::MissionEngine::create (which recovers it via parse_task_class_from_goal) since every seed path — CLI, REST, Slack — creates the mission from this folded string, not the Ticket itself.

Source

pub fn resolve_state(repo_root: &Path, slug: &str) -> ResolvedTicketState

Resolve the effective ticket state under FRONTMATTER PRECEDENCE (design ticket-state-frontmatter): a terminal state: key in the committed .md wins over the .status sidecar — the sidecar is a write-through cache, never the truth. A PRESENT sidecar that disagrees is reported as a StateDivergence (a missing sidecar is a cold cache, not a divergence). An absent key or an explicit open defers to the sidecar; no sidecar at all is TicketState::New.

Source

pub fn read_state(repo_root: &Path, slug: &str) -> TicketState

Read the effective pipeline state; a missing or unreadable status file is TicketState::New, and an invalid slug never touches the filesystem. Frontmatter precedence per Self::resolve_state: a terminal state: key wins, and a diverging sidecar cache is logged — never a silent divergence (design rule 2).

Source

pub fn write_state( repo_root: &Path, slug: &str, state: TicketState, note: Option<String>, ) -> Result<()>

Write the pipeline state (plus an optional note) as JSON. Nothing time-based is recorded, so the file is a pure function of its inputs.

Source

pub fn write_lifecycle( repo_root: &Path, slug: &str, state: TicketLifecycle, note: Option<String>, ) -> Result<()>

The ONE lifecycle write path (design ticket-state-frontmatter, rule 2: the .status sidecar is a write-through cache of the committed frontmatter state: — every lifecycle change writes BOTH, so no reader of either file can observe them apart, and a fresh clone loses only the cache, never the verdict).

Upserts the state: (and state-note:, replacing or removing it) lines inside the ticket .md’s frontmatter block — every other byte preserved — then mirrors the terminal pipeline projection into the sidecar via Self::write_state.

Terminal states only: Open is the ABSENCE of a terminal claim, so there is nothing to cache — un-close a ticket by removing the state: key and resetting the pipeline by hand. Pipeline transitions (Drafting/Review/Queued/…) keep using Self::write_state, which never touches the committed .md.

Source

pub fn record_mission( repo_root: &Path, slug: &str, mission_id: &str, ) -> Result<()>

Durably link the ticket to the mission kranz draft created for it. kranz ticket approve <slug> resolves through this link — goal-text matching breaks the moment plan.approved rewrites the mission goal to the orchestrator’s refined phrasing (observed live on the first drafted batch).

Source

pub fn mission_for(repo_root: &Path, slug: &str) -> Option<String>

The mission recorded by Self::record_mission, if any.

Source

pub fn slug_for_mission(repo_root: &Path, mission_id: &str) -> Option<String>

Reverse of Self::mission_for: find the ticket whose .status records this mission id. Used when a mission-id approve/queue path (e.g. Slack /kranz approve m-…) must still advance the linked ticket’s pipeline state. Tickets without a mission link are skipped.

Slugs are scanned in sorted order so the result is deterministic (the event-sourced engine must not depend on read_dir order) if two tickets ever record the same mission id — an unexpected state, so a duplicate link is also logged.

Source

pub fn append_needs_context( repo_root: &Path, slug: &str, questions: &[String], ) -> Result<()>

Append the orchestrator’s verbatim clarifying questions to the ticket .md under a ## Needs context (from orchestrator) heading, and set the state to TicketState::NeedsContext.

Source

pub fn append_wrong_plan( repo_root: &Path, slug: &str, reason: &str, ) -> Result<()>

Append the planner’s wrong-plan escalation reason to the ticket .md under a ## Wrong plan (from orchestrator) heading, and set the state to TicketState::WrongPlan with the .status note carrying the reason prefixed WRONG-PLAN: . Mirrors Self::append_needs_context’s shape (bounded, scrubbed, atomic).

Source

pub fn seed_traced_from_mission( repo_root: &Path, slug: &str, mission_id: &str, ) -> Result<bool>

Seed or update the ticket’s traced-from-mission frontmatter link (the flight-surgeon console’s defect→mission join). kranz draft --from-mission calls this; hand-edited tickets need nothing here — their field parses through Ticket::parse like any other. Only the frontmatter block is rewritten (an existing link line in place, or a new line right after the opening fence; a frontmatter-less ticket gains a two-line block above its body) — body bytes are preserved. Returns Ok(true) when the file changed, Ok(false) when the link already named this mission.

Trait Implementations§

Source§

impl Clone for Ticket

Source§

fn clone(&self) -> Ticket

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Ticket

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Ticket

Source§

fn eq(&self, other: &Ticket) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Ticket

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more