Skip to content

Commit d198d88

Browse files
svelderrainruizGitHub Copilot
andauthored
#949 Add canonical GitHub metadata apply helper (#952)
* #949 Add canonical GitHub metadata apply helper * #949 Fix metadata clear-path GraphQL variables * #949 Make issue metadata mutations field-aware --------- Co-authored-by: GitHub Copilot <copilot@users.noreply.github.com>
1 parent 85aa314 commit d198d88

10 files changed

Lines changed: 2740 additions & 3 deletions

docs/DEVELOPER_GUIDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,12 @@ Quick reference for building, testing, and releasing the LVCompare composite act
178178
fields, and writes `tests/results/_agent/project/portfolio-apply-report.json`. The report also carries normalized
179179
built-in board metadata (`Type`, `Milestone`, `Reviewers`, linked PRs, parent issue, `Sub-issues progress`) so
180180
future agents can reason about intake state without a second `gh project item-list` scrape.
181+
- `node tools/npm/run-script.mjs priority:github:metadata:apply -- --url <issue-or-pr-url> ...`
182+
Applies canonical GitHub metadata directly on the issue or PR: issue type, milestone, assignees, requested
183+
reviewers, parent issue, and sub-issue linkage. The helper writes
184+
`tests/results/_agent/issue/github-metadata-apply-report.json` and verifies the post-apply state against the
185+
projected target state. Use this when the issue/PR metadata itself is the source of truth; use the project helper
186+
only for board fields.
181187
- `node tools/priority/standing-priority-handoff.mjs [--dry-run] <next-issue>`
182188
Removes the `standing-priority` label from the current issue (if any), applies it to `<next-issue>`, and re-runs the cache sync (`tools/priority/sync-standing-priority.mjs`). Use `--dry-run` to preview the actions without mutating labels.
183189
- Standing-priority repository resolution is owner-agnostic. Order:

docs/knowledgebase/GitHub-Intake-Layer.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,27 @@ instead of inferring the correct path from prose alone.
9696
just to reason about intake state. Use this instead of ad hoc `gh project item-add` plus repeated
9797
`gh project item-edit` sequences.
9898

99+
- Canonical GitHub metadata application:
100+
101+
```powershell
102+
node tools/npm/run-script.mjs priority:github:metadata:apply -- `
103+
--url https://github.com/LabVIEW-Community-CI-CD/compare-vi-cli-action/issues/949 `
104+
--issue-type Feature `
105+
--milestone "LabVIEW CI Platform v1 (2026Q2)"
106+
107+
node tools/npm/run-script.mjs priority:github:metadata:apply -- `
108+
--url https://github.com/LabVIEW-Community-CI-CD/compare-vi-cli-action/pull/950 `
109+
--assignee svelderrainruiz `
110+
--reviewer copilot-swe-agent `
111+
--reviewer LabVIEW-Community-CI-CD/maintainers
112+
```
113+
114+
This helper mutates the real issue/PR metadata future agents actually reason about: issue type, milestone,
115+
assignees, requested reviewers, parent issue linkage, and sub-issue linkage. It writes
116+
`tests/results/_agent/issue/github-metadata-apply-report.json` with requested, resolved, projected, observed, and
117+
verification state. Inputs are explicit by design: passing assignees/reviewers/sub-issues means the full desired set
118+
for that surface, while `--clear-*` flags intentionally drive the empty state.
119+
99120
- Issue bodies:
100121

101122
```powershell
@@ -175,6 +196,9 @@ Human-authored PRs should use the `human-change` template so they do not acciden
175196
- Prefer `priority:project:portfolio:apply` when you need deterministic project-field stamping for issues or PRs; the
176197
board remains a visibility layer, but the helper removes the manual CLI mutation seam and emits richer built-in board
177198
metadata for future-agent routing.
199+
- Prefer `priority:github:metadata:apply` when the source of truth must change on the issue or PR itself. Use it for
200+
issue type, milestone, assignee, reviewer, parent, and sub-issue mutations; do not try to treat project board fields
201+
as a substitute for those canonical surfaces.
178202
- Use `Write-GitHubIntakeAtlas.ps1` when you need a single human-readable and machine-readable snapshot of the entire
179203
intake layer.
180204

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://comparevi.dev/schemas/github-intake-metadata-apply-report-v1.schema.json",
4+
"title": "GitHub Intake Metadata Apply Report v1",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"required": [
8+
"schema",
9+
"schemaVersion",
10+
"generatedAt",
11+
"dryRun",
12+
"target",
13+
"requested",
14+
"resolved",
15+
"observed",
16+
"operations",
17+
"verification",
18+
"summary",
19+
"execution"
20+
],
21+
"properties": {
22+
"schema": {
23+
"const": "github-intake/metadata-apply-report@v1"
24+
},
25+
"schemaVersion": {
26+
"type": "string"
27+
},
28+
"generatedAt": {
29+
"type": "string",
30+
"format": "date-time"
31+
},
32+
"dryRun": {
33+
"type": "boolean"
34+
},
35+
"target": {
36+
"type": "object",
37+
"additionalProperties": false,
38+
"required": ["url", "number", "title", "contentType", "repository", "id"],
39+
"properties": {
40+
"url": { "type": "string", "format": "uri" },
41+
"number": { "type": "integer", "minimum": 1 },
42+
"title": { "type": ["string", "null"] },
43+
"contentType": { "enum": ["Issue", "PullRequest"] },
44+
"repository": { "type": "string", "pattern": "^[^/]+/[^/]+$" },
45+
"id": { "type": "string" }
46+
}
47+
},
48+
"requested": { "$ref": "#/$defs/surfaceGroup" },
49+
"resolved": { "$ref": "#/$defs/surfaceGroup" },
50+
"observed": {
51+
"type": "object",
52+
"additionalProperties": false,
53+
"required": ["before", "projectedAfter", "after"],
54+
"properties": {
55+
"before": { "$ref": "#/$defs/observedSnapshot" },
56+
"projectedAfter": { "$ref": "#/$defs/observedSnapshot" },
57+
"after": {
58+
"anyOf": [
59+
{ "$ref": "#/$defs/observedSnapshot" },
60+
{ "type": "null" }
61+
]
62+
}
63+
}
64+
},
65+
"operations": {
66+
"type": "array",
67+
"items": { "$ref": "#/$defs/operation" }
68+
},
69+
"verification": {
70+
"type": "object",
71+
"additionalProperties": false,
72+
"required": ["ok", "attempts", "delayMs", "maxAttempts", "fields", "skipped"],
73+
"properties": {
74+
"ok": { "type": "boolean" },
75+
"attempts": { "type": "integer", "minimum": 0 },
76+
"delayMs": { "type": "integer", "minimum": 0 },
77+
"maxAttempts": { "type": "integer", "minimum": 0 },
78+
"skipped": { "type": "boolean" },
79+
"fields": {
80+
"type": "array",
81+
"items": {
82+
"type": "object",
83+
"additionalProperties": false,
84+
"required": ["surface", "ok", "expected", "actual"],
85+
"properties": {
86+
"surface": { "$ref": "#/$defs/surfaceName" },
87+
"ok": { "type": "boolean" },
88+
"expected": {},
89+
"actual": {}
90+
}
91+
}
92+
}
93+
}
94+
},
95+
"summary": {
96+
"type": "object",
97+
"additionalProperties": false,
98+
"required": [
99+
"requestedCount",
100+
"appliedCount",
101+
"plannedCount",
102+
"unchangedCount",
103+
"unsupportedCount",
104+
"failedCount",
105+
"skippedCount"
106+
],
107+
"properties": {
108+
"requestedCount": { "type": "integer", "minimum": 0 },
109+
"appliedCount": { "type": "integer", "minimum": 0 },
110+
"plannedCount": { "type": "integer", "minimum": 0 },
111+
"unchangedCount": { "type": "integer", "minimum": 0 },
112+
"unsupportedCount": { "type": "integer", "minimum": 0 },
113+
"failedCount": { "type": "integer", "minimum": 0 },
114+
"skippedCount": { "type": "integer", "minimum": 0 }
115+
}
116+
},
117+
"execution": {
118+
"type": "object",
119+
"additionalProperties": false,
120+
"required": ["status", "errors"],
121+
"properties": {
122+
"status": {
123+
"enum": ["planned", "pass", "fail", "error"]
124+
},
125+
"errors": {
126+
"type": "array",
127+
"items": { "type": "string" }
128+
}
129+
}
130+
}
131+
},
132+
"$defs": {
133+
"surfaceName": {
134+
"enum": ["issueType", "milestone", "assignees", "reviewers", "parentIssue", "subIssues"]
135+
},
136+
"surfaceState": {
137+
"type": "object",
138+
"additionalProperties": false,
139+
"required": ["active", "clear", "value"],
140+
"properties": {
141+
"active": { "type": "boolean" },
142+
"clear": { "type": "boolean" },
143+
"value": {}
144+
}
145+
},
146+
"surfaceGroup": {
147+
"type": "object",
148+
"additionalProperties": false,
149+
"required": ["issueType", "milestone", "assignees", "reviewers", "parentIssue", "subIssues"],
150+
"properties": {
151+
"issueType": { "$ref": "#/$defs/surfaceState" },
152+
"milestone": { "$ref": "#/$defs/surfaceState" },
153+
"assignees": { "$ref": "#/$defs/surfaceState" },
154+
"reviewers": { "$ref": "#/$defs/surfaceState" },
155+
"parentIssue": { "$ref": "#/$defs/surfaceState" },
156+
"subIssues": { "$ref": "#/$defs/surfaceState" }
157+
}
158+
},
159+
"observedSnapshot": {
160+
"type": "object",
161+
"additionalProperties": false,
162+
"required": [
163+
"id",
164+
"url",
165+
"number",
166+
"title",
167+
"contentType",
168+
"repository",
169+
"assignees",
170+
"reviewers",
171+
"milestone",
172+
"issueType",
173+
"parentIssue",
174+
"subIssues"
175+
],
176+
"properties": {
177+
"id": { "type": "string" },
178+
"url": { "type": "string", "format": "uri" },
179+
"number": { "type": "integer", "minimum": 1 },
180+
"title": { "type": ["string", "null"] },
181+
"contentType": { "enum": ["Issue", "PullRequest"] },
182+
"repository": { "type": "string", "pattern": "^[^/]+/[^/]+$" },
183+
"assignees": {
184+
"type": "array",
185+
"items": { "type": "string" }
186+
},
187+
"reviewers": {
188+
"type": "array",
189+
"items": { "type": "string" }
190+
},
191+
"milestone": {
192+
"type": ["object", "null"],
193+
"additionalProperties": true
194+
},
195+
"issueType": {
196+
"type": ["object", "null"],
197+
"additionalProperties": true
198+
},
199+
"parentIssue": {
200+
"type": ["object", "null"],
201+
"additionalProperties": true
202+
},
203+
"subIssues": {
204+
"type": "array",
205+
"items": {
206+
"type": ["object", "null"],
207+
"additionalProperties": true
208+
}
209+
}
210+
}
211+
},
212+
"mutationAction": {
213+
"type": "object",
214+
"additionalProperties": false,
215+
"required": ["kind", "status", "summary", "details", "error"],
216+
"properties": {
217+
"kind": {
218+
"enum": [
219+
"update-issue",
220+
"update-pull-request",
221+
"replace-assignees",
222+
"add-reviewers",
223+
"remove-reviewers",
224+
"add-sub-issue",
225+
"remove-sub-issue"
226+
]
227+
},
228+
"status": {
229+
"enum": ["planned", "applied", "failed", "skipped"]
230+
},
231+
"summary": { "type": "string" },
232+
"details": {
233+
"type": "object",
234+
"additionalProperties": true
235+
},
236+
"error": { "type": ["string", "null"] }
237+
}
238+
},
239+
"operation": {
240+
"type": "object",
241+
"additionalProperties": false,
242+
"required": ["surface", "status", "applicable", "requested", "reason", "before", "desired", "after", "actions"],
243+
"properties": {
244+
"surface": { "$ref": "#/$defs/surfaceName" },
245+
"status": {
246+
"enum": ["not-requested", "planned", "unchanged", "applied", "skipped", "unsupported", "failed"]
247+
},
248+
"applicable": { "type": "boolean" },
249+
"requested": { "type": "boolean" },
250+
"reason": { "type": ["string", "null"] },
251+
"before": {},
252+
"desired": {},
253+
"after": {},
254+
"actions": {
255+
"type": "array",
256+
"items": { "$ref": "#/$defs/mutationAction" }
257+
}
258+
}
259+
}
260+
}
261+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"priority:onboard:downstream": "node tools/priority/downstream-onboarding.mjs",
9292
"priority:onboard:success": "node tools/priority/downstream-onboarding-success.mjs",
9393
"priority:develop:sync": "pwsh -NoLogo -NoProfile -File tools/priority/Sync-OriginUpstreamDevelop.ps1",
94+
"priority:github:metadata:apply": "tsc -p tsconfig.cli.json && node dist/tools/cli/github-metadata.js",
9495
"priority:policy": "node tools/priority/check-policy.mjs",
9596
"priority:policy:route": "node tools/priority/policy-engine.mjs",
9697
"priority:issue:route": "node tools/priority/issue-router.mjs",

0 commit comments

Comments
 (0)