@knpkv/jira-clockify 1.4.0 → 1.5.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.
Files changed (84) hide show
  1. package/README.md +72 -18
  2. package/dist/package.json +1 -1
  3. package/dist/src/agent/schedule.d.ts +6 -5
  4. package/dist/src/agent/schedule.d.ts.map +1 -1
  5. package/dist/src/agent/schedule.js +117 -102
  6. package/dist/src/agent/schedule.js.map +1 -1
  7. package/dist/src/agent/sessions.d.ts +27 -33
  8. package/dist/src/agent/sessions.d.ts.map +1 -1
  9. package/dist/src/agent/sessions.js +121 -83
  10. package/dist/src/agent/sessions.js.map +1 -1
  11. package/dist/src/agent/writePlanning.d.ts +2 -0
  12. package/dist/src/agent/writePlanning.d.ts.map +1 -1
  13. package/dist/src/agent/writePlanning.js +4 -1
  14. package/dist/src/agent/writePlanning.js.map +1 -1
  15. package/dist/src/cli/agentWrite.d.ts.map +1 -1
  16. package/dist/src/cli/agentWrite.js +12 -5
  17. package/dist/src/cli/agentWrite.js.map +1 -1
  18. package/dist/src/cli/auth.d.ts +39 -2
  19. package/dist/src/cli/auth.d.ts.map +1 -1
  20. package/dist/src/cli/auth.js +29 -5
  21. package/dist/src/cli/auth.js.map +1 -1
  22. package/dist/src/cli/config.d.ts +2 -2
  23. package/dist/src/cli/config.d.ts.map +1 -1
  24. package/dist/src/cli/config.js +34 -6
  25. package/dist/src/cli/config.js.map +1 -1
  26. package/dist/src/cli/layers.d.ts.map +1 -1
  27. package/dist/src/cli/layers.js +2 -2
  28. package/dist/src/cli/layers.js.map +1 -1
  29. package/dist/src/cli/reconcile.d.ts +6 -0
  30. package/dist/src/cli/reconcile.d.ts.map +1 -1
  31. package/dist/src/cli/reconcile.js +2 -0
  32. package/dist/src/cli/reconcile.js.map +1 -1
  33. package/dist/src/cli/setup.d.ts +1 -1
  34. package/dist/src/cli/setup.d.ts.map +1 -1
  35. package/dist/src/services/AgentSessionReader.d.ts +26 -12
  36. package/dist/src/services/AgentSessionReader.d.ts.map +1 -1
  37. package/dist/src/services/AgentSessionReader.js +163 -69
  38. package/dist/src/services/AgentSessionReader.js.map +1 -1
  39. package/dist/src/services/CodexTranscript.d.ts +50 -13
  40. package/dist/src/services/CodexTranscript.d.ts.map +1 -1
  41. package/dist/src/services/CodexTranscript.js +49 -13
  42. package/dist/src/services/CodexTranscript.js.map +1 -1
  43. package/dist/src/services/ConfigService.d.ts +2 -0
  44. package/dist/src/services/ConfigService.d.ts.map +1 -1
  45. package/dist/src/services/ConfigService.js +4 -1
  46. package/dist/src/services/ConfigService.js.map +1 -1
  47. package/dist/src/services/ProviderSnapshots.d.ts +41 -0
  48. package/dist/src/services/ProviderSnapshots.d.ts.map +1 -0
  49. package/dist/src/services/ProviderSnapshots.js +81 -0
  50. package/dist/src/services/ProviderSnapshots.js.map +1 -0
  51. package/dist/src/services/ReconcileService.d.ts +11 -3
  52. package/dist/src/services/ReconcileService.d.ts.map +1 -1
  53. package/dist/src/services/ReconcileService.js +212 -109
  54. package/dist/src/services/ReconcileService.js.map +1 -1
  55. package/dist/src/services/SavedEntries.d.ts +24 -4
  56. package/dist/src/services/SavedEntries.d.ts.map +1 -1
  57. package/dist/src/services/SavedEntries.js +335 -19
  58. package/dist/src/services/SavedEntries.js.map +1 -1
  59. package/dist/src/services/SessionAttributor.d.ts +12 -0
  60. package/dist/src/services/SessionAttributor.d.ts.map +1 -1
  61. package/dist/src/services/SessionAttributor.js +13 -1
  62. package/dist/src/services/SessionAttributor.js.map +1 -1
  63. package/dist/src/services/SessionLine.d.ts +29 -0
  64. package/dist/src/services/SessionLine.d.ts.map +1 -0
  65. package/dist/src/services/SessionLine.js +8 -0
  66. package/dist/src/services/SessionLine.js.map +1 -0
  67. package/dist/src/services/SourceLedger.d.ts +84 -1
  68. package/dist/src/services/SourceLedger.d.ts.map +1 -1
  69. package/dist/src/services/SourceLedger.js +178 -12
  70. package/dist/src/services/SourceLedger.js.map +1 -1
  71. package/dist/src/services/TicketRanking.d.ts +39 -0
  72. package/dist/src/services/TicketRanking.d.ts.map +1 -0
  73. package/dist/src/services/TicketRanking.js +87 -0
  74. package/dist/src/services/TicketRanking.js.map +1 -0
  75. package/dist/src/services/internal/retryTransport.d.ts +9 -0
  76. package/dist/src/services/internal/retryTransport.d.ts.map +1 -0
  77. package/dist/src/services/internal/retryTransport.js +15 -0
  78. package/dist/src/services/internal/retryTransport.js.map +1 -0
  79. package/dist/src/testing/fakeHeadless.d.ts +11 -0
  80. package/dist/src/testing/fakeHeadless.d.ts.map +1 -1
  81. package/dist/src/testing/fakeHeadless.js +66 -3
  82. package/dist/src/testing/fakeHeadless.js.map +1 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +7 -7
