feat(linkedin-studio): S16 — optional manual saves in analytics + close deferred onboarding Write MAJOR
Lifts the original v4.0.0 Non-Goal: an optional, manually-entered `saves`
metric through the analytics layer, built location-agnostic (option c) so
UI-brief §9b/M0 relocates the data dir in one place later.
- types: PostMetrics.saves? + Weekly/Monthly summary.totalSaves? (optional);
new RankableMetric type for the always-numeric index-access whitelist
- parser: dedicated parseOptionalCount() — blank/non-numeric/negative -> undefined
("unknown != 0"), genuine 0 kept; saves NOT folded into engagementRate
- reports: totalSaves set only when >=1 post carries saves (backward-compat)
- cli: saves surfaced in import summary + weekly/monthly totals + per-post
- S16-pre: onboarding.md allowed-tools gains Write (closes S15-deferred MAJOR)
- docs (three-doc rule): plugin README boundary + analytics README + root README
+ plugin CLAUDE.md + CHANGELOG; dwell stays explicitly unmeasurable
Independent /trekreview: brief-conformance 0 findings; code-correctness 2 MAJOR
(own lockstep misses) FIXED in-session (parseOptionalCount + edge tests). Gate:
tsc clean, analytics 116/116, lint 74/0/0, hooks 98/98. Within-v4.1.0 refinement
(no surface/count/version change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6234fda001
commit
e029841800
17 changed files with 412 additions and 117 deletions
|
|
@ -14,11 +14,18 @@ export interface PostMetrics {
|
|||
shares: number;
|
||||
clicks: number;
|
||||
engagementRate: number; // (reactions+comments+shares+clicks)/impressions * 100
|
||||
// NOTE: `saves` and `dwell` are intentionally absent. They are NOT in the
|
||||
// LinkedIn analytics CSV export this tool parses, and there is no self-serve API
|
||||
// to pull them. Saves are visible (count-only) in the native post analytics UI
|
||||
// (~Sept 2025 onward) — read them there; dwell is internal to LinkedIn for
|
||||
// organic posts. Do not add these fields without a real ingest source.
|
||||
// `saves` is OPTIONAL and manually entered. LinkedIn's CSV export does NOT
|
||||
// include it and there is no self-serve API to pull it — but the count IS
|
||||
// visible in the native post analytics UI (~Sept 2025 onward). The ingest
|
||||
// path is the user adding a `Saves` column to the CSV they read off it; the
|
||||
// parser picks it up when present (see csv-parser.ts). When the column or a
|
||||
// cell is absent, `saves` stays undefined — "unknown", never coerced to 0.
|
||||
// It is deliberately NOT folded into engagementRate (which stays comparable
|
||||
// to historical, saves-free data) — saves is surfaced as its own signal.
|
||||
saves?: number;
|
||||
// NOTE: `dwell` remains absent and unmeasurable. Dwell time is internal to
|
||||
// LinkedIn for organic posts — not exportable, no UI count to transcribe, no
|
||||
// API. Do not fabricate a dwell field or surface.
|
||||
}
|
||||
|
||||
export interface AnalyticsBatch {
|
||||
|
|
@ -40,6 +47,7 @@ export interface WeeklyReport {
|
|||
totalComments: number;
|
||||
totalShares: number;
|
||||
totalClicks: number;
|
||||
totalSaves?: number; // optional — present only when ≥1 post carries manual saves data
|
||||
avgEngagementRate: number;
|
||||
avgImpressionsPerPost: number;
|
||||
};
|
||||
|
|
@ -59,6 +67,21 @@ export interface WeeklyReport {
|
|||
|
||||
export type TrendDirection = "up" | "down" | "stable";
|
||||
|
||||
/**
|
||||
* Metric keys that are always present and numeric — safe for trend/alert ranking
|
||||
* and `metrics[key]` index access. Excludes the optional, manually-entered
|
||||
* `saves`, which is sparse and would type as `number | undefined` under index
|
||||
* access (and is not a rankable trend metric). This is the runtime whitelist the
|
||||
* CLI and alert engine have always used.
|
||||
*/
|
||||
export type RankableMetric =
|
||||
| "impressions"
|
||||
| "reactions"
|
||||
| "comments"
|
||||
| "shares"
|
||||
| "clicks"
|
||||
| "engagementRate";
|
||||
|
||||
export interface Alert {
|
||||
type: "spike" | "drop" | "milestone";
|
||||
severity: "info" | "warning" | "critical";
|
||||
|
|
@ -98,6 +121,7 @@ export interface MonthlyReport {
|
|||
totalComments: number;
|
||||
totalShares: number;
|
||||
totalClicks: number;
|
||||
totalSaves?: number; // optional — present only when ≥1 post carries manual saves data
|
||||
avgEngagementRate: number;
|
||||
avgImpressionsPerPost: number;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue