From platinum-seo-engine
Use when: kullanıcı "aylık portföy", "portfolio monthly roundup", "tüm projeler aylık", "aylık roundup", "monthly roundup", "portföy aylık özet", "active_projects aylık rapor" der ya da cadence.monthly_roundup.day_of_month cron tetiklenir; portföy çapında bir aylık KPI roll-up + EditorialOverrides notları istendiğinde devreye girer. Also use when: portfolio.config.json v1.1 mevcut + active_projects doluyken (1-12 entry, schema maxItems=12); her aktif proje için master.xlsx ulaşılabilir; aylık 30 günlük dilim üzerinden exec_summary + keywords_up + pages_up + tech_seo_done + content_revised + new_content + next_month_plan toplaması istenir; per-project EditorialOverrides (sla_days / priority / cadence / override_rationale / ramp_plan) precedence ile raporlanır; READ-ONLY agregasyon yeterli (yazma kapsamı YOK). Do not use when: portfolio.config.json yokken (init-portfolio önce çalışmalı, DURUR PortfolioConfigMissingError); tek proje aylık istenirse (monthly-report skill'ine yönlendir, bu skill PORTFÖY scope); haftalık dökümle karıştırma (portfolio-weekly-brief skill'i ayrı); master.xlsx writer çağrısı isteniyorsa (FORBIDDEN — bu skill 100% READ-ONLY, hiçbir master_task hücresine yazmaz).
How this skill is triggered — by the user, by Claude, or both
Slash command
/platinum-seo-engine:portfolio-monthly-roundupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Multi-project LOCAL aggregator for the **monthly** cadence. Reads
Multi-project LOCAL aggregator for the monthly cadence. Reads
projects/_portfolio/portfolio.config.json v1.1, iterates
active_projects[], and for each project emits a 30-day roll-up
covering the 7-section subset of monthly-report.schema.json
(exec_summary + keywords_up + pages_up + tech_seo_done +
content_revised + new_content + next_month_plan) plus per-project
EditorialOverrides (sla_days / priority / cadence /
override_rationale / ramp_plan) surfaced via precedence rules.
This skill follows the convention authority established by
skills/planning/master-task-sync/SKILL.md (Phase 8 W-D1 — pure
function transform + READ-ONLY discipline + plugin-agnostik) and
inherits the Wave 1 path convention from
skills/reporting/portfolio-weekly-brief/SKILL.md and the
read-only grep-guard from
skills/reporting/portfolio-overview/SKILL.md. The discipline
(DURUR raises, no silent fallback, schema-first, plugin agnostik,
append-only state contract) is reused verbatim. Deviate only with
an ADR.
Unlike Phase 8 master_task_sync, this skill never writes to any
project's master.xlsx or _state/events.jsonl. Outputs land
exclusively under the portfolio workspace
(projects/_portfolio/outputs/reports/ + inbox/local/).
| Name | Type | Default | Notes |
|---|---|---|---|
portfolio_root | string | env | Optional. Defaults to PSEO_WORKSPACE_ROOT env or .pse-workspace marker discovery. |
period_end | string | — | Optional ISO date override (YYYY-MM-DD). Default: most recent cadence.monthly_roundup.day_of_month. |
workspace_root is resolved via PSEO_WORKSPACE_ROOT env or
explicit test override (mirrors workflow_runner / events_writer /
W-D1 / W-E3 / W-E4).
master.xlsx#none — sentinel acknowledging that the skill READS
per-project master.xlsx#dashboard + #completed_work +
#new_content_plan but writes ZERO cells anywhere. The
frontmatter outputs[] entry is intentional schema documentation
(no openpyxl mutation, no transaction layer call).projects/_portfolio/outputs/reports/{period_end}-portfolio-monthly-roundup.md
— human-readable Markdown roundup (multi-project monthly KPI
roll-up table + EditorialOverrides notes section + totals).projects/_portfolio/inbox/local/{period_end}-portfolio-monthly-roundup.json
— drift-recovery snapshot; full PortfolioMonthlyRoundup envelope
including per-project rollups + override precedence + totals.The literal projects/_portfolio/{outputs,inbox}/local/ prefix
appears in the transform module — paths are NEVER synthesized
from a slug template. workspace_root resolution order:
--workspace-root CLI flag (test override).PSEO_WORKSPACE_ROOT environment variable..pse-workspace marker walk-up from cwd.If all three fail → WorkspaceRootUnsetError (DURUR).
| # | Source | Discipline |
|---|---|---|
| 1 | portfolio.config.json v1.1 | active_projects + cadence.monthly_roundup + EditorialOverrides |
| 2 | per-project master.xlsx#dashboard | KPI cells (R10, R47-R52, R59) — read_only=True, data_only=True |
| 3 | per-project master.xlsx#completed_work | columns A-F (id, task_or_content, url, date, category, note) |
| 4 | per-project master.xlsx#new_content_plan | columns A-K (id, title, …, lifecycle_status) |
| 5 | per-project _state/events.jsonl | event_kind=work, last 30 days only |
schemas/portfolio-config.schema.json v1.1:
cadence.monthly_roundup.day_of_month — integer 1-31 (drives
default period_end).cadence.monthly_roundup.hour — integer 0-23 (advisory, used by
the scheduler — not by this transform).EditorialOverrides.sla_days — integer 1-30 (per-project
override of slas.weekly_sync_max_days).EditorialOverrides.priority — integer 1-10 (per-project
override of priority; precedence over ActiveProjectEntry.priority).EditorialOverrides.cadence — enum [weekly, biweekly, monthly]
(per-project override of report cadence).EditorialOverrides.override_rationale — string minLength: 10
(MANDATORY when any other EditorialOverrides field is set; the
transform raises EditorialOverrideRationaleError on a payload
shorter than 10 characters even if jsonschema validation is
skipped).EditorialOverrides.ramp_plan — string (optional retirement
plan).schemas/monthly-report.schema.json v1.0 — 7-section subset reused
for the per-project roll-up:
exec_summary (narrative + headlines).keywords_up (improved positions, top-N).pages_up (clicks/impressions delta gains, top-N).tech_seo_done (completed_work entries with category tech_seo).content_revised (completed_work entries with category content_revise).new_content (completed_work entries with category content_new).next_month_plan (priority-sorted top-10 master_task TODO).(gsc_positive_trends, competitor_snapshot, backlink_delta
are intentionally excluded — they require GSC/DFS pulls that this
local-aggregation skill does not perform.)
The transform raises PortfolioConfigSchemaError (DURUR) if any
of these fields drift from the schema. Tests assert the cadence /
EditorialOverrides branches against the canonical Draft 7 schema.
scripts/reporting/portfolio_monthly_roundup.py (pure function,
< 600 lines — transform size gate; ADR-027 sets the < 1500L policy):
build_portfolio_roundup(portfolio_config, workspace_root, now, period_end=None) -> PortfolioMonthlyRoundup — top-level
idempotent aggregator.aggregate_project(...) -> ProjectRollup — one project's
30-day rollup including override-resolved priority + cadence.resolve_editorial_overrides(entry, portfolio_priority) -> ResolvedOverrides — isolated precedence logic (test sentinel).read_events_in_window(events_path, ...) — fault-tolerant
events.jsonl reader (last 30 days).read_dashboard_kpis(workbook_path) — openpyxl thin wrapper,
read_only=True, INLINE (no shared excel helper imported).read_completed_work_window(workbook_path, window_start, window_end) — same READ-ONLY discipline.render_roundup_markdown(roundup, template_path) —
string.Template $var substitution, matches
scripts/reporting/render_template.py engine.assert_read_only_module() — source-grep helper that fails when
any forbidden write call site appears (W-E3 paterni reuse).templates/reports/portfolio-monthly-roundup.template.md —
Markdown with YAML frontmatter (period_start, period_end,
generated_at, scope, projects_count). Body includes:
$totals_summary — single-line rollup sentence.$projects_kpi_table — multi-row Markdown table with columns
slug | priority | cadence | tasks_done | new_content | content_revised | tech_seo_done | next_month_priority.$editorial_overrides_notes — per-project override rationale
block (only renders projects with active overrides).string.Template engine (NOT jinja2) — same as
scripts/reporting/render_template.py (Phase 1 mirası). All
variables are stringified by build_template_vars() so the
template engine never sees a non-string value.
For each active_projects entry:
effective_priority = entry.editorial_overrides.priority
if isset else entry.priority
effective_cadence = entry.editorial_overrides.cadence
if isset else "monthly"
effective_sla_days = entry.editorial_overrides.sla_days
if isset else slas.weekly_sync_max_days
Constraint: any EditorialOverrides override field set =>
override_rationale required + minLength 10.
Schema validates; transform raises
EditorialOverrideRationaleError on a 9-char rationale.
The per-project overrides are surfaced in the
$editorial_overrides_notes section of the markdown report and in
overrides_resolved of the JSON snapshot.
Stop and flag the manager — do not patch, do not fall back.
portfolio.config.json absent
/ unreadable. Run portfolio init first.schemas/portfolio-config.schema.json.active_projects > 12 (schema
maxItems sentinel). Move surplus to pending_onboard.cross_query.read_only != true
(schema const: true) OR transform module contains a forbidden
write call site (assert_read_only_module() grep guard).PSEO_WORKSPACE_ROOT env unset
AND no explicit workspace_root passed AND no .pse-workspace
marker discoverable from cwd.(Per-project missing master.xlsx is NOT a DURUR — graceful
skip with warning, snapshot still emitted.)
Re-run with identical workbook + events.jsonl + portfolio config + same
now→ byte-identical snapshot + report.
Mechanically:
(effective_priority, slug).sort_keys=True + indent=2.generated_at is microsecond-truncated to second precision.transaction.append / transaction.update /
transaction.delete calls anywhere.events_writer.append_* calls anywhere (Q-RP-01 governance
refinement deferred to Phase 14 — this skill DOES NOT write
events.jsonl).openpyxl.load_workbook(read_only=True, data_only=True) for
every workbook open.master.xlsx#none outputs[] entry documents the READ-ONLY
contract: no logical sheet is touched.assert_read_only_module() greps the transform source for the
forbidden token set: transaction.*, events_writer.*,
.save(), .delete(), .append(. Test suite invokes this
helper as gate #11.portfolio.config.json.active_projects[].slug.Schema-first override Section 4c paterni reuse (drift-check Phase 5 doğum belgesi). event_kind=audit row append per rules/events-writer.md Section 4c — event_type field YASAK per Section 6 disambiguation.
import os
import sys
from pathlib import Path
sys.path.insert(0, os.getcwd())
project_slug = os.environ.get("PSEO_PROJECT_ID")
if not project_slug:
print("PSEO_PROJECT_ID not set — audit event skip")
sys.exit(0)
report_date = os.environ.get("REPORT_DATE", "2026-05-06")
workspace_root_env = os.environ.get("PSEO_WORKSPACE_ROOT")
workspace_root = (
Path(workspace_root_env) if workspace_root_env
else Path.home() / "Documents" / "platinum-seo-workspace"
)
from scripts.state import events_writer
events_writer.append_audit(
project_id=project_slug,
audit_action="accessed",
audit_target=f"reports:portfolio-monthly-roundup:{report_date}",
actor="agent:portfolio-monthly-roundup",
workspace_root=workspace_root,
notes=f"local_aggregation report-only (portfolio-monthly-roundup; date={report_date})",
)
schemas/portfolio-config.schema.json v1.1
(cadence.monthly_roundup branch + EditorialOverrides
precedence), schemas/monthly-report.schema.json v1.0 (7-section
subset reuse), schemas/skill-frontmatter.schema.json (this
frontmatter validates against it).scripts/reporting/render_template.py
(string.Template $var rendering convention — re-instantiated
inline in render_roundup_markdown for purity). NO
transaction.py, NO events_writer.py, NO workflow_runner.py
imports.scripts/reporting/portfolio_monthly_roundup.py.templates/reports/portfolio-monthly-roundup.template.md.tests/skills/test_portfolio_monthly_roundup.py (8-10
cases incl. cadence schema validate, EditorialOverrides
precedence sentinel, override_rationale minLength 10 sentinel,
multi-project KPI roll-up, missing project tolerance, 30-day
events filter, natural_language min length, forbidden tokens
guard, READ-ONLY grep guard, path convention).scripts/reporting/portfolio_weekly_brief.py
(Phase 9 W-E4 — path convention + workspace_root resolver) +
scripts/reporting/portfolio_overview.py (Phase 9 W-E3 —
assert_read_only_module helper + 5 DURUR exception classes).schemas/skill-frontmatter.schema.json Draft 7; cadence +
EditorialOverrides branches validated against
portfolio-config.schema.json v1.1.Use when / Also use when / Do not use when
are STRING content inside description, not separate fields.transaction.* / events_writer.*
imports in the transform module (W-E3 helper grep guard).now → byte-stable
output (asserted by smoke test).projects/_portfolio/{outputs,inbox}/local/ literal in
transform + PSEO_WORKSPACE_ROOT env resolution + per-skill
test_path_convention sentinel.minLength 10
sentinel test — rejects 9-char payload.npx claudepluginhub popiliadam/platinum-seo-engine --plugin platinum-seo-engineUse when: kullanıcı "haftalık portföy", "portfolio weekly", "tüm projeler haftalık", "haftalık brief", "weekly brief", "portföy haftalık raporu", "active_projects haftalık özet" der ya da cadence.weekly_brief.day cron tetiklenir. Also use when: portfolio.config.json v1.1 mevcut + active_projects doldurulmuş; her proje için master.xlsx + _state/events.jsonl ulaşılabilir; haftalık 7 günlük delta + last_sync_at freshness rapor olarak istenir; tek bir slug değil PORTFÖY ÇAPINDA özet beklenir; READ-ONLY agregasyon yeterli (yazma kapsamı YOK). Do not use when: portfolio.config.json yokken (init-portfolio önce çalışmalı, DURUR PortfolioConfigSchemaError); tek proje weekly istenirse (weekly-summary skill'ine yönlendir, bu skill PORTFÖY scope); aylık dökümle karıştırma (monthly-report skill'i ayrı); master.xlsx writer çağrısı isteniyorsa (FORBIDDEN — bu skill 100% READ-ONLY, hiçbir master_task hücresine yazmaz).
Generates an interactive HTML dashboard visualizing portfolio status — entity counts, progress, market matrix, pricing, competitors, customer profiles, and claims. Opens in browser with drill-down navigation.
Generates a weekly business retrospective from snapshots, learnings, skill-usage telemetry, and optional revenue data. Outputs decisions, shipped items, stalled work, skill activity, and suggested next focus.