Skip to content

Add AAIF Meetups Toolkit (flattened single-plugin marketplace) + repo hardening#1

Merged
dpbrinkm merged 4 commits into
mainfrom
toolkit-and-hardening
Jun 30, 2026
Merged

Add AAIF Meetups Toolkit (flattened single-plugin marketplace) + repo hardening#1
dpbrinkm merged 4 commits into
mainfrom
toolkit-and-hardening

Conversation

@rparundekar

@rparundekar rparundekar commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Bootstraps the aaif/meetups repo: lands the full AAIF Meetups Toolkit as a flattened single-plugin Claude Code marketplace, with repo hardening (pre-commit, CI, schema validation). main is an empty README; this PR brings everything in for review.

Commits

  1. Add AAIF Meetups Toolkit plugin + marketplace — 12 Agent Skills + manifests + docs.
  2. Harden repo — pre-commit, CI, manifest $schema, changelog.

Layout: flattened single-plugin marketplace

The repo root is both the marketplace and the plugin — no plugins/<name>/ nesting. Per the docs, a marketplace plugin's source may be "./" (the marketplace root). The $schema enforces this (source must match ^\./.*).

.claude-plugin/
  ├── marketplace.json   (source: "./")
  └── plugin.json
skills/                  (12 skills)

Installs unchanged: /plugin marketplace add aaif/meetups/plugin install aaif-meetups@aaif.

Hardening

  • .pre-commit-config.yaml — hygiene, check-json/check-yaml, Ruff (bug-focused lint, no style churn), codespell, gitleaks, and a local SKILL.md frontmatter validator.
  • .github/workflows/validate.yml — CI runs pre-commit + claude plugin validate . (one call validates marketplace and plugin.json schema since source: "./").
  • $schema on both manifests (SchemaStore — same as anthropics/claude-code).
  • pyproject.toml, CHANGELOG.md, CONTRIBUTING updates.

🐛 Bug fixed in passing

