> For the complete documentation index, see [llms.txt](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/.spec_system/archive/sessions/phase28-session05-signal-aging-half-lives-and-saturation-refinement/spec.md).

# Session Specification

**Session ID**: `phase28-session05-signal-aging-half-lives-and-saturation-refinement` **Phase**: 28 - Trend Finder Trends-Finderz Adoption **Status**: Not Started **Created**: 2026-06-14

***

## 1. Session Overview

This session upgrades Trend Finder's recency and saturation derivations so source roles age at different speeds and crowded topics are described with more observed-signal context. Today, a five-day-old arXiv research item and a five-day-old Hacker News discussion item decay through the same recency buckets. This session replaces that uniform recency input with half-life weighted freshness by source role and publishes a bounded per-topic `signalAging` status.

The session also refines the Phase 27 saturation estimate. Saturation remains a derived/display signal used by radar, hidden-gem, lifecycle, and Workbench surfaces; it does not directly adjust the six-factor score in this session. The refinement adds source diffusion, mention density, mainstream-term pressure, and single-source/developer dominance handling while preserving legacy parsing and explicit unavailable states.

This is the second Tier 2 derived-quality layer in Phase 28. It depends on the completed calibration-version work from Session 03, the visibility-band work from Session 04, and the Phase 27 saturation/lifecycle baseline. It must leave lifecycle score multipliers, named score contributions, research-only flags, retention pruning, and action verdicts to later sessions.

***

## 2. Objectives

