@cleocode/contracts 2026.9.22 → 2026.9.24

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.
Files changed (102) hide show
  1. package/dist/__tests__/evidence-atom-schema.test.js +3 -2
  2. package/dist/__tests__/evidence-atom-schema.test.js.map +1 -1
  3. package/dist/acceptance-gate-schema.d.ts.map +1 -1
  4. package/dist/acceptance-gate-schema.js +4 -1
  5. package/dist/acceptance-gate-schema.js.map +1 -1
  6. package/dist/branch-lock.d.ts +9 -0
  7. package/dist/branch-lock.d.ts.map +1 -1
  8. package/dist/branch-lock.js +25 -3
  9. package/dist/branch-lock.js.map +1 -1
  10. package/dist/cloud/api.d.ts +1 -1
  11. package/dist/data-accessor.d.ts +209 -16
  12. package/dist/data-accessor.d.ts.map +1 -1
  13. package/dist/data-accessor.js.map +1 -1
  14. package/dist/decide-sites.d.ts +157 -0
  15. package/dist/decide-sites.d.ts.map +1 -0
  16. package/dist/decide-sites.js +28 -0
  17. package/dist/decide-sites.js.map +1 -0
  18. package/dist/decide.d.ts +214 -0
  19. package/dist/decide.d.ts.map +1 -1
  20. package/dist/decide.js +54 -0
  21. package/dist/decide.js.map +1 -1
  22. package/dist/dispatch/operations-registry.d.ts.map +1 -1
  23. package/dist/dispatch/operations-registry.js +174 -8
  24. package/dist/dispatch/operations-registry.js.map +1 -1
  25. package/dist/doctor.d.ts +9 -0
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/evidence-atom-schema.d.ts.map +1 -1
  28. package/dist/evidence-atom-schema.js +3 -0
  29. package/dist/evidence-atom-schema.js.map +1 -1
  30. package/dist/exit-codes.d.ts +7 -0
  31. package/dist/exit-codes.d.ts.map +1 -1
  32. package/dist/exit-codes.js +7 -0
  33. package/dist/exit-codes.js.map +1 -1
  34. package/dist/index.d.ts +12 -7
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/knowledge-health.d.ts +21 -0
  39. package/dist/knowledge-health.d.ts.map +1 -1
  40. package/dist/nexus-account.d.ts +160 -0
  41. package/dist/nexus-account.d.ts.map +1 -0
  42. package/dist/nexus-account.js +68 -0
  43. package/dist/nexus-account.js.map +1 -0
  44. package/dist/operations/admin.d.ts +5 -0
  45. package/dist/operations/admin.d.ts.map +1 -1
  46. package/dist/operations/focus.d.ts +7 -3
  47. package/dist/operations/focus.d.ts.map +1 -1
  48. package/dist/operations/nexus-scope-map.d.ts +8 -0
  49. package/dist/operations/nexus-scope-map.d.ts.map +1 -1
  50. package/dist/operations/nexus-scope-map.js +8 -0
  51. package/dist/operations/nexus-scope-map.js.map +1 -1
  52. package/dist/operations/nexus.d.ts +265 -0
  53. package/dist/operations/nexus.d.ts.map +1 -1
  54. package/dist/operations/nexus.js +9 -1
  55. package/dist/operations/nexus.js.map +1 -1
  56. package/dist/operations/orchestrate.d.ts +6 -1
  57. package/dist/operations/orchestrate.d.ts.map +1 -1
  58. package/dist/operations/output-contracts-data.d.ts.map +1 -1
  59. package/dist/operations/output-contracts-data.js +115 -1
  60. package/dist/operations/output-contracts-data.js.map +1 -1
  61. package/dist/operations/tasks.d.ts +97 -9
  62. package/dist/operations/tasks.d.ts.map +1 -1
  63. package/dist/operations/tasks.js.map +1 -1
  64. package/dist/project-lifecycle.d.ts +6 -2
  65. package/dist/project-lifecycle.d.ts.map +1 -1
  66. package/dist/session.d.ts +19 -1
  67. package/dist/session.d.ts.map +1 -1
  68. package/dist/session.js +9 -2
  69. package/dist/session.js.map +1 -1
  70. package/dist/task.d.ts +49 -1
  71. package/dist/task.d.ts.map +1 -1
  72. package/dist/tools/index.d.ts +1 -1
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/score-task-priority.d.ts +28 -3
  75. package/dist/tools/score-task-priority.d.ts.map +1 -1
  76. package/package.json +2 -2
  77. package/src/__tests__/__snapshots__/operations-registry.test.ts.snap +1 -1
  78. package/src/__tests__/evidence-atom-schema.test.ts +3 -2
  79. package/src/acceptance-gate-schema.ts +4 -1
  80. package/src/branch-lock.ts +38 -3
  81. package/src/data-accessor.ts +215 -16
  82. package/src/decide-sites.ts +179 -0
  83. package/src/decide.ts +235 -0
  84. package/src/dispatch/operations-registry.ts +183 -8
  85. package/src/doctor.ts +9 -0
  86. package/src/evidence-atom-schema.ts +3 -0
  87. package/src/exit-codes.ts +7 -0
  88. package/src/index.ts +72 -0
  89. package/src/knowledge-health.ts +22 -0
  90. package/src/nexus-account.ts +181 -0
  91. package/src/operations/admin.ts +5 -0
  92. package/src/operations/focus.ts +7 -3
  93. package/src/operations/nexus-scope-map.ts +9 -0
  94. package/src/operations/nexus.ts +281 -0
  95. package/src/operations/orchestrate.ts +6 -2
  96. package/src/operations/output-contracts-data.ts +125 -1
  97. package/src/operations/tasks.ts +104 -12
  98. package/src/project-lifecycle.ts +6 -2
  99. package/src/session.ts +20 -2
  100. package/src/task.ts +51 -1
  101. package/src/tools/index.ts +1 -0
  102. package/src/tools/score-task-priority.ts +29 -3
