@kitsy/coop-core 2.2.4 → 2.3.0

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.
package/dist/index.cjs CHANGED
@@ -64,6 +64,7 @@ __export(index_exports, {
64
64
  check_permission: () => check_permission,
65
65
  check_unblocked: () => check_unblocked,
66
66
  check_wip: () => check_wip,
67
+ cloneTaskTransitions: () => cloneTaskTransitions,
67
68
  completeItem: () => completeItem,
68
69
  complexity_penalty: () => complexity_penalty,
69
70
  compute_all_readiness: () => compute_all_readiness,
@@ -100,6 +101,7 @@ __export(index_exports, {
100
101
  get_user_role: () => get_user_role,
101
102
  has_legacy_project_layout: () => has_legacy_project_layout,
102
103
  has_v2_projects_layout: () => has_v2_projects_layout,
104
+ isTaskStatus: () => isTaskStatus,
103
105
  is_external_dependency: () => is_external_dependency,
104
106
  is_project_initialized: () => is_project_initialized,
105
107
  list_projects: () => list_projects,
@@ -133,6 +135,7 @@ __export(index_exports, {
133
135
  renderAgentPrompt: () => renderAgentPrompt,
134
136
  repo_default_project_id: () => repo_default_project_id,
135
137
  repo_default_project_name: () => repo_default_project_name,
138
+ resolveTaskTransitions: () => resolveTaskTransitions,
136
139
  resolve_external_dependencies: () => resolve_external_dependencies,
137
140
  resolve_project: () => resolve_project,
138
141
  risk_penalty: () => risk_penalty,
@@ -156,6 +159,7 @@ __export(index_exports, {
156
159
  validate: () => validate,
157
160
  validateReferential: () => validateReferential,
158
161
  validateRepo: () => validateRepo,
162
+ validateResolvedTaskTransitions: () => validateResolvedTaskTransitions,
159
163
  validateSemantic: () => validateSemantic,
160
164
  validateStructural: () => validateStructural,
161
165
  validateTransition: () => validateTransition,
@@ -2178,6 +2182,8 @@ var TASK_FIELD_ORDER = [
2178
2182
  "track",
2179
2183
  "delivery_tracks",
2180
2184
  "priority_context",
2185
+ "promoted_at",
2186
+ "promoted_track",
2181
2187
  "assignee",
2182
2188
  "depends_on",
2183
2189
  "tags",
@@ -3704,6 +3710,23 @@ function clone_ledger2(ledger) {
3704
3710
  function normalize_track3(track) {
3705
3711
  return (track ?? "unassigned").trim().toLowerCase();
3706
3712
  }
3713
+ function promotion_timestamp(task, track) {
3714
+ if (!task.promoted_at) {
3715
+ return 0;
3716
+ }
3717
+ const promotedTrack = task.promoted_track?.trim();
3718
+ const scopedTrack = track?.trim();
3719
+ if (promotedTrack) {
3720
+ if (!scopedTrack) {
3721
+ return 0;
3722
+ }
3723
+ if (normalize_track3(promotedTrack) !== normalize_track3(scopedTrack)) {
3724
+ return 0;
3725
+ }
3726
+ }
3727
+ const parsed = Date.parse(task.promoted_at);
3728
+ return Number.isNaN(parsed) ? 0 : parsed;
3729
+ }
3707
3730
  function task_matches_track(task, track) {
3708
3731
  const normalized = normalize_track3(track);
3709
3732
  if (normalize_track3(task.track) === normalized) {
@@ -3809,6 +3832,11 @@ function schedule_next(graph, options = {}) {
3809
3832
  fits_wip: true
3810
3833
  }));
3811
3834
  scored.sort((a, b) => {
3835
+ const promotionA = promotion_timestamp(a.task, options.track);
3836
+ const promotionB = promotion_timestamp(b.task, options.track);
3837
+ if (promotionA !== promotionB) {
3838
+ return promotionB - promotionA;
3839
+ }
3812
3840
  if (a.score !== b.score) return b.score - a.score;
3813
3841
  return a.task.id.localeCompare(b.task.id);
3814
3842
  });
@@ -4436,12 +4464,52 @@ function check_unblocked(task, dependencyStatuses) {
4436
4464
  // src/state/machine.ts
4437
4465
  var VALID_TASK_TRANSITIONS = /* @__PURE__ */ new Map([
4438
4466
  ["todo", /* @__PURE__ */ new Set(["in_progress", "blocked", "canceled"])],
4439
- ["blocked", /* @__PURE__ */ new Set(["todo"])],
4467
+ ["blocked", /* @__PURE__ */ new Set(["todo", "in_progress", "canceled"])],
4440
4468
  ["in_progress", /* @__PURE__ */ new Set(["in_review", "blocked", "todo", "canceled"])],
4441
- ["in_review", /* @__PURE__ */ new Set(["done", "todo"])],
4442
- ["done", /* @__PURE__ */ new Set()],
4443
- ["canceled", /* @__PURE__ */ new Set(["todo"])]
4469
+ ["in_review", /* @__PURE__ */ new Set(["done", "todo", "blocked", "canceled"])],
4470
+ ["done", /* @__PURE__ */ new Set(["todo", "blocked", "canceled"])],
4471
+ ["canceled", /* @__PURE__ */ new Set(["todo", "blocked"])]
4444
4472
  ]);
4473
+ function isTaskStatus(value) {
4474
+ return typeof value === "string" && VALID_TASK_TRANSITIONS.has(value);
4475
+ }
4476
+ function cloneTaskTransitions() {
4477
+ return new Map(Array.from(VALID_TASK_TRANSITIONS.entries(), ([from, targets]) => [from, new Set(targets)]));
4478
+ }
4479
+ function resolveTaskTransitions(config) {
4480
+ const resolved = cloneTaskTransitions();
4481
+ const rawWorkflow = config && typeof config === "object" && !Array.isArray(config) && typeof config.workflow === "object" ? config.workflow : null;
4482
+ const rawTransitions = rawWorkflow && typeof rawWorkflow.task_transitions === "object" && rawWorkflow.task_transitions !== null && !Array.isArray(rawWorkflow.task_transitions) ? rawWorkflow.task_transitions : null;
4483
+ if (!rawTransitions) {
4484
+ return resolved;
4485
+ }
4486
+ for (const [from, to] of Object.entries(rawTransitions)) {
4487
+ if (!isTaskStatus(from)) {
4488
+ continue;
4489
+ }
4490
+ if (!Array.isArray(to)) {
4491
+ continue;
4492
+ }
4493
+ const allowed = /* @__PURE__ */ new Set();
4494
+ for (const entry of to) {
4495
+ if (isTaskStatus(entry)) {
4496
+ allowed.add(entry);
4497
+ }
4498
+ }
4499
+ resolved.set(from, allowed);
4500
+ }
4501
+ return resolved;
4502
+ }
4503
+ function validateResolvedTaskTransitions(transitions) {
4504
+ const errors = [];
4505
+ for (const status of VALID_TASK_TRANSITIONS.keys()) {
4506
+ const targets = transitions.get(status);
4507
+ if (!targets || targets.size === 0) {
4508
+ errors.push(`Task status '${status}' cannot have zero outbound transitions.`);
4509
+ }
4510
+ }
4511
+ return errors;
4512
+ }
4445
4513
  function toStatusMap2(value) {
4446
4514
  if (!value) return /* @__PURE__ */ new Map();
4447
4515
  if (value instanceof Map) return new Map(value);
@@ -4482,7 +4550,7 @@ function validateGovernanceAndDeps(task, targetStatus, context) {
4482
4550
  return null;
4483
4551
  }
4484
4552
  function transition(task, targetStatus, context = {}) {
4485
- const allowed = VALID_TASK_TRANSITIONS.get(task.status) ?? /* @__PURE__ */ new Set();
4553
+ const allowed = resolveTaskTransitions(context.config).get(task.status) ?? /* @__PURE__ */ new Set();
4486
4554
  if (!allowed.has(targetStatus)) {
4487
4555
  return {
4488
4556
  success: false,
@@ -4765,6 +4833,10 @@ function isIsoDate(value) {
4765
4833
  }
4766
4834
  return date.toISOString().slice(0, 10) === value;
4767
4835
  }
4836
+ function isIsoDateTime(value) {
4837
+ const date = new Date(value);
4838
+ return !Number.isNaN(date.valueOf());
4839
+ }
4768
4840
  function validateStringArrayField(errors, field, value, options = {}) {
4769
4841
  if (value === void 0) {
4770
4842
  return;
@@ -4891,6 +4963,28 @@ function validateStructural(task, context = {}) {
4891
4963
  validateStringArrayField(errors, "fix_versions", task.fix_versions);
4892
4964
  validateStringArrayField(errors, "released_in", task.released_in);
4893
4965
  validatePriorityContext(errors, task.priority_context);
4966
+ if (task.promoted_at !== void 0) {
4967
+ if (typeof task.promoted_at !== "string" || !isIsoDateTime(task.promoted_at)) {
4968
+ errors.push(
4969
+ error4(
4970
+ "promoted_at",
4971
+ "struct.promoted_at_iso_datetime",
4972
+ "Field 'promoted_at' must be an ISO datetime string."
4973
+ )
4974
+ );
4975
+ }
4976
+ }
4977
+ if (task.promoted_track !== void 0 && task.promoted_track !== null) {
4978
+ if (typeof task.promoted_track !== "string" || task.promoted_track.trim().length === 0) {
4979
+ errors.push(
4980
+ error4(
4981
+ "promoted_track",
4982
+ "struct.promoted_track_string",
4983
+ "Field 'promoted_track' must be a non-empty string or null."
4984
+ )
4985
+ );
4986
+ }
4987
+ }
4894
4988
  if (task.story_points !== void 0 && (!Number.isFinite(task.story_points) || task.story_points < 0)) {
4895
4989
  errors.push(
4896
4990
  error4(
@@ -5010,14 +5104,7 @@ function validateStructural(task, context = {}) {
5010
5104
  }
5011
5105
 
5012
5106
  // src/validator/transition.ts
5013
- var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
5014
- ["todo", /* @__PURE__ */ new Set(["in_progress", "blocked", "canceled"])],
5015
- ["blocked", /* @__PURE__ */ new Set(["todo"])],
5016
- ["in_progress", /* @__PURE__ */ new Set(["in_review", "blocked", "todo", "canceled"])],
5017
- ["in_review", /* @__PURE__ */ new Set(["done", "todo"])],
5018
- ["done", /* @__PURE__ */ new Set()],
5019
- ["canceled", /* @__PURE__ */ new Set(["todo"])]
5020
- ]);
5107
+ var VALID_TRANSITIONS = VALID_TASK_TRANSITIONS;
5021
5108
  function error5(field, rule, message) {
5022
5109
  return { level: "error", field, rule, message };
5023
5110
  }
@@ -5031,14 +5118,15 @@ function asMap2(value) {
5031
5118
  return new Map(Object.entries(value));
5032
5119
  }
5033
5120
  function validate_transition(fromStatus, toStatus) {
5034
- const allowed = VALID_TRANSITIONS.get(fromStatus);
5121
+ const allowed = VALID_TASK_TRANSITIONS.get(fromStatus);
5035
5122
  return Boolean(allowed?.has(toStatus));
5036
5123
  }
5037
5124
  function validateTransition(task, toStatus, context = {}) {
5038
5125
  const issues = [];
5039
5126
  const fromStatus = task.status;
5040
- if (!validate_transition(fromStatus, toStatus)) {
5041
- const allowed = Array.from(VALID_TRANSITIONS.get(fromStatus) ?? []);
5127
+ const transitions = resolveTaskTransitions(context.config);
5128
+ if (!(transitions.get(fromStatus)?.has(toStatus) ?? false)) {
5129
+ const allowed = Array.from(transitions.get(fromStatus) ?? []);
5042
5130
  issues.push(
5043
5131
  error5(
5044
5132
  "status",
@@ -5839,6 +5927,7 @@ function validateRepo(rootDir) {
5839
5927
  check_permission,
5840
5928
  check_unblocked,
5841
5929
  check_wip,
5930
+ cloneTaskTransitions,
5842
5931
  completeItem,
5843
5932
  complexity_penalty,
5844
5933
  compute_all_readiness,
@@ -5875,6 +5964,7 @@ function validateRepo(rootDir) {
5875
5964
  get_user_role,
5876
5965
  has_legacy_project_layout,
5877
5966
  has_v2_projects_layout,
5967
+ isTaskStatus,
5878
5968
  is_external_dependency,
5879
5969
  is_project_initialized,
5880
5970
  list_projects,
@@ -5908,6 +5998,7 @@ function validateRepo(rootDir) {
5908
5998
  renderAgentPrompt,
5909
5999
  repo_default_project_id,
5910
6000
  repo_default_project_name,
6001
+ resolveTaskTransitions,
5911
6002
  resolve_external_dependencies,
5912
6003
  resolve_project,
5913
6004
  risk_penalty,
@@ -5931,6 +6022,7 @@ function validateRepo(rootDir) {
5931
6022
  validate,
5932
6023
  validateReferential,
5933
6024
  validateRepo,
6025
+ validateResolvedTaskTransitions,
5934
6026
  validateSemantic,
5935
6027
  validateStructural,
5936
6028
  validateTransition,
package/dist/index.d.cts CHANGED
@@ -305,6 +305,8 @@ interface TaskPlanning {
305
305
  track?: string;
306
306
  delivery_tracks?: string[];
307
307
  priority_context?: Record<string, TaskPriority>;
308
+ promoted_at?: string;
309
+ promoted_track?: string | null;
308
310
  assignee?: string | null;
309
311
  depends_on?: string[];
310
312
  tags?: string[];
@@ -800,6 +802,12 @@ interface CoopConfig {
800
802
  on_task_transition?: string;
801
803
  on_delivery_complete?: string;
802
804
  };
805
+ workflow?: {
806
+ task_transitions?: Partial<Record<TaskStatus, TaskStatus[]>>;
807
+ track_overrides?: Record<string, {
808
+ task_transitions?: Partial<Record<TaskStatus, TaskStatus[]>>;
809
+ }>;
810
+ };
803
811
  api?: {
804
812
  host?: string;
805
813
  port?: number;
@@ -1375,6 +1383,7 @@ declare function check_unblocked(task: Task, dependencyStatuses?: DependencyStat
1375
1383
  type TransitionContext = {
1376
1384
  actor?: string;
1377
1385
  dependencyStatuses?: Map<string, TaskStatus> | Record<string, TaskStatus>;
1386
+ config?: CoopConfig | Record<string, unknown>;
1378
1387
  now?: string | Date;
1379
1388
  eventEmitter?: CoopEventEmitter;
1380
1389
  };
@@ -1384,6 +1393,10 @@ type TransitionResult = {
1384
1393
  error?: string;
1385
1394
  };
1386
1395
  declare const VALID_TASK_TRANSITIONS: Map<TaskStatus, Set<TaskStatus>>;
1396
+ declare function isTaskStatus(value: unknown): value is TaskStatus;
1397
+ declare function cloneTaskTransitions(): Map<TaskStatus, Set<TaskStatus>>;
1398
+ declare function resolveTaskTransitions(config?: CoopConfig | Record<string, unknown>): Map<TaskStatus, Set<TaskStatus>>;
1399
+ declare function validateResolvedTaskTransitions(transitions: Map<TaskStatus, Set<TaskStatus>>): string[];
1387
1400
  declare function transition(task: Task, targetStatus: TaskStatus, context?: TransitionContext): TransitionResult;
1388
1401
 
1389
1402
  type ValidationLevel = "error" | "warning";
@@ -1426,6 +1439,7 @@ declare const VALID_TRANSITIONS: Map<TaskStatus, Set<TaskStatus>>;
1426
1439
  interface TransitionValidationContext {
1427
1440
  actor?: string;
1428
1441
  dependencyStatuses?: Map<string, TaskStatus> | Record<string, TaskStatus>;
1442
+ config?: CoopConfig | Record<string, unknown>;
1429
1443
  }
1430
1444
  declare function validate_transition(fromStatus: TaskStatus, toStatus: TaskStatus): boolean;
1431
1445
  declare function validateTransition(task: Task, toStatus: TaskStatus, context?: TransitionValidationContext): ValidationError[];
@@ -1539,4 +1553,4 @@ declare function validateRepo(rootDir: string): {
1539
1553
  warnings: string[];
1540
1554
  };
1541
1555
 
1542
- export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, type TaskComment, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTimeLog, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_priority, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
1556
+ export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, type TaskComment, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTimeLog, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, cloneTaskTransitions, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_priority, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, isTaskStatus, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolveTaskTransitions, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateResolvedTaskTransitions, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
package/dist/index.d.ts CHANGED
@@ -305,6 +305,8 @@ interface TaskPlanning {
305
305
  track?: string;
306
306
  delivery_tracks?: string[];
307
307
  priority_context?: Record<string, TaskPriority>;
308
+ promoted_at?: string;
309
+ promoted_track?: string | null;
308
310
  assignee?: string | null;
309
311
  depends_on?: string[];
310
312
  tags?: string[];
@@ -800,6 +802,12 @@ interface CoopConfig {
800
802
  on_task_transition?: string;
801
803
  on_delivery_complete?: string;
802
804
  };
805
+ workflow?: {
806
+ task_transitions?: Partial<Record<TaskStatus, TaskStatus[]>>;
807
+ track_overrides?: Record<string, {
808
+ task_transitions?: Partial<Record<TaskStatus, TaskStatus[]>>;
809
+ }>;
810
+ };
803
811
  api?: {
804
812
  host?: string;
805
813
  port?: number;
@@ -1375,6 +1383,7 @@ declare function check_unblocked(task: Task, dependencyStatuses?: DependencyStat
1375
1383
  type TransitionContext = {
1376
1384
  actor?: string;
1377
1385
  dependencyStatuses?: Map<string, TaskStatus> | Record<string, TaskStatus>;
1386
+ config?: CoopConfig | Record<string, unknown>;
1378
1387
  now?: string | Date;
1379
1388
  eventEmitter?: CoopEventEmitter;
1380
1389
  };
@@ -1384,6 +1393,10 @@ type TransitionResult = {
1384
1393
  error?: string;
1385
1394
  };
1386
1395
  declare const VALID_TASK_TRANSITIONS: Map<TaskStatus, Set<TaskStatus>>;
1396
+ declare function isTaskStatus(value: unknown): value is TaskStatus;
1397
+ declare function cloneTaskTransitions(): Map<TaskStatus, Set<TaskStatus>>;
1398
+ declare function resolveTaskTransitions(config?: CoopConfig | Record<string, unknown>): Map<TaskStatus, Set<TaskStatus>>;
1399
+ declare function validateResolvedTaskTransitions(transitions: Map<TaskStatus, Set<TaskStatus>>): string[];
1387
1400
  declare function transition(task: Task, targetStatus: TaskStatus, context?: TransitionContext): TransitionResult;
1388
1401
 
1389
1402
  type ValidationLevel = "error" | "warning";
@@ -1426,6 +1439,7 @@ declare const VALID_TRANSITIONS: Map<TaskStatus, Set<TaskStatus>>;
1426
1439
  interface TransitionValidationContext {
1427
1440
  actor?: string;
1428
1441
  dependencyStatuses?: Map<string, TaskStatus> | Record<string, TaskStatus>;
1442
+ config?: CoopConfig | Record<string, unknown>;
1429
1443
  }
1430
1444
  declare function validate_transition(fromStatus: TaskStatus, toStatus: TaskStatus): boolean;
1431
1445
  declare function validateTransition(task: Task, toStatus: TaskStatus, context?: TransitionValidationContext): ValidationError[];
@@ -1539,4 +1553,4 @@ declare function validateRepo(rootDir: string): {
1539
1553
  warnings: string[];
1540
1554
  };
1541
1555
 
1542
- export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, type TaskComment, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTimeLog, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_priority, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
1556
+ export { type AIAgent, type AIResource, type AgentSpec, type AllocationResult, ArtifactType, type AuthConfig, type AuthPolicy, type AutoTransitionResult, type AvailabilityWindow, type BacklogItem, type BacklogItemData, COOP_EVENT_TYPES, CURRENT_SCHEMA_VERSION, type CapacityLedger, type ComparisonResult, type ComparisonRow, type ComputeNode, type ComputeResource, type CoopConfig, type CoopEvent, CoopEventEmitter, type CoopEventType, type CoopProjectRef, type CoopWorkspaceConfig, type CreateItemParams, type CriticalPathResult, type CriticalPathTaskMetrics, DEFAULT_SCORE_WEIGHTS, type Delivery, type DeliveryAtRisk, type DeliveryBudget, type DeliveryCommitted, type DeliveryGovernance, type DeliveryRisk, type DeliveryRiskType, type DeliveryScope, DeliveryStatus, type DetectedDeliveryRisk, type EffectiveCapacity, type EventByType, type ExecutionConstraints, type ExecutionPermissions, ExecutorType, type ExternalDependencyRef, type ExternalDependencyResolution, type ExternalDependencyResolutionStatus, type ExternalDependencyResolverOptions, type ExternalRepoConfig, type FeasibilityResult, type FeasibilityRisk, type FeasibilityStatus, type FeasibilitySummary, type FilterSpec, type FrontmatterParseResult, type GraphCycleDetected, type GraphValidationContext, type GraphValidationResult, type HookRunResult, type HumanMember, type HumanResource, ITEM_STATUSES, ITEM_TYPES, type Idea, IdeaStatus, IndexManager, type IndexStatus, type ItemLinks, type ItemStatus, type ItemType, MIGRATIONS, type MigrateRepositoryOptions, type MigrationContext, type MigrationDefinition, type MigrationReport, type MonteCarloHistogramBucket, type MonteCarloOptions, type MonteCarloResult, type MonteCarloWorkerPayload, type ParsedDelivery, type ParsedIdea, type ParsedTask, type Permission, type PermissionContext, type PluginAction, type PluginActionConsole, type PluginActionGitHubPr, type PluginActionHandler, type PluginActionHandlerResult, type PluginActionWebhook, type PluginManifest, type PluginRunOptions, type PluginRunRecord, type PluginTrigger, type PolicyAction, type ReadinessComputation, type ReadinessPartitions, type ReadinessState, type ReadinessTransitionEvent, type ReadinessWarning, type ReferentialValidationContext, type RepoConfig, type RepoState, type ResourceProfile, RiskLevel, type Role, type Run, type RunCompleted, type RunFailed, type RunResourcesConsumed, type RunStarted, RunStatus, type RunStepResult, RunStepStatus, RunbookAction, type RunbookStep, type ScheduleOptions, type ScoreContext, type ScoredTask, type SemanticValidationContext, type SimulationResult, type StructuralValidationContext, type Task, type TaskAssigned, type TaskComment, TaskComplexity, type TaskComputed, type TaskCore, type TaskCreated, TaskDeterminism, type TaskEstimate, type TaskEstimation, type TaskExecution, type TaskGovernance, type TaskGraph, type TaskPlanning, TaskPriority, type TaskResources, type TaskScheduleEntry, TaskStatus, type TaskTimeLog, type TaskTransitioned, type TaskTransitionedEvent, TaskType, type Track, type TrackUtilization, type TrackWip, type TransitionContext, type TransitionResult, type TransitionValidationContext, type UpdateItemParams, VALID_TASK_TRANSITIONS, VALID_TRANSITIONS, type ValidationContext, type ValidationError, type ValidationLevel, type ValidationResult, type VelocityMetrics, type VelocityPoint, type VelocityTrend, type WhatIfBaseline, type WhatIfModification, type WriteTaskOptions, allocate, allocate_ai, allocate_ai_tokens, analyze_feasibility, analyze_what_if, build_capacity_ledger, build_graph, check_blocked, check_permission, check_unblocked, check_wip, cloneTaskTransitions, completeItem, complexity_penalty, compute_all_readiness, compute_critical_path, compute_readiness, compute_readiness_with_corrections, compute_score, compute_velocity, coop_project_config_path, coop_project_root, coop_projects_dir, coop_workspace_config_path, coop_workspace_dir, createItem, create_seeded_rng, critical_path_weight, deleteItem, dependency_unlock_weight, detect_cycle, detect_delivery_risks, determinism_weight, effective_priority, effective_weekly_hours, effort_or_default, ensureCoopLayout, ensure_workspace_layout, executor_fit_weight, external_dependencies_for_task, extract_subgraph, findRepoRoot, find_external_dependencies, getItemById, get_remaining_tokens, get_user_role, has_legacy_project_layout, has_v2_projects_layout, isTaskStatus, is_external_dependency, is_project_initialized, list_projects, loadState, load_auth_config, load_completed_runs, load_graph, load_plugins, migrate_repository, migrate_task, monte_carlo_forecast, parseDeliveryContent, parseDeliveryFile, parseFrontmatterContent, parseFrontmatterFile, parseIdeaContent, parseIdeaFile, parseTaskContent, parseTaskFile, parseYamlContent, parseYamlFile, parse_external_dependency, partition_by_readiness, pert_hours, pert_stddev, priority_weight, queryItems, read_project_config, read_schema_version, read_workspace_config, renderAgentPrompt, repo_default_project_id, repo_default_project_name, resolveTaskTransitions, resolve_external_dependencies, resolve_project, risk_penalty, run_hook, run_monte_carlo_chunk, run_plugins_for_event, sample_pert_beta, sample_task_hours, schedule_next, simulate_schedule, stringifyFrontmatter, stringifyYamlContent, task_effort_hours, topological_sort, transition, transitive_dependencies, transitive_dependents, type_weight, updateItem, urgency_weight, validate, validateReferential, validateRepo, validateResolvedTaskTransitions, validateSemantic, validateStructural, validateTransition, validate_graph, validate_transition, writeTask, writeYamlFile, write_schema_version, write_workspace_config };
package/dist/index.js CHANGED
@@ -1967,6 +1967,8 @@ var TASK_FIELD_ORDER = [
1967
1967
  "track",
1968
1968
  "delivery_tracks",
1969
1969
  "priority_context",
1970
+ "promoted_at",
1971
+ "promoted_track",
1970
1972
  "assignee",
1971
1973
  "depends_on",
1972
1974
  "tags",
@@ -2620,6 +2622,23 @@ function clone_ledger(ledger) {
2620
2622
  function normalize_track2(track) {
2621
2623
  return (track ?? "unassigned").trim().toLowerCase();
2622
2624
  }
2625
+ function promotion_timestamp(task, track) {
2626
+ if (!task.promoted_at) {
2627
+ return 0;
2628
+ }
2629
+ const promotedTrack = task.promoted_track?.trim();
2630
+ const scopedTrack = track?.trim();
2631
+ if (promotedTrack) {
2632
+ if (!scopedTrack) {
2633
+ return 0;
2634
+ }
2635
+ if (normalize_track2(promotedTrack) !== normalize_track2(scopedTrack)) {
2636
+ return 0;
2637
+ }
2638
+ }
2639
+ const parsed = Date.parse(task.promoted_at);
2640
+ return Number.isNaN(parsed) ? 0 : parsed;
2641
+ }
2623
2642
  function task_matches_track(task, track) {
2624
2643
  const normalized = normalize_track2(track);
2625
2644
  if (normalize_track2(task.track) === normalized) {
@@ -2725,6 +2744,11 @@ function schedule_next(graph, options = {}) {
2725
2744
  fits_wip: true
2726
2745
  }));
2727
2746
  scored.sort((a, b) => {
2747
+ const promotionA = promotion_timestamp(a.task, options.track);
2748
+ const promotionB = promotion_timestamp(b.task, options.track);
2749
+ if (promotionA !== promotionB) {
2750
+ return promotionB - promotionA;
2751
+ }
2728
2752
  if (a.score !== b.score) return b.score - a.score;
2729
2753
  return a.task.id.localeCompare(b.task.id);
2730
2754
  });
@@ -3352,12 +3376,52 @@ function check_unblocked(task, dependencyStatuses) {
3352
3376
  // src/state/machine.ts
3353
3377
  var VALID_TASK_TRANSITIONS = /* @__PURE__ */ new Map([
3354
3378
  ["todo", /* @__PURE__ */ new Set(["in_progress", "blocked", "canceled"])],
3355
- ["blocked", /* @__PURE__ */ new Set(["todo"])],
3379
+ ["blocked", /* @__PURE__ */ new Set(["todo", "in_progress", "canceled"])],
3356
3380
  ["in_progress", /* @__PURE__ */ new Set(["in_review", "blocked", "todo", "canceled"])],
3357
- ["in_review", /* @__PURE__ */ new Set(["done", "todo"])],
3358
- ["done", /* @__PURE__ */ new Set()],
3359
- ["canceled", /* @__PURE__ */ new Set(["todo"])]
3381
+ ["in_review", /* @__PURE__ */ new Set(["done", "todo", "blocked", "canceled"])],
3382
+ ["done", /* @__PURE__ */ new Set(["todo", "blocked", "canceled"])],
3383
+ ["canceled", /* @__PURE__ */ new Set(["todo", "blocked"])]
3360
3384
  ]);
3385
+ function isTaskStatus(value) {
3386
+ return typeof value === "string" && VALID_TASK_TRANSITIONS.has(value);
3387
+ }
3388
+ function cloneTaskTransitions() {
3389
+ return new Map(Array.from(VALID_TASK_TRANSITIONS.entries(), ([from, targets]) => [from, new Set(targets)]));
3390
+ }
3391
+ function resolveTaskTransitions(config) {
3392
+ const resolved = cloneTaskTransitions();
3393
+ const rawWorkflow = config && typeof config === "object" && !Array.isArray(config) && typeof config.workflow === "object" ? config.workflow : null;
3394
+ const rawTransitions = rawWorkflow && typeof rawWorkflow.task_transitions === "object" && rawWorkflow.task_transitions !== null && !Array.isArray(rawWorkflow.task_transitions) ? rawWorkflow.task_transitions : null;
3395
+ if (!rawTransitions) {
3396
+ return resolved;
3397
+ }
3398
+ for (const [from, to] of Object.entries(rawTransitions)) {
3399
+ if (!isTaskStatus(from)) {
3400
+ continue;
3401
+ }
3402
+ if (!Array.isArray(to)) {
3403
+ continue;
3404
+ }
3405
+ const allowed = /* @__PURE__ */ new Set();
3406
+ for (const entry of to) {
3407
+ if (isTaskStatus(entry)) {
3408
+ allowed.add(entry);
3409
+ }
3410
+ }
3411
+ resolved.set(from, allowed);
3412
+ }
3413
+ return resolved;
3414
+ }
3415
+ function validateResolvedTaskTransitions(transitions) {
3416
+ const errors = [];
3417
+ for (const status of VALID_TASK_TRANSITIONS.keys()) {
3418
+ const targets = transitions.get(status);
3419
+ if (!targets || targets.size === 0) {
3420
+ errors.push(`Task status '${status}' cannot have zero outbound transitions.`);
3421
+ }
3422
+ }
3423
+ return errors;
3424
+ }
3361
3425
  function toStatusMap2(value) {
3362
3426
  if (!value) return /* @__PURE__ */ new Map();
3363
3427
  if (value instanceof Map) return new Map(value);
@@ -3398,7 +3462,7 @@ function validateGovernanceAndDeps(task, targetStatus, context) {
3398
3462
  return null;
3399
3463
  }
3400
3464
  function transition(task, targetStatus, context = {}) {
3401
- const allowed = VALID_TASK_TRANSITIONS.get(task.status) ?? /* @__PURE__ */ new Set();
3465
+ const allowed = resolveTaskTransitions(context.config).get(task.status) ?? /* @__PURE__ */ new Set();
3402
3466
  if (!allowed.has(targetStatus)) {
3403
3467
  return {
3404
3468
  success: false,
@@ -3681,6 +3745,10 @@ function isIsoDate(value) {
3681
3745
  }
3682
3746
  return date.toISOString().slice(0, 10) === value;
3683
3747
  }
3748
+ function isIsoDateTime(value) {
3749
+ const date = new Date(value);
3750
+ return !Number.isNaN(date.valueOf());
3751
+ }
3684
3752
  function validateStringArrayField(errors, field, value, options = {}) {
3685
3753
  if (value === void 0) {
3686
3754
  return;
@@ -3807,6 +3875,28 @@ function validateStructural(task, context = {}) {
3807
3875
  validateStringArrayField(errors, "fix_versions", task.fix_versions);
3808
3876
  validateStringArrayField(errors, "released_in", task.released_in);
3809
3877
  validatePriorityContext(errors, task.priority_context);
3878
+ if (task.promoted_at !== void 0) {
3879
+ if (typeof task.promoted_at !== "string" || !isIsoDateTime(task.promoted_at)) {
3880
+ errors.push(
3881
+ error4(
3882
+ "promoted_at",
3883
+ "struct.promoted_at_iso_datetime",
3884
+ "Field 'promoted_at' must be an ISO datetime string."
3885
+ )
3886
+ );
3887
+ }
3888
+ }
3889
+ if (task.promoted_track !== void 0 && task.promoted_track !== null) {
3890
+ if (typeof task.promoted_track !== "string" || task.promoted_track.trim().length === 0) {
3891
+ errors.push(
3892
+ error4(
3893
+ "promoted_track",
3894
+ "struct.promoted_track_string",
3895
+ "Field 'promoted_track' must be a non-empty string or null."
3896
+ )
3897
+ );
3898
+ }
3899
+ }
3810
3900
  if (task.story_points !== void 0 && (!Number.isFinite(task.story_points) || task.story_points < 0)) {
3811
3901
  errors.push(
3812
3902
  error4(
@@ -3926,14 +4016,7 @@ function validateStructural(task, context = {}) {
3926
4016
  }
3927
4017
 
3928
4018
  // src/validator/transition.ts
3929
- var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
3930
- ["todo", /* @__PURE__ */ new Set(["in_progress", "blocked", "canceled"])],
3931
- ["blocked", /* @__PURE__ */ new Set(["todo"])],
3932
- ["in_progress", /* @__PURE__ */ new Set(["in_review", "blocked", "todo", "canceled"])],
3933
- ["in_review", /* @__PURE__ */ new Set(["done", "todo"])],
3934
- ["done", /* @__PURE__ */ new Set()],
3935
- ["canceled", /* @__PURE__ */ new Set(["todo"])]
3936
- ]);
4019
+ var VALID_TRANSITIONS = VALID_TASK_TRANSITIONS;
3937
4020
  function error5(field, rule, message) {
3938
4021
  return { level: "error", field, rule, message };
3939
4022
  }
@@ -3947,14 +4030,15 @@ function asMap2(value) {
3947
4030
  return new Map(Object.entries(value));
3948
4031
  }
3949
4032
  function validate_transition(fromStatus, toStatus) {
3950
- const allowed = VALID_TRANSITIONS.get(fromStatus);
4033
+ const allowed = VALID_TASK_TRANSITIONS.get(fromStatus);
3951
4034
  return Boolean(allowed?.has(toStatus));
3952
4035
  }
3953
4036
  function validateTransition(task, toStatus, context = {}) {
3954
4037
  const issues = [];
3955
4038
  const fromStatus = task.status;
3956
- if (!validate_transition(fromStatus, toStatus)) {
3957
- const allowed = Array.from(VALID_TRANSITIONS.get(fromStatus) ?? []);
4039
+ const transitions = resolveTaskTransitions(context.config);
4040
+ if (!(transitions.get(fromStatus)?.has(toStatus) ?? false)) {
4041
+ const allowed = Array.from(transitions.get(fromStatus) ?? []);
3958
4042
  issues.push(
3959
4043
  error5(
3960
4044
  "status",
@@ -4754,6 +4838,7 @@ export {
4754
4838
  check_permission,
4755
4839
  check_unblocked,
4756
4840
  check_wip,
4841
+ cloneTaskTransitions,
4757
4842
  completeItem,
4758
4843
  complexity_penalty,
4759
4844
  compute_all_readiness,
@@ -4790,6 +4875,7 @@ export {
4790
4875
  get_user_role,
4791
4876
  has_legacy_project_layout,
4792
4877
  has_v2_projects_layout,
4878
+ isTaskStatus,
4793
4879
  is_external_dependency,
4794
4880
  is_project_initialized,
4795
4881
  list_projects,
@@ -4823,6 +4909,7 @@ export {
4823
4909
  renderAgentPrompt,
4824
4910
  repo_default_project_id,
4825
4911
  repo_default_project_name,
4912
+ resolveTaskTransitions,
4826
4913
  resolve_external_dependencies,
4827
4914
  resolve_project,
4828
4915
  risk_penalty,
@@ -4846,6 +4933,7 @@ export {
4846
4933
  validate,
4847
4934
  validateReferential,
4848
4935
  validateRepo,
4936
+ validateResolvedTaskTransitions,
4849
4937
  validateSemantic,
4850
4938
  validateStructural,
4851
4939
  validateTransition,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kitsy/coop-core",
3
3
  "description": "Core models, parser, validator, graph, and planning engine for COOP.",
4
- "version": "2.2.4",
4
+ "version": "2.3.0",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "publishConfig": {