-
Notifications
You must be signed in to change notification settings - Fork 2
PR: Create project fields schema and automation workflows #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9924a6b
7372136
ceedf5a
2467dbe
1497036
8a7ca46
04e3d42
dd23b8f
e7006c5
a04952b
a0f6b77
12688bc
79a79c0
a541ae3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,52 +1,107 @@ | ||
| --- | ||
| title: "Project Meta Sync Agent Spec" | ||
| version: "v1.0" | ||
| last_updated: "2025-10-21" | ||
| version: "v1.1" | ||
| last_updated: "2025-11-13" | ||
| author: "LightSpeed" | ||
| maintainer: "Ash Shaw" | ||
| description: "Spec for the Project Meta Sync Agent." | ||
| tags: ["lightspeed","project","meta","agents"] | ||
| description: "Spec for the Project Meta Sync Agent that maps labels and branch conventions to GitHub ProjectV2 fields." | ||
| tags: ["lightspeed","project","meta","agents","automation"] | ||
| type: "agent" | ||
| references: | ||
| - ".github/automation/project-fields.yml" | ||
| - ".github/automation/project-labeler.yml" | ||
| - ".github/automation/labels.yml" | ||
| - "schemas/automation/project-fields.schema.json" | ||
| - ".github/workflows/project-meta-sync.yml" | ||
| - ".github/agents/project-meta-sync.js" | ||
| --- | ||
|
|
||
| # Role | ||
| Sync project board meta fields (Status, Priority, Type) from labels and branch names. | ||
| Sync GitHub ProjectV2 fields (Status, Priority, Type, Area, etc.) from labels and branch name conventions. | ||
|
|
||
| # Purpose | ||
| - Keep GitHub Projects and issues/PRs in sync. | ||
| - Automate project field updates based on repo activity. | ||
| - Keep GitHub Projects V2 and issues/PRs in sync automatically. | ||
| - Automate project field updates based on repository activity. | ||
| - Provide a single source of truth for project field mappings via `.github/automation/project-fields.yml`. | ||
|
|
||
| # Type of Task | ||
| - Add new items to project on issue/PR events. | ||
| - Map labels/branches to project fields. | ||
| - Add new items to project board on issue/PR events. | ||
| - Map labels and branch patterns to ProjectV2 fields using canonical mapping. | ||
| - Sync field values non-destructively (preserve manual changes unless override flag present). | ||
|
|
||
|
Comment on lines
19
to
31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explicit Inputs section before Process. The specification lacks a structured Inputs section documenting what data/events trigger the agent and what information it receives. This is essential for agent specifications per guidelines. Propose adding an Inputs section after "Type of Task" and before "Process" (around line 32): # Inputs
- **Issue/PR events**: `opened`, `edited`, `labeled`, `unlabeled`, `closed`, `reopened`, `synchronize`
- **Event context**: issue/PR metadata, labels, branch name, author, merge status
- **Configuration**: `.github/automation/project-fields.yml`, `.github/automation/project-labeler.yml`
- **User override**: `meta:auto-sync` label to enable manual field overwrites🧰 Tools🪛 LanguageTool[uncategorized] ~25-~25: The official name of this software platform is spelled with a capital “H”. (GITHUB) 🤖 Prompt for AI Agents |
||
| # Process | ||
| - Trigger on issue/PR open/edit/label. | ||
| - Use mapping rules to set Status, Priority, Type. | ||
| - Update project fields via API. | ||
| 1. Trigger on issue/PR open/edit/label/unlabel/close events. | ||
| 2. Load canonical field mappings from `.github/automation/project-fields.yml`. | ||
| 3. Derive field values using: | ||
| - Label-based mapping (e.g., `status:in-progress` → Status: "In progress") | ||
| - Branch-based mapping (e.g., `feat/*` → Type: "Feature") | ||
| - Event-based mapping (e.g., closed + merged → Status: "Done") | ||
| 4. Update ProjectV2 fields via GraphQL API. | ||
| 5. Log all changes for audit trail. | ||
|
|
||
| # Constraints | ||
| - Must not overwrite manual changes without warning. | ||
| - Support per-project mapping config. | ||
| - Must not overwrite manual field changes without explicit override (via `meta:auto-sync` label). | ||
| - Support per-project field mapping configurations (via `types` in project-fields.yml). | ||
| - Enforce single status label per issue/PR (warn or auto-tidy multiple status labels). | ||
| - Validate all mappings against JSON schema (`schemas/automation/project-fields.schema.json`). | ||
|
|
||
| # What to do | ||
| - Ensure project fields are always up to date with labels. | ||
| - Ensure ProjectV2 fields are always synchronized with canonical labels and branch conventions. | ||
| - Use `.github/automation/project-fields.yml` as the single source of truth for field definitions. | ||
| - Reference `.github/automation/project-labeler.yml` for label-to-field mapping rules. | ||
| - Log all field updates with timestamps and triggering events. | ||
|
|
||
| # What not do | ||
| - Do not remove items from project without confirmation. | ||
| # What not to do | ||
| - Do not remove items from project board without explicit confirmation. | ||
| - Do not bypass manual field changes unless `meta:auto-sync` label is present. | ||
| - Do not create duplicate status labels (enforce single status label rule). | ||
|
|
||
| # Best Practices | ||
| - Log all changes. | ||
| - Allow per-repo/project config. | ||
| - Log all changes with event context (issue/PR number, triggering action, old/new values). | ||
| - Allow per-repository and per-project configuration overrides. | ||
| - Validate project-fields.yml against schema before applying changes. | ||
| - Use GraphQL batching for efficient field updates. | ||
| - Provide clear error messages and warnings for mapping conflicts. | ||
|
|
||
| # Guardrails | ||
| - Notify maintainers on mapping conflicts. | ||
| - Provide rollback/audit if possible. | ||
| - Notify maintainers on mapping conflicts or schema validation failures. | ||
| - Provide rollback capability via audit log. | ||
| - Rate-limit API calls to prevent quota exhaustion. | ||
| - Skip updates if field values haven't changed (idempotent operations). | ||
|
Comment on lines
32
to
+70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explicit Dependencies, Security, and Error Handling sections. Per agent specification guidelines, the spec must document dependencies (external APIs, libraries), security constraints, and error handling strategies. These are currently implicit or missing. Propose adding sections after "Guardrails" (around line 71): # Dependencies
- GitHub GraphQL API (v4) for ProjectV2 field updates
- Node.js runtime with `@octokit/graphql` or equivalent
- `.github/automation/project-fields.yml` and `.github/automation/project-labeler.yml` files
- JSON Schema validator for `schemas/automation/project-fields.schema.json`
# Security
- Use GitHub Actions secrets for API token (`GITHUB_TOKEN` with minimum required permissions)
- Validate all external inputs (labels, branch names) against schema
- Enforce rate-limiting to prevent API quota exhaustion
- Do not log sensitive data (API responses, token details)
- Audit all field mutations for compliance monitoring
# Error Handling
- If schema validation fails, log warning and skip field updates
- If GraphQL API call fails, retry with exponential backoff (max 3 retries)
- If mapping conflict detected (multiple status labels), warn maintainers and defer update
- If `meta:auto-sync` label state is ambiguous, default to non-destructive behavior
- Propagate errors to workflow run summary for visibility🧰 Tools🪛 LanguageTool[uncategorized] ~34-~34: The official name of this software platform is spelled with a capital “H”. (GITHUB) [uncategorized] ~50-~50: The official name of this software platform is spelled with a capital “H”. (GITHUB) [uncategorized] ~51-~51: The official name of this software platform is spelled with a capital “H”. (GITHUB) 🤖 Prompt for AI Agents |
||
|
|
||
| # Checklist | ||
| - [ ] Items added to project. | ||
| - [ ] Meta fields synced. | ||
| - [ ] Items added to project board for new issues/PRs. | ||
| - [ ] Status field synced from labels and event state. | ||
| - [ ] Priority field synced from labels and branch patterns. | ||
| - [ ] Type field synced from branch prefix or labels. | ||
| - [ ] Area field synced from file changes (if configured). | ||
| - [ ] Manual changes preserved (unless override present). | ||
| - [ ] Single status label enforced. | ||
| - [ ] All changes logged for audit. | ||
|
Comment on lines
72
to
+80
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add Troubleshooting section. Agent specs should include common troubleshooting scenarios and how to resolve them. This helps users debug issues. Propose adding a Troubleshooting section after the Checklist: # Troubleshooting
- **Fields not syncing**: Verify `meta:auto-sync` label is present if overriding manual changes; check schema validation in logs
- **Mapping conflicts**: Multiple status labels detected—remove extras or configure label automation via project-labeler.yml
- **GraphQL rate-limit errors**: Check API quota usage; agent retries automatically with backoff
- **Schema validation failures**: Ensure project-fields.yml matches `schemas/automation/project-fields.schema.json`; refer to Configuration Files🤖 Prompt for AI Agents |
||
|
|
||
| # Outputs | ||
| - Project board updates. | ||
| - Sync logs. | ||
| - ProjectV2 field updates via GraphQL API. | ||
| - Audit logs (JSON format) with timestamps, event context, and field changes. | ||
| - Warnings for mapping conflicts or validation errors. | ||
|
|
||
| # Configuration Files | ||
| - **`.github/automation/project-fields.yml`**: Canonical project field definitions (source of truth) | ||
| - **`.github/automation/project-labeler.yml`**: Label mapping rules (harmonised with labels.yml) | ||
| - **`.github/automation/labels.yml`**: Canonical label definitions | ||
| - **`schemas/automation/project-fields.schema.json`**: JSON Schema for field validation | ||
| - **`.github/workflows/project-meta-sync.yml`**: Workflow trigger configuration | ||
| - **`.github/agents/project-meta-sync.js`**: Agent implementation (Node.js) | ||
|
|
||
| # Field Mapping Examples | ||
| | Label/Branch | Field | Value | | ||
| |--------------|-------|-------| | ||
| | `status:in-progress` | Status | "In progress" | | ||
| | `status:needs-review` | Status | "In review" | | ||
| | `priority:critical` | Priority | "Critical" | | ||
| | `feat/*` branch | Type | "Feature" | | ||
| | `fix/*` branch | Type | "Bug" | | ||
| | `hotfix/*` branch | Priority | "Critical" | | ||
| | closed + merged | Status | "Done" | | ||
|
|
||
| # Override Mechanism | ||
| Add the `meta:auto-sync` label to allow the agent to overwrite manual field changes. Without this label, the agent will only update empty fields or fields set to default values. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| #!/usr/bin/env node | ||
| /** | ||
| * Project Meta Sync Agent | ||
| * | ||
| * Maps labels/branch conventions to ProjectV2 fields (Status, Priority, Type) | ||
| * and updates the corresponding item. Non-destructive by default. | ||
| * | ||
| * @author LightSpeed | ||
| * @requires @octokit/graphql, js-yaml, fs | ||
| * @see .github/agents/project-meta-sync.agent.md | ||
| */ | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const { graphql } = require('@octokit/graphql'); | ||
| const yaml = require('js-yaml'); | ||
|
|
||
| // Parse CLI args | ||
| const args = process.argv.slice(2); | ||
| const eventName = args.find(a => a.startsWith('--event'))?.split('=')[1]; | ||
| const payloadPath = args.find(a => a.startsWith('--payload'))?.split('=')[1]; | ||
|
|
||
| if ( | ||
| !eventName || typeof eventName !== 'string' || eventName.trim() === '' || | ||
| !payloadPath || typeof payloadPath !== 'string' || payloadPath.trim() === '' | ||
| ) { | ||
| console.error('Usage: project-meta-sync.js --event=<event_name> --payload=<path_to_payload>'); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Load event payload | ||
| let event; | ||
| try { | ||
| event = JSON.parse(fs.readFileSync(payloadPath, 'utf8')); | ||
| } catch (e) { | ||
| console.error('Failed to parse event payload:', e.message); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // Environment | ||
| const GITHUB_TOKEN = process.env.GITHUB_TOKEN; | ||
|
|
||
| if (!GITHUB_TOKEN) { | ||
| console.error('❌ GITHUB_TOKEN not set'); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| // GraphQL client | ||
|
|
||
| /** | ||
| * Load canonical project fields mapping (if present) | ||
| * TODO: Implement loading of normalized mapping dictionary | ||
| */ | ||
| function loadFieldsMapping() { | ||
| const fieldsPath = path.resolve('.github/automation/project-fields.yml'); | ||
| if (fs.existsSync(fieldsPath)) { | ||
| return yaml.load(fs.readFileSync(fieldsPath, 'utf8')); | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Derive Status from labels and event | ||
| */ | ||
| function deriveStatus(labels, eventName, eventAction, isMerged = false) { | ||
|
||
| const labelNames = labels.map(l => l.name || l); | ||
|
|
||
| // Check status labels | ||
| if (labelNames.some(l => l === 'status:in-progress')) return 'In progress'; | ||
| if (labelNames.some(l => l === 'status:needs-review')) return 'In review'; | ||
| if (labelNames.some(l => l === 'status:needs-qa')) return 'In QA'; | ||
| if (labelNames.some(l => l === 'status:blocked')) return 'Blocked'; | ||
| if (labelNames.some(l => l === 'status:ready')) return 'Ready'; | ||
|
|
||
| // Check closed/merged events | ||
| if (eventAction === 'closed') { | ||
| if (eventName === 'issues') return 'Done'; | ||
| if (eventName === 'pull_request' && isMerged) return 'Done'; | ||
| } | ||
|
|
||
| // Default to Triage | ||
| return 'Triage'; | ||
| } | ||
|
|
||
| /** | ||
| * Derive Priority from labels | ||
| */ | ||
| function derivePriority(labels) { | ||
|
||
| const labelNames = labels.map(l => l.name || l); | ||
|
|
||
| if (labelNames.some(l => l === 'priority:critical')) return 'Critical'; | ||
| if (labelNames.some(l => l === 'priority:important')) return 'Important'; | ||
| if (labelNames.some(l => l === 'priority:normal')) return 'Normal'; | ||
| if (labelNames.some(l => l === 'priority:minor')) return 'Minor'; | ||
|
|
||
| return null; // No priority set | ||
| } | ||
|
|
||
| /** | ||
| * Derive Type from branch name or labels | ||
| */ | ||
| function deriveType(branchName, labels) { | ||
|
||
| // Try branch conventions first | ||
| if (branchName) { | ||
| if (branchName.startsWith('feat/')) return 'Feature'; | ||
| if (branchName.startsWith('fix/')) return 'Bug'; | ||
| if (branchName.startsWith('doc/') || branchName.startsWith('docs/')) return 'Documentation'; | ||
| if (branchName.startsWith('chore/') || branchName.startsWith('build/')) return 'Task'; | ||
| } | ||
|
|
||
| // Try labels | ||
| const labelNames = labels.map(l => l.name || l); | ||
| if (labelNames.some(l => l === 'type:feature')) return 'Feature'; | ||
| if (labelNames.some(l => l === 'type:bug')) return 'Bug'; | ||
| if (labelNames.some(l => l === 'type:documentation')) return 'Documentation'; | ||
| if (labelNames.some(l => l === 'type:task')) return 'Task'; | ||
|
|
||
| return null; // No type set | ||
| } | ||
|
|
||
| /** | ||
| * Main sync logic | ||
| */ | ||
| async function sync() { | ||
| console.log(`project-meta-sync: handling ${eventName} event`); | ||
|
|
||
| // Extract item details from event | ||
| const item = event.issue || event.pull_request; | ||
| if (!item) { | ||
| console.log('No issue or PR in event payload'); | ||
| return; | ||
| } | ||
|
|
||
| const labels = item.labels || []; | ||
| const branchName = event.pull_request?.head?.ref || null; | ||
| const eventAction = event.action; | ||
| const isMerged = event.pull_request?.merged || false; | ||
|
|
||
| // Derive field values | ||
| const status = deriveStatus(labels, eventName, eventAction, isMerged); | ||
| const priority = derivePriority(labels); | ||
| const type = deriveType(branchName, labels); | ||
|
|
||
| console.log(`Derived fields:`, { status, priority, type }); | ||
|
|
||
| // TODO: Enforce single status:* label (warn or auto-tidy) | ||
| const statusLabels = labels.filter(l => (l.name || l).startsWith('status:')); | ||
| if (statusLabels.length > 1) { | ||
| console.warn(`[WARN] Multiple status labels found: ${statusLabels.map(l => l.name || l).join(', ')}`); | ||
| console.warn('Consider using label-sync to enforce single status label'); | ||
| } | ||
|
|
||
| // TODO: Use GraphQL to upsert ProjectV2 item & fields | ||
| // For now, just log what would be updated | ||
| console.log(`Would update ProjectV2 item for ${item.html_url}:`); | ||
| console.log(` Status: ${status}`); | ||
| if (priority) console.log(` Priority: ${priority}`); | ||
| if (type) console.log(` Type: ${type}`); | ||
|
|
||
| // TODO: Guard against overwriting manual fields unless override label present | ||
| const hasOverride = labels.some(l => (l.name || l) === 'meta:auto-sync'); | ||
| if (!hasOverride) { | ||
| console.log('ℹ️ No override label (meta:auto-sync) - would check for manual changes'); | ||
| } | ||
|
|
||
| // TODO: Load normalized mapping dictionary if present | ||
| const fieldsMapping = loadFieldsMapping(); | ||
| if (fieldsMapping) { | ||
| console.log('✅ Loaded project fields mapping'); | ||
| } | ||
| } | ||
|
|
||
| // Run | ||
| sync().catch(err => { | ||
| console.error('❌ Error:', err.message); | ||
| process.exit(1); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing frontmatter fields:
stabilityanddomain.Per coding guidelines, agent spec metadata must include a
domain(exactly one from the approved taxonomy) and astabilityfield with a value ofstable,experimental, orincubating.Extend the YAML frontmatter to include:
(Adjust
domainif a more specific domain applies, and confirmstabilityreflects the agent's readiness for production.)📝 Committable suggestion
🤖 Prompt for AI Agents
🧩 Analysis chain
Verify agent spec is referenced from agent index and confirm domain/stability.
Confirm the following:
.github/agents/README.mdor similar)domainvalue:generic,automation, or another approved taxonomy entrystabilityvalue: is this agentexperimental,incubating, or approachingstable?Run the script below to search for index references:
🏁 Script executed:
Length of output: 53
I need to gather more information. Let me search the codebase for the complete agent spec file, check if other agent specs exist to understand the pattern, and verify the required fields.
🏁 Script executed:
Length of output: 5795
Let me fix the script syntax to find other agent specs and check for existing domain/stability patterns:
🏁 Script executed:
Length of output: 1607
Let me verify whether any of the active agent specs have domain/stability fields:
🏁 Script executed:
Length of output: 1007
Add required frontmatter fields (
domainandstability) and complete spec content sections.The spec is missing two required frontmatter fields per coding guidelines for
.github/agents/*.agent.md:domain: Must be one of:wp-core,block-theme,plugin-hardening,perf,a11y,i18n,security,headless, orgeneric. Given this agent syncs GitHub ProjectV2 fields via automation,genericorautomationwould be appropriate (confirm which fits your taxonomy).stability: Must beexperimental,incubating, orstable. Determine the maturity level of this agent.Additionally, per agent spec guidelines, the following content sections need expansion:
No centralized agent index was found in the repository. If specs should be discoverable via an index (e.g.,
.github/agents/README.md), create or update it to reference this agent.🤖 Prompt for AI Agents