Issue-to-task creation

Updated Jul 14, 2026

Corbusier can create an internal task directly from external issue metadata and retrieve it by the external issue reference. Issue-origin tasks start in the draft state and record lifecycle timestamps (created_at, updated_at) at creation time.

Issue references are unique per tenant, not globally. Two tenants may each map the same external issue reference to their own internal task without colliding, while a duplicate within the same tenant is still rejected.

use std::sync::Arc;

use corbusier::context::{CorrelationId, RequestContext, SessionId, UserId};
use corbusier::tenant::TenantId;
use corbusier::task::{
    adapters::memory::InMemoryTaskRepository,
    domain::IssueRef,
    services::{CreateTaskFromIssueRequest, TaskLifecycleService},
};
use mockable::DefaultClock;

async fn create_task_from_issue() -> Result<(), Box<dyn std::error::Error>> {
    let service = TaskLifecycleService::new(
        Arc::new(InMemoryTaskRepository::new()),
        Arc::new(DefaultClock),
    );
    let ctx = RequestContext::new(
        TenantId::new(),
        CorrelationId::new(),
        UserId::new(),
        SessionId::new(),
    );

    let request = CreateTaskFromIssueRequest::new(
        "github",
        "corbusier/core",
        120,
        "Track issue metadata",
    )
    .with_labels(vec!["feature".to_owned(), "roadmap-1.2.1".to_owned()]);

    let created = service.create_from_issue(&ctx, request).await?;
    let issue_ref = IssueRef::from_parts("github", "corbusier/core", 120)?;
    let fetched = service.find_by_issue_ref(&ctx, &issue_ref).await?;

    assert_eq!(fetched, Some(created));
    Ok(())
}