aaif-create-chapter and aaif-create-online-series had argument-hint: "<X>" [--slug …] — a quoted YAML scalar followed by bare text, which is invalid YAML. The frontmatter failed to parse, so the skill loaded with all metadata silently dropped (including the description that drives auto-activation). Both single-quoted. The check-skill-frontmatter hook now guards against regressions (validate's marketplace check does not parse skill frontmatter).

Deliberately not done

  • No ruff-format/pyupgrade — would rewrite the delicate OOXML scripts (~+579/−161); a formatting sweep is its own PR.
  • No pretty-format-json — it escaped the manifests' em-dashes and exploded the keyword arrays.
  • Hardcoded Google IDs left as-is — resource IDs (not secrets), intentionally shipped per the README; gitleaks confirms no credentials.

Self-review fixes (post-open, commits 3cd505e, 67fcb5e)

A 5-agent self-review ran; doc fixes + four correctness fixes were applied (full breakdown in the PR comments):

  • luma check is now 3-state (live / absent / could-not-verify) — no longer reports every network error as a hard "NOT LIVE (404)".
  • Residual rebrand tokens fail the run (non-zero exit) instead of a missable inline note under "Done."
  • clean.py / intake.py hard-error on a missing required header instead of silently reporting an empty queue; fixed a latent TypeError in clean.py when the Email column is absent.
  • gws_json raises on empty/non-JSON stdout instead of silently skipping a subtree.

Deferred to follow-ups: expanding OOXML rebrand part-coverage (now surfaced loudly by the residual hard-fail), de-duplicating the rebrand engine across the two create scripts, and adding unit tests.

Verification

  • pre-commit run --all-files — all hooks pass.
  • claude plugin validate . — passes (marketplace + plugin.json).

🤖 Generated with Claude Code

rparundekar and others added 2 commits June 30, 2026 14:20
Single-plugin Claude Code marketplace (root is both the marketplace and the
plugin via source "./"). 12 Agent Skills for running AAIF in-person and online
meetups: content writing (announcement, carousel, Luma description, speaker
invite/bio, day-of slides, attendee reminder, recap) and ops (create-chapter,
create-online-series, triage-intake, clean-data).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .pre-commit-config.yaml: hygiene hooks, check-json/yaml, Ruff (bug-focused
  lint, no style churn), codespell, gitleaks, and a local SKILL.md frontmatter
  validator (scripts/check_frontmatter.py).
- .github/workflows/validate.yml: CI runs pre-commit + `claude plugin validate`.
- pyproject.toml: Ruff + codespell config.
- $schema on marketplace.json and plugin.json (SchemaStore).
- CHANGELOG.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rparundekar

Copy link
Copy Markdown
Collaborator Author

Hero Self-Review

Five pr-review-toolkit agents (code-review, silent-failure, test, comments, type-design) ran against main...toolkit-and-hardening. Aggregated below. Most script findings are pre-existing behavior in the helper scripts (now landing fresh via this bootstrap PR); the doc findings are from this session's flatten/hardening work.

Critical (1)

  • [silent-failure] skills/aaif-create-{chapter,online-series}/scripts/*.py luma_ok() — a bare except Exception: return False collapses timeouts, DNS/SSL/connection errors, and 403/429/5xx all into a hard-coded NOT LIVE (404) message. The organizer is told as fact that the Luma page doesn't exist (and to create it) when it may exist but be unreachable → duplicate pages. Should distinguish live / confirmed-404 / could-not-verify.

Important (8)

  • [code-review · type-design · silent-failure] skills/aaif-clean-data/scripts/clean.py:129not (row[ni] or row[ei] or "") raises TypeError (row[None]) when the Email header is absent and a name cell is empty. Every other ei/ci access is None-guarded; only this gate isn't. Localized, safe fix.
  • [silent-failure] residual rebrand tokens are printed inline (!! residual) but the run still exits 0 with Done. — a chapter/series can ship still saying "San Francisco" and report success. Accumulate residuals → loud summary + non-zero exit.
  • [code-review] OOXML rebrand (rebrand_part) only covers slides/document/sharedStrings/metadata — text in slide masters/layouts/notes, Word headers/footers, and DrawingML text boxes is copied through unbranded. (Same root cause as the residual finding; making residual a hard failure at least surfaces it.)
  • [silent-failure] missing required header → silent false success: clean.py prints "Clean — nothing to fix." and intake.py reports "0 awaiting review" when the key column (Full name / Timestamp) isn't found. Header rename is realistic given these read by name. Treat missing required header as a hard error.
  • [silent-failure] gws_json returns {} on empty-but-successful stdout → list_children returns [] → a subtree silently fails to clone, run still says Done.
  • [comments] CHANGELOG.md:5 references plugins/aaif-meetups/.claude-plugin/plugin.json — the old nested path removed by the flatten; manifest is now .claude-plugin/plugin.json. (This session's bug.)
  • [comments] scripts/check_frontmatter.py:6 docstring claims "claude plugin validate catches this in CI" — empirically false (I verified validate does not parse skill frontmatter); contradicts CONTRIBUTING/validate.yml. (This session's bug.)
  • [comments] .pre-commit-config.yaml:52 comment "claude plugin validate is the CI authority" — same inaccuracy; the check-skill-frontmatter hook is the actual frontmatter authority. (This session's bug.)

Suggestions (selected)

  • [comments] .pre-commit-config.yaml:28-35 — "no auto-format" comment sits next to ruff-check --fix, which does mutate files (removes unused imports). Clarify.
  • [code-review · silent-failure] intake.py:46 fragile json.loads(txt[txt.index("{"):]) vs the safer clean.py:41 approach; align + wrap in a clear error.
  • [code-review] slugify can yield an empty slug for all-non-ASCII names → aaif- URLs; require --slug when empty.
  • [type-design · test · comments] the rebrand engine is duplicated near-verbatim across create_chapter.py and create_series.py; one bugfix must land twice. Candidate for a shared helper.
  • [test] cheapest high-value tests: transform_text rebrand rules (order-dependence, \bSF\b boundaries, casing), check_frontmatter.check(), and clean.py normalizers + colletter off-by-one. Pure functions, no fixtures.
  • [code-review · type-design] minor handle leaks (check_frontmatter.py:18, ZipFile(...).testzip()); name param vs ctx["name"] ambiguity in clone_and_rebrand.

Strengths

  • No injection vectors: every gws call passes an argument list (never shell=True); all user-derived values go through json.dumps.
  • All 12 SKILL.md frontmatter blocks parse with non-empty name+description; both manifests parse; all 5 scripts py_compile clean.
  • Flattened source: "./" layout is valid; claude plugin validate . correctly covers both manifests.
  • Defensive design already present: _gws transient retry, residual_tokens post-check, --dry-run guards, correct binary-part passthrough. check_frontmatter.py has clean "list of errors, empty = OK" contracts.

Self-review by Claude Code

rparundekar and others added 2 commits June 30, 2026 14:50
- CHANGELOG: point at .claude-plugin/plugin.json, not the removed nested path.
- check_frontmatter docstring + pre-commit comments: claude plugin validate does
  NOT parse skill frontmatter; this hook is the guard (locally and in CI).
- Clarify the ruff comment: --fix removes unused imports but does not reformat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address silent-failure / correctness findings from the self-review:

- luma check is now 3-state (live / absent / could-not-verify) instead of
  reporting every timeout, DNS/SSL or 5xx error as a hard "NOT LIVE (404)".
- residual rebrand tokens are accumulated and now fail the run (non-zero exit)
  instead of an easy-to-miss inline note under a "Done." success message.
- clean.py / intake.py treat a missing required header (Full name/Email,
  Timestamp/Status) as a hard error rather than silently reporting "nothing to
  fix" / "0 awaiting review".
- gws_json raises on empty or non-JSON stdout instead of returning {} (which
  silently skipped a subtree during clone).
- clean.py: guard the Email column before indexing (was a latent TypeError when
  the Email header is absent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rparundekar

Copy link
Copy Markdown
Collaborator Author

Hero Self-Review Improvements

Applied the doc fixes + all four correctness fixes. Two commits: 3cd505e (docs), 67fcb5e (scripts).

Critical (1 / 1 fixed):

  • create_chapter.py / create_series.py luma_okluma_status: now 3-state (live / absent / unknown) instead of collapsing every timeout, DNS/SSL, or 5xx into a fabricated NOT LIVE (404). Catches HTTPError (404 → absent, else unknown) separately. Unit-verified: 200→live, 404→absent, 503→unknown, network-error→unknown.

Important (8 / 8 fixed):

  • clean.py:129 — guarded the Email column before indexing; the latent TypeError (row[None]) when the Email header is absent is gone.
  • residual rebrand tokens — accumulated across the walk; the run now prints a WARNING: summary and exits non-zero instead of an easy-to-miss inline note under Done. (both create scripts; --rebrand-local too).
  • clean.py / intake.py missing required header (Full name/Email, Timestamp/Status) — now a hard ABORT naming the actual headers, instead of "nothing to fix" / "0 awaiting review".
  • gws_json — raises on empty or non-JSON stdout instead of returning {} (which silently skipped a subtree during clone).
  • CHANGELOG.md:5 — points at .claude-plugin/plugin.json, not the removed nested path.
  • check_frontmatter.py docstring + .pre-commit-config.yaml frontmatter comment — corrected: claude plugin validate does not parse skill frontmatter; this hook is the guard (locally and in CI).

Suggestions (1 fixed, rest deferred):

  • Fixed: .pre-commit-config.yaml ruff comment now clarifies --fix removes unused imports but does not reformat.

Skipped / deferred (with reasons):

  • OOXML rebrand part coverage (masters/layouts/headers/footers/text-boxes) — real gap, but a larger change; the residual-token check is now a hard failure, so any unbranded part fails the run loudly rather than shipping silently. Worth a focused follow-up.
  • De-duplicate the rebrand engine across the two create scripts, and the suggested unit tests (transform_text, check_frontmatter, clean normalizers/colletter) — valuable but out of scope for this bootstrap PR; left as follow-ups.
  • Minor: intake.py first-brace JSON slicing, slugify empty-slug guard, file-handle context managers, name vs ctx["name"] rename — low-severity polish, deferred.

Commits: 3cd505e, 67fcb5e


Applied by Claude Code

@rparundekar rparundekar marked this pull request as ready for review June 30, 2026 10:07
Copilot AI review requested due to automatic review settings June 30, 2026 10:07
@dpbrinkm dpbrinkm merged commit b3b8a1c into main Jun 30, 2026
3 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Bootstraps the aaif/meetups repository by adding the AAIF Meetups Toolkit as a flattened single-plugin Claude Code marketplace (marketplace root is also the plugin source) and introducing repo hardening via pre-commit and CI validation.

Changes:

  • Adds 12 AAIF meetup “Agent Skills” (content + ops) with supporting Python helper scripts for Drive/Sheets workflows.
  • Introduces repo hardening: pre-commit hooks (YAML/JSON checks, Ruff, codespell, gitleaks, SKILL frontmatter validation) and a GitHub Actions workflow to run validation.
  • Adds manifests (plugin.json, marketplace.json) and repository documentation (README/CONTRIBUTING/CHANGELOG) plus basic tooling config (pyproject.toml).

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
skills/aaif-triage-intake/SKILL.md New intake-triage skill instructions for reviewing and digesting the intake queue.
skills/aaif-triage-intake/scripts/intake.py Script to pull and format intake queue rows from Google Sheets via gws.
skills/aaif-speaker-invite/SKILL.md New writing skill for speaker outreach messages.
skills/aaif-speaker-bio/SKILL.md New writing skill for speaker bios.
skills/aaif-recap-post/SKILL.md New writing skill for LinkedIn recap posts.
skills/aaif-luma-description/SKILL.md New writing skill for Luma event page descriptions.
skills/aaif-dayof-slides/SKILL.md New writing skill to generate “day-of” deck slide text.
skills/aaif-create-online-series/SKILL.md New ops skill instructions for creating/rebranding an online series in Drive.
skills/aaif-create-online-series/scripts/create_series.py Script to clone/rebrand TemplateSeries Drive assets and check Luma status.
skills/aaif-create-chapter/SKILL.md Ops skill instructions for creating/rebranding a city chapter in Drive.
skills/aaif-create-chapter/scripts/create_chapter.py Script to clone/rebrand TemplateCity Drive assets and check Luma status.
skills/aaif-clean-data/SKILL.md Ops skill instructions for scanning/applying intake data cleanup + installing error flags.
skills/aaif-clean-data/scripts/clean.py Cleanup engine for Intake Ops sheet (scan/apply/install bright-red Issues rule).
skills/aaif-carousel-copy/SKILL.md New writing skill for LinkedIn carousel copy.
skills/aaif-attendee-reminder/SKILL.md New writing skill for attendee reminders.
skills/aaif-announcement-post/SKILL.md New writing skill for RSVP-open announcement posts.
scripts/check_frontmatter.py Pre-commit/CI hook to validate SKILL.md YAML frontmatter parsing.
README.md Full repository/product README including install and usage docs.
pyproject.toml Ruff + codespell configuration for helper scripts.
LICENSE MIT license.
CONTRIBUTING.md Contribution guidelines, including frontmatter conventions and validation steps.
CHANGELOG.md Initial changelog for plugin versions and notable changes.
.pre-commit-config.yaml Pre-commit hooks for hygiene, linting, secret scanning, and frontmatter validation.
.gitignore Ignores Python caches and Ruff cache.
.github/workflows/validate.yml CI workflow running pre-commit and claude plugin validate ..
.claude-plugin/plugin.json Claude Code plugin manifest (aaif-meetups).
.claude-plugin/marketplace.json Claude Code marketplace manifest (flattened, source: "./").

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/aaif-speaker-invite/SKILL.md
Comment thread skills/aaif-speaker-bio/SKILL.md
Comment thread skills/aaif-announcement-post/SKILL.md
Comment thread skills/aaif-carousel-copy/SKILL.md
Comment thread skills/aaif-luma-description/SKILL.md
Comment thread skills/aaif-dayof-slides/SKILL.md
Comment thread skills/aaif-attendee-reminder/SKILL.md
Comment thread skills/aaif-triage-intake/scripts/intake.py
Comment thread skills/aaif-clean-data/scripts/clean.py
Comment thread scripts/check_frontmatter.py
@rparundekar rparundekar deleted the toolkit-and-hardening branch June 30, 2026 10:50
@rparundekar

Copy link
Copy Markdown
Collaborator Author

Respond-to-PR Improvements

PR #1 was already merged when these Copilot comments were addressed, so the fixes land in follow-up #2 (branch fix/copilot-pr1-feedback). All 11 threads have been replied to and resolved.

Code changes (11 applied, all in #2):

  • 8× SKILL.md argument-hint:4 — bare flow sequence [...] → single-quoted scalar, per CONTRIBUTING.md convention (announcement-post, attendee-reminder, carousel-copy, dayof-slides, luma-description, recap-post, speaker-bio, speaker-invite).
  • skills/aaif-triage-intake/scripts/intake.py:46 — fragile json.loads(txt[txt.index("{"):]) → safe JSON-start lookup with a clear error on missing/invalid gws output.
  • skills/aaif-clean-data/scripts/clean.py:252 — Issues "bad email" rule SEARCH("@")REGEXMATCH(^local@domain\.tld$), so foo@/foo@bar are flagged.
  • scripts/check_frontmatter.py:19open(...).read()with open(...) context manager.

Questions answered: none
Declined: none

Verification: py_compile clean, all 12 SKILL.md frontmatter blocks parse, pre-commit run passes (ruff, codespell, gitleaks, frontmatter).

Remaining unresolved threads: none


Applied by Claude Code

rparundekar added a commit that referenced this pull request Jun 30, 2026
Address Copilot PR #1 feedback + document required OAuth scopes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants