Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ Create a commit with a provided message. Validates the message, with comprehensi
### **git.showAnalytics**
Open a full-screen dashboard displaying Git analytics: churn (commits per file), volatility (recent change frequency), authorship (commits per author), commit trends (over time), and top contributors. Includes a **Risk Hotspots** scatter — each file plotted by change frequency (x) against volatility (y), bubble size by total lines changed, colored by risk tier — so refactor candidates surface in the top-right at a glance. A **Change Companions** table surfaces files that change together in the same commit (co-change count + co-change rate), revealing hidden coupling — candidates to refactor together or split apart. Real-time chart rendering with drill-down.

**Export (all report dashboards).** Each report webview (Git Analytics, Hygiene Analytics, Session Briefing) offers two save paths: **↓ CSV / ↓ JSON** quick-save the report dialog-free to `.meridian/artifacts/` with a timestamped filename (per [ADR 014](./adr/014-dotdir-doctrine.md); the dir self-ignores so artifacts never enter git), and **Save as…** opens a dialog (format + location) for saving anywhere.
**Export (all report dashboards).** Each report webview (Git Analytics, Hygiene Analytics, Session Briefing) offers two save paths: **↓ CSV / ↓ JSON** quick-save the report dialog-free to `.meridian/artifacts/` with a timestamped filename (per [ADR 014](./adr/014-dotdir-doctrine.md); the dir self-ignores so artifacts never enter git), and **Save as…** opens a dialog (format + location) for saving anywhere. Every report render (initial open, refresh, or filter) also refreshes that report's `.meridian/latest/` agent-readable snapshot ([ADR 020](./adr/020-latest-snapshot-contract.md)).

### **git.sessionBriefing**
Generate a session-orientation summary. Aggregates git working-tree status, recent commits, run-log activity (`recentRuns`), git analytics (`activityWindow` — including momentum trends and a commit-frequency sparkline showing the shape behind the trend arrow), hygiene scan state (`hygieneSnapshot`), and a pending-change risk preview (`pendingChangeRisk` — each uncommitted file joined against the computed analytics risk model: churn, volatility, and risk tier, with files absent from the analytics window marked `new` (no history) or `cold` (changed but quiet — low, not unknown); a flag is raised when several high-risk files are in flight), and a pending-change companion preview (`pendingChangeCompanions` — files that historically ship in the same commit as your current edits but are not in the dirty set yet, i.e. possibly-forgotten siblings such as tests/types/docs; a flag is raised when several are likely missing), and a longitudinal pulse slice (`pulse` — movement since your previous briefing plus trend lines over the stored history in `.meridian/pulse/`; snapshots are captured automatically per briefing, throttled to one per 10 minutes, capped and local-only per [ADR 019](./adr/019-pulse-and-retention.md)) into a deterministic `SessionBriefing` record, then layers optional AI prose on top. Optional slices degrade gracefully when data is unavailable; the prose layer degrades to the raw aggregate when no language model is available. Useful for standup notes, context switching, pre-commit risk triage, or morning orientation.
Generate a session-orientation summary. Aggregates git working-tree status, recent commits, run-log activity (`recentRuns`), git analytics (`activityWindow` — including momentum trends and a commit-frequency sparkline showing the shape behind the trend arrow), hygiene scan state (`hygieneSnapshot`), and a pending-change risk preview (`pendingChangeRisk` — each uncommitted file joined against the computed analytics risk model: churn, volatility, and risk tier, with files absent from the analytics window marked `new` (no history) or `cold` (changed but quiet — low, not unknown); a flag is raised when several high-risk files are in flight), and a pending-change companion preview (`pendingChangeCompanions` — files that historically ship in the same commit as your current edits but are not in the dirty set yet, i.e. possibly-forgotten siblings such as tests/types/docs; a flag is raised when several are likely missing), and a longitudinal pulse slice (`pulse` — movement since your previous briefing plus trend lines over the stored history in `.meridian/pulse/`; snapshots are captured automatically per briefing, throttled to one per 10 minutes, capped and local-only per [ADR 019](./adr/019-pulse-and-retention.md)) into a deterministic `SessionBriefing` record, then layers optional AI prose on top. Optional slices degrade gracefully when data is unavailable; the prose layer degrades to the raw aggregate when no language model is available. Raised flags are surfaced as actionable cards in the briefing panel, each with a jump-to-section link and one-click actions. Useful for standup notes, context switching, pre-commit risk triage, or morning orientation.