package/README.md CHANGED
@@ -119,20 +119,33 @@ Codex rollouts live under `~/.codex/sessions` in creation-date directories. Thos
119
119
  identify the working project, so JCF decodes their metadata locally before applying session roots
120
120
  to each stretch of work. Resumed rollouts in older directories remain eligible for the current
121
121
  week. Large tool payloads are discarded while streaming the file. Current `UserMessage` events
122
- and older `user_message` events count as presence; injected instructions, response-item prompt
123
- copies, compacted history and native subagent rollouts do not. Selecting Claude or Codex as the
124
- matching agent does not restrict which of these two transcript sources is scanned.
125
-
126
- Only messages _you typed_ count as presence — the agent's own output, its tool results, and the
127
- prompts it sends its own subagents do not, since they show it was busy rather than that you were
128
- working. On one real day that was 66 events out
129
- of 1641, and the difference between 1h 53m and 3h 51m. A session counts as active between its own
130
- consecutive prompts, gaps longer than the idle cap are
122
+ and older `user_message` events open a turn; completed items continue it and `task_complete` ends
123
+ it. Injected instructions, response-item prompt copies, compacted history and native subagent
124
+ rollouts establish nothing. Selecting Claude or Codex as the matching agent does not restrict which
125
+ of these two transcript sources is scanned.
126
+
127
+ Presence is a _supervised turn_. A message you typed — including one typed while the agent was busy,
128
+ which Claude stores as a queued command — opens a turn, and the agent's work inside it counts until
129
+ the agent ends the turn, a task notification or auto-continuation takes over, or the agent is silent
130
+ for longer than the idle cap. Agent output outside a turn you started never counts: a task
131
+ notification, an expanded skill (`isMeta`) or a subagent's own prompt cannot open one, since they
132
+ show the agent was busy rather than that you were working. Gaps longer than the idle cap are
131
133
  credited to nothing, and any moment you were working on several tickets at once is divided equally
132
- between them — so a day's proposals can never add up to more than that day's wall clock. Time
133
- spent reading and thinking between prompts is therefore under-counted rather than over-counted; a day
134
+ between them — so a day's proposals can never add up to more than that day's wall clock. A day
134
135
  with a timer still running is reported but never proposed.
135
136
 
137
+ When parallel tickets compete for the same minutes, tickets in your open sprint come first, then
138
+ tickets you have not logged that day, then the strength of their evidence. Key-only JQL searches —
139
+ one for the open sprint, one per day for logged tickets — supply those facts, which also appear beside candidates in the matching prompt as tie-breakers.
140
+ When Jira cannot answer, every ticket ranks equally. Inside one unbroken stretch each ticket gets a
141
+ single block, ordered by when it first appeared and packed back to back; a ticket that cannot reach
142
+ a minute folds into the one ranked above it. Work an orchestrator agent prompts into a worker pane
143
+ counts like your own typing. An orchestrating session that no branch, path or confident agent answer
144
+ places is split across the open-sprint tickets assigned to you that it mentions, weighted by mentions
145
+ and computed per active stretch, so settled time never moves when the session later mentions more.
146
+ Splitting needs a verified Jira answer about the sprint; without one the session stays unplaced.
147
+ `jcf watch` reads deterministically and never splits: such a session stays unplaced there.
148
+
136
149
  Everything you need to judge a row is on the row itself, in the picker: when the work item started
137
150
  and ended, the issue summary, its assignee, the attribution signal, what each side already holds, and
138
151
  how many blocks the total spans. So the number is checkable against your memory of the day rather
@@ -175,12 +188,13 @@ edits, including removal of the source suffix, do not reset its consumed seconds
175
188
  read still observes genuine duration changes and deletions. The suffix can import a legacy entry on
176
189
  first read, but text alone does not override an existing ID binding.
177
190
 
178
- The same private file now stores version 3: it also remembers the IDs and original starts of ordinary
191
+ The same private file now stores version 5: it also remembers the IDs and original starts of ordinary
179
192
  entries seen in a newly reviewed forward interval, separately by provider and account. Repeated or
180
193
  overlapping review can recognize those entries. A new unmarked entry in older coverage or a changed
181
194
  start for a remembered ID needs manual review; the tool does not infer its origin from a timestamp.
182
- Versions 1 and 2 load without inventing ordinary entries and upgrade only on a successful atomic
183
- write. New Clockify evidence is scoped to the configured endpoint, workspace, and user verified by
195
+ Versions 1 through 4 upgrade only on a successful atomic write. Versions 1 and 2 load without
196
+ inventing ordinary entries; earlier versions never invent replacement intents or Jira creation
197
+ checkpoints. New Clockify evidence is scoped to the configured endpoint, workspace, and user verified by
184
198
  that credential. Older Clockify scopes cannot prove the endpoint: affected windows, observations,
