Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/workflows/pm-auto-archive-closed-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: pm — auto-archive closed PRs in project

# When a PR closes (merged or not), archive its project board item immediately.
# Built-in "Auto-archive items" workflow only archives by age (30+ days closed),
# which leaves the active views cluttered with freshly-closed PRs. This Action
# archives on close so the board stays focused on in-flight + open work.
#
# Required repo secret: PM_PROJECT_TOKEN (same as the other pm-* workflows)

on:
pull_request:
types: [closed]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to archive (for manual re-runs)'
required: false

permissions:
contents: read

jobs:
archive:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.PM_PROJECT_TOKEN }}
PROJECT_OWNER: litentry
PROJECT_NUMBER: '19'
steps:
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq

- name: Determine PR number
id: pr
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
else
echo "number=${{ github.event.inputs.pr_number }}" >> "$GITHUB_OUTPUT"
fi

- name: Resolve project ID + PR item ID
id: resolve
run: |
project_id=$(gh project view "$PROJECT_NUMBER" --owner "$PROJECT_OWNER" --format json | jq -r '.id')
echo "project_id=$project_id" >> "$GITHUB_OUTPUT"

pr_num="${{ steps.pr.outputs.number }}"
item_id=$(gh api graphql -f query='
query($owner: String!, $number: Int!) {
organization(login: $owner) {
projectV2(number: $number) {
items(first: 100, orderBy: {field: POSITION, direction: ASC}) {
nodes {
id
content { ... on PullRequest { number } }
}
}
}
}
}
' -F "owner=$PROJECT_OWNER" -F "number=$PROJECT_NUMBER" \
| jq -r --arg n "$pr_num" '.data.organization.projectV2.items.nodes[] | select(.content.number == ($n|tonumber)) | .id' \
| head -n1)

if [ -z "$item_id" ] || [ "$item_id" = "null" ]; then
echo "info PR #$pr_num is not on the project board — nothing to archive"
echo "found=false" >> "$GITHUB_OUTPUT"
else
echo "item_id=$item_id" >> "$GITHUB_OUTPUT"
echo "found=true" >> "$GITHUB_OUTPUT"
fi

- name: Archive the PR's project item
if: steps.resolve.outputs.found == 'true'
run: |
gh api graphql -f query='
mutation($project: ID!, $item: ID!) {
archiveProjectV2Item(input: { projectId: $project, itemId: $item }) {
item { id }
}
}
' -F "project=${{ steps.resolve.outputs.project_id }}" -F "item=${{ steps.resolve.outputs.item_id }}" \
>/dev/null && echo "ok archived PR #${{ steps.pr.outputs.number }} from project board"
25 changes: 16 additions & 9 deletions pm/PROJECT-DASHBOARD-GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ GitHub's Projects v2 API has **specific limits**. Knowing what's automatable up
| Set Status on add / close / PR-merge | ✅ | Built-in workflows (Item added / Item closed / Pull request merged — all enabled) |
| Auto-close issue when Status=Done | ✅ | Built-in "Auto-close issue" workflow |
| Link PR to issue | ✅ | Built-in "Pull request linked to issue" workflow |
| Sync `priority/p*` + `phase/v*` labels → fields | ✅ | `.github/workflows/pm-sync-fields-from-labels.yml` (this repo) |
| Sync `priority/p*` + `kind/*` labels → Priority/Kind fields | ✅ | `.github/workflows/pm-sync-fields-from-labels.yml` (this repo) |
| Auto-archive closed PRs from the board | ✅ | `.github/workflows/pm-auto-archive-closed-pr.yml` |
| Create / configure project fields | ✅ | `pm/scripts/setup-project-fields.sh` |
| Audit workflow drift | ✅ | `.github/workflows/pm-workflow-audit.yml` (daily) |
| Bulk backfill historical issues | ✅ | `bash pm/scripts/add-to-project.sh` |
| Create a new issue with canonical labels + fields | ✅ | `/agentkeys-issue-create` Claude Code skill |
| **Configure a workflow's filter expression** | ❌ | **UI ONLY** — API has no `updateProjectV2Workflow` mutation |
| **Configure a workflow's trigger / action** | ❌ | **UI ONLY** — same reason |
| **Create or configure custom views (group-by, layout, filters)** | ❌ | **UI ONLY** — no `createProjectV2View` / `updateProjectV2View` mutation exists |
Expand All @@ -43,13 +45,14 @@ gh auth refresh -s project,read:project
# Verify access
gh project list --owner litentry | grep "19"

# Create project fields (Priority/Phase/Estimate/Risk/Notes)
# Create project fields (Priority/Kind/Risk/Notes/Iteration/Blocked-by)
# Idempotent: detects existing fields with empty options and rebuilds; cleans "Project X" zombies.
bash pm/scripts/setup-project-fields.sh
```

### Add a CI secret for the GitHub Actions

The 2 PM workflows (`pm-workflow-audit.yml`, `pm-sync-fields-from-labels.yml`) need a token with org-project scopes — the default `GITHUB_TOKEN` does not have them.
The 3 PM workflows (`pm-workflow-audit.yml`, `pm-sync-fields-from-labels.yml`, `pm-auto-archive-closed-pr.yml`) need a token with org-project scopes — the default `GITHUB_TOKEN` does not have them.

1. Create a fine-grained PAT at https://github.com/settings/tokens
- Org permissions: **Projects = read & write**
Expand Down Expand Up @@ -210,16 +213,20 @@ After the board exists:

### Engineer creating new work

**Recommended**: invoke the `/agentkeys-issue-create` Claude Code skill — it walks you through Kind / Priority / Size / Area / Milestone / Blocked-by dropdowns and creates the issue with the right labels + project-field values.

Direct CLI fallback (project field values must be set separately in the UI):

```bash
# Just create the issue with the right labels — built-in + GH Action workflows do the rest:
# 1. "Auto-add to project" built-in workflow → adds it to the board with Status=Todo
# 2. pm-sync-fields-from-labels.yml GH Action → mirrors priority/* + phase/* labels into the
# Priority + Phase project fields
gh issue create --repo litentry/agentKeys \
--title "Phase 2: <something>" \
--title "<something>" \
--body "Scope..." \
--milestone "M2: First vendor wedge (incl memory system)" \
--label "area/mcp,kind/feature,phase/v2,priority/p2"
--label "area/mcp"

# Then in the project UI: set Kind, Priority, Size on the new item.
# Or wait for pm-sync-fields-from-labels.yml — it auto-syncs priority/* labels if you
# add them (but priority labels were removed in the migration; field is now primary).
```

For repeatable issue creation (e.g., planning a sprint), prefer the declarative path:
Expand Down
27 changes: 17 additions & 10 deletions pm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,33 @@ When you create a new issue via `gh issue create` (or web UI), the milestone/lab
Recommended pattern:

```bash
# Create issue with milestone + labels inline
# Recommended path: use the /agentkeys-issue-create skill (interactive, fills all metadata)
# Or directly with gh:
gh issue create --repo litentry/agentKeys \
--title "..." --body "..." \
--milestone "M1: First MCP demo + Volcano Ark PoC" \
--label "area/mcp,kind/feature,priority/p1"
--label "area/mcp"

# Then record it in issue-assignments.json for the next sync to honor
# Then set Kind / Priority / Size in the project UI (or let the skill do it)
```

## Labels schema
## Labels schema (post-migration)

Five label namespaces. An issue typically has one from each, plus optional extras:
Repo labels are now LEAN. Most categorization moved to project fields. Remaining label namespaces:

| Namespace | Examples | Purpose |
|---|---|---|
| `area/*` | `area/mcp`, `area/memory`, `area/firmware` | Which subsystem |
| `kind/*` | `kind/feature`, `kind/bug`, `kind/research` | What kind of work |
| `phase/*` | `phase/v0`, `phase/v1`, `phase/v2` | Coarse roadmap phase (orthogonal to milestone for cross-milestone work) |
| `status/*` | `status/ready`, `status/blocked`, `status/investigating`, `status/deprecated` | Workflow state |
| `priority/*` | `priority/p0`, `priority/p1`, `priority/p2`, `priority/p3` | Triage priority |
| `area/*` | `area/mcp`, `area/memory`, `area/firmware` (17 total, distinct colors per area) | Which subsystem — multi-value, renders as repo-list filter |
| `status/*` | `status/ready`, `status/in-progress`, `status/deprecated` (non-red); `status/blocked`, `status/investigating` (red) | Workflow state |
| Human-attention flags (red) | `needs-arch-review`, `needs-investigation`, `vendor-blocker` | Flagged for human follow-up |
| Community labels | `good first issue`, `help wanted` | Community discoverability |

**Migrated to project fields (no longer labels):**
- `priority/p0..p3` → **Priority field** (Urgent / High / Medium / Low)
- `kind/*` → **Kind field** (Feature / Bug / Research / Docs / Refactor / Security / CI)
- `phase/v*` → **Milestones** (M1..M7)

**Red is reserved** for human-interaction labels (status/blocked, needs-*, vendor-blocker). Area labels avoid the red family.

## Milestones overview

Expand Down
70 changes: 29 additions & 41 deletions pm/labels.json
Original file line number Diff line number Diff line change
@@ -1,49 +1,37 @@
{
"_note": "Repo label taxonomy. Kept lean since most categorization moved to project fields (Kind, Priority, Size) — labels here are for cross-cutting flags + repo-list filtering only.",
"_color_convention": "Red family (b60205, d73a4a, dc2626) is RESERVED for labels requiring human attention/intervention. Area labels use a distinct color per functional area, drawn from blue/teal/green/purple families.",
"_migrated_away": "priority/p* (→ Priority project field), phase/v* (→ Milestones), kind/* (→ Kind project field). Removed by setup. To re-introduce any of these, undo via gh label create — but prefer the field over a label.",
"labels": [
{ "name": "area/mcp", "color": "0e8a16", "description": "MCP server, MCP tool integration, MCP protocol work" },
{ "name": "area/memory", "color": "0e8a16", "description": "Memory worker, namespaces, semantic/episodic/profile/procedural storage" },
{ "name": "area/identity", "color": "0e8a16", "description": "HDKD actor tree, K-key inventory, identity ceremony" },
{ "name": "area/broker", "color": "0e8a16", "description": "Broker server, cap-token issuance, OIDC issuance" },
{ "name": "area/signer", "color": "0e8a16", "description": "Signer / TEE worker, K3 / K10 / K11 handling" },
{ "name": "area/tee", "color": "0e8a16", "description": "TEE-specific work (signer, attestation, sealing)" },
{ "name": "area/audit", "color": "0e8a16", "description": "Audit worker, two-tier audit (off-chain feed + on-chain anchor)" },
{ "name": "area/credential", "color": "0e8a16", "description": "Credential worker, vault, per-data-class isolation" },
{ "name": "area/payment", "color": "0e8a16", "description": "Payment worker, spending caps, ACP/AMP rail adapters" },
{ "name": "area/ui", "color": "0e8a16", "description": "Parent-control UI, vendor onboarding portal, audit dashboard" },
{ "name": "area/firmware", "color": "0e8a16", "description": "ESP32 firmware, device-side code, MCU work" },
{ "name": "area/ci", "color": "0e8a16", "description": "CI pipelines, GitHub Actions workflows, harness automation" },
{ "name": "area/infra", "color": "0e8a16", "description": "Deployment, broker host, scripts/setup-*.sh, AWS / chain provisioning" },
{ "name": "area/cli", "color": "0e8a16", "description": "agentkeys CLI, operator workstation" },
{ "name": "area/daemon", "color": "0e8a16", "description": "agentkeys-daemon (sidecar) work" },
{ "name": "area/scraper", "color": "0e8a16", "description": "Provisioner scrapers, automation for service signup flows" },
{ "name": "area/docs", "color": "0e8a16", "description": "Documentation, runbooks, architecture, research" },
{ "name": "area/mcp", "color": "1D76DB", "description": "MCP server, MCP tool integration, MCP protocol work" },
{ "name": "area/memory", "color": "5319E7", "description": "Memory worker, namespaces, semantic/episodic/profile/procedural storage" },
{ "name": "area/identity", "color": "8B5CF6", "description": "HDKD actor tree, K-key inventory, identity ceremony" },
{ "name": "area/broker", "color": "006B75", "description": "Broker server, cap-token issuance, OIDC issuance" },
{ "name": "area/signer", "color": "0E8A8C", "description": "Signer / TEE worker, K3 / K10 / K11 handling" },
{ "name": "area/tee", "color": "0E4C7E", "description": "TEE-specific work (signer, attestation, sealing)" },
{ "name": "area/audit", "color": "BFB200", "description": "Audit worker, two-tier audit (off-chain feed + on-chain anchor)" },
{ "name": "area/credential", "color": "0FAA86", "description": "Credential worker, vault, per-data-class isolation" },
{ "name": "area/payment", "color": "0E8A16", "description": "Payment worker, spending caps, ACP/AMP rail adapters" },
{ "name": "area/ui", "color": "C5B0F0", "description": "Parent-control UI, vendor onboarding portal, audit dashboard" },
{ "name": "area/firmware", "color": "5C4033", "description": "ESP32 firmware, device-side code, MCU work" },
{ "name": "area/ci", "color": "94A3B8", "description": "CI pipelines, GitHub Actions workflows, harness automation" },
{ "name": "area/infra", "color": "4A5D23", "description": "Deployment, broker host, scripts/setup-*.sh, AWS / chain provisioning" },
{ "name": "area/cli", "color": "64748B", "description": "agentkeys CLI, operator workstation" },
{ "name": "area/daemon", "color": "2D6A4F", "description": "agentkeys-daemon (sidecar) work" },
{ "name": "area/scraper", "color": "52796F", "description": "Provisioner scrapers, automation for service signup flows" },
{ "name": "area/docs", "color": "0EA5E9", "description": "Documentation, runbooks, architecture, research" },

{ "name": "kind/feature", "color": "a2eeef", "description": "New feature implementation" },
{ "name": "kind/bug", "color": "d73a4a", "description": "Defect; something broken or behaving wrong" },
{ "name": "kind/refactor", "color": "fbca04", "description": "Internal restructuring; no external behavior change" },
{ "name": "kind/research", "color": "ffb760", "description": "Investigation, exploration, prototyping" },
{ "name": "kind/docs", "color": "0075ca", "description": "Documentation-only change" },
{ "name": "kind/security", "color": "b60205", "description": "Security-sensitive — apply extra review rigor" },
{ "name": "kind/devx", "color": "c5def5", "description": "Developer experience, internal tooling, ergonomics" },

{ "name": "phase/v0", "color": "5319e7", "description": "Already shipped (Stage 7+ era)" },
{ "name": "phase/v1", "color": "5319e7", "description": "Phase 1 work (M1 + immediate follow-ups)" },
{ "name": "phase/v2", "color": "5319e7", "description": "Phase 2-3 work (vendor wedge + runtime neutrality)" },
{ "name": "phase/v3", "color": "5319e7", "description": "Phase 4-5 work (delegation depth + native mobile)" },
{ "name": "phase/v4", "color": "5319e7", "description": "Phase 6-7 work (TEE depth + standards)" },

{ "name": "status/ready", "color": "0e8a16", "description": "Ready for engineering pickup" },
{ "name": "status/blocked", "color": "d93f0b", "description": "Blocked on external dependency or upstream decision" },
{ "name": "status/investigating", "color": "fbca04", "description": "Under investigation; scope not yet locked" },
{ "name": "status/deprecated", "color": "cfd3d7", "description": "No longer relevant; flagged for close after review" },
{ "name": "status/ready", "color": "0e8a16", "description": "Ready for engineering pickup" },
{ "name": "status/in-progress", "color": "1d76db", "description": "Active engineering work in flight" },
{ "name": "status/deprecated", "color": "cfd3d7", "description": "No longer relevant; flagged for close after review" },

{ "name": "priority/p0", "color": "b60205", "description": "Critical — drop other work" },
{ "name": "priority/p1", "color": "d93f0b", "description": "High — this milestone's headline" },
{ "name": "priority/p2", "color": "fbca04", "description": "Medium — important but not blocking" },
{ "name": "priority/p3", "color": "c5def5", "description": "Low — nice to have, can slip" },
{ "name": "status/blocked", "color": "b60205", "description": "Blocked on external dependency or upstream decision — needs human unblock" },
{ "name": "status/investigating", "color": "d73a4a", "description": "Under investigation; needs human follow-up to lock scope" },
{ "name": "needs-arch-review", "color": "dc2626", "description": "Needs explicit arch.md compatibility review before merge" },
{ "name": "needs-investigation", "color": "b60205", "description": "Root cause unclear; assign to someone to investigate" },
{ "name": "vendor-blocker", "color": "b60205", "description": "Blocks a vendor pilot or partnership conversation" },

{ "name": "needs-arch-review", "color": "5319e7", "description": "Needs explicit arch.md compatibility review before merge" },
{ "name": "vendor-blocker", "color": "b60205", "description": "Blocks a vendor pilot or partnership conversation" }
{ "name": "good first issue", "color": "7057ff", "description": "Good for newcomers" },
{ "name": "help wanted", "color": "008672", "description": "Extra attention is needed" }
]
}
23 changes: 16 additions & 7 deletions pm/scripts/setup-project-fields.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ existing_fields_json=$(gh api graphql -f query='
# "Project Priority", "Project Project Priority", etc. — clutter that confuses operators
# and breaks group-by-field views. Detect + delete any "Project <managed-name>" zombie.
cleanup_zombies() {
local managed_names="Priority Phase Estimate Risk Notes"
local managed_names="Priority Kind Phase Estimate Risk Notes"
for n in $managed_names; do
local zombie_name="Project $n"
local zombie_id
Expand Down Expand Up @@ -135,14 +135,18 @@ create_field() {

echo "setup-project-fields target=$PROJECT_OWNER/$PROJECT_NUMBER"

# Priority — single-select, four levels matching priority/* labels
create_field "Priority" SINGLE_SELECT "P0,P1,P2,P3"
# Priority — single-select, mapped from priority/p* labels (p0→Urgent, etc.)
create_field "Priority" SINGLE_SELECT "Urgent,High,Medium,Low"

# Phase — single-select, matches phase/* labels (one phase per issue is the norm)
create_field "Phase" SINGLE_SELECT "v0,v1,v2,v3,v4"
# Kind — single-select, mapped from kind/* labels (one kind per issue)
create_field "Kind" SINGLE_SELECT "Feature,Bug,Research,Docs,Refactor,Security,CI"

# Estimate — t-shirt sizes for rough sizing
create_field "Estimate" SINGLE_SELECT "XS,S,M,L,XL"
# Phase — DEPRECATED. We use GitHub Milestones for phase tracking now.
# The Phase field may still exist on the project; this script leaves it untouched.
# Delete it manually via the UI when ready.

# Estimate — DEPRECATED. GitHub's built-in Size field (XS/S/M/L/XL) replaces it.
# Leave existing Estimate column untouched if present.

# Iteration — sprint window (project's built-in Iteration type; if not supported,
# fall back to a TEXT field that operators fill manually). gh CLI doesn't support
Expand All @@ -156,6 +160,11 @@ create_field "Risk" SINGLE_SELECT "Low,Medium,High,Critical"
# Notes — free-form text for one-line context per item
create_field "Notes" TEXT

# Blocked by — TEXT, list of "#NN, #MM" issue refs that block this one. We do a
# topological sort manually in views; the field is the source of truth for the
# blocking-graph until GitHub ships a real issue-relationship field type.
create_field "Blocked by" TEXT

echo ""
echo "ok setup-project-fields complete"
echo ""
Expand Down
Loading
Loading