fix(linkedin-studio): S14 harden import — drop baseline/metadata over-promise, prose now matches CLI
import.md repeatedly promised baseline comparison, rolling 4-week averages, and baselines.json/metadata.json the importer "creates" — but no code anywhere writes either file; detectAlerts flags intra-batch std-dev from the batch's own mean, not a stored baseline. Step 5b's `cat baselines.json` was always empty, so the command fell forever to "first import — baselines will be established" on every import. Rewrote 5 sections (Step 5, 5b, 7, State Tracking, Reference Files) to the truth: real CLI fields + YYYY-MM-DD-<shortid> filename, honest intra-batch surfacing, cross-week analysis deferred to /linkedin:report. Verified with a grounded run against a throwaway fixture. Axes a/b/c/d all PASS post-fix; lint 81/0/0, counts 29/19 unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
This commit is contained in:
parent
270e99d002
commit
a413279c58
2 changed files with 102 additions and 83 deletions
|
|
@ -130,94 +130,59 @@ If importing multiple files, run the command for each file sequentially.
|
|||
|
||||
## Step 5: Capture and Present Results
|
||||
|
||||
The CLI will output:
|
||||
- Number of posts imported
|
||||
- Date range covered (earliest to latest post)
|
||||
- Any duplicate posts detected
|
||||
- Anomalies or alerts detected
|
||||
The CLI prints (see `cli.ts` `handleImport`):
|
||||
- `Posts imported:` — count of valid rows (rows with an empty title or an unparseable date are skipped, each with a `Warning:` line)
|
||||
- `Date range:` — earliest to latest post in the batch
|
||||
- `Batch ID:` and `Saved to: posts/<file>` — the batch file written
|
||||
- `Saves entered:` — only when the CSV carried a `Saves` column (manual entry)
|
||||
- An anomaly block — either `Immediate alerts detected:` with 🔴/⚠️/ℹ️ spike/drop lines, or `No anomalies detected in imported data.`
|
||||
|
||||
**Parse the output** and present a summary:
|
||||
**Surface the CLI's output to the user** — for example:
|
||||
|
||||
```
|
||||
Import completed successfully!
|
||||
Import successful!
|
||||
─────────────────────────────────────
|
||||
Posts imported: 42
|
||||
Date range: 2025-12-01 to 2026-01-29
|
||||
Saved to: posts/2025-12-01-batch-a1b2c3d4.json
|
||||
Saves entered: 1,204 across 18 post(s) (manual)
|
||||
|
||||
Summary:
|
||||
- Posts imported: 42
|
||||
- Date range: 2025-12-01 to 2026-01-29
|
||||
- Duplicates skipped: 3
|
||||
- Anomalies detected: 2 posts with unusually high engagement
|
||||
|
||||
Alerts:
|
||||
- Post "AI agents are eating..." (2026-01-15): 340% above baseline impressions
|
||||
- Post "The future of no-code..." (2026-01-22): Viral threshold reached (10k+ impressions)
|
||||
|
||||
Data saved to:
|
||||
- ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/posts/YYYY-WXX.json
|
||||
Immediate alerts detected:
|
||||
─────────────────────────────────────
|
||||
ℹ️ [INFO] Post "AI agents are eating..." has unusually high impressions: 21,400 (2.4 std deviations above mean)
|
||||
```
|
||||
|
||||
### Step 5b: Import Analysis & Anomaly Detection
|
||||
The saved file is named `posts/YYYY-MM-DD-<shortid>.json` (the batch's earliest post date + a short batch id), not by ISO week.
|
||||
|
||||
After successful import, automatically analyze the imported data for anomalies and patterns.
|
||||
### Step 5b: Surface the Anomalies the Importer Detected
|
||||
|
||||
**Anomaly Detection:**
|
||||
Compare the imported week's data against existing baselines (if available from previous imports):
|
||||
The import CLI runs **intra-batch** anomaly detection during Step 4 (`detectAlerts`
|
||||
in `cli.ts`): for the just-imported batch it flags any post whose impressions
|
||||
deviate sharply — by standard deviation — from *that batch's own mean*, printing
|
||||
either `Immediate alerts detected:` (🔴/⚠️/ℹ️ spike/drop lines) or
|
||||
`No anomalies detected in imported data.`
|
||||
|
||||
1. **Engagement anomalies:**
|
||||
- Any post with >3x average impressions -> flag as "breakout post"
|
||||
- Any post with <0.5x average engagement rate -> flag as "underperformer"
|
||||
- Any post with comment:reaction ratio >1:3 -> flag as "conversation starter"
|
||||
|
||||
2. **Pattern recognition:**
|
||||
- Most successful day of week (by average impressions)
|
||||
- Most successful format (if detectable from post content)
|
||||
- Posting frequency vs. previous weeks
|
||||
|
||||
**Read baselines for comparison:**
|
||||
```bash
|
||||
cat ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/baselines.json 2>/dev/null
|
||||
```
|
||||
|
||||
**If baselines exist**, compare each imported post's metrics against baseline means. If no baselines exist yet, note that this is the first import and baselines will be established.
|
||||
Surface those lines as-is, and state the scope honestly: a flagged post stands out
|
||||
**among the posts in this export**, not against a stored historical baseline — the
|
||||
importer keeps no baseline file. Cross-week comparison is Step 6's job.
|
||||
|
||||
**Present as:**
|
||||
```
|
||||
### Import Analysis — YYYY-WXX
|
||||
### Import Summary — <batch date range>
|
||||
|
||||
X posts imported (Y new, Z updated)
|
||||
X posts imported (Y skipped: empty title or unparseable date)
|
||||
|
||||
#### Standout Posts
|
||||
Breakout: "[hook text...]" — X impressions (3.2x your average)
|
||||
Conversation Starter: "[hook text...]" — X comments (ratio 1:2.5)
|
||||
#### Standout in this batch
|
||||
ℹ️ "[hook text...]" — 21,400 impressions (2.4 std dev above this batch's mean)
|
||||
⚠️ "[hook text...]" — 180 impressions (2.1 std dev below this batch's mean)
|
||||
|
||||
#### Patterns Detected
|
||||
- Best day: Tuesday (avg 2,100 impressions vs. 1,400 other days)
|
||||
- Best time: Posts before 8 AM outperformed by 35%
|
||||
- Format winner: Listicles averaged 40% more engagement
|
||||
|
||||
#### Baseline Update
|
||||
Your rolling 4-week averages have been updated:
|
||||
- Impressions: X -> Y (change Z%)
|
||||
- Engagement rate: X% -> Y% (change Z%)
|
||||
(or: "No standout deviations within this batch.")
|
||||
```
|
||||
|
||||
**If this is the first import (no baselines):**
|
||||
```
|
||||
### Import Analysis — YYYY-WXX
|
||||
|
||||
X posts imported (first import — baselines will be established)
|
||||
|
||||
#### Initial Observations
|
||||
Top post: "[hook text...]" — X impressions
|
||||
Most discussed: "[hook text...]" — X comments
|
||||
|
||||
#### Baselines Established
|
||||
Your initial baselines are now set:
|
||||
- Avg impressions per post: X
|
||||
- Avg engagement rate: X%
|
||||
- Avg comments per post: X
|
||||
|
||||
Import 2-3 more weeks of data for meaningful trend analysis.
|
||||
```
|
||||
Cross-week analysis — best day of week, format performance, week-over-week trend,
|
||||
rolling averages — is **not** computed here; it is produced by `/linkedin:report`
|
||||
(Step 6), which reads the full post history via the `trends`/`heatmap` CLI. Defer
|
||||
that analysis to Step 6 rather than restating it.
|
||||
|
||||
## Step 6: Analytics-to-Strategy Feedback Loop
|
||||
|
||||
|
|
@ -266,12 +231,12 @@ Write the updated state file
|
|||
|
||||
Present next steps using AskUserQuestion based on the analysis results:
|
||||
|
||||
**If data shows declining engagement** (current < baseline by >15%):
|
||||
**If the report's trend is down** (impressions or engagement trending DOWN):
|
||||
- "Run /linkedin:report for full weekly breakdown"
|
||||
- "Run content audit to review strategy"
|
||||
- "Analyze your top post to understand what worked"
|
||||
|
||||
**If data shows strong performance** (current > baseline by >15%):
|
||||
**If the report's trend is up** (impressions or engagement trending UP):
|
||||
- "Run /linkedin:report for the full numbers"
|
||||
- "Create more content in your top format"
|
||||
- "Draft your next post while insights are fresh"
|
||||
|
|
@ -318,15 +283,13 @@ If the import fails:
|
|||
## Reference Files
|
||||
|
||||
The import system creates:
|
||||
- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/posts/YYYY-WXX.json` - Weekly post data
|
||||
- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/metadata.json` - Import tracking and baseline metrics
|
||||
- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/baselines.json` - Statistical baselines for anomaly detection
|
||||
- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/posts/YYYY-MM-DD-<shortid>.json` - one JSON batch per import (earliest post date + short batch id)
|
||||
|
||||
Weekly and monthly report files (under `weekly-reports/` and `monthly-reports/`) are created separately by `/linkedin:report`, not by import.
|
||||
|
||||
## State Tracking
|
||||
|
||||
After import, the system automatically:
|
||||
- Updates baseline metrics (mean, median, std dev for each metric)
|
||||
- Detects and flags anomalies (posts >2 sigma from baseline)
|
||||
- Organizes posts by ISO week for trend analysis
|
||||
- Preserves historical data (never overwrites existing weeks)
|
||||
- Updates `last_import_date` and `last_import_week` in state file
|
||||
After import:
|
||||
- A new batch file `posts/YYYY-MM-DD-<shortid>.json` is written, one per import — existing batch files are never overwritten; `loadAllPosts` deduplicates by post id at read time (latest import wins)
|
||||
- Intra-batch spike/drop alerts are computed and surfaced (std deviation from the batch's own mean — no persisted baseline)
|
||||
- `last_import_date` and `last_import_week` are updated in the state file (`~/.claude/linkedin-studio.local.md`, see Step 6b)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue