| name | monthly-report |
| description | Use when: kullanıcı "aylık rapor", "monthly report", "ay sonu özet",
"geçen ay neler oldu", "ay raporu üret", "aylık müşteri raporu",
"monthly client report" der ya da /pseo-monthly çağırır.
Also use when: ayın son iş gününde aktif proje için
outputs/reports/{date}-monthly.md üretilecek; master.xlsx'te
master_task / completed_work / gsc_performance / opportunity /
content_decay / tech_seo / schema / new_content_plan /
content_improve sheet'leri ay boyunca dolmuş; monthly-report.schema.json
v1.0 formatında 10 zorunlu section + framing_policy "positive_client"
default + output_formats subset of html|pdf|notion isteniyor; Phase 9
reporting suite parçası, LOCAL aggregation (no MCP, no DFS).
Do not use when: haftalık özet (weekly-summary), portföy genel raporu
(portfolio-overview), portföy haftalık brief (portfolio-weekly-brief),
drift kontrol (drift-check), tek-seferlik analiz isteniyorsa — bunların
ayrı skill ve komutları vardır; master.xlsx yokken (DURUR
WorkbookMissingError, init-project önce çalışmalı); paid MCP credit
harcanması bekleniyorsa (bu skill 0 credit; DFS backlinks Phase 10+'ta
ayrı skill üzerinden).
|
| version | 1.0 |
| status | active |
| category | reporting |
| inputs | {"project_slug":{"type":"string","required":true,"description":"Slug; resolves projects/{slug}/master.xlsx + events.jsonl."},"period_end":{"type":"string","required":false,"description":"ISO YYYY-MM-DD; default last business day of current month."}} |
| outputs | ["master.xlsx#none","outputs/reports/{date}-monthly.md","inbox/local/{date}-monthly-{slug}.json"] |
| consumes | ["init-project:projects/{slug}/master.xlsx","master-task-sync:master.xlsx#master_task","done-protocol:master.xlsx#completed_work","content-decay:master.xlsx#content_decay","tech-audit:master.xlsx#tech_seo","schema-audit:master.xlsx#schema","new-content-plan:master.xlsx#new_content_plan","gsc-pull:master.xlsx#gsc_performance","quick-wins:master.xlsx#opportunity"] |
| produces | ["weekly-summary","portfolio-overview"] |
| triggers | {"manual":["/pseo-monthly"],"natural_language":"\"aylık rapor\", \"monthly report\", \"ay sonu özet\",\n\"geçen ay neler oldu\", \"ay raporu üret\",\n\"aylık müşteri raporu\", \"monthly client report\"\n","hooks":[],"scheduled":[{"cron":"0 9 28-31 * 1-5","mode":"report-only"}]} |
| mcp_tools | {"required":[],"optional":[]} |
| budget | {"uses_paid_mcp":false,"estimated_credits":0} |
| autonomy | {"confidence":"HIGH","requires_approval":false,"safe_auto_execute":true} |
monthly-report — reporting skill (Phase 9 Wave 1)
LOCAL aggregator: reads master.xlsx logical sheets + last-28-day events.jsonl
context (READ-ONLY) → emits a JSON report conforming to
schemas/monthly-report.schema.json v1.0 (10 required sections,
framing_policy default positive_client, output_formats subset of
[html, pdf, notion], data_sources provenance).
This skill follows the convention authority established by
skills/planning/master-task-sync/SKILL.md (Phase 8 W-D1 — local
aggregation pattern). Per the Phase 9 W-E1 worker brief (REVIZE 1+2+3):
outputs[] has exactly 3 entries — master.xlsx#none (READ-ONLY
confirm), outputs/reports/{date}-monthly.md, and
inbox/local/{date}-monthly-{slug}.json. No events.jsonl write.
events.jsonl is READ-ONLY for last-28-day work-event context;
whether reporting runs are themselves audit-worthy events is
Q-RP-01, deferred to Phase 14 governance refinement (manager OQ
append at closeout).
master_task is READ-ONLY; this skill never writes back. The
next_month_plan section reads top-10 TODO rows.
mcp_tools.required = [] and mcp_tools.optional = []. The
mcp__gsc__search_analytics tool MAY be invoked opportunistically by
a future skill body for cross-check, but is not declared here because
the LOCAL pipeline must run end-to-end without it (HIGH autonomy +
cron-ready).
budget.uses_paid_mcp = false and estimated_credits = 0 (no DFS, no
paid MCP, no Scrapling cloud).
Inputs (frontmatter contract)
| Name | Type | Default | Notes |
|---|
project_slug | string | — | Required. Resolves projects/{slug}/master.xlsx. |
period_end | string | last business day of current month | ISO YYYY-MM-DD. period_start = period_end - 27 days. |
workspace_root is resolved via PSEO_WORKSPACE_ROOT env var or an
explicit override (mirrors workflow_runner / events_writer / sibling
reporting skills).
Outputs (artifacts produced)
master.xlsx#none — declarative READ-ONLY confirmation. The transform
contains zero transaction.append / transaction.update calls
against master_task (or any other sheet); see acceptance gate #8.
projects/{slug}/outputs/reports/{period_end}-monthly.md — rendered
markdown via string.Template ($variable substitution; mirrors
scripts/reporting/render_template.py contract).
projects/{slug}/inbox/local/{period_end}-monthly-{project_slug}.json
— drift-recovery snapshot of the full schema-shaped report dict.
Sheets consumed (READ-ONLY)
| Sheet | Producer skill | Section feed |
|---|
master_task | master-task-sync | next_month_plan (top-10 TODO) |
completed_work | done-protocol | tech_seo_done / content_revised / new_content |
content_improve | content-decay | pages_up auxiliary |
new_content_plan | new-content-plan | new_content auxiliary |
gsc_performance | gsc-pull | gsc_positive_trends + pages_up |
opportunity | quick-wins | keywords_up |
content_decay | content-decay | content_revised auxiliary |
tech_seo | tech-audit | tech_seo_done auxiliary |
schema | schema-audit | content_revised auxiliary |
events.jsonl (last 28 days) is read for contextual work-event
enrichment; never written.
10 Required Sections (schemas/monthly-report.schema.json line 31-42)
exec_summary — auto-generated from sections 2-7 headlines
gsc_positive_trends — current vs prior 28-day deltas
keywords_up — top-20 queries with positive position delta
pages_up — top-10 pages with positive clicks_delta
tech_seo_done — completed_work filtered by category prefix "tech"
content_revised — completed_work filtered by category prefix "revis"
new_content — completed_work filtered by category prefix "new"
competitor_snapshot — Phase 10+ Scrapling S1/S3 (empty shape now)
backlink_delta — paid DFS MCP (zero shape; this skill = LOCAL only)
next_month_plan — top-10 TODO rows from master_task by priority
framing_policy defaults to positive_client (§22.3) and affects TONE
only, never facts (FIX-K K1). The internal framing may foreground declines;
both framings carry the SAME numbers in the additive sections below.
Additive optional sections (11–12)
Beyond the 10 required sections the report carries two additive, OPTIONAL
sections — NOT in schemas/monthly-report.schema.json sections.required, so
older reports stay schema-valid. Both are framing-INVARIANT (byte-identical
under positive_client and internal; their builders take no framing_policy):
decliners (FIX-K K1) — declining pages/content + the net signed clicks
delta. The facts survive framing; only the surrounding tone may differ.
measurement_context (GAP-M-W2) — a measurement-honesty annotation, per
rules/measurement-discipline.md (core-update overlap + intervention
cohorts):
core_updates_overlap + measurement_quality ∈ {clean,
update_overlap, post_update_settling, insufficient_history}: does the
window overlap a Google Search Ranking update (incl. the 7-day settling
buffer)? Deltas in an overlapping window MUST NOT be attributed to engine
work without this annotation.
intervention_outcomes: treated-vs-control quick-win verdicts (difference
in pp; n<30 — directional only; no p-values), never a raw treated delta.
Calendar source (overlap read)
The overlap read uses scripts/reporting/update_calendar.py. By DEFAULT the
bundled engine seed google-update-calendar.json is the source (no network,
cron-ready) — --calendar-path overrides it. An OPTIONAL fresher workspace
overlay (shared/cache/google-update-calendar.json, maintained out-of-band by
scripts/maintenance/refresh_update_calendar.py) is supplied via
--overlay-calendar-path (overlay wins by id). A missing calendar ⇒
measurement_quality = insufficient_history (overlap undeterminable — NEVER
fabricated as clean). This keeps the skill LOCAL / no-MCP.
Intervention outcomes wiring
Before rendering, locate quick-wins cohort snapshots
(_state/metrics/quickwin-cohorts/{date}-cohort.json) at least 21 days old,
pair each with the newest GSC inbox payload
(inbox/gsc/{date}-search_analytics-{slug}.json), compute treated-vs-control
outcomes, and pass the JSON array to monthly_report.py --cohort-results:
python3 scripts/reporting/intervention_outcome.py \
--cohort-dir projects/{slug}/_state/metrics/quickwin-cohorts/ \
--post projects/{slug}/inbox/gsc/{date}-search_analytics-{slug}.json \
--today {date} --min-age-days 21 \
--output projects/{slug}/_state/metrics/{date}-cohort-results.json
python3 scripts/reporting/monthly_report.py \
--project-slug {slug} --workspace-root "$PSEO_WORKSPACE_ROOT" \
--period-end {date} \
--cohort-results projects/{slug}/_state/metrics/{date}-cohort-results.json \
--output-dir projects/{slug}/outputs/reports/
Absent cohort files ⇒ intervention_outcomes = [] (the section says "kohort
verisi yok"); never invent an outcome. This wiring writes NOTHING to
master.xlsx and emits NO events.jsonl row — the skill stays READ-ONLY.
DURUR conditions (8 + base)
Stop and flag the manager — do not patch, do not fall back.
- FramingPolicyEnumViolation — value not in
{positive_client, internal}.
- OutputFormatEnumViolation — value not in
{html, pdf, notion}.
- DataSourceEnumViolation — value not in 7-value enum.
- MissingSectionError — assembler dropped a required section.
- ProjectIdShapeError —
project_id fails ^[a-z][a-z0-9-]*$.
- WorkbookMissingError —
projects/{slug}/master.xlsx not found.
- TemplateRenderError —
string.Template.substitute raised
KeyError (missing $key) or ValueError (bad syntax).
- MonthlyReportError (base) — invalid
period_end, openpyxl
missing, etc. Always surfaced with a descriptive message.
Audit Event Emit — DEFERRED (Q-RP-01, Phase 14+)
This reporting skill is READ-ONLY: it does not write to append-only
_state/events.jsonl. An audit-trail event_kind=audit row (per
rules/events-writer.md Section 4c) was proposed under Q-RP-01 but is
deferred to Phase 14+ governance refinement and is NOT wired — the
frontmatter (safe_auto_execute: true, outputs[] excludes events.jsonl),
the /pseo-monthly command, and events-writer.md all already encode this
deferral.
Do NOT add a runnable events_writer.append_audit(...) block here: on an
autonomous run it would mutate append-only state, contradicting the READ-ONLY
contract this skill, its frontmatter, and its paired test all assert. When
Q-RP-01 actually resolves, wire the emit through the sanctioned writer under
operator review — never as an inline skill block.
Cross-references
- Schemas:
schemas/monthly-report.schema.json (10 required sections +
framing_policy enum + output_formats enum + data_sources enum),
schemas/master-excel.schema.json (sheet shapes for the 9 consumed
sheets), schemas/skill-frontmatter.schema.json (this frontmatter),
schemas/events.schema.json (events.jsonl shape; READ-ONLY contract).
- Rules:
rules/measurement-discipline.md R-137 (core-update overlap
annotation — measurement_context.core_updates_overlap), R-138 (intervention
cohort tagging — measurement_context.intervention_outcomes, treated-vs-control).
- Cross-modules (IMPORT-only):
scripts/reporting/render_template.py
(Phase 1 mirası, string.Template substitution),
scripts/reporting/update_calendar.py (R-137 overlap read; engine seed ∪
overlay), scripts/reporting/intervention_outcome.py (R-138 treated-vs-control
outcome; run before this skill, fed via --cohort-results).
- Transform:
scripts/reporting/monthly_report.py.
- Template:
templates/reports/monthly-report.template.md.
- Tests:
tests/skills/test_monthly_report.py (8-10 tests; schema
validate output JSON + sentinel + edge case + forbidden-token grep).
- Pattern reference:
scripts/planning/master_task_sync.py (Phase 8
W-D1 local aggregation pure-transform pattern; reused verbatim).
Discipline checklist