fix(shared/apm): repair gh-aw [a b] import-input serialization that breaks apm-prep#1033
Conversation
PR #982 added a Compute APM credential-group matrix step that feeds `${{ github.aw.import-inputs.packages }}` to `jq --argjson`. gh-aw substitutes that template at compile time using Go's default slice formatter, which emits `[microsoft/apm#main]` (space-separated, no quotes) instead of valid JSON `["microsoft/apm#main"]`. jq rejects the malformed input and apm-prep fails, blocking every PR run of the review panel and every triage-panel labelled issue. The bug shipped latent in #982 (locks not regenerated) and surfaced in #1026 when the locks were recompiled. Pinning gh-aw does not help: the same compiler version (v0.68.3) produced both shapes -- the difference was the new compute step that started routing the substituted value through `--argjson`. Fix: add a small Bash+Python repair_string_array helper in shared/apm.md that detects malformed Go-slice strings and rewrites them as JSON before they reach jq. Already-valid JSON and 'null' pass through untouched. apps[] (object arrays) is not repairable this way -- consumers must use the legacy single-app inputs until upstream gh-aw exposes a JSON-encoding helper for import-inputs (paper-cut filed upstream). - shared/apm.md: add repair_string_array helper for AW_APM_PACKAGES - pr-review-panel.lock.yml + triage-panel.lock.yml: recompile Verified locally with the live shared/apm.md run-block against four inputs ([single], [multi space-separated], null, already-valid JSON); all produce a correctly-typed matrix.group.packages array. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Repairs gh-aw import-inputs.packages array substitution so the shared APM workflow can safely pass packages through jq --argjson when gh-aw serializes string slices as Go-style [a b] instead of valid JSON, unblocking apm-prep for downstream panel workflows.
Changes:
- Added a
repair_string_arrayshim in.github/workflows/shared/apm.mdto normalize non-JSON[a b]into JSON["a","b"]while leaving valid JSON untouched. - Regenerated
pr-review-panel.lock.ymlandtriage-panel.lock.ymlso the compiled workflows include the shim.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/shared/apm.md | Adds a packages input repair step before feeding values into jq --argjson. |
| .github/workflows/pr-review-panel.lock.yml | Regenerated compiled workflow containing the new packages repair shim. |
| .github/workflows/triage-panel.lock.yml | Regenerated compiled workflow containing the new packages repair shim. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 3
| if printf '%s' "$raw" | jq -e 'type=="array"' >/dev/null 2>&1; then | ||
| echo "$raw"; return | ||
| fi | ||
| python3 -c 'import json, re, sys; s=sys.argv[1].strip(); s=s[1:-1] if s.startswith("[") and s.endswith("]") else s; print(json.dumps([t for t in re.split(r"[\s,]+", s) if t]))' "$raw" |
There was a problem hiding this comment.
repair_string_array introduces a new runtime dependency on python3, but this job already assumes only bash + jq. On runs-on: ubuntu-slim it's not guaranteed python3 is present, so this can break the workflow even when jq is available. Prefer implementing the repair using jq (already required) or add an explicit command -v python3 check with a clear error (and then recompile the lock workflows).
| python3 -c 'import json, re, sys; s=sys.argv[1].strip(); s=s[1:-1] if s.startswith("[") and s.endswith("]") else s; print(json.dumps([t for t in re.split(r"[\s,]+", s) if t]))' "$raw" | |
| printf '%s' "$raw" | jq -Rn ' | |
| input | |
| | gsub("^\\s+|\\s+$"; "") | |
| | if startswith("[") and endswith("]") then .[1:-1] else . end | |
| | split("[\\s,]+"; null) | |
| | map(select(length > 0)) | |
| ' |
| apps_json=${AW_APM_APPS:-null} | ||
| legacy_id=${AW_APM_LEGACY_APP_ID:-} | ||
|
|
||
| # gh-aw substitutes `["microsoft/apm#main"]` at |
There was a problem hiding this comment.
The comment claims gh-aw substitutes "[\"microsoft/apm#main\"]"/["microsoft/apm#main"], but the actual substituted value in this workflow is "[microsoft/apm#main]" (no quotes around elements), which is the invalid-JSON shape that breaks jq --argjson. Updating the comment to reflect the real broken format will make future debugging less confusing.
| # gh-aw substitutes `["microsoft/apm#main"]` at | |
| # gh-aw substitutes `[microsoft/apm#main]` at |
| # gh-aw substitutes `["microsoft/apm#main"]` at | ||
| # compile time using Go's default slice formatter, which emits | ||
| # `[a b c]` (space-separated, no quotes) instead of valid JSON. | ||
| # That breaks `jq --argjson` below. Repair string-array inputs | ||
| # in place; leave already-valid JSON untouched. apps[] (objects) |
There was a problem hiding this comment.
The comment says the substituted value looks like ["microsoft/apm#main"], but the actual value in this workflow is "[microsoft/apm#main]" (no quotes around elements), which is the invalid JSON that triggers the jq --argjson failure. Please update the comment to match the real broken format so it doesn't imply the input is already valid JSON.
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…#1051) * fix(shared/apm): re-shape single-artifact download to expected layout actions/download-artifact (>=v5) silently overrides 'merge-multiple: false' and flattens contents directly into 'path/' whenever exactly one artifact matches the pattern. Our 'Validate downloaded bundles match matrix manifest' step assumed the per-artifact subdir always exists, so any single-group consumer (the common case: one credential group, one packed bundle) hits 'missing APM bundles (group did not pack successfully): apm-<id>' even though the bundle downloaded and verified successfully. This was latent until #1033 unblocked apm-prep -- previously apm-prep itself was failing on the malformed jq input, so the agent job never got far enough to run validation. With prep fixed, every single-group PR run of pr-review-panel and triage-panel now fails at the validation step. Fix: add a normalisation step that, when matrix has exactly one group, re-creates the expected '${prefix}apm-<id>/' subdir and moves the flattened bundle file(s) into it. No-op for multi-group matrices where download-artifact already produces per-artifact subdirs. Validation script is unchanged. Refs: actions/download-artifact src/download-artifact.ts -- the 'isSingleArtifactDownload || mergeMultiple || artifacts.length === 1' check that triggers the flatten. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * address panel review: restore setup-cli lock entry, harden group_id, changelog - Restore github/gh-aw-actions/setup-cli@v0.71.2 entry in actions-lock.json (still referenced by .github/workflows/agentics-maintenance.yml; the prior recompile dropped it because pr-review-panel and triage-panel don't use it). - Sanitise group_id with strict allowlist (^[A-Za-z0-9_-]+$) before interpolating into the shell path, as defence-in-depth even though apm-prep produces a sanitised id today. - Guard jq '.group | length' against null with '.group // []'. - Link upstream actions/download-artifact source in the comment so future maintainers can verify the workaround is still needed. - CHANGELOG entry under [Unreleased] -> Fixed referencing #1051. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore(release): cut 0.11.0 Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps pyproject.toml + uv.lock to 0.11.0. Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because this release ships one BREAKING removal (`apm marketplace build` -> exits 2, use `apm pack`) plus several net-new features (Dev Container Feature, Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack` unification, multi-org `apps[]`). Strict semver in 0.x: minor for features-with-break, patch only for bugfixes. Milestone admin (done out-of-band): - Renamed milestone #8 `0.10.1` -> `0.11.0` - Created milestone #9 `0.12.0` as next-up bucket - Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0` - 6 closed items stay in `0.11.0` PRs shipping in 0.11.0 (22 commits since v0.10.0): User-facing features: - #1042/#722 `apm pack` unifies bundle + marketplace.json (BREAKING: `apm marketplace build` removed) - #1038 `marketplace:` block in apm.yml + `apm marketplace migrate` - #803 /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives - #861 Dev Container Feature `ghcr.io/microsoft/apm/apm-cli` - #982/#984 shared/apm.md `apps:` array for cross-org private packages - #820 `target:` in apm.yml validates at parse time - #1032 `apm marketplace add` honors manifest.name (Claude Code parity) - #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms User-facing fixes: - #1015 ADO Entra ID auth + `apm install --update` pre-flight abort - #1019/#1020 GEMINI.md only created when target requested - #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms - #1018 POSIX paths in auto-discovery output (Windows compat) - #996 drop stray 'specify' from generated file footer Maintainer tooling: - #1043 NOTICE.md per CELA template - #1045/#1044 NOTICE drift gate + license-policy gate in CI - #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut) - #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1 - #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file - #1022 review-panel: true fan-out + binary verdict + label automation - #918 complexity audit + benchmarks suite - #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder) Files changed: - pyproject.toml: 0.10.0 -> 0.11.0 - uv.lock: regenerated (version field only) - CHANGELOG.md: [Unreleased] promoted to [0.11.0] - 2026-04-29 NOTICE drift check passes against the bumped lockfile. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): tighten 0.11.0 entries to lead with user impact Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): move Dev Container Feature to Maintainer tooling (not yet published) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore(changelog): de-dupe within 0.11.0 (combine #722 Removed bullets, drop #820 Fixed pointer) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TL;DR
PR #982's new
Compute APM credential-group matrixstep inshared/apm.mdfeeds${{ github.aw.import-inputs.packages }}tojq --argjson. gh-aw substitutes that template at compile time using Go's default slice formatter, which emits[microsoft/apm#main](space-separated, no quotes) instead of valid JSON.jqrejects it andapm-prepcrashes, blocking everypr-review-panelandtriage-panelrun since lock files were last regenerated.This PR adds a small repair shim in
shared/apm.mdand recompiles both lock files. Upstream paper-cut filed at github/gh-aw#29076.How we got here
apm-prepstep using--argjson; locks not regenerated, bug latentAW_APM_PACKAGES: "[microsoft/apm#main]"jq: invalid JSON text passed to --argjsonPinning gh-aw doesn't help: same compiler version (v0.68.3) produced both shapes -- the difference was that #982 introduced the new compute step that started routing the substituted value through
--argjson. The bug is in ourshared/apm.md, not in the gh-aw binary -- though the upstream serializer choice is what makes the bug possible (hence github/gh-aw#29076).What changed
shared/apm.mdgains a tinyrepair_string_arrayBash+Python helper that:nulland empty values through untouched,jq -e 'type=="array"') through untouched,The two consumer lock files are regenerated with
gh aw compile pr-review-panel triage-panel. No source change to eitherpr-review-panel.mdortriage-panel.md.apps[](object arrays) is not repairable this way -- the Go-slice format for objects is[map[k:v] map[k:v]]and is non-trivially round-trippable. No current workflow usesapps[]via import-inputs (only the legacy single-app inputs are exercised), so this PR leaves apps alone with a comment pointing at the upstream issue.Verification
End-to-end simulation against the live
shared/apm.mdrun-block:AW_APM_PACKAGESmatrix.group.packages[microsoft/apm#main]["microsoft/apm#main"][microsoft/apm#main github/awesome-copilot/skills/foo]["microsoft/apm#main","github/awesome-copilot/skills/foo"]["already","json"]["already","json"]nullAudit of other gh-aw workflows
Only
pr-review-panel.mdandtriage-panel.mdimportshared/apm.md;cli-consistency-checker.md,daily-doc-updater.md, anddaily-test-improver.mddo not have anyimports:block and are unaffected.Risk
Low. The repair is fully gated behind an
is-this-already-valid-JSONcheck, so well-formed inputs (today and after upstream fix) are untouched. No changes to schema, no breaking change for vendored copies ofshared/apm.md-- theSource of truth/apm-action pinheader lines stay byte-identical.Follow-ups
[a b]instead of JSON, breakingjq --argjsonconsumers github/gh-aw#29076 -- request that import-input arrays be substituted as JSON (or that a compile-time JSON helper be exposed). Once shipped, therepair_string_arrayshim becomes a no-op and can be deleted.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com