Rensei docs

Lifecycle Config

spec.lifecycle stage-to-tracker-state mapping.

spec.lifecycle maps the platform's abstract SDLC stage vocabulary to your tracker's native state names, binding each stage to the exact state transitions that should start and stop it. This is what makes Rensei's SDLC engine tracker-agnostic - the same workflow definition adapts to Linear, Jira, or any future issue tracker by changing only the lifecycle config.

How it works

Schema

spec:
  lifecycle:
    # Stage ID: either a known platform stage or a custom free-form string
    research:
      trigger:
        event: issue.transitioned
        when:
          to: "Icebox"          # exact tracker-native state name
      exit:
        transition_to: "Triage" # optional - platform drives issue to this state on success
        on_fail: "Rejected"     # optional - driven on stage failure
      prompt: |                 # optional - override the platform default prompt
        Research this issue thoroughly...

    development:
      trigger:
        event: issue.transitioned
        when: { to: "Backlog" }
      exit:
        transition_to: "In Review"
        on_fail: "Rejected"

    qa:
      trigger:
        event: issue.transitioned
        when: { to: "In Review" }
      exit:
        transition_to: "Done"
        on_fail: "Rejected"

Stage entry fields

Prop

Type

Platform stages vs custom stages

The platform ships a built-in registry of known stage IDs - research, backlog-writer, development, coordination, qa, acceptance - each with a default prompt template and budget defaults.

You may use any free-form string as a stage ID. Unknown stage IDs produce a validation warning (not an error) and are dispatched with default budgets. Custom stages must provide an explicit prompt field since no platform template exists.

spec:
  lifecycle:
    my-custom-spike:         # unknown stage - fine, but you must supply a prompt
      trigger:
        event: issue.transitioned
        when: { to: "Spiking" }
      exit:
        transition_to: "Backlog"
      prompt: |
        Investigate the technical feasibility of...

Validation rules

validateLifecycleConfig enforces:

  1. Structural shape - Zod validation of each stage entry.
  2. No duplicate trigger states - two stages cannot share the same trigger.when.to value. The trigger normaliser would otherwise fan out N stage events for one transition, which is almost never intended.
  3. Warning for unknown stage ids - the config is still accepted; the platform logs a warning and dispatches with defaults.

Native-state validity (does "Icebox" actually exist in your Linear team?) is not checked at save time. The trigger normaliser fails closed at runtime when no stage matches an inbound transition - no event is emitted, no agent is dispatched.

State names are case-sensitive and must match your tracker's canonical names exactly. Linear teams use "Started", not "In Progress". Check Settings → Teams → [team] → Workflow in Linear for the exact names. A mismatch silently no-ops - the trigger normaliser finds no matching stage and discards the event.

Prompt resolution order

When the platform prepares to dispatch a stage, it resolves the agent prompt as follows:

  1. lifecycle[stageId].prompt - the inline override from this config
  2. The registered TS module for the stage ID - the platform's canonical default
  3. Error - the author must provide a prompt or register the stage

Auto-derivation from canvas

At publish time, deriveLifecycleFromCanvas walks the workflow definition and reconstructs spec.lifecycle from agent.dispatch_stage node config fields. This means the lifecycle config is node-first - you set triggerWhen and exit directly on the dispatch node in the canvas, not in a separate lifecycle tab.

The reconstructed lifecycle is validated before publish. The editor shows the derived config in the YAML pane under spec.lifecycle.

Full example (Linear mapping)

The following maps the platform's five canonical stages to a Rensei/Linear team workflow:

spec:
  lifecycle:
    research:
      trigger:
        event: issue.transitioned
        when: { to: "Icebox" }
      exit:
        transition_to: "Triage"

    backlog-writer:
      trigger:
        event: issue.transitioned
        when: { to: "Triage" }
      exit:
        transition_to: "Backlog"

    development:
      trigger:
        event: issue.transitioned
        when: { to: "Backlog" }
      exit:
        transition_to: "Finished"
        on_fail: "Rejected"

    qa:
      trigger:
        event: issue.transitioned
        when: { to: "Finished" }
      exit:
        transition_to: "Delivered"
        on_fail: "Rejected"

    acceptance:
      trigger:
        event: issue.transitioned
        when: { to: "Delivered" }
      exit:
        transition_to: "Accepted"
        on_fail: "Rejected"

On this page