185
199
  pending intents, and bindings remain intact and held for private manual review. Neither switching
186
200
  the endpoint nor editing a description turns them into a fresh empty account. Jira evidence remains
@@ -328,14 +342,37 @@ an explicit full read collects new session evidence.
328
342
  ### Ticket overlap and live matching
329
343
 
330
344
  With the default 900-second dwell floor, overlapping attributed work is allocated
331
- in sequential blocks of at least fifteen minutes when the stretch permits it.
332
- Tickets share the stretch evenly; if fewer slots fit, the strongest credited
333
- tickets retain them. Mixed stretches reserve unplaced credit before allocating known tickets. Separate
345
+ in sequential blocks across every evidenced ticket. Each ticket receives a writable
346
+ minute when the stretch permits it. Open-sprint tickets assigned to you rank first,
347
+ then tickets not yet logged that day, then evidence weight. Shorter shares remain
348
+ credited; the scheduler never invents minutes. Mixed stretches reserve unplaced credit before allocating known tickets. Separate
334
349
  work, idle gaps, wholly unplaced stretches and midnight stay
335
350
  separate. Short standalone work is never inflated. Setting dwell to zero retains
336
351
  raw overlap sharing. Proposal blocks and confirmed amounts use the same timeline;
337
352
  `activeSeconds` retains the original activity duration.
338
353
 
354
+ ### Ignoring session tickets
355
+
356
+ ```sh
357
+ jcf config set session-ignore PROJ-42 # Ignore in every week
358
+ jcf config unset session-ignore PROJ-42 # Restore
359
+ jcf config # Lists ignored tickets
360
+ ```
361
+
362
+ `sessionIgnoredTickets` defaults to `[]` and persists validated issue keys. Ignored
363
+ branch, path or standing matches fall through to the attribution agent, whose
364
+ candidate list excludes ignored keys. An ignored key cannot become a final match.
365
+ If the agent chooses another evidenced key, that work can be proposed there.
366
+ Otherwise the ignored session's windows are removed before sharing: overlapping
367
+ tickets receive those minutes; solo time is not offered elsewhere.
368
+
369
+ Session reports and `--json` include `ignored` rows with `ticketKey`, `day` and
370
+ `seconds`. These are raw active windows unioned per ignored ticket/day, before
371
+ sharing, so simultaneous sessions on one key count once. They explain suppressed
372
+ evidence and must not be added to allocated totals. A fresh recorded read also
373
+ filters newly ignored tickets out of retained proposals; rescan sessions to
374
+ redistribute overlapping credit or restore an ignored ticket.
375
+
339
376
  `SessionAttributor.attribute` accepts an optional activity observer. Reconciliation
340
377
  forwards visible text and process status as `AgentActivity`, tagged with the batch
341
378
  number and batch count. JCF web streams these events during matching, while the CLI
@@ -354,7 +391,7 @@ totals; their days are excluded from proposals until the timer stops.
354
391
  `SavedEntries.layer` is included in `Headless.layer`. Its `update` operation takes a
355
392
  server-retained `RecordedEntry` snapshot plus new start/end milliseconds and description,
356
393
  rechecks provider ownership and the current snapshot, and returns the actual saved
357
- entry. `SavedEntryError.reason` distinguishes validation, conflict and provider failures.
394
+ entry. `SavedEntryError.reason` distinguishes validation, conflict, provider and partial failures.
358
395
  Recorded intervals carry optional whole-entry metadata, including exact original
359
396
  bounds and description, even when the calendar slices an entry at midnight.
360
397
 
@@ -369,6 +406,23 @@ media have no plain-text representation. Updates leave
369
406
  Jira estimates and visibility unchanged. The service serializes local updates, but the
370
407
  provider APIs offer no atomic protection from external edits between re-read and save.
371
408
 
409
+ Changing tickets creates a replacement before deleting the original. Before POST, the private
410
+ ledger stores a `Pending` replacement intent containing the provider/account scope, original entry
411
+ ID and ticket, and requested ticket, bounds and description. The returned replacement ID is persisted
412
+ before verification. A verified replacement retains the original session claim, or is remembered as
413
+ ordinary provider time, before DELETE. Deleting the original atomically releases its tracking and
414
+ clears the intent. The provider/account scope and intent stay in the owner-only ledger. Entry IDs,
415
+ ticket, bounds and description appear in the authenticated saved-entry snapshot; HTTP responses
416
+ never carry private provider account locators or credentials.
417
+
418
+ An interrupted or uncertain create stays held and is never repeated automatically. A partial move
419
+ holds session writes for that provider, including after restart. Refresh and inspect both entries;
420
+ if the replacement was verified, explicitly deleting either entry resolves the intent while keeping
421
+ the other entry's tracking. An unverified replacement, or a remote delete whose local completion
422
+ could not persist, requires private ledger recovery after checking the provider. Never clear an
423
+ intent just to retry POST. Ordinary replacements keep their ID and actual start, so extending a
424
+ reviewed window does not turn the tool's own move into an unknown-entry hold.
425
+
372
426
  Session reports optionally retain `sessionEvidence` with each session's ticket and
373
427
  individual active intervals. This lets web description requests correlate saved time
