Skip to content

M5: Synthesizer — skill invocation, audit chain, Slack delivery #8

Description

@yashoza19

Summary

Implement M5: end-to-end management report synthesis from confirmed ledger entries — skill invocation, audit chain persistence, and Slack channel delivery.

Design reference: docs/DESIGN.md §5.4 (Synthesizer), §7.3–7.4 (contracts), §8 (Skills integration)

Current state (partial implementation)

Component Status
skills/weekly-status-synthesizer/SKILL.md Exists — rules ported from weekly_status-main/.claude/commands/format-status.md
src/status/skills/synthesizer.py Stub — build_synthesis_input() + run_synthesizer() with dry-run fallback
src/status/cli.py status report Exists — --dry-run defaults True; writes markdown to stdout or -o file
report_run / report_entry tables Schema exists in models.py + Alembic — not written to yet
Slack report delivery Not implemented
Skill publish status skills publish --skill synthesizer works if SYNTHESIZER_SKILL_ID unset (creates new)

Known bugs to fix in M5

In src/status/skills/synthesizer.py:

  • display_name=entry.person_id — should use Person.display_name from DB join
  • flags=[] hardcoded — should include unacknowledged flag rows for the week
  • No persistence to report_run / report_entry after skill returns

Tasks

1. Publish synthesizer skill

status skills publish --skill synthesizer
# Set SYNTHESIZER_SKILL_ID and pin SYNTHESIZER_SKILL_VERSION in .env / OpenShift Secret

Validate against fixtures built from confirmed ledger rows (create fixtures/synthesis-input-*.json from real confirmed data — scrub PII, gitignore if needed).

2. Complete build_synthesis_input()

  • Join Person for display_name
  • Load unacknowledged flags: Flag where week_ending matches and acknowledged = false
  • Include all participation rows for the week (confirmed, expired, sent, send_failed, on_leave)
  • Only include status_entry where is_current AND confirmed_at IS NOT NULL

Repo helpers exist in src/status/db/repo.py:

  • get_confirmed_entries_for_week
  • get_participation_for_week

3. Persist audit chain

After successful skill invocation, write:

report_run:

  • week_ending, prompt_version, model, generated_at
  • output_uri — optional path or object store URI if saving markdown file
  • delivered_at — set when posted to Slack

report_entry:

  • One row per cited entry: (run_id, entry_id, section) where section comes from synthesizer metadata if available

New module suggested: src/status/db/report.py

def persist_report_run(session, week_ending, output: SynthesisOutput, *, model, prompt_version) -> ReportRun:
    ...

4. Wire status report CLI

  • Change default --dry-run to False when SYNTHESIZER_SKILL_ID is set (keep --dry-run flag for testing)
  • Add --deliver flag to post markdown to REPORT_CHANNEL_ID via Slack Web API
  • Add --persist (default true) to write report_run rows

5. Slack delivery

Post synthesized markdown to private channel (REPORT_CHANNEL_ID):

  • Use slack_sdk.WebClient.chat_postMessage with mrkdwn text
  • Long reports may need splitting (Slack 40k char limit) — split on section headers if needed
  • Set report_run.delivered_at on success

Optional: attach as snippet or upload file if formatting breaks in chat.

6. Synthesis validation

SynthesisOutput schema in src/status/skills/schemas.py — ensure Pydantic validates:

  • markdown non-empty
  • week_ending matches input
  • Metadata fields: sections_used, entries_cited, non_responders, asks

On validation failure: do not persist partial report; surface error to CLI.

7. Tests

  • tests/test_synthesizer.py — build input from fixture DB session (SQLite or mocked)
  • Test dry-run path unchanged
  • Test report_run + report_entry rows created on persist
  • Golden test: synthesis input JSON shape, not markdown prose

Synthesis rules (skill must enforce)

From design doc / SKILL.md:

  • Sections: Partner Enablement, Certification / CI, Mindshare
  • Format: * **Name** - description with links on noun phrases
  • No raw Jira IDs in visible text
  • Alphabetical within sections
  • Non-responders explicit
  • Every ask from ledger appears in report
  • Organize by epic/project, not by person

Acceptance criteria

  • status report --week 2026-08-14 invokes skill when configured (not dry-run)
  • report_run and report_entry populated with audit chain
  • --deliver posts to REPORT_CHANNEL_ID
  • Confirmed entries only; expired participation reflected in output metadata
  • Tests pass: pytest tests/test_synthesizer.py

Branch / PR

  • Branch: feat/m5-synthesizer
  • Base: main
  • PR title: feat(synthesizer): management report generation and delivery (M5)

Depends on

  • M0–M3 on main (ledger + confirm flow)
  • At least one week of confirmed entries in Postgres for integration testing
  • M4 not strictly required but improves data quality before first real report

Out of scope (M6 / Phase 2)

  • Monday 09:00 auto-expire unconfirmed (participation.status = 'expired') — M6
  • CronJob lock-and-report
  • DOCX export / email (weekly_status-main commands)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/featureCategorizes issue or PR as related to a new feature.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions