github-projects-manager
Skill13 starsChanged 13 days agoMITView on GitHub
The only write path to GitHub Projects V2, plus its governance: issue creation with board placement, field updates, batch transitions, board queries, audits, health checks, new-board setup. Reads schemas and rules from workflow/projects/*.yaml. Trigger: "create issue", "set field", "board query", "audit board", "board health", "project setup", "is everything on the board".
In this repository44 other files it gives its agents.
- AGENTS.mdAGENTS.md
- skills/board-pilot/references/agents.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- a2aSkill
- archiveSkill
- board-pilotSkill
- bridge-auditSkill
- bridge-contributeSkill
- bridge-curatorSkill
- bridge-explorerSkill
- bridge-greetingSkill
- bridge-leak-checkSkill
- bridge-learnSkill
- bridge-onboardSkill
- bridge-overlaySkill
- bridge-promoteSkill
- bridge-statusSkill
- bridge-syncSkill
- briefingSkill
- calendarSkill
- capability-brokerSkill
- channelSkill
- cmuxSkill
- cockpitSkill
- dashboardSkill
- debriefSkill
- doc-systemSkill
- html-canvasSkill
- html-slidesSkill
- knowledge-repo-initSkill
- mail-draftSkill
- mandantsSkill
- meeting-transcriptionSkill
- object-storeSkill
- onboard-simSkill
- remoteSkill
- scheduleSkill
- secretsSkill
- spec-kickoffSkill
- task-close-postmortemSkill
- tmuxSkill
- tracker-syncSkill
- workloadSkill
- workspaceSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
21 rules. About 0 are common, 8 are distinctive, and 13 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
13
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 47
Read workflow/projects/{slug}.yaml for field values, governance rules, and state mappings
- Very specific: line 49
If a bound context exists (e.g.
- Very specific: line 55
When the request comes from conversation rather than from a calling skill that already fixed the values, propose first and create on a yes: …
- Distinctive: line 62
Every issue creation follows this exact sequence — no shortcuts:
- Distinctive: line 70
After creation, immediately classify with at minimum:
- Distinctive: line 72
All billing fields if the project has them (Billing Scope, Root Cause, Approval)
- Distinctive: line 140
Before setting fields, resolve all required IDs:
- Very specific: line 181
Run updates in parallel (& + wait) — max 10 concurrent
- Very specific: line 182
Verify all updated items after wait completes
- Distinctive: line 214
Verify a schema change by re-reading the items, not the field: the question is never "did the option get renamed" (the mutation response …
- Very specific: line 235
Run both with the tested helper (config-first — pass the Status strings from status_mapping, not guesses):
- Very specific: line 244
All four inputs come from workflow/projects/{slug}.yaml — project.org, project.number, status_mapping.declined/done, and the optional …
- Distinctive: line 257
Run both for a full board review.
- Very specific: line 261
When called by a coordinator skill (e.g.
- Very specific: line 267
Read project_custom_fields from the bound context for:
- Very specific: line 274
Config-first: Always read workflow/projects/{slug}.yaml before any operation.
- Distinctive: line 276
Verify after write: Every create or update must be verified.
- Very specific: line 277
Respect readonly fields: If a field is in project_fields_readonly, never write to it even if the caller requests it.
- Distinctive: line 283
Connectedness is mandatory: every issue this skill creates or touches for a project must be on that project's board.
- Very specific: line 314
item-addsets no Status: After adding, set Status, Item Type and Priority explicitly and confirm by readback, or the item sits on the … - Very specific: line 316
--projectongh issue createis unreliable: Create first, then attach: gh issue create, then gh project item-add <N> --owner <org> …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
GitHub Projects Manager
Execution and governance layer for GitHub Projects V2. This skill runs the
actual CLI and GraphQL commands, and it decides when a write is allowed: the
K/W/B governance rules, the board health checks (R1 to R7) and the setup of a
new board live in references/governance.md and references/setup.md.
Decision tree
User intent?
├─ "Create issue" / "Track this" → Issue Creation (below) + governance.md K rules
├─ "Set field" / "Set status" → Field Updates (below); into review → governance.md § Review comment
├─ "Show board" / "What's active?" → Board query (graphql-patterns.md § Board Query)
├─ "Audit board" / "Reconcile" → Board Hygiene (below), scripts/board-audit.sh
├─ "Board health" / "Governance" → references/governance.md (R1 to R7)
├─ "Convert drafts to issues" → references/governance.md § Draft to issue conversion
├─ "Set up a project" / new board → references/setup.md
└─ Change fields or options → Field Schema Operations (below), human gateWhen to use
- Creating issues and adding them to project boards
- Setting or updating custom project fields (Status, Priority, Billing Scope, Approval, Root Cause, Person Days, etc.)
- Batch field transitions (e.g. "set Approval to Submitted on 16 items")
- Querying board state (items by status, field counts, stale items)
- Converting drafts to issues, keeping their field values
- Checking governance and board health (R1 to R7) and setting up new boards
- Auditing board hygiene — coverage (open issues that never made it onto the board) and reconciliation (board Status disagreeing with the issue's real state / close-reason). See Board Hygiene below.
Loading sequence
- Identify the target project (from caller context, issue URL, or
#number) - Read
workflow/projects/{slug}.yamlfor field values, governance rules, and state mappings - If a bound context exists (e.g.
workflow/contexts/customer-a.yaml), readproject_custom_fieldsfor field-specific overrides and readonly field lists
Issue Creation (3-step atomic)
When the request comes from conversation rather than from a calling skill
that already fixed the values, propose first and create on a yes: title, target
repo (project.issue_repo), project, and the field values taken from the
config (fields.status.default, a suggested Priority and Type). The governance
level and rules in the config's governance: block decide what is mandatory
(references/governance.md).
Every issue creation follows this exact sequence — no shortcuts:
Step 1: gh issue create --repo {org}/{issue_repo} --title --body --label
Step 2: gh project item-add {number} --owner {org} --url {issue_url}
Step 3: Verify — confirm the item appears on the boardAfter creation, immediately classify with at minimum:
Status(from project config default)- All billing fields if the project has them (Billing Scope, Root Cause, Approval)
Connectedness is not optional. An issue that exists but is not on the board
is invisible to everyone who works from the board. Step 2 + 3 are mandatory —
never gh issue create without adding to the project and confirming it landed.
The same applies when you touch an existing issue for this project: if it is
not on the board, add it. Periodically verify the whole set with the coverage
audit (Board Hygiene below) — orphaned issues are a silent failure mode.
Hint: does the issue also need a Bridge task? The board is not always the
end of the job. When the issue comes out of a Bridge session, decide with
protocols/standing-orders/board-task-criteria.md before you report done. An
issue that hands work to someone else, or that you will follow up on in a later
session, is usually a Class A task: it also gets work/tasks/<slug>/STATUS.md
with a sync.github block naming the issue and the board, and the generated
board is rebuilt. Many issues do not qualify (a report filed once and not
followed up stays a log row), so this is a decision, not a step. This skill does
not create the task itself.
Field Updates
Approach: gh project item-edit (preferred)
For standard project fields, use the gh project item-edit CLI command.
This is simpler than GraphQL and works for single-select, text, number and
date fields. Two forms — prefer the by-name form for one-off edits, the
by-ID form for batches:
# By name — no ID lookups at all. Field name and (for single-select) the exact
# option name, both straight out of workflow/projects/{slug}.yaml.
gh project item-edit $NUM --owner "$ORG" \
--url https://github.com/{org}/{repo}/issues/{n} \
--field "Priority" --value "🟡 Medium"
# Remove a value (leave the field empty) — there is no "empty" option to select
gh project item-edit $NUM --owner "$ORG" --url "$ISSUE_URL" \
--field "Priority" --clear--url is the issue URL, not the project URL; --owner selects the
project, so the two may have different owners (that is the normal case on a
repo-spanning board).
# By ID — worth the three lookups only when you edit many items in one run
# and cache the IDs.
gh project item-edit --project-id "$PROJECT_ID" \
--id "$ITEM_ID" \
--field-id "$FIELD_ID" \
--single-select-option-id "$OPTION_ID"
gh project item-edit --project-id "$PROJECT_ID" \
--id "$ITEM_ID" --field-id "$FIELD_ID" --number 0.5
gh project item-edit --project-id "$PROJECT_ID" \
--id "$ITEM_ID" --field-id "$FIELD_ID" --text "E16"
gh project item-edit --project-id "$PROJECT_ID" \
--id "$ITEM_ID" --field-id "$FIELD_ID" --date "2026-08-07"For a non-draft issue, one field value per invocation — three fields means three calls.
Resolving IDs
Before setting fields, resolve all required IDs:
# Get Project ID from item-add output, or:
PROJECT_ID=$(gh api graphql -f query='
query($org: String!, $num: Int!) {
organization(login: $org) {
projectV2(number: $num) { id }
}
}' -f org="$ORG" -F num=$NUM \
--jq '.data.organization.projectV2.id')
# Get all field IDs + option IDs in one call:
gh project field-list $NUM --owner $ORG --limit 100 --format json \
| jq -r '.fields[] | "\(.name) | \(.id) | \(.options // [] | map("\(.name)=\(.id)") | join("; "))"'
--limitis not optional.gh project field-listandgh project item-listboth default to 30 and truncate silently — no warning, no non-zero exit, just a short list that looks complete. A board with 33 fields returns 30 of them; a board with 377 items returns 30. Every audit, every coverage diff and every "the field does not exist" conclusion drawn from a default-limit call is wrong on any board past 30. Always pass--limit, and cross-check the real size againstitems { totalCount }/fields { totalCount }in GraphQL before trusting a count.
Cache field IDs for the duration of a run — they don't change between calls within the same session.
Fallback: GraphQL mutations
For complex operations or when gh project item-edit fails, use the
GraphQL patterns documented in references/graphql-patterns.md.
Batch Operations
For batch field updates (e.g. transitioning 16 items from
Org Internal to Submitted):
- Resolve field + option IDs once
- Get all item IDs for the target issues
- Run updates in parallel (
&+wait) — max 10 concurrent - Verify all updated items after
waitcompletes - Report:
Updated N/M items successfully
Field Schema Operations (changing the board itself)
Everything above writes values. Adding a field, or renaming/adding/removing
the options of a single-select, changes the board's schema — a different
class of operation with a different blast radius, and one the gh project CLI
does not cover at all. GraphQL only: createProjectV2Field,
updateProjectV2Field, deleteProjectV2Field. Mutations and a worked
rename recipe: references/graphql-patterns.md § Field Schema Operations.
Three rules, all of them learned the expensive way:
singleSelectOptionsreplaces the entire option list. The input is not a patch. An option you pass with its existingidis renamed/recolored in place and every item keeping that value follows along. An option you pass without anidis created. An option you simply leave out is deleted, and every item that held it silently loses its value. Always fetch the live options first and send the full list back.- Rename by ID, never by delete-and-recreate. Renaming through the IDs is lossless: item values, saved views and the board's built-in workflows all reference the option ID, not the name. Delete-and-recreate looks identical in the UI and throws the data away.
- A schema change is the human's call, not yours. Field values are routine;
the shape of someone's board is not. Propose, get an explicit yes, then write —
especially when the board belongs to someone else. Afterwards, update
workflow/projects/{slug}.yamlin the same breath:fields, the*_mappingblocks,state_map,governance.review_states/done_states, andids.option_ids. A registry that still describes the old shape is worse than no registry, because it reads as verified.
Verify a schema change by re-reading the items, not the field: the question is never "did the option get renamed" (the mutation response already says so) but "did every item keep its value".
Board Hygiene (audit + reconcile)
Beyond creating and updating, this skill verifies the board tells the truth.
Two read-only checks, both driven by workflow/projects/{slug}.yaml:
- Coverage — open issues in the project's tracked repos that are not on the
board (orphans). A per-issue check is
projectItems.totalCount == 0. Reconciliation — the board Status vs the issue's real
state/stateReason. Classify each Status intodeclined | done | activefrom the config'sstatus_mapping, then:Board class Issue must be Mismatch declined CLOSED+NOT_PLANNEDOPEN → close not_planned · CLOSED/COMPLETED → human decides done CLOSEDOPEN → finish or move board off done active OPENCLOSED → stale board
Run both with the tested helper (config-first — pass the Status strings from
status_mapping, not guesses):
scripts/board-audit.sh --owner "$ORG" --project "$NUM" \
--declined "<status_mapping.declined>" --done "<status_mapping.done>" \
--repos "<audit.coverage_repos, comma-joined>"All four inputs come from workflow/projects/{slug}.yaml — project.org,
project.number, status_mapping.declined/done, and the optional
audit.coverage_repos list. Nothing board-specific is baked into the skill.
Contradictions are never auto-fixed — "declined but closed as completed" or
"closed but board still active" go to the human (board wrong, or issue wrong?).
Repair primitives (add-to-board, close as not_planned, change an already-closed
issue's reason without reopening, set board Status, archived-repo issues) and
the full recipes live in references/board-audit.md.
Hygiene checks the board against the issues. Health checks the items
against each other and the governance rules (in progress without assignee,
blocked with assignee, stale items): R1 to R7 in references/governance.md.
Run both for a full board review.
Bound Context Integration
When called by a coordinator skill (e.g. customer-a-coordinator), the
caller provides:
- Project number and org
- Which fields to set and with which values
- A bound context path (e.g.
workflow/contexts/customer-a.yaml)
Read project_custom_fields from the bound context for:
- Exact field names and valid options
- Readonly fields (DO NOT WRITE)
- Field naming conventions (e.g. English-only for CustomerA)
Rules
- Config-first: Always read
workflow/projects/{slug}.yamlbefore any operation. Never hardcode field values, emojis, or status names. - Verify after write: Every create or update must be verified.
- Respect readonly fields: If a field is in
project_fields_readonly, never write to it even if the caller requests it. - Atomic transitions: Batch updates are all-or-nothing. If one fails, report the failure — don't silently skip.
- No issue closure without review: Per governance, issues go through "In Review" before "Done" — never skip to Done directly.
- Connectedness is mandatory: every issue this skill creates or touches for a project must be on that project's board. Orphans are a silent failure — run the coverage audit periodically, not just at create time.
- Reconcile, don't assume: board Status and issue state must agree (declined↔closed/not_planned, done↔closed, active↔open). Fix the clear direction; route genuine contradictions to the human — never auto-flip a closed issue's meaning or a customer board's Status on a guess.
- Never trust a default limit: pass
--limiton everyfield-list/item-listcall and paginate every GraphQLitems/fieldsconnection. A truncated read is indistinguishable from a complete one. - Schema changes are gated and paired: creating a field or editing its
options needs an explicit human yes, and the matching
workflow/projects/{slug}.yamlupdate in the same change.
Known traps
Four failure modes that all produce plausible output, which is why they survive
review. Each one was paid for once. They live with the tool rather than with any
one caller, because every path that writes to a board hits the same gh.
- Never parse the output of a writing
ghcommand. A hook may append a log reminder to stdout after anyghcall with remote effect, soURL=$(gh issue create … | tail -1)yields the hook line instead of the URL. One script reported four failures this way while all four issues had been created; a retry would have produced four duplicates. Verify success against the target state (gh issue list,projectItemson the issue), never against the return value of the writing command. gh project item-list --limittruncates silently. At--limit 100three of four freshly added items were missing; at--limit 200all were there. A missing item is therefore not proof of a faileditem-add. The reliable read is the item ID straight off the issue:repository.issue.projectItemsvia GraphQL.item-addsets no Status. After adding, set Status, Item Type and Priority explicitly and confirm by readback, or the item sits on the board without a column.--projectongh issue createis unreliable. Create first, then attach:gh issue create, thengh project item-add <N> --owner <org> --url <issue-url>.
Error Recovery
| Error | Cause | Fix |
| "Could not resolve to a ProjectV2" | Wrong project number or org | Check workflow/projects/{slug}.yaml |
| "Resource not accessible" | Token lacks project scope | gh auth refresh -s project |
| "Field value not found" | Emoji mismatch or typo | Re-read field-list, use exact option name |
| "Item not found" | Issue not on board yet | Run gh project item-add first |
| Invalid option ID | Schema drift | Re-fetch gh project field-list |
| "type String! was provided invalid value" | Digit-only option ID sent with -F (coerced to Int) | Use -f value=$OPTION_ID (string), not -F |
| Orphans miscounted / wrong | comm fed sort -n (numeric) input | Sort both lists with LC_ALL=C sort (lexical) before comm |
| Field or item "missing" that exists in the UI | field-list / item-list truncated at the default limit of 30 | Re-run with --limit, confirm against totalCount |
| Items lost their single-select value after a field edit | updateProjectV2Field sent a partial option list — omitted options were deleted | Fetch live options first, resend the full list with existing ids |
Reference
references/graphql-patterns.md— Full GraphQL mutation and query patternsreferences/board-audit.md— Coverage + reconciliation rules, repair primitives (close-reason change, archived-repo issues), gotchasscripts/board-audit.sh— Read-only board-hygiene checker (coverage + reconcile)scripts/issue_transition.py— One issue to its board's review or done state:planreads what the boards say,reviewposts the review comment (a reason is required) and setsreview_states[0],donepasses through review on every board with review states (K1), setsdone_states[0]and, once every board reads back at its target, closes the issue (not planned where a board declined it). The board, its status field and states come fromworkflow/projects/*.yaml. The cockpit's issue card runs it on a person's click only (K1)workflow/projects/{slug}.yaml— Per-project field configsreferences/governance.md: K/W/B rules, governance levels, board health (R1 to R7), review comment, draft to issue conversionreferences/setup.md: new board, fields, registry entry, views, labels
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