374
428
  without another scan. `SessionAttributor.describe` accepts a nullable ticket key for
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knpkv/jira-clockify",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "TUI for Jira-Clockify time tracking, attachable to neovim",
5
5
  "license": "MIT",
6
6
  "author": "knpkv",
@@ -2,6 +2,7 @@ interface Run {
2
2
  readonly startMs: number;
3
3
  readonly endMs: number;
4
4
  readonly bucketIds: ReadonlyArray<string>;
5
+ readonly weights?: ReadonlyMap<string, number> | undefined;
5
6
  }
6
7
  export interface ScheduledRun extends Run {
7
8
  /** Earliest source instant needed to reconstruct this allocation. */
@@ -10,11 +11,11 @@ export interface ScheduledRun extends Run {
10
11
  readonly settlementEndMs: number;
11
12
  }
12
13
  /**
13
- * Arrange a contiguous stretch into sequential allocations. Keep the strongest tickets when there
14
- * is not enough time for every ticket to receive the floor. Wholly unplaced time and midnight are barriers. Mixed stretches reserve their unplaced credit
15
- * before allocating known tickets; changing unplaced shares must not fragment every known ticket.
16
- * A genuinely shorter stretch stays short: allocation must never invent time or bridge idle gaps.
14
+ * Pack a contiguous stretch into one block per ticket, then its reserved unplaced credit.
15
+ * First activity orders the blocks; priority determines writable shares when time is scarce.
16
+ * Blocks may move anywhere inside their source cluster, but never invent time or bridge idle gaps.
17
+ * Sub-minute shares fold upward in rank; an entirely shorter stretch keeps its raw short evidence.
17
18
  */
18
- export declare const scheduleRuns: (runs: ReadonlyArray<Run>, minimumSeconds: number, attributed: (id: string) => boolean) => ReadonlyArray<ScheduledRun>;
19
+ export declare const scheduleRuns: (runs: ReadonlyArray<Run>, dwellSeconds: number, attributed: (id: string) => boolean, priority?: (id: string, day: string) => number) => ReadonlyArray<ScheduledRun>;
19
20
  export {};
20
21
  //# sourceMappingURL=schedule.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../../src/agent/schedule.ts"],"names":[],"mappings":"AAGA,UAAU,GAAG;IACX,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CAC1C;AAID,MAAM,WAAW,YAAa,SAAQ,GAAG;IACvC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SACjB,aAAa,CAAC,GAAG,CAAC,kBACR,MAAM,cACV,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,KAClC,aAAa,CAAC,YAAY,CA6K5B,CAAA"}
1
+ {"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../../src/agent/schedule.ts"],"names":[],"mappings":"AAIA,UAAU,GAAG;IACX,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;CAC3D;AAID,MAAM,WAAW,YAAa,SAAQ,GAAG;IACvC,qEAAqE;IACrE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CACjC;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SACjB,aAAa,CAAC,GAAG,CAAC,gBACV,MAAM,cACR,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,aACxB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,KAC7C,aAAa,CAAC,YAAY,CAoL5B,CAAA"}
@@ -1,15 +1,16 @@
1
1
  import { Data } from "effect";
2
2
  import { localDay } from "../utils/time.js";
3
+ import { MINIMUM_WRITE_SECONDS } from "./writePlanning.js";
3
4
  class ScheduleAllocationError extends Data.TaggedError("ScheduleAllocationError") {
4
5
  }
5
6
  /**
6
- * Arrange a contiguous stretch into sequential allocations. Keep the strongest tickets when there
7
- * is not enough time for every ticket to receive the floor. Wholly unplaced time and midnight are barriers. Mixed stretches reserve their unplaced credit
8
- * before allocating known tickets; changing unplaced shares must not fragment every known ticket.
9
- * A genuinely shorter stretch stays short: allocation must never invent time or bridge idle gaps.
7
+ * Pack a contiguous stretch into one block per ticket, then its reserved unplaced credit.
8
+ * First activity orders the blocks; priority determines writable shares when time is scarce.
9
+ * Blocks may move anywhere inside their source cluster, but never invent time or bridge idle gaps.
10
+ * Sub-minute shares fold upward in rank; an entirely shorter stretch keeps its raw short evidence.
10
11
  */