---

Expand Down Expand Up @@ -97,6 +97,8 @@ Per-workspace Meridian state lives under `.meridian/` at the workspace root (see
- `.meridian/.meridianignore` — gitignore-syntax patterns excluded from hygiene scans. Editor syntax highlighting is provided via the built-in `ignore` language association. Legacy `.meridianignore` at the workspace root is auto-relocated on activation.
- `.meridian/settings.json` — sparse JSON overrides for `meridian.*` settings. Present keys take precedence over VS Code user/workspace settings; absent keys fall through. Example: `{ "hygiene.prune.minAgeDays": 7 }`.
- `.meridian/pulse/` — local, self-gitignored pulse history (`pulse.v1.jsonl`) behind the session briefing's pulse slice; self-capping, no maintenance needed ([ADR 019](./adr/019-pulse-and-retention.md)).
- `.meridian/latest/` — local, self-gitignored, agent-readable snapshot of the three reports (`session-briefing.v1.json`, `git-analytics.v1.json`, `hygiene-analytics.v1.json`), each a `{ schemaVersion, kind, generatedAt, report }` envelope overwritten on every report render ("latest = last rendered"); no history, no runtime integration — coding agents read the files directly ([ADR 020](./adr/020-latest-snapshot-contract.md)).
- `.meridian/AGENTS.md` — generated once on first snapshot write, documenting the `.meridian/latest/` contract and a paste-ready agent-rules snippet; never overwritten once present, safe to edit ([ADR 020](./adr/020-latest-snapshot-contract.md)).

---

Expand Down
23 changes: 10 additions & 13 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,8 @@ deferred. (See [ADR 012](./adr/012-product-reanchor.md) for the re-anchor.)

## Now

1. **Session-briefing UI polish (continued)** — richer presentation over the
`aggregateSessionBriefing()` record: actionable cards, inline panel links,
quick filters. The pulse slice (ADR 019) landed; remaining polish is
presentation-only. Prose stays optional and degrades to the raw aggregate
when `vscode.lm` is unavailable.
- Primary files: `src/domains/git/session-briefing-ui/`,
`src/domains/git/session-aggregator.ts`.

2. **Agent-readable snapshot (stretch, file-shaped)** — a stable, versioned
JSON "latest" convention (e.g. `.meridian/latest/briefing.json`) that
Copilot/Cursor/CLI agents read directly, plus a documented pointer users
paste into their agent rules. No runtime integration, no LM-tool surface —
the inversion of the pre-ADR-012 approach.
Nothing currently in flight. Next natural candidates live in Deferred below —
none are commitments until picked up here.

---

Expand All @@ -44,6 +33,14 @@ deferred. (See [ADR 012](./adr/012-product-reanchor.md) for the re-anchor.)

## Done

- **Session-briefing actionable cards** — raised flags render as actionable
cards in the briefing panel with jump-to-section links and one-click
actions, completing the presentation-only UI polish over
`aggregateSessionBriefing()`.
- **Agent-readable latest snapshot** (ADR 020) — stable, versioned
`.meridian/latest/*.v1.json` snapshots of all three reports, refreshed on
every render; `.meridian/AGENTS.md` as a generated discovery pointer. File-
shaped only — no runtime integration, no LM-tool surface.
- **Ecosystem registry** (ADR 018) — single source for language/toolchain
semantics; JVM (Maven/Gradle/Kotlin/Scala) first-class; exclusion/bucket/
categorization drift structurally eliminated.
Expand Down
4 changes: 3 additions & 1 deletion docs/adr/011-session-briefing-aggregator.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ The aggregator is **promoted** to the product's connective tissue and forward he
Consequence: `activityWindow` / `hygieneSnapshot` reach the user via the `summary` text (HTML), the prose `data` passthrough (ADR 004), and JSON export; the CSV export does not surface them. Widening those slices without extending the summary/prose surface ships data the HTML/CSV paths do not show.
- **Additive wire-shape extension is sanctioned and backward-compatible.** Adding *optional* fields to `ActivityWindow` / `HygieneSnapshot` (e.g. retaining `trends`, top churn/volatility files, a bounded dead-code sample already computed and discarded in-aggregator) is the sanctioned way to enrich the briefing under the "extend this aggregator" rule below. It is backward-compatible across all three paths: the HTML reader ignores unknown slices, JSON export is whole-object (new fields ride along), and the CSV serializer is explicit-field (unaffected, never malformed). Constraints: fields stay optional; bounded (sample-limited via `SESSION_BRIEFING` constants, not unbounded); the **git-core fail-fast guarantee is inviolate** — `recentCommits` must remain sourced from the fail-fast git-core path and must **not** be re-derived from a fail-soft peripheral (e.g. analytics), as that would silently demote a guaranteed field to best-effort. This does **not** touch the [ADR 009](./009-run-log-schema-versioning.md) run-log on-disk schema (analytics/hygiene are not run-log sourced); no schema-version bump.
- **Freshness (2026-05-19) — realized additive retentions.** The sanctioned extension above is now exercised: `ActivityWindow` carries `trends`, `topChurnFiles`, and `commitFrequency` (the last a `SESSION_BRIEFING.SPARKLINE_MAX_POINTS`-bounded tail of the analytics commit-frequency series, rendered as an inline-SVG sparkline — no chart dependency added to the briefing webview); `HygieneSnapshot` carries `deadCodeSample`. Each stays optional, bounded by a `SESSION_BRIEFING` constant, and analytics-sourced (fail-soft), leaving the git-core fail-fast guarantee intact. Path-routing has correspondingly advanced past the bullets above: the **HTML reader now structurally renders** `activityWindow.{trends,topChurnFiles,commitFrequency}` and `hygieneSnapshot.{counts,deadCodeSample}` (not via `summary` text only), and the **CSV serializer now emits** the `trends` / top-contributors / `topChurnFiles` / hygiene / dead-code tables (still explicit-field, so unknown future slices remain unaffected). `commitFrequency` is the one deliberate exception — viz-only: it rides JSON export and the ADR 004 prose `data` passthrough, and is intentionally **not** added to the explicit-field CSV or the deterministic plain-text summary (a numeric series is noise in both).
- **Freshness (2026-05-26) — UI polish pass.** Webview now renders the previously-dead `recentRuns` slice (timestamp · command · phase badge · duration · errorCode), with the inert `workflowName` / `skillName` fields tolerated as fallback display labels. Risk badges in Top Churn and Pending-Change Risk plus the dead-code adornment now deep-link via a single new `openReport` webview→host message routed through `meridian.reports.open` (reveal-or-compute lives at one site — `tree-setup.ts`). The arg shape is pinned by a shared `ReportOpenArg` type exported from `reports-tree-provider.ts` and imported at both call sites, so the webview-provider deep-link does not drag `vscode.TreeItem` into a test-mocked import graph. Summary cards open Source Control; matched flag strings scroll to their section; the `"No hygiene scan yet"` flag is replaced inline by a CTA firing `meridian.hygiene.scan` (the one direct Meridian command-id invocation from a webview — sibling cross-panel navigation goes through `meridian.reports.open` indirection); pending-change rows gain a diff icon firing `git.openChange` through the existing `resolveWorkspacePath` guard. Three client-side path-substring filters added (Top Churn, Pending-Change Risk, Uncommitted) — pure DOM, no host round-trip, no `vscode.setState` persistence in v1. **The `SESSION_BRIEFING` prompt was rewritten from markdown to plain-text** to match the deterministic-fallback shape and the webview's `textContent` render; "AI Summary" header relabeled to "Summary". Wire shape unchanged, prose-data allowlist unchanged. **Asymmetric by design (v1):** cross-panel deep-links flow Briefing → {Git Analytics, Hygiene} only — the inverse is a deferred follow-up since the briefing is the hub per ADR 012. The webview FLAG_ANCHORS regex table couples to literal flag prefixes emitted by `session-aggregator.ts`; a regression test in `sessionAggregator.test.ts` pins the prefix contract on the aggregator side.
- **Freshness (2026-05-26) — UI polish pass.** Webview now renders the previously-dead `recentRuns` slice (timestamp · command · phase badge · duration · errorCode), with the inert `workflowName` / `skillName` fields tolerated as fallback display labels. Risk badges in Top Churn and Pending-Change Risk plus the dead-code adornment now deep-link via a single new `openReport` webview→host message routed through `meridian.reports.open` (reveal-or-compute lives at one site — `tree-setup.ts`). The arg shape is pinned by a shared `ReportOpenArg` type exported from `reports-tree-provider.ts` and imported at both call sites, so the webview-provider deep-link does not drag `vscode.TreeItem` into a test-mocked import graph. Summary cards open Source Control; matched flag strings scroll to their section; the `"No hygiene scan yet"` flag is replaced inline by a CTA firing `meridian.hygiene.scan` (the one direct Meridian command-id invocation from a webview — sibling cross-panel navigation goes through `meridian.reports.open` indirection); pending-change rows gain a diff icon firing `git.openChange` through the existing `resolveWorkspacePath` guard. Three client-side path-substring filters added (Top Churn, Pending-Change Risk, Uncommitted) — pure DOM, no host round-trip, no `vscode.setState` persistence in v1. **The `SESSION_BRIEFING` prompt was rewritten from markdown to plain-text** to match the deterministic-fallback shape and the webview's `textContent` render; "AI Summary" header relabeled to "Summary". Wire shape unchanged, prose-data allowlist unchanged. **Asymmetric by design (v1):** cross-panel deep-links flow Briefing → {Git Analytics, Hygiene} only — the inverse is a deferred follow-up since the briefing is the hub per ADR 012. *(Superseded 2026-07-07: the FLAG_ANCHORS regex table described here was replaced — see the flag-cards freshness note below.)*

- **Freshness (2026-07-07) — structured `flagItems` + actionable flag cards.** `SessionBriefing` gains a **required** `flagItems: FlagItem[]` (`{ id, severity, message }`, ids typed by the producer-side `FlagId` union in `types.ts`); the legacy `flags: string[]` is now **derived** from it (`flagItems.map(i => i.message)`, insertion order) at the aggregator return site, so the two representations cannot drift. `FlagItem` deliberately carries no anchors/actions/DOM concepts — it is frozen into the public ADR 020 JSON contract (ids and severities are open sets for external consumers). The webview's regex-based `FLAG_ANCHORS` table is **gone**, replaced by an id-keyed `FLAG_UI` map in `script.js` driving action cards (jump-to-section anchors plus buttons reusing the existing `openScm` / `openReport` / `runHygieneScan` messages — no new message types). Flag *messages* remain pinned byte-for-byte in `sessionAggregator.test.ts` because they still feed `deterministicSummary` and the prose data; the id/severity assignments are pinned there too.

- **Freshness (2026-05-19) — `pendingChangeRisk`, the 4th additive slice (Plan A1, branch `feat/pending-change-risk`).** A new optional **top-level** `SessionBriefing.pendingChangeRisk` slice (deliberately *not* nested in `ActivityWindow`: its fail-soft predicate differs — it is present iff analytics is available, but its dirty-set input is git-core/fail-fast). It is the deterministic join of the dirty-set (`gitProvider.getAllChanges`, already fetched) against the already-computed `GitAnalyticsReport.files` risk model — **pure post-processing, zero new I/O**. Files absent from the analytics window are annotated `new` (status `A` — no history) or `cold` (status `M`/`D` — changed but quiet in-window: *low, not unknown*). It is **full-surface, not viz-only**: structural HTML table, explicit-field CSV block, JSON whole-object, a one-line deterministic `summary` sentence + bulleted high-risk list, and a `flags` entry at `PENDING_RISK.HOTSPOT_FLAG_THRESHOLD`; bounded by `PENDING_RISK.MAX_FILES` applied **after** the deterministic risk→volatility→path sort so the worst files are never truncated. Notes for future readers: (1) **Test-coverage signal is an explicit NON-GOAL** — it was scoped out because the only deterministic way to compute it (AST/stem-match importer resolution) is the low-precision heuristic this project rejected on ROI grounds; do not "helpfully" re-add it. (2) The ADR 004 prose `data` is a **hand-picked allowlist** in `session-handler.ts`, *not* a whole-object passthrough — a new slice does **not** "ride free" into prose and must be added explicitly (this slice was). (3) Rename normalization is now a single shared `normalizeRenamePath` (`git-path.ts`) used by both the analytics `FileMetric` path and this join — previously an untested inline regex in `analytics-service`; divergence would silently break the join. (4) Known pre-existing limits, not regressions: a renamed file often surfaces with status `M` (not `R`) because `git-provider`'s porcelain `statusMap` misses the brace-form numstat path — A1 does not modify `git-provider`; and dirty files under `ANALYTICS_EXCLUDE` (e.g. `out/`) are always `new`/`cold` since analytics never sees them.

Expand Down
Loading
Loading