model-eol 0.7.0 → 0.8.1

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/lib/feeds.mjs CHANGED
@@ -195,6 +195,28 @@ export const lifecycleFor = (entry, options) => {
195
195
  }
196
196
  }
197
197
 
198
+ export const captureWindowFor = (entry, lifecycle, { today = new Date() } = {}) => {
199
+ if (!['retiring', 'retired', 'scheduled'].includes(lifecycle.status)) return null
200
+ const todayDate = today.toISOString().slice(0, 10)
201
+ const futureDate = shutdown => typeof shutdown === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(shutdown) && shutdown > todayDate
202
+ const publisherClock = lifecycle.via === 'publisher' || lifecycle.via === 'publisher-fallback'
203
+ const alternatives = (entry.distributions ?? [])
204
+ .filter(row => (publisherClock || row.via !== lifecycle.via) && row.status !== 'retired' && futureDate(row.shutdown))
205
+ .map(row => ({ via: row.via, until: row.shutdown, date_precision: row.date_precision ?? null, status: row.status ?? null }))
206
+ if (!publisherClock && futureDate(entry.shutdown)) {
207
+ alternatives.push({ via: 'publisher', until: entry.shutdown, date_precision: entry.date_precision ?? null, status: null })
208
+ }
209
+ const compare = (a, b) => a < b ? -1 : a > b ? 1 : 0
210
+ alternatives.sort((a, b) => compare(a.until, b.until) || compare(a.via, b.via))
211
+ const until = lifecycle.status === 'retired' ? null : lifecycle.shutdown
212
+ return {
213
+ until,
214
+ date_precision: until !== null ? lifecycle.date_precision ?? null : null,
215
+ via: lifecycle.via,
216
+ alternatives,
217
+ }
218
+ }
219
+
198
220
  export const findingFromRef = (ref, options) => {
199
221
  const {
200
222
  days,
@@ -243,6 +265,7 @@ export const findingFromRef = (ref, options) => {
243
265
  replacement_options: ref.entry.replacement_options ?? null,
244
266
  replacement_note: ref.entry.replacement_note ?? null,
245
267
  threshold_days: days,
268
+ capture: captureWindowFor(ref.entry, lifecycle, { today }),
246
269
  ...(waiver ? { waiver } : {}),
247
270
  }
248
271
  return finding
package/lib/reports.mjs CHANGED
@@ -40,7 +40,16 @@ const waiverTail = finding => !finding.waiver
40
40
  ? ` [waived until ${finding.waiver.expires} by ${finding.waiver.owner}: ${finding.waiver.reason}]`
41
41
  : ` [waiver expired on ${finding.waiver.expires}; owner ${finding.waiver.owner}: ${finding.waiver.reason}]`
42
42
 
43
- export const findingTail = (finding, days) => `${lifecycleTail(finding, days)}${waiverTail(finding)}`
43
+ const captureTail = finding => {
44
+ if (!['retiring', 'retired'].includes(finding.status) || !finding.capture?.alternatives?.length) return ''
45
+ const alternatives = finding.capture.alternatives.map(alternative => {
46
+ const until = ['tentative', 'earliest'].includes(alternative.date_precision) ? 'at least until' : 'until'
47
+ return `via ${alternative.via} ${until} ${alternative.until}`
48
+ })
49
+ return ` [still answers ${alternatives.join(', ')}]`
50
+ }
51
+
52
+ export const findingTail = (finding, days) => `${lifecycleTail(finding, days)}${waiverTail(finding)}${captureTail(finding)}`
44
53
 
45
54
  const statusStyle = finding =>
46
55
  finding.waiver?.active === true ? 'yellow' :
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "model-eol",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "AI model deprecation feeds with a zero-dependency checker, inventory CLI, and migration bot.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -93,7 +93,7 @@
93
93
  "type": ["string", "null"],
94
94
  "minLength": 1,
95
95
  "default": null,
96
- "description": "Opt-in repository-owned command run independently for each patchable migration only in the read-only evaluate phase. Write-capable publication consumes its commit-bound result manifest and never executes this command."
96
+ "description": "Opt-in repository-owned command run independently for each patchable migration only in the read-only evaluate phase. Receives MODEL_EOL_OLD_ID, MODEL_EOL_NEW_ID, MODEL_EOL_PLAN, MODEL_EOL_REPORT, MODEL_EOL_VIA, and MODEL_EOL_EVAL_MODE. Write-capable publication consumes its commit-bound result manifest and never executes this command."
97
97
  },
98
98
  "timeout_ms": {
99
99
  "type": "integer",