11
- export const scheduleRuns = (runs, minimumSeconds, attributed) => {
12
- if (minimumSeconds <= 0) {
12
+ export const scheduleRuns = (runs, dwellSeconds, attributed, priority) => {
13
+ if (dwellSeconds <= 0) {
13
14
  return runs.map((run) => ({ ...run, sourceStartMs: run.startMs, settlementEndMs: run.endMs }));
14
15
  }
15
16
  const settlementEndByRun = new Map();
@@ -38,25 +39,34 @@ export const scheduleRuns = (runs, minimumSeconds, attributed) => {
38
39
  const settlementEndMs = settlementEndByRun.get(first) ?? last.endMs;
39
40
  const weights = new Map();
40
41
  const active = new Map();
42
+ const firstActivity = new Map();
41
43
  for (const run of cluster) {
44
+ const totalWeight = run.bucketIds.reduce((sum, id) => sum + (run.weights?.get(id) ?? 1), 0);
42
45
  for (const id of run.bucketIds) {
43
- weights.set(id, (weights.get(id) ?? 0) + (run.endMs - run.startMs) / run.bucketIds.length);
46
+ const weight = (run.endMs - run.startMs) * (run.weights?.get(id) ?? 1) / totalWeight;
47
+ weights.set(id, (weights.get(id) ?? 0) + weight);
44
48
  active.set(id, (active.get(id) ?? 0) + run.endMs - run.startMs);
49
+ // Mention ratios may evolve while a group is live. Ordering uses its raw first presence,
50
+ // so later text cannot swap two same-start tickets after a restart.
51
+ if (!firstActivity.has(id)) {
52
+ firstActivity.set(id, { startMs: run.startMs, weight: (run.endMs - run.startMs) / run.bucketIds.length });
53
+ }
45
54
  }
46
55
  }
47
56
  const duration = Math.floor((last.endMs - first.startMs) / 1000);
48
57
  const unplaced = [...weights].filter(([id]) => !attributed(id));
49
58
  const reserved = Math.floor(unplaced.reduce((sum, [, weight]) => sum + weight, 0) / 1000);
50
59
  const available = duration - reserved;
51
- const limit = Math.max(1, Math.floor(available / minimumSeconds));
52
- const rankedOwners = [...weights].filter(([id]) => attributed(id))
60
+ const day = localDay(new Date(first.startMs));
61
+ const stableOwners = [...weights].filter(([id]) => attributed(id))
53
62
  .sort(([a, weightA], [b, weightB]) => weightB - weightA || a.localeCompare(b));
54
- const owners = rankedOwners.slice(0, limit);
63
+ const rankedOwners = [...stableOwners]
64
+ .sort(([a, weightA], [b, weightB]) => (priority?.(b, day) ?? 0) - (priority?.(a, day) ?? 0) || weightB - weightA || a.localeCompare(b));
55
65
  const allocations = new Map();
56
66
  let remaining = available;
57
- let remainingWeight = owners.reduce((sum, [, weight]) => sum + weight, 0);
58
- for (const [index, [id, weight]] of owners.entries()) {
59
- const proportional = index === owners.length - 1
67
+ let remainingWeight = stableOwners.reduce((sum, [, weight]) => sum + weight, 0);
68
+ for (const [index, [id, weight]] of stableOwners.entries()) {
69
+ const proportional = index === stableOwners.length - 1
60
70
  ? remaining
61
71
  : Math.floor(remaining * weight / remainingWeight);
62
72
  const seconds = Math.min(proportional, Math.floor((active.get(id) ?? 0) / 1000));
@@ -64,105 +74,110 @@ export const scheduleRuns = (runs, minimumSeconds, attributed) => {
64
74
  remaining -= seconds;
65
75
  remainingWeight -= weight;
66
76
  }
67
- // An activity cap can leave part of a proportional share unused. Offer that residual to every
68
- // attributable owner, including one omitted by the normal minimum-size limit, rather than
69
- // silently losing credited time. The residual itself may be shorter than the normal floor.
70
- for (const [id] of rankedOwners) {
71
- if (remaining <= 0)
72
- break;
73
- const allocated = allocations.get(id) ?? 0;
74
- const capacity = Math.floor((active.get(id) ?? 0) / 1000) - allocated;
75
- const seconds = Math.min(remaining, Math.max(0, capacity));
76
- allocations.set(id, allocated + seconds);
77
- remaining -= seconds;
78
- }
79
- const remainingMs = new Map([...allocations].map(([id, seconds]) => [id, seconds * 1000]));
80
- const futureActiveMs = new Map(active);
81
- const lastActiveMs = new Map();
82
- for (const run of cluster)
83
- for (const id of run.bucketIds)
84
- lastActiveMs.set(id, run.endMs);
85
- const placementOrder = rankedOwners.map(([id]) => id).sort((a, b) => (lastActiveMs.get(a) ?? 0) - (lastActiveMs.get(b) ?? 0));
86
- const gaps = [];
87
- for (const run of cluster) {
88
- const runMs = run.endMs - run.startMs;
89
- for (const id of run.bucketIds)
90
- futureActiveMs.set(id, (futureActiveMs.get(id) ?? 0) - runMs);
91
- const present = placementOrder.filter((id) => run.bucketIds.includes(id) && (remainingMs.get(id) ?? 0) > 0);
92
- let cursor = run.startMs;
93
- for (const id of present) {
94
- const reservedForOthers = present.reduce((sum, other) => other === id ?
95
- sum :
96
- sum + Math.max(0, (remainingMs.get(other) ?? 0) - (futureActiveMs.get(other) ?? 0)), 0);
97
- const placedMs = Math.min(remainingMs.get(id) ?? 0, Math.max(0, run.endMs - cursor - reservedForOthers));
98
- if (placedMs <= 0)
77
+ // Place credit inside its supporting runs. An augmenting path can move an existing owner's
78
+ // credit to another supporting run, freeing room without stealing that owner's allocation.
79
+ const placed = cluster.map(() => new Map());
80
+ const claimed = new Map();
81
+ const move = (id, wantedMs, visited) => {
82
+ let leftMs = wantedMs;
83
+ for (const [index, run] of cluster.entries()) {
84
+ if (leftMs <= 0)
85
+ break;
86
+ if (!run.bucketIds.includes(id) || visited.has(index))
99
87
  continue;
100
- result.push({
101
- bucketIds: [id],
102
- startMs: cursor,
103
- endMs: cursor + placedMs,
104
- sourceStartMs: first.startMs,
105
- settlementEndMs
106
- });
107
- remainingMs.set(id, (remainingMs.get(id) ?? 0) - placedMs);
108
- cursor += placedMs;
88
+ const held = placed[index];
89
+ if (held === undefined)
90
+ continue;
91
+ visited.add(index);
92
+ const freeMs = run.endMs - run.startMs - [...held.values()].reduce((sum, value) => sum + value, 0);
93
+ const directMs = Math.min(leftMs, freeMs);
94
+ held.set(id, (held.get(id) ?? 0) + directMs);
95
+ leftMs -= directMs;
96
+ for (const [donor, donorMs] of [...held]) {
97
+ if (leftMs <= 0)
98
+ break;
99
+ if (donor === id || donorMs <= 0)
100
+ continue;
101
+ const movedMs = move(donor, Math.min(leftMs, donorMs), visited);
102
+ held.set(donor, (held.get(donor) ?? 0) - movedMs);
103
+ held.set(id, (held.get(id) ?? 0) + movedMs);
104
+ leftMs -= movedMs;
105
+ }
109
106
  }
110
- if (cursor < run.endMs) {
111
- gaps.push({ startMs: cursor, endMs: run.endMs, bucketIds: run.bucketIds });
107
+ return wantedMs - leftMs;
108
+ };
109
+ let budgetMs = available * 1000;
110
+ const claim = (id, targetSeconds) => {
111
+ let wantedMs = Math.min(budgetMs, Math.max(0, targetSeconds * 1000 - (claimed.get(id) ?? 0)));
112
+ while (wantedMs > 0) {
113
+ const movedMs = move(id, wantedMs, new Set());
114
+ if (movedMs <= 0)
115
+ break;
116
+ claimed.set(id, (claimed.get(id) ?? 0) + movedMs);
117
+ budgetMs -= movedMs;
118
+ wantedMs -= movedMs;
112
119
  }
113
- }
114
- let gapIndex = 0;
115
- let gapCursor = gaps[0]?.startMs ?? last.endMs;
116
- const nextGap = () => {
117
- gapIndex++;
118
- gapCursor = gaps[gapIndex]?.startMs ?? last.endMs;
119
120
  };
120
- let offset = available;
121
- for (const [index, [id, weight]] of unplaced.entries()) {
122
- const seconds = index === unplaced.length - 1 ? duration - offset : Math.floor(weight / 1000);
123
- let milliseconds = seconds * 1000;
124
- while (milliseconds > 0) {
125
- const gap = gaps[gapIndex];
126
- if (gap === undefined)
127
- throw new ScheduleAllocationError({ message: "Unplaced credit exceeds source activity" });
128
- const placedMs = Math.min(milliseconds, gap.endMs - gapCursor);
129
- result.push({
130
- bucketIds: [id],
131
- startMs: gapCursor,
132
- endMs: gapCursor + placedMs,
133
- sourceStartMs: first.startMs,
134
- settlementEndMs
135
- });
136
- milliseconds -= placedMs;
137
- gapCursor += placedMs;
138
- if (gapCursor === gap.endMs)
139
- nextGap();
121
+ // Retain each owner's first second, then offer writable minutes in priority order. A floor is
122
+ // bounded by joint source availability, not just the owner's independent active-time cap.
123
+ for (const [id] of rankedOwners)
124
+ claim(id, 1);
125
+ for (const [id] of rankedOwners)
126
+ claim(id, MINIMUM_WRITE_SECONDS);
127
+ for (const [id] of rankedOwners)
128
+ claim(id, allocations.get(id) ?? 0);
129
+ for (const [id] of rankedOwners)
130
+ claim(id, Math.floor((active.get(id) ?? 0) / 1000));
131
+ if (budgetMs > 0) {
132
+ throw new ScheduleAllocationError({ message: "Attribution cannot fit inside its source activity" });
133
+ }
134
+ if (available >= MINIMUM_WRITE_SECONDS) {
135
+ // Folding from lowest rank upward can combine two short shares into another writable minute.
136
+ for (let index = rankedOwners.length - 1; index > 0; index--) {
137
+ const owner = rankedOwners[index];
138
+ const higher = rankedOwners[index - 1];
139
+ if (owner === undefined || higher === undefined)
140
+ continue;
141
+ const milliseconds = claimed.get(owner[0]) ?? 0;
142
+ if (milliseconds <= 0 || milliseconds >= MINIMUM_WRITE_SECONDS * 1000)
143
+ continue;
144
+ claimed.set(higher[0], (claimed.get(higher[0]) ?? 0) + milliseconds);
145
+ claimed.set(owner[0], 0);
146
+ }
147
+ const highest = rankedOwners[0]?.[0];
148
+ const highestMs = highest === undefined ? 0 : claimed.get(highest) ?? 0;
149
+ if (highest !== undefined && highestMs > 0 && highestMs < MINIMUM_WRITE_SECONDS * 1000) {
150
+ const writable = rankedOwners.find(([id]) => (claimed.get(id) ?? 0) >= MINIMUM_WRITE_SECONDS * 1000)?.[0];
151
+ if (writable !== undefined) {
152
+ claimed.set(writable, (claimed.get(writable) ?? 0) + highestMs);
153
+ claimed.set(highest, 0);
154
+ }
140
155
  }
141
- offset += seconds;
142
156
  }
143
- let residualMs = [...remainingMs.values()].reduce((sum, milliseconds) => sum + milliseconds, 0);
144
- for (let index = gapIndex; index < gaps.length && residualMs > 0; index++) {
145
- const gap = gaps[index];
146
- if (gap === undefined)
147
- continue;
148
- if (residualMs <= 0)
149
- break;
150
- const recipient = rankedOwners.find(([id]) => gap.bucketIds.includes(id))?.[0];
151
- if (recipient === undefined)
152
- continue;
153
- const startMs = index === gapIndex ? gapCursor : gap.startMs;
154
- const placedMs = Math.min(residualMs, gap.endMs - startMs);
157
+ // Later work may change total weights, but never the first-activity order. Logging can change
158
+ // advisory rank without moving an unchanged packed allocation and its consumed source identity.
159
+ const orderedOwners = [...stableOwners].sort(([a], [b]) => (firstActivity.get(a)?.startMs ?? 0) - (firstActivity.get(b)?.startMs ?? 0) ||
160
+ (firstActivity.get(b)?.weight ?? 0) - (firstActivity.get(a)?.weight ?? 0) || a.localeCompare(b));
161
+ let cursor = first.startMs;
162
+ const append = (id, milliseconds) => {
163
+ if (milliseconds <= 0)
164
+ return;
155
165
  result.push({
156
- bucketIds: [recipient],
157
- startMs,
158
- endMs: startMs + placedMs,
166
+ bucketIds: [id],
167
+ startMs: cursor,
168
+ endMs: cursor + milliseconds,
159
169
  sourceStartMs: first.startMs,
160
170
  settlementEndMs
161
171
  });
162
- residualMs -= placedMs;
163
- }
164
- if (residualMs > 0) {
165
- throw new ScheduleAllocationError({ message: "Attribution cannot fit inside its source activity" });
172
+ cursor += milliseconds;
173
+ };
174
+ for (const [id] of orderedOwners)
175
+ append(id, claimed.get(id) ?? 0);
176
+ let offset = available;
177
+ for (const [index, [id, weight]] of unplaced.entries()) {
178
+ const seconds = index === unplaced.length - 1 ? duration - offset : Math.floor(weight / 1000);
179
+ append(id, seconds * 1000);
180
+ offset += seconds;
166
181
  }
167
182
  cluster = [];
168
183
  };
@@ -1 +1 @@
1
- {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../../src/agent/schedule.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schedule.js","sourceRoot":"","sources":["../../../src/agent/schedule.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import * as SourceConsumption from "./sourceConsumption.js";
3
3
  * Which evidence placed a session on an Issue Key. `none` means no signal did — the session is
4
4
  * an Unattributed Session and is reported with its hours rather than guessed at.
5
5
  */
6
- export type AttributionSignal = "branch" | "path" | "standing" | "agent" | "none";
6
+ export type AttributionSignal = "branch" | "path" | "standing" | "agent" | "split" | "none";
7
7
  /**
8
8
  * Attribution Signals in precedence order — the first to yield an Issue Key wins. Also the
9
9
  * trust order: a bucket built from several sessions reports its *weakest* signal, because a row
@@ -19,6 +19,8 @@ export interface AttributableSession {
19
19
  readonly gitBranch: string | null;
20
20
  /** Issue Keys mined from the transcript — the closed choice set for a Coding Agent. */
21
21
  readonly candidateKeys: ReadonlyArray<string>;
22
+ /** Counts from the full in-scope transcript, before its bounded agent digest is constructed. */
23
+ readonly mentionCounts?: ReadonlyMap<string, number>;
22
24
  }
23
25
  /** How one Agent Session was placed (or not placed) on an Issue Key. */
24
26
  export interface SessionAttribution {
@@ -32,7 +34,16 @@ export interface SessionAttribution {
32
34
  * Such credit is reported so the hours stay visible, but is never offered for confirmation.
33
35
  */
34
36
  readonly belowConfidenceFloor: boolean;
37
+ /** Sprint candidates sharing this session, weighted by transcript mentions. Absent for single-ticket attribution. */
38
+ readonly targets?: ReadonlyArray<{
39
+ readonly ticketKey: string;
40
+ readonly weight: number;
41
+ }>;
35
42
  }
43
+ /** Count transcript mentions without deduplicating repeated ticket keys. */
44
+ export declare const ticketMentionCounts: (text: string) => ReadonlyMap<string, number>;
45
+ /** Split only unplaced or low-confidence sessions onto evidenced tickets in my known open sprint. */
46
+ export declare const splitSessionAttribution: (session: AttributableSession, attribution: SessionAttribution, sprint: ReadonlySet<string> | undefined, ignored: ReadonlySet<string>) => SessionAttribution;
36
47
  /**
37
48
  * True when a string is an Issue Key and nothing else.
38
49
  *
@@ -104,6 +115,7 @@ export interface DeterministicAttribution {
104
115
  */
105
116
  export declare const deterministicAttribution: (session: AttributableSession, options: {
106
117
  readonly standingMap: Readonly<Record<string, string>>;
118
+ readonly ignoredTickets?: ReadonlySet<string> | undefined;
107
119
  }) => DeterministicAttribution | null;
108
120
  /** What a Coding Agent decided about one session. `null` is a valid, expected answer. */
109
121
  export interface AgentChoice {
@@ -120,6 +132,7 @@ export declare const attributeSession: (session: AttributableSession, options: {
120
132
  /** Only consulted when no deterministic signal placed the session. */
121
133
  readonly agentChoice?: AgentChoice | null | undefined;
122
134
  readonly confidenceFloor: number;
135
+ readonly ignoredTickets?: ReadonlySet<string> | undefined;
123
136
  }) => SessionAttribution;
124
137
  /** One recorded moment of work inside an Agent Session. */
125
138
  export interface SessionActivity {
@@ -211,41 +224,16 @@ export interface OwnedRun {
211
224
  readonly startMs: number;
212
225
  readonly endMs: number;
213
226
  readonly bucketIds: ReadonlyArray<string>;
227
+ readonly weights?: ReadonlyMap<string, number> | undefined;
214
228
  }
215
229
  /**
216
- * Coalesce ownership so it changes no more often than the Dwell Floor.
230
+ * Coalesce rapid ownership changes without removing overlapping tickets.
217
231
  *
218
- * **What this is for.** Three concurrent sessions on three Issue Keys interleave their prompts, and
219
- * read literally that says the work changed ticket every few minutes. It did not — that is an
220
- * artefact of reading several transcripts at once. A day of it is two dozen slivers on a calendar and
221
- * indefensible on a timesheet.
222
- *
223
- * **Two rules, in order.**
224
- *
225
- * 1. *A ticket present for less than the floor inside one connected source cluster, interrupting work
226
- * that resumes after it, was not a ticket that was worked on.* It was a keystroke inside other work — a branch
227
- * checked, a file opened, a question asked — so its time goes to the work around it. This is what
228
- * actually removes the slivers: they rarely touch anything, because a transcript goes quiet
229
- * between prompts, and they are as often overlapping as adjacent, because concurrent sessions
230
- * overlap by definition. Adjacency and overlap were both tried as the test and both left the
231
- * interleaving as they found it. The requirement for work on *both* sides is what keeps a genuine
232
- * short piece of work — eight minutes on another ticket, and then the day moves on — from being
233
- * swallowed by what came before it.
234
- * 2. *Tenancy, not adjacency.* Between tickets that were genuinely worked on, whoever takes the
235
- * timeline holds it for at least the floor: a stretch beginning inside that tenure and owned by
236
- * someone else is credited to the incumbent. This catches two real tickets alternating quickly.
237
- *
238
- * **What it never does.** It reassigns time and never creates or drops any — idle gaps are not swept
239
- * into a tenure — so the inequality that makes a proposal safe to accept survives intact. Nothing is
240
- * ever welded across a local midnight, because runs are bucketed by the day they start in.
241
- *
242
- * **Unplaced hours take no part.** A stretch nothing placed neither holds a tenure nor loses its time
243
- * to one: promoting it would bill work no transcript placed on that ticket, and demoting an
244
- * attributed sliver into it would quietly discard billable work. An attributed tenure simply
245
- * continues across it.
246
- *
247
- * **A minor ticket with nothing to belong to keeps its own time.** Four minutes alone in an otherwise
248
- * empty day has no surrounding work to join, and dropping it would lose work that happened.
232
+ * A short sole-owner interruption returns to surrounding major work only when that work resumes
233
+ * in the same connected source cluster. Overlapping tickets keep their ownership regardless of
234
+ * their presence length. Tenancy holds between disjoint attributed owners for the dwell period.
235
+ * Unplaced time retains its own credit and cannot acquire or lose an attributed ticket's time.
236
+ * Idle gaps and local midnight are barriers; no coalescing creates additional wall-clock time.
249
237
  */
250
238
  export declare const applyDwellFloor: (slices: ReadonlyArray<OwnedRun>, options: {
251
239
  readonly dwellSeconds: number;
@@ -324,6 +312,10 @@ export declare const splitCredits: (windows: ReadonlyArray<SessionWindows>, attr
324
312
  * {@link DEFAULT_DWELL_SECONDS}; zero turns the rule off and reports the raw interleaving.
325
313
  */
326
314
  readonly dwellSeconds?: number | undefined;
315
+ /** Higher values receive writable minutes first when a shared stretch is too short. */
316
+ readonly priority?: ((ticketKey: string, day: string) => number) | undefined;
317
+ /** Remove these tickets' windows before sharing; overlapping tickets absorb them, never solo time. */
318
+ readonly ignoredTickets?: ReadonlySet<string> | undefined;
327
319
  }) => CreditSplit;
328
320
  /** What Clockify and Jira already hold for one `(Issue Key, day)` bucket. */
329
321
  export interface RecordedBucket {
@@ -346,6 +338,8 @@ export interface RecordedBucket {
346
338
  }
347
339
  /** A Proposed Worklog: the gap between what a session accounts for and what is already recorded. */
348
340
  export interface SessionProposal {
341
+ /** Provider-window review holds. Evidence remains readable; the named providers cannot write it. */
342
+ readonly writeBlocked?: Readonly<Partial<Record<SourceConsumption.Source, string>>> | undefined;
349
343
  readonly ticketKey: string;
350
344
  readonly day: string;
351
345
  readonly signal: AttributionSignal;