@@ -25,8 +25,13 @@ import type { TaskAnalysisResult, TaskRef } from '../results.js';
25
25
  * Common task types (API contract — matches CLI src/types/task.ts)
26
26
  */
27
27
  import type { TaskStatus } from '../status-registry.js';
28
- import type { TaskKind, TaskPriority, TaskSeverity, TaskType } from '../task.js';
29
- import type { MinimalTaskRecord, TaskMatch, TaskRecord } from '../task-record.js';
28
+ import type { TaskClaim, TaskKind, TaskPriority, TaskSeverity, TaskType } from '../task.js';
29
+ import type {
30
+ MinimalTaskRecord,
31
+ RecordProjectionDisclosure,
32
+ TaskMatch,
33
+ TaskRecord,
34
+ } from '../task-record.js';
30
35
  import type { ExternalTask, ExternalTaskLink, ReconcileResult } from '../task-sync.js';
31
36
  import type {
32
37
  CompletionEvaluateParams,
@@ -285,10 +290,16 @@ export interface TaskShowRelationsEntry {
285
290
  * Result of `tasks.show` — the full task record plus its canonical view
286
291
  * projection. `view` is null when the task has no lifecycle pipeline.
287
292
  *
293
+ * The inherited `_withheld` marker is set by the default (MVI) projection
294
+ * when it withholds a field from every `acRows` element: the key
295
+ * `acRows/*\/id` names the row UUIDs and its value is their UTF-8 byte total
296
+ * (T12523). `--full` returns the rows unprojected, with no marker.
297
+ *
288
298
  * @task T1703
289
299
  * @task T9966 — attachments[] always present (empty array when none)
300
+ * @task T12523 — `_withheld` for the projected `acRows`
290
301
  */
291
- export interface TasksShowResult {
302
+ export interface TasksShowResult extends RecordProjectionDisclosure {
292
303
  /** Full task record (string-widened for dispatch layer serialization). */
293
304
  task: TaskRecord;
294
305
  /** Canonical task view projection produced by `computeTaskView`. Null when unavailable. */
@@ -307,6 +318,8 @@ export interface TasksShowResult {
307
318
  * Acceptance-criterion rows hydrated from the `task_acceptance_criteria`
308
319
  * table (T10502). Each entry carries the stable UUID `id`, the
309
320
  * `AC<ordinal>` alias, the ordinal itself, and the canonical AC text.
321
+ * The default (MVI) projection withholds `id` and says so in this result's
322
+ * `_withheld` (`acRows/*\/id`); `--full` returns it (T12523).
310
323
  *
311
324
  * Optional — undefined when the task has no rows in the table (e.g.
312
325
  * legacy tasks not yet backfilled by T10505). Consumers should fall
@@ -329,8 +342,14 @@ export interface TasksShowResult {
329
342
  * @task T10508
330
343
  */
331
344
  export interface TaskShowAcRowEntry {
332
- /** UUIDv4 stable identifier, immutable for the AC's lifetime. */
333
- id: string;
345
+ /**
346
+ * UUIDv4 stable identifier, immutable for the AC's lifetime.
347
+ *
348
+ * Absent under the default (MVI) projection, which cites a criterion by its
349
+ * `alias` and lists `acRows/*\/id` in the result's `_withheld`; present with
350
+ * `--full` or a `--field /data/acRows/<i>/id` pointer (T12523).
351
+ */
352
+ id?: string;
334
353
  /** Display alias derived from ordinal — `AC1`, `AC2`, etc. */
335
354
  alias: string;
336
355
  /** 1-based ordinal — never reused per task (gaps remain on shrink). */
@@ -921,6 +940,31 @@ export interface TasksComplexityEstimateResult {
921
940
  export interface TasksHistoryParams {
922
941
  taskId?: string;
923
942
  limit?: number;
943
+ /**
944
+ * With `taskId`: only ranking-input changes (priority, severity, kind,
945
+ * depends) — who, which session, why, before/after (T12693, D11161).
946
+ */
947
+ ranking?: boolean;
948
+ }
949
+
950
+ // tasks.ranking.revert
951
+ /** Parameters for `tasks.ranking.revert` (T12693, D11161). */
952
+ export interface TasksRankingRevertParams {
953
+ /** The `ranking_changed` audit row id to undo (from `cleo history ranking`). */
954
+ entryId: string;
955
+ /** Why it is reverted; recorded on the new audit row. */
956
+ reason?: string;
957
+ /** Revert even though a field changed again since. */
958
+ force?: boolean;
959
+ }
960
+ /** Result of `tasks.ranking.revert`. */
961
+ export interface TasksRankingRevertResult {
962
+ /** Task whose ranking inputs were restored. */
963
+ taskId: string;
964
+ /** Fields set back. */
965
+ fields: string[];
966
+ /** The values they were set back to. */
967
+ revertedTo: Record<string, unknown>;
924
968
  }
925
969
  /**
926
970
  * Result of `tasks.history` — audit log entries for a task.
@@ -1655,6 +1699,13 @@ export interface TasksCompleteQueryParams {
1655
1699
  * @task T11954 (DHQ-071)
1656
1700
  */
1657
1701
  waiveDependsReason?: string;
1702
+ /**
1703
+ * Optimistic-concurrency guard (`--if-match`): the task `updatedAt` the
1704
+ * caller read. When the stored version differs, completion fails with
1705
+ * `E_CONFLICT` carrying the current version and the changed fields.
1706
+ * @task T12503
1707
+ */
1708
+ expectedUpdatedAt?: string;
1658
1709
  }
1659
1710
  /**
1660
1711
  * Result of `tasks.complete` — completion confirmation with unblocked tasks.
@@ -1799,51 +1850,91 @@ export interface TasksArchiveQueryResult {
1799
1850
  archivedTasks: Array<{ id: string }>;
1800
1851
  }
1801
1852
 
1802
- // tasks.claim
1853
+ /**
1854
+ * Params for `tasks.claim` — take, renew or override the caller session's
1855
+ * leased claim on a task (T12502). The human `assignee` is never written.
1856
+ *
1857
+ * @task T1703
1858
+ * @task T12502
1859
+ */
1803
1860
  export interface TasksClaimParams {
1861
+ /** Task to claim. */
1804
1862
  taskId: string;
1805
- agentId: string;
1863
+ /** Agent identity recorded with the lease; defaults to the session's agent / `CLEO_AGENT_ID`. */
1864
+ agentId?: string;
1865
+ /** Renew the caller's own lease instead of taking one (`--renew`). */
1866
+ renew?: boolean;
1867
+ /** Take over an EXPIRED lease held by another session (`--take-over`, audited). */
1868
+ takeOver?: boolean;
1869
+ /** Take over a LIVE lease held by another session (`--force-claim`, audited). */
1870
+ forceClaim?: boolean;
1806
1871
  }
1807
1872
  /**
1808
- * Result of `tasks.claim` — agent claim confirmation.
1873
+ * Result of `tasks.claim` — the lease now held.
1809
1874
  *
1810
1875
  * @task T1703
1876
+ * @task T12502
1811
1877
  */
1812
1878
  export interface TasksClaimResult {
1813
1879
  /** The task ID that was claimed. */
1814
1880
  taskId: string;
1815
- /** The agent ID that now holds the claim. */
1816
- agentId: string;
1881
+ /** The agent ID recorded with the lease, or `null`. */
1882
+ agentId: string | null;
1883
+ /** The lease held by the caller's session. */
1884
+ claim: TaskClaim;
1817
1885
  }
1818
1886
 
1819
- // tasks.unclaim
1887
+ /**
1888
+ * Params for `tasks.unclaim` — release the caller session's lease (T12502).
1889
+ *
1890
+ * @task T12502
1891
+ */
1820
1892
  export interface TasksUnclaimParams {
1893
+ /** Task to release. */
1821
1894
  taskId: string;
1895
+ /** Release a lease held by ANOTHER session (`--force-claim`, audited). */
1896
+ forceClaim?: boolean;
1822
1897
  }
1823
1898
  /**
1824
1899
  * Result of `tasks.unclaim` — agent release confirmation.
1825
1900
  *
1826
1901
  * @task T1703
1902
+ * @task T12502
1827
1903
  */
1828
1904
  export interface TasksUnclaimResult {
1829
1905
  /** The task ID whose claim was released. */
1830
1906
  taskId: string;
1907
+ /** `true` when a lease was cleared; `false` when the task was unclaimed. */
1908
+ released: boolean;
1831
1909
  }
1832
1910
 
1833
- // tasks.start (dispatch-level)
1911
+ /**
1912
+ * Params for `tasks.start`. T12502: the start takes the caller session's
1913
+ * claim lease; the flags are the explicit, audited overrides.
1914
+ *
1915
+ * @task T12502
1916
+ */
1834
1917
  export interface TasksStartQueryParams {
1918
+ /** Task to start. */
1835
1919
  taskId: string;
1920
+ /** Take over an EXPIRED lease held by another session (`--take-over`). */
1921
+ takeOver?: boolean;
1922
+ /** Take over a LIVE lease held by another session (`--force-claim`). */
1923
+ forceClaim?: boolean;
1836
1924
  }
1837
1925
  /**
1838
1926
  * Result of `tasks.start` — work-start confirmation.
1839
1927
  *
1840
1928
  * @task T1703
1929
+ * @task T12502
1841
1930
  */
1842
1931
  export interface TasksStartQueryResult {
1843
1932
  /** The task ID that is now active. */
1844
1933
  taskId: string;
1845
1934
  /** The task ID that was previously active (auto-stopped), or null. */
1846
1935
  previousTask: string | null;
1936
+ /** The claim lease this start holds, or `null` for an unbound caller (no lease). */
1937
+ claim?: TaskClaim | null;
1847
1938
  }
1848
1939
 
1849
1940
  // tasks.stop (dispatch-level)
@@ -2277,6 +2368,7 @@ export type TasksOps = {
2277
2368
  readonly 'relates.add': readonly [TasksRelatesAddParams, TasksRelatesAddResult];
2278
2369
  readonly 'relates.add-batch': readonly [TasksRelatesAddBatchParams, TasksRelatesAddBatchResult];
2279
2370
  readonly 'relates.remove': readonly [TasksRelatesRemoveParams, TasksRelatesRemoveResult];
2371
+ readonly 'ranking.revert': readonly [TasksRankingRevertParams, TasksRankingRevertResult];
2280
2372
  readonly start: readonly [TasksStartQueryParams, TasksStartQueryResult];
2281
2373
  readonly stop: readonly [TasksStopQueryParams, TasksStopQueryResult];
2282
2374
  readonly 'sync.reconcile': readonly [TasksSyncReconcileParams, TasksSyncReconcileResult];
@@ -124,8 +124,12 @@ export interface RerootProjectResult {
124
124
  checkpointPath: string;
125
125
  /** Top-level entries renamed from the old root into the new one. */
126
126
  renamed: string[];
127
- /** Whether `.cleo/project-id` was already present or had to be written. */
128
- projectIdFile: 'present' | 'written';
127
+ /**
128
+ * Whether the tracked identity was already present (`.cleo/project.json`),
129
+ * present only as the legacy `.cleo/project-id` (`legacy`, T12716), or had
130
+ * to be written.
131
+ */
132
+ projectIdFile: 'present' | 'written' | 'legacy';
129
133
  /** Absolute path of the tombstone left at the old root. */
130
134
  tombstone: string;
131
135
  /** Registry status after the rebind. */
package/src/session.ts CHANGED
@@ -132,6 +132,17 @@ export interface Session {
132
132
  * @defaultValue undefined
133
133
  */
134
134
  parentSessionId?: string | null;
135
+ /**
136
+ * ID of the orchestrator session that SPAWNED this one, recorded by the
137
+ * spawn itself (T12502). Unlike {@link Session.parentSessionId}, which a
138
+ * process declares for itself at `session start`, only the spawn writes
139
+ * this edge, so the task-claim chokepoint trusts it to let a spawn family
140
+ * pass a live lease between its members. Read-only on the session record:
141
+ * the whole-row session upsert never writes it.
142
+ *
143
+ * @defaultValue undefined
144
+ */
145
+ spawnedBySessionId?: string | null;
135
146
  /** Provider-specific agent identifier string. @defaultValue undefined */
136
147
  agentIdentifier?: string | null;
137
148
  /** ISO 8601 timestamp of when the handoff was consumed. @defaultValue undefined */
@@ -233,9 +244,16 @@ export class SessionView {
233
244
  return this._sessions.length;
234
245
  }
235
246
 
236
- /** Find the currently active session (if any). */
247
+ /**
248
+ * First `active` session in THIS collection — a scan over the rows the
249
+ * caller loaded, NOT the caller's own session (T12500). With several agents
250
+ * active it names an arbitrary one of them.
251
+ *
252
+ * @deprecated For "my session" use `resolveBoundSession` (mutations) or
253
+ * `resolveSessionForRead` (reads, labels an unbound guess).
254
+ */
237
255
  findActive(): Session | undefined {
238
- return this._sessions.find((s) => s.status === 'active');
256
+ return this._sessions.find((s) => s.status === 'active'); // get-active-session-allowed: collection scan over caller-supplied rows, not identity (T12500)
239
257
  }
240
258
 
241
259
  /** Find a session by ID. */
package/src/task.ts CHANGED
@@ -187,6 +187,14 @@ export interface CriterionEvidenceLink {
187
187
  artifactPaths: string[];
188
188
  /** Indices of actual validated result atoms in the containing gate's atoms array. */
189
189
  resultAtomIndices: number[];
190
+ /**
191
+ * How the `implemented` link is supported (T12689): `files` when a path the
192
+ * criterion names is among the inspected artifacts; `self-attested` when the
193
+ * criterion names no path in them, so the link rests on the agent's claim.
194
+ * Paths parsed from criterion text are advisory (T12118), so this flags
195
+ * rather than refuses. Absent on other gates and on older receipts.
196
+ */
197
+ basis?: 'files' | 'self-attested';
190
198
  }
191
199
 
192
200
  /** Verification failure log entry. */
@@ -390,6 +398,12 @@ export type EvidenceAtom =
390
398
  * (T12671): the task is linked through it and its files are the change.
391
399
  */
392
400
  componentPrNumber?: number;
401
+ /**
402
+ * Paths the (component) PR deleted that stay deleted in the landing
403
+ * commit (T12689). A deletion has no bytes to hash, so a PR whose every
404
+ * change is a deletion implements with `pr:` + `note:` instead of `files:`.
405
+ */
406
+ deletedPaths?: string[];
393
407
  }
394
408
  | {
395
409
  /**
@@ -709,9 +723,22 @@ export interface Task {
709
723
  */
710
724
  pipelineStage?: string | null;
711
725
 
712
- /** Agent ID that has claimed/is assigned to this task. Null when unclaimed. @defaultValue undefined */
726
+ /**
727
+ * First-class human / owner assignee (`cleo assignee`, Studio boards).
728
+ * Separate from the agent work lease in {@link Task.claim}: `cleo start`,
729
+ * `cleo claim` and spawn never write it (T12502). @defaultValue undefined
730
+ */
713
731
  assignee?: string | null;
714
732
 
733
+ /**
734
+ * The agent work lease held on this task, when one is recorded (T12502).
735
+ * Absent when the task is unclaimed. Distinct from the human
736
+ * {@link Task.assignee}. Read-only on the task object: full-row writes
737
+ * never carry it, only the claim chokepoint changes it.
738
+ * @defaultValue undefined
739
+ */
740
+ claim?: TaskClaim;
741
+
715
742
  /**
716
743
  * Abort reason when a worker was stopped due to runaway detection (T1658).
717
744
  *
@@ -1006,3 +1033,26 @@ export interface TaskWorkState {
1006
1033
  /** ID of the primary session managing this work state. @defaultValue undefined */
1007
1034
  primarySession?: string | null;
1008
1035
  }
1036
+
1037
+ /**
1038
+ * A leased agent claim on a task (T12502 · epic T12497).
1039
+ *
1040
+ * Taken by `cleo start`, `cleo claim` and spawn with a compare-and-set in the
1041
+ * same write transaction; renewed by the holder session's activity or
1042
+ * `cleo claim <id> --renew`; released by `cleo stop`, completion,
1043
+ * cancellation, archive and the holder session ending. A claim whose
1044
+ * `leaseExpiresAt` has passed stays recorded until another session takes it
1045
+ * over explicitly (`--take-over`, audited).
1046
+ *
1047
+ * @task T12502
1048
+ */
1049
+ export interface TaskClaim {
1050
+ /** Session holding the lease. */
1051
+ sessionId: string;
1052
+ /** Agent identity of the holder (`CLEO_AGENT_ID` / spawn handle), or `null`. */
1053
+ agentId: string | null;
1054
+ /** ISO-8601 UTC instant the lease was taken. */
1055
+ claimedAt: string;
1056
+ /** ISO-8601 UTC instant the lease lapses unless renewed. */
1057
+ leaseExpiresAt: string;
1058
+ }
@@ -63,6 +63,7 @@ export type {
63
63
  ScoreFactor,
64
64
  ScoreTaskContext,
65
65
  ScoreTaskInput,
66
+ ScoreTaskKey,
66
67
  ScoreTaskResult,
67
68
  } from './score-task-priority.js';
68
69
  // === Web + browser agent tools (T1742 / T11456) ===
@@ -6,7 +6,7 @@
6
6
  * @epic T9835
7
7
  */
8
8
 
9
- import type { TaskPriority } from '../task.js';
9
+ import type { TaskKind, TaskPriority, TaskSeverity } from '../task.js';
10
10
 
11
11
  /** Minimal task shape required by scoreTask. */
12
12
  export interface ScoreTaskInput {
@@ -24,6 +24,10 @@ export interface ScoreTaskInput {
24
24
  createdAt?: string;
25
25
  /** Labels for pattern matching. */
26
26
  labels?: string[];
27
+ /** Severity axis (orthogonal to priority); unset for most tasks. */
28
+ severity?: TaskSeverity | null;
29
+ /** Kind axis; not part of the order — an unset severity is unknown, never imputed from `kind` (D11161). */
30
+ kind?: TaskKind | null;
27
31
  }
28
32
 
29
33
  /** Context provided to scoreTask to enable phase-aware and dependency-aware scoring. */
@@ -38,6 +42,8 @@ export interface ScoreTaskContext {
38
42
  successPatterns?: Array<{ pattern: string }>;
39
43
  /** Matched failure patterns from BRAIN (optional penalty scoring). */
40
44
  failurePatterns?: Array<{ pattern: string }>;
45
+ /** Open tasks that depend on each task id — its leverage (bounded bonus, T12661). */
46
+ leverage?: ReadonlyMap<string, number>;
41
47
  }
42
48
 
43
49
  /** A scoring factor contributing to the final score. */
@@ -48,12 +54,32 @@ export interface ScoreFactor {
48
54
  delta: number;
49
55
  /** Human-readable explanation. */
50
56
  detail: string;
57
+ /**
58
+ * Comparator tier the factor belongs to (T12691): 1 priority band, 2 severity,
59
+ * 3 bounded tiebreak; `null` for an informational factor outside the order.
60
+ */
61
+ tier?: 1 | 2 | 3 | null;
62
+ }
63
+
64
+ /** The lexicographic sort key of a scored task (T12691); higher sorts first. */
65
+ export interface ScoreTaskKey {
66
+ /** Owner priority band: critical 4, high 3, medium 2, low 1. */
67
+ band: number;
68
+ /** Attested severity: P0 4, P1 3, P2 2, P3 1, unknown 0. */
69
+ severity: number;
70
+ /** Bounded computed tiebreak (deps, phase, leverage, age). */
71
+ tiebreak: number;
51
72
  }
52
73
 
53
74
  /** Result of scoreTask. */
54
75
  export interface ScoreTaskResult {
55
- /** Final computed score. */
76
+ /**
77
+ * The key as one number that sorts identically (band × 10000 + severity ×
78
+ * 1000 + tiebreak), for display and single-number consumers.
79
+ */
56
80
  score: number;
57
- /** Individual scoring factors. */
81
+ /** Individual scoring factors, each tagged with its comparator tier. */
58
82
  factors: ScoreFactor[];
83
+ /** The lexicographic sort key (T12691). */
84
+ key?: ScoreTaskKey;
59
85
  }