1. Add declaration-level source-role half-life defaults and a pure deterministic signal-aging helper with bounded status, freshness, penalty, boost, and source contribution outputs.
2. Feed half-life weighted recency into momentum scoring without adding a new lifecycle vocabulary or changing the six-factor weight table.
3. Refine saturation with source diffusion, mention density, mainstream-term pressure, and single-source/developer dominance while keeping saturation display-derived.
4. Publish schema, view-model, card, Workbench, fixture, documentation, and tests for `signalAging`, refined saturation, unavailable states, and legacy parsing.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase27-session02-deterministic-derived-signals-and-risk-flags` - provides the saturation baseline and risk-flag pattern this session refines but does not replace.
* [x] `phase27-session06-lifecycle-stage-taxonomy` - provides the current Trend Finder lifecycle vocabulary that this session must not duplicate.
* [x] `phase28-session03-calibration-version-and-confidence-dampener` - provides scoring-version protection before recency math changes.
* [x] `phase28-session04-topic-noise-gate-and-visibility-bands` - provides visibility and stale/dormant reason-code context consumed by later sessions.

### Required Tools/Knowledge

* Bun 1.3.14 package/runtime commands.
* TypeScript, Zod, Vitest, React 19, Tailwind CSS 4, and existing AI OS path aliases.
* Existing Trend Finder scoring, source declaration, schema, view-model, Trend card, Workbench, fixture, and documentation patterns.
* Trends-Finderz reference files: `EXAMPLES/trends-finderz/lib/product/signal-aging.ts`, `EXAMPLES/trends-finderz/lib/trends/lifecycle.ts`, and `EXAMPLES/trends-finderz/lib/scoring/trend-snapshots.ts`.

### Environment Requirements

* Run commands from the repository root.
* Keep generated private runtime data, cache files, screenshots, and source dumps out of commits.
* Do not add new source adapters, network calls, storage paths, third-party dependencies, or credential flows.

***

## 4. Scope

### In Scope (MVP)

* Trend Finder source roles can declare or resolve `halfLifeHours` - implemented through source-role defaults and additive source declaration metadata.
* Trend Finder can compute half-life weighted freshness per evidence item - integrated into the existing momentum recency component in `scripts/lib/ai-runtime/scoring.ts`.
* Trend Finder can derive per-topic `signalAging` - status, score, latest age, median age, stale/cooling/fresh counts, decay penalty, freshness boost, recent confirmation score, old-source pressure, and bounded per-source-role contributions.
* Trend Finder can emit an explicit `unavailable` signal-aging state when there is insufficient evidence, invalid dates, or too little history to make a safe aging statement.
* Trend Finder can use Trends-Finderz lifecycle freshness and staleness thresholds as calibration fixtures without porting `Emerging`, `Dormant`, or other Trends-Finderz lifecycle labels into the payload.
* Trend Finder can refine saturation using source diffusion, mention density, mainstream-term pressure, and single-source/developer dominance while preserving the existing saturation field and legacy parse behavior.
* Operators can inspect signal-aging status and refined saturation on Trend cards and Workbench rows with bounded labels and accessible chips.
* Documentation describes only shipped aging and refined saturation behavior.

### Out of Scope (Deferred)

* A second lifecycle stage taxonomy - *Reason: Phase 27 owns the Trend Finder lifecycle vocabulary; this session adds only `signalAging`.*
* Feeding aging or saturation into a score multiplier - *Reason: Session 06 owns lifecycle/aging multiplier behavior and named score contributions.*
* Research-only role-composition risk flag - *Reason: Session 07 owns the new risk flag and private-cache retention pass.*
* Per-topic action verdicts or evidence-to-action QA - *Reason: Session 08 consumes the Tier 2 gates after they exist.*
* User-editable mainstream-term lists or keyword packs - *Reason: Session 13 owns reviewed keyword-pack configuration.*
* New source adapters, network paths, persistence, or analyst calls - *Reason: this session is deterministic derivation over already-collected evidence.*

***

## 5. Technical Approach

### Architecture

Add a pure signal-aging helper under `scripts/lib/ai-runtime/signal-aging.ts`. The helper should accept generated time, scoring evidence, role half-life metadata, and optional historical context, then return bounded outputs that can be safely serialized into the browser payload. It should use role-level half-life defaults aligned to the Trends-Finderz source examples: fast decay for discussion/news, slower decay for developer/creator, and slowest decay for research.

Update `scripts/lib/ai-runtime/scoring.ts` so `calculateScoreBreakdown()` uses half-life weighted recency for momentum. Keep `OPPORTUNITY_SCORE_WEIGHTS` unchanged and avoid adding score multipliers. In `scoreSingleTopic()`, derive `signalAging` after evidence context is known and before lifecycle input is built so lifecycle can keep using refined saturation and future sessions can consume the aging status.

Refine `calculateSaturation()` in `scoring.ts` while retaining the existing `saturation` field. The formula should still return `null` when evidence or a healthy source denominator is unavailable. When available, it should blend the current Phase 27 source/volume/recurrence signals with source diffusion, mention density, mainstream-term pressure, and a bounded single-source or developer-dominance discount. Constants must be recalibrated for Trend Finder scales, not copied directly from Trends-Finderz.

Additive payload parsing stays in `src/extensions/trend-finder/schema.ts` with Zod defaults and catch paths. Browser UI changes should be projection-first: format `signalAging` labels in `view-model.ts`, render compact chips in `trend-card.tsx`, add Workbench fields/filter/sort support, and keep all labels bounded, deterministic, and ASCII-safe.

### Design Patterns

* Pure function, then tests, then wire: aging and saturation math should be reviewable before UI work.
* Zod additive defaults: old generated payloads and fixtures parse without migration.
* Browser-safe projection: publish bounded labels, numbers, enums, and reason codes only; do not expose raw evidence dumps, private paths, prompts, or source payloads.
* Display-derived saturation: refine the value but do not make it a hidden score adjustment in this session.
* Existing lifecycle vocabulary only: use Trends-Finderz lifecycle details as calibration fixtures, not payload labels.

### Technology Stack

* Bun 1.3.14 for scripts and test execution.
* TypeScript for application and script code.
* Zod for payload parsing and legacy defaults.
* React 19 and Tailwind CSS 4 for UI changes.
* Vitest for script, schema, view-model, Workbench, and component coverage.

***

## 6. Deliverables

### Files to Create

| File                                                    | Purpose                                                                                                  | Est. Lines |
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------- |
| `scripts/lib/ai-runtime/signal-aging.ts`                | Pure half-life weighted signal-aging derivation and role contribution helpers                            | \~280      |
| `scripts/lib/ai-runtime/__tests__/signal-aging.test.ts` | Unit tests for half-life decay, status transitions, unavailable states, and lifecycle threshold fixtures | \~260      |

### Files to Modify

| File                                                                            | Changes                                                                               | Est. Lines |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------- |
| `scripts/extensions/trend-finder/sources/types.ts`                              | Add optional `halfLifeHours` source metadata and exported role half-life shape        | \~40       |
| `scripts/extensions/trend-finder/sources/apify-source-config.ts`                | Add declaration/default half-life metadata and validation/defaulting                  | \~80       |
| `scripts/extensions/trend-finder/sources/__tests__/apify-source-config.test.ts` | Cover half-life defaulting, override validation, and invalid metadata warnings        | \~80       |
| `scripts/lib/ai-runtime/scoring.ts`                                             | Use weighted recency, derive `signalAging`, refine saturation, and publish fields     | \~180      |
| `scripts/lib/ai-runtime/__tests__/scoring.test.ts`                              | Cover role-specific recency differences, refined saturation, and scoring integration  | \~160      |
| `src/extensions/trend-finder/schema.ts`                                         | Add signal-aging enums, bounded contribution schemas, defaults, and topic fields      | \~180      |
| `src/extensions/trend-finder/view-model.ts`                                     | Add signal-aging status labels, contribution labels, and saturation detail projection | \~120      |
| `src/extensions/trend-finder/components/trend-card.tsx`                         | Render signal-aging and refined saturation chips with accessible labels               | \~80       |
| `src/extensions/trend-finder/signal-workbench-model.ts`                         | Add aging fields, filters, facets, search labels, and deterministic sort keys         | \~120      |
| `src/extensions/trend-finder/components/signal-workbench-controls.tsx`          | Add aging status controls with accessible labels and keyboard input support           | \~50       |
| `src/extensions/trend-finder/fixtures.ts`                                       | Add representative fresh, cooling, stale, and unavailable aging fixture data          | \~80       |
| `src/extensions/trend-finder/__tests__/view-model.test.ts`                      | Cover legacy defaults and signal-aging view-model labels                              | \~80       |
| `src/extensions/trend-finder/__tests__/signal-workbench-model.test.ts`          | Cover aging filters, facets, searches, and sort ordering                              | \~90       |
| `src/extensions/trend-finder/components/__tests__/trend-card.test.tsx`          | Cover aging chip rendering and accessible labels                                      | \~80       |
| `docs/extensions/trend-finder-scoring.md`                                       | Document shipped half-life aging and refined saturation behavior                      | \~80       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] A five-day-old research signal and a five-day-old discussion signal decay differently according to role half-life configuration.
* [ ] Momentum recency uses half-life weighted freshness while the six-factor weight table remains unchanged.
* [ ] Each topic carries `signalAging` with status, score, age summary, stale counts, recent confirmation, old-source pressure, and bounded role contributions.
* [ ] Topics with missing or insufficient evidence show explicit unavailable aging state rather than guessed freshness.
* [ ] Saturation keeps the existing field and adds diffusion, density, mainstream pressure, and dominance handling without becoming a hidden score multiplier.
* [ ] Trend cards and Workbench rows expose aging and refined saturation through bounded labels, filters, and deterministic ordering.
* [ ] Trends-Finderz lifecycle labels are not introduced into Trend Finder payloads or UI.
* [ ] Legacy payloads parse with neutral defaults and existing fixtures remain usable.

### Testing Requirements

* [ ] Unit tests written and passing for half-life decay, status transitions, unavailable states, source-role contributions, and Trends-Finderz lifecycle threshold fixtures.
* [ ] Scoring integration tests cover role-specific recency differences, refined saturation behavior, legacy raw opportunity score behavior, and no score multiplier introduction.
* [ ] Source-config tests cover half-life defaulting, bounds, and invalid metadata warnings.
* [ ] Schema/view-model tests cover legacy defaults, labels, and bounded contribution projection.
* [ ] Workbench and Trend card tests cover aging filters, sort, chip rendering, and accessible labels.
* [ ] Manual testing completed for Trends and Signal Workbench surfaces.

### Non-Functional Requirements

* [ ] No new dependencies, source adapters, storage paths, external calls, or credential flows are introduced.
* [ ] Browser payload additions remain bounded and keep the shared payload-size discipline.
* [ ] Labels, summaries, and contribution strings are deterministic, length-bounded, and ASCII-safe.
* [ ] No raw source dumps, private paths, prompt text, token-shaped strings, or generated private data are exposed in UI summaries or payload fields.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.

***

## 8. Implementation Notes

### Key Considerations

* `SCORING_VERSION` should be bumped with this recency/saturation math change so movement, retro, and prediction surfaces can flag cross-version comparisons rather than treating score changes as organic movement.
* `signalAging` should stay separate from the Phase 27 lifecycle stage. The lifecycle output remains `unknown`, `whisper`, `builder`, `creator`, or `saturated`.
* Role half-lives should be defaults attached to source roles, with validated per-source overrides available only through declaration metadata.
* Saturation remains display-derived in this session. Session 06 is the first place to feed aging/lifecycle into a bounded multiplier.

### Potential Challenges

* Recency recalibration may move rankings: mitigate with a scoring-version bump, focused tests, and no change to `OPPORTUNITY_SCORE_WEIGHTS`.
* Source roles are coarser than Trends-Finderz source names: mitigate with role-level defaults and per-source declaration overrides for reviewed exceptions.
* Saturation can over-penalize broad legitimate topics: mitigate with bounded mainstream-term pressure and dominance discounts that never erase the Phase 27 baseline.
* Payload growth from contribution arrays can accumulate: mitigate with a small contribution limit, bounded labels, and schema max lengths.

### Relevant Considerations

* \[P02] **Extension payloads and labels stay bounded**: `signalAging` publishes bounded enums, numbers, and short labels only.
* \[P05] **Script-only runtime boundary**: aging and saturation math stay under `scripts/lib/` and `scripts/extensions/`; browser code reads validated payload fields only.
* \[P24] **Browser-safe export and triage boundaries**: no raw evidence, private paths, prompts, or local notes enter the generated payload.
* \[P27] **Trend Finder payload growth needs release checks**: contribution arrays and labels must remain bounded and be covered by later closeout payload checks.
* \[P27] **Deterministic fallback before AI enrichment**: all work in this session is deterministic and does not add AI calls.
* \[P01] **Extract pure functions, then test, then wire**: build `signal-aging.ts` and focused tests before scoring/UI integration.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Recency changes could silently distort ranking if not version-stamped and tested against cross-role fixtures.
* Aging chips could imply actionability or lifecycle changes that this session does not own.
* Saturation refinement could overstate mainstream pressure if constants are copied from Trends-Finderz instead of calibrated to Trend Finder scales.

***

## 9. Testing Strategy

### Unit Tests

* Test half-life decay for research, developer, creator, news, launch, and discussion roles at the same age.
* Test `fresh`, `active`, `cooling`, `stale`, and `unavailable` aging states.
* Test contribution limits, score bounds, invalid dates, and lifecycle threshold fixtures including stale/dormant age cutoffs without payload label leakage.
* Test saturation refinement for diffusion, mention density, mainstream-term pressure, and single-source/developer dominance.

### Integration Tests

* Extend scoring tests so role-specific recency changes momentum while the six-factor weights and raw weighted score behavior remain intact.
* Extend source-config tests for `halfLifeHours` validation/defaulting.
* Extend schema/view-model tests for legacy payloads and bounded aging labels.
* Extend Workbench model tests for aging filters, facets, search text, and sort ordering.

### Manual Testing

* Run focused Vitest files for signal aging, scoring, source config, schema, view model, Workbench model, and Trend card rendering.
* Inspect Trend cards and Signal Workbench in fixture data for fresh, cooling, stale, and unavailable states.
* Verify no new source setup, network call, credential flow, or persistence path appears in the UI.

### Edge Cases

* Missing or invalid `publishedAt` dates.
* Single evidence item with no healthy source denominator.
* Multiple evidence items from the same source role but different ages.
* Research-only fresh evidence versus stale discussion evidence at the same absolute age.
* Mainstream terms in generic topic titles versus specific technical topic titles.
* Legacy payloads with no `signalAging` field.

***

## 10. Dependencies

### External Libraries

* No new external libraries.

### Other Sessions

* **Depends on**: `phase27-session02-deterministic-derived-signals-and-risk-flags`, `phase27-session06-lifecycle-stage-taxonomy`, `phase28-session03-calibration-version-and-confidence-dampener`, and `phase28-session04-topic-noise-gate-and-visibility-bands`.
* **Depended by**: `phase28-session06-lifecycle-multiplier-and-named-contributions`, `phase28-session08-action-verdicts-and-consistency-qa`, and later Brief / Workbench decision surfaces.

***

## Next Steps

Run the implement workflow step to begin AI-led implementation.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/.spec_system/archive/sessions/phase28-session05-signal-aging-half-lives-and-saturation-refinement/spec.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
