Skip to content
Merged
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
116 changes: 84 additions & 32 deletions .github/workflows/issue-fields-backfill.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ name: Issue Fields • Bulk Backfill
#
# PREREQUISITES
# ─────────────
# 1. vars.LS_APP_ID — GitHub App ID (integer)
# 1. vars.LS_APP_CLIENT_ID — GitHub App client ID (preferred)
# vars.LS_APP_ID — Legacy GitHub App ID (fallback)
# 2. secrets.LS_APP_PRIVATE_KEY — GitHub App private key (PEM)
# 3. vars.LS_PROJECT_NUMBER — Projects v2 board number (integer, e.g. 33)
# OR vars.LS_PROJECT_URL — Full URL (e.g. https://github.com/orgs/…/projects/33)
Expand Down Expand Up @@ -74,13 +75,14 @@ jobs:

- name: Create GitHub App token
id: app-token
uses: actions/create-github-app-token@v2
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.LS_APP_ID }}
client-id: ${{ vars.LS_APP_CLIENT_ID != '' && vars.LS_APP_CLIENT_ID || vars.LS_APP_ID }}
Comment on lines +78 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow relevant lines =="
if [ -f .github/workflows/issue-fields-backfill.yml ]; then
  nl -ba .github/workflows/issue-fields-backfill.yml | sed -n '1,130p'
else
  echo "workflow file not found"
fi

echo
echo "== actionlint references =="
rg -n "actionlint|create-github-app-token|app-id|client-id" .github/workflows . 2>/dev/null | sed -n '1,200p'

echo
echo "== read-only metadata/version snippets if available =="
python3 - <<'PY'
from pathlib import Path
import re
for p in Path('.github').rglob('*.yml'):
    txt=p.read_text(errors='ignore')
    if 'actionlint' in txt or 'create-github-app-token' in txt:
        print(f"\n--- {p} ---")
        for i,line in enumerate(txt.splitlines(),1):
            if 'actionlint' in line or 'create-github-app-token' in line or 'app-id' in line or 'client-id' in line:
                print(f"{i}: {line}")
PY

Repository: lightspeedwp/.github

Length of output: 228


🌐 Web query:

actions/create-github-app-token v3 README client-id app-input README

💡 Result:

In the actions/create-github-app-token v3 documentation, the client-id input is the recommended way to provide the GitHub App identifier for authentication [1][2]. While the action still supports app-id for backwards compatibility, it has been officially deprecated [1][2]. The action's runtime logic is configured to prefer client-id; if both inputs are provided, client-id takes precedence [2][3]. If neither input is provided, the action will return an error [2][3]. To use the action in a workflow, you should store your GitHub App's Client ID in a repository variable (e.g., APP_CLIENT_ID) and your private key in a repository secret (e.g., APP_PRIVATE_KEY), and then reference them in your workflow step [4][1][5]: - uses: actions/create-github-app-token@v3 id: app-token with: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} The documentation does not refer to an input named app-input; rather, it uses client-id (the recommended input) or the legacy app-id to specify the application identity [1][2][5].

Citations:


🌐 Web query:

ReviewDog actionlint command config actionlint GitHub Actions input unknown

💡 Result:

The error "input unknown" or "not defined" when using the reviewdog/action-actionlint GitHub Action typically occurs because the input parameter you are attempting to use is not supported by the specific version of the action you have configured [1][2]. To resolve this issue, check the following steps: 1. Verify Valid Inputs: Ensure the input name you are using is defined in the action's configuration. The currently supported inputs for reviewdog/action-actionlint are [3][4]: - github_token - tool_name - level - reporter - filter_mode - fail_level - fail_on_error (deprecated) - reviewdog_flags - actionlint_flags 2. Check Your Version: If you are trying to use a newer feature like 'fail_level', ensure you are using a recent version of the action that supports it [2]. If you are pinned to an older version (e.g., using a specific tag or hash), the newer inputs will not be recognized, resulting in the "not defined" error [2]. Update your workflow's uses statement to point to a later version or the latest release [5][4]. 3. Common Pitfall: Users often encounter this error when trying to add unsupported parameters, such as 'workdir', which is not a valid input for this action [1]. Since actionlint automatically targets the repository's workflow directory, specific working directory overrides are generally not supported as action inputs [1]. 4. Distinguish Action vs. Tool: Remember that actionlint is the underlying tool, and reviewdog/action-actionlint is the wrapper [5][6]. Errors about "unknown inputs" usually refer to the wrapper's with: configuration, not the flags passed to the actionlint tool itself [2]. If you need to pass additional configuration to the actionlint tool, use the actionlint_flags input instead [5][3][4].

Citations:


🌐 Web query:

github actions create-github-app-token v3 client-id

💡 Result:

In version 3 of the actions/create-github-app-token action, the client-id input was introduced as the recommended parameter for identifying the GitHub App [1][2]. Key details regarding the use of client-id include: - Recommendation: It is now the preferred way to specify the GitHub App, replacing the legacy app-id input [3][2]. - Backwards Compatibility: The action still accepts app-id for backwards compatibility, though it is marked as deprecated [2][4]. - Precedence: If both client-id and app-id are provided, the action will prioritize client-id [2]. - Configuration: You should store your App's Client ID in your repository or organization variables (e.g., as APP_CLIENT_ID) and pass it to the action via the workflow file [3][5]: - uses: actions/create-github-app-token@v3 with: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} Ensure that the corresponding private key for the GitHub App is stored as a repository secret and passed to the private-key input [3][6]. If neither client-id nor app-id is provided, the action will return an error [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo errexit

echo "== candidate files =="
git ls-files '.github/workflows/*.yml' '.github/workflows/*.yaml' 2>/dev/null | sed -n '1,120p'

echo
echo "== target workflow with line numbers =="
sed -n '1,140p' .github/workflows/issue-fields-backfill.yml | awk '{printf "%6d\t%s\n", NR, $0}'

echo
echo "== actionlint refs across tracked workflow files =="
git ls-files '.github/workflows/*.yml' '.github/workflows/*.yaml' 2>/dev/null \
  | xargs grep -nE "actionlint|create-github-app-token|app-id|client-id" 2>/dev/null || true

Repository: lightspeedwp/.github

Length of output: 8449


🌐 Web query:

rhysd actionlint releases unknown inputs create-github-app-token client-id

💡 Result:

The issue where actionlint reports unknown inputs or missing required inputs when using actions/create-github-app-token@v3 with the client-id input is a known false-positive caused by stale metadata in actionlint's internal registry [1][2]. When actions/create-github-app-token updated its v3 action.yml to introduce client-id as the canonical input (deprecating the legacy app-id), actionlint's bundled definition of popular actions was not immediately synchronized [1][2]. This caused actionlint to: - Incorrectly report that the required app-id input was missing [1][3][2]. - Incorrectly report that the valid client-id input was undefined [1][3][2]. This has been addressed in recent updates to actionlint [3][2]. To resolve this, ensure you are using an up-to-date version of actionlint [4][5]. If you are on an older version and cannot upgrade, you can suppress these specific false-positives by adding the following to your.github/actionlint.yaml configuration file [1]: paths:.github/workflows//*.{yml,yaml}: ignore: - 'missing input "app-id" which is required by action "actions/create-github-app-token@' - 'input "client-id" is not defined in action "actions/create-github-app-token@'

Citations:


Refresh actionlint’s action metadata or pin the validator.

actions/create-github-app-token@v3 legitimately supports client-id, but older actionlint metadata can still flag it as unknown and block the required zero-error workflow validation. Bump the validator or patch its action registry; don’t fall back to the deprecated app-id.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 78-78: missing input "app-id" which is required by action "actions/create-github-app-token@v3". all required inputs are "app-id", "private-key"

(action)


[error] 80-80: input "client-id" is not defined in action "actions/create-github-app-token@v3". available inputs are "app-id", "github-api-url", "owner", "permission-actions", "permission-administration", "permission-checks", "permission-codespaces", "permission-contents", "permission-custom-properties-for-organizations", "permission-dependabot-secrets", "permission-deployments", "permission-email-addresses", "permission-enterprise-custom-properties-for-organizations", "permission-environments", "permission-followers", "permission-git-ssh-keys", "permission-gpg-keys", "permission-interaction-limits", "permission-issues", "permission-members", "permission-metadata", "permission-organization-administration", "permission-organization-announcement-banners", "permission-organization-copilot-seat-management", "permission-organization-custom-org-roles", "permission-organization-custom-properties", "permission-organization-custom-roles", "permission-organization-events", "permission-organization-hooks", "permission-organization-packages", "permission-organization-personal-access-token-requests", "permission-organization-personal-access-tokens", "permission-organization-plan", "permission-organization-projects", "permission-organization-secrets", "permission-organization-self-hosted-runners", "permission-organization-user-blocking", "permission-packages", "permission-pages", "permission-profile", "permission-pull-requests", "permission-repository-custom-properties", "permission-repository-hooks", "permission-repository-projects", "permission-secret-scanning-alerts", "permission-secrets", "permission-security-events", "permission-single-file", "permission-starring", "permission-statuses", "permission-team-discussions", "permission-vulnerability-alerts", "permission-workflows", "private-key", "repositories", "skip-token-revoke"

(action)

🪛 zizmor (1.28.0)

[error] 78-78: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/issue-fields-backfill.yml around lines 78 - 80, Update the
workflow validation setup for .github/workflows/issue-fields-backfill.yml by
refreshing actionlint’s action metadata or pinning actionlint to a version that
recognizes the client-id input for actions/create-github-app-token@v3. Keep
client-id configured and do not replace it with the deprecated app-id fallback;
ensure required workflow validation completes with zero errors.

Source: Linters/SAST tools

private-key: ${{ secrets.LS_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
Comment on lines +78 to +82

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== workflow excerpt =="
sed -n '1,280p' .github/workflows/issue-fields-backfill.yml

echo
echo "== related tokens/permissions =="
rg -n "create-github-app-token|permissions:|id-token|contents:|pull-requests:|issues:" .github/workflows/issue-fields-backfill.yml .github/workflows || true

echo
echo "== package/action versions nearby =="
rg -n "actions/create-github-app-token" .github/workflows || true

Repository: lightspeedwp/.github

Length of output: 21518


🌐 Web query:

actions/create-github-app-token v3 owner repositories inputs installation token default permissions documentation

💡 Result:

The actions/create-github-app-token version 3 action is used to generate an installation access token for a GitHub App within a workflow [1][2]. Below is the summary of the requested inputs and default permission behavior. Input Parameters: - owner: Optional. Specifies the owner (user or organization) of the GitHub App installation [1][3]. If left empty, it defaults to the current repository owner [1][4]. - repositories: Optional. A comma or newline-separated list of repository names to which the token should have access [1][3]. - If owner is provided and repositories is empty, the token is scoped to all repositories in that owner's installation [1][3]. - If both owner and repositories are empty, the token is scoped to only the current repository [1][3]. - permission-: The action supports granular permission control via inputs prefixed with permission- (e.g., permission-pull-requests) [1][4]. These are dynamically generated based on the GitHub OpenAPI specification [5]. Default Permission Behavior: By default, if no specific permissions are requested via the permission- inputs, the generated installation token inherits all permissions that were granted to the GitHub App installation [1][4][6]. Important Considerations: - It is recommended to explicitly list the specific permissions required for your use case to adhere to the principle of least privilege, consistent with GitHub's guidelines for controlling GITHUB_TOKEN permissions [1][4]. - You cannot grant the token permissions or access to repositories that the App installation itself does not already possess [5][6]. - The version 3 release introduced several changes, including the use of client-id (replacing the deprecated app-id) and support for enterprise-level GitHub Apps [7][8].

Citations:


🌐 Web query:

"actions/create-github-app-token@v3" "repositories:" "permission-" README

💡 Result:

In the actions/create-github-app-token@v3 action, the permission- prefix is used to explicitly define the permissions granted to the generated installation access token [1]. By default, the token inherits all permissions assigned to the GitHub App installation [1][2]. However, it is recommended to explicitly list only the permissions required for your specific use case to follow the principle of least privilege [1]. To configure these permissions, you prefix the desired permission key with permission- in your workflow configuration (e.g., permission-pull-requests for pull-requests) [1]. For example: - To grant read access to pull requests: permission-pull-requests: read - To grant write access to metadata: permission-metadata: write Important considerations: 1. Installation vs. App Permissions: The permissions you set must be granted to the GitHub App installation itself [1][2]. If you request a permission that the installation does not possess, the action will return an error [1]. 2. Administrative Approval: If you add new permissions to an app after it has been installed, an account administrator must approve these changes before they become effective on the installation [1][2]. 3. Repositories Input: The repositories input allows you to scope the token to specific repositories [1][2]. If this input is omitted, the token is scoped to the current repository by default [2]. If the owner input is provided but repositories is empty, the token is scoped to all repositories in that installation [1][2].

Citations:


Whittle the GitHub App token down a smidge.

The owner input without repositories expands the installation token to every repo in the installation, and absent permission-* inputs the token can inherit every app permission. Scope it to the current repo and declare only the permissions the backfill actually needs, such as permission-issues: write, permission-content: read, and permission-projects: write (where the installation has them).

🔐 Suggested tightening
           owner: ${{ github.repository_owner }}
+          repositories: ${{ github.event.repository.name }}
+          # Add only the required permission-* inputs after confirming app scopes.
         id-token: write
🧰 Tools
🪛 zizmor (1.28.0)

[error] 81-81: dangerous use of GitHub App tokens (github-app): token granted access to all repositories for this owner's app installation

(github-app)


[error] 77-77: dangerous use of GitHub App tokens (github-app): app token inherits blanket installation permissions

(github-app)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/issue-fields-backfill.yml around lines 77 - 81, Update the
actions/create-github-app-token@v3 configuration to limit the token to the
current repository by adding the repositories input, and explicitly declare only
the required permissions, including issues write, contents read, and projects
write where supported by the installation. Keep the existing app-id,
private-key, and owner inputs unchanged.

Sources: Path instructions, MCP tools, Linters/SAST tools


- name: Run bulk backfill
uses: actions/github-script@v7
uses: actions/github-script@v9
env:
APP_TOKEN: ${{ steps.app-token.outputs.token }}
DRY_RUN: ${{ github.event.inputs.dry_run }}
Expand Down Expand Up @@ -114,6 +116,17 @@ jobs:
(config?.organization_issue_fields?.enabled_issue_types ?? []).map(s => s.toLowerCase())
);

function resolveProjectNumberFromUrl(url) {
try {
const segments = new URL(url).pathname.split('/').filter(Boolean);
const projectsIdx = segments.indexOf('projects');
if (projectsIdx < 0 || projectsIdx === segments.length - 1) return Number.NaN;
return parseInt(segments[projectsIdx + 1], 10);
} catch {
return Number.NaN;
}
}
Comment on lines +119 to +128

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file info =="
wc -l .github/workflows/issue-fields-backfill.yml 2>/dev/null || true

echo "== relevant sections =="
sed -n '100,135p' .github/workflows/issue-fields-backfill.yml 2>/dev/null || true
sed -n '205,235p' ..github/workflows/issue-fields-backfill.yml 2>/dev/null || true

echo "== parseInt usage in file =="
rg -n "parseInt|resolveProjectNumberFromUrl|projectNumber|MAX|NaN|Int!" .github/workflows/issue-fields-backfill.yml 2>/dev/null || true

echo "== deterministic parseInt/coercion probe =="
node - <<'JS'
const inputs = ['123', '123junk', '0123', '1.5', '1.23e2', '123abcXYZ', '9007199254740992', '2147483648', '2147483647', '2147483646'];
for (const s of inputs) {
  console.log(`${s.padEnd(23)} parseInt=`, parseInt(s, 10),
    `Number=`, Number(s),
    `digitsOnly=`, /^\d+$/.test(s),
    `safePositiveInt=`, Number.isSafeInteger(Number(s)) && Number(s) > 0);
}
JS

Repository: lightspeedwp/.github

Length of output: 4032


Enforce a strict project-number parser.

parseInt can accept leading digits, so values like 123junk are parsed as 123; the later numeric check also allows values that exceed GraphQL’s signed 32-bit range (2147483647). For both URL-derived and PROJECT_NUMBER inputs, use one strict handler: decimal digits only, safe integer, and 1 ≤ n ≤ 2147483647.

Applies to lines 119-124 and 246-247.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/issue-fields-backfill.yml around lines 118 - 127, Update
resolveProjectNumberFromUrl and the PROJECT_NUMBER input handling to use one
shared strict project-number parser: accept decimal digits only, convert without
partial parsing, and require a safe integer in the inclusive range 1–2147483647;
return the existing invalid/NaN result for malformed, zero, negative, or
out-of-range values.

Source: MCP tools


// ─── Resolve project URL/number ─────────────────────────────
let projectUrl = process.env.PROJECT_URL;
const projectNumber = parseInt(process.env.PROJECT_NUMBER || '0', 10);
Expand Down Expand Up @@ -143,24 +156,54 @@ jobs:

// ─── Step 2 (optional): Native issue type sync ───────────────
if (syncNativeTypes) {
core.info('Fetching org issue types...');
core.info('Fetching issue types...');
let orgTypeMap = new Map();
let orgFetchError = '';

try {
const typeData = await github.graphql(`
query($org: String!) {
organization(login: $org) {
issueTypes(first: 50) {
nodes { id name isEnabled }
nodes { id name }
}
}
}
`, { org: owner });
for (const node of typeData.organization.issueTypes.nodes) {
if (node.isEnabled !== false) orgTypeMap.set(node.name.toLowerCase(), node.id);
orgTypeMap.set(node.name.toLowerCase(), node.id);
}
core.info(`Org has ${orgTypeMap.size} enabled issue type(s): ${[...orgTypeMap.keys()].join(', ')}`);
core.info(`Organization has ${orgTypeMap.size} issue type(s): ${[...orgTypeMap.keys()].join(', ')}`);
} catch (err) {
core.warning(`Could not fetch org issue types: ${err.message}`);
orgFetchError = err.message;
core.warning(
`Could not fetch org issue types: ${err.message}. ` +
'Attempting repository-scoped issue type fallback.'
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

if (orgTypeMap.size === 0) {
try {
const repoTypeData = await github.graphql(`
query($owner: String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
issueTypes(first: 50) {
nodes { id name }
}
}
}
`, { owner, repo });
for (const node of repoTypeData.repository.issueTypes.nodes) {
orgTypeMap.set(node.name.toLowerCase(), node.id);
}
core.info(`Repository fallback has ${orgTypeMap.size} issue type(s): ${[...orgTypeMap.keys()].join(', ')}`);
} catch (err) {
core.warning(
`Could not fetch repository issue types: ${err.message}. ` +
`${orgFetchError ? `Org query error: ${orgFetchError}. ` : ''}` +
'Ensure the app token has permission to read issue types at org or repository scope.'
);
}
}

for (const issue of issues) {
Expand All @@ -178,12 +221,12 @@ jobs:
if (!dryRun) {
try {
await github.graphql(`
mutation($issueId: ID!, $typeId: ID) {
updateIssue(input: { id: $issueId, typeId: $typeId }) {
mutation($issueId: ID!, $issueTypeId: ID) {
updateIssue(input: { id: $issueId, issueTypeId: $issueTypeId }) {
issue { id issueType { name } }
}
}
`, { issueId: issue.node_id, typeId });
`, { issueId: issue.node_id, issueTypeId: typeId });
report.nativeTypeSet.push(`#${issue.number} → ${targetName}`);
} catch (err) {
report.errors.push(`#${issue.number} native type failed: ${err.message}`);
Expand All @@ -199,34 +242,43 @@ jobs:
core.info(`Syncing project fields to ${projectUrl}...`);

// Resolve project node ID and fields
const [, , , orgOrUser, , projNumStr] = new URL(projectUrl).pathname.split('/');
const projNum = parseInt(projNumStr, 10);
const projNumFromUrl = resolveProjectNumberFromUrl(projectUrl);
const projNum = Number.isInteger(projectNumber) && projectNumber > 0
? projectNumber
: projNumFromUrl;
Comment on lines +245 to +248

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject conflicting project identifiers.

When PROJECT_NUMBER and PROJECT_URL resolve to different valid projects, this silently chooses PROJECT_NUMBER while the log still displays projectUrl. Reject the mismatch or make one source explicitly authoritative and report that choice; otherwise the backfill can target the wrong project.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/issue-fields-backfill.yml around lines 217 - 220, Update
the project-identifier resolution around resolveProjectNumberFromUrl so that
when both PROJECT_NUMBER and PROJECT_URL produce valid numbers, conflicting
values are rejected before backfill proceeds, with an actionable error. Preserve
the existing fallback behavior when only one source is valid, and ensure any
mismatch handling reports the conflicting identifiers rather than silently
preferring projectNumber.


let projectId, fieldMeta;
try {
const projData = await github.graphql(`
query($owner: String!, $number: Int!) {
organization(login: $owner) {
projectV2(number: $number) {
id
fields(first: 30) {
nodes {
... on ProjectV2Field { id name dataType }
... on ProjectV2SingleSelectField { id name dataType options { id name } }
if (!Number.isInteger(projNum) || projNum <= 0) {
core.warning(
`Could not parse a valid project number. ` +
`PROJECT_NUMBER="${process.env.PROJECT_NUMBER || ''}" PROJECT_URL="${projectUrl}"`
);
Comment on lines +251 to +255

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

The documented fail-fast guardrail is not implemented.

  • .github/workflows/issue-fields-backfill.yml#L223-L227: validate before writes and propagate an actual failure when the project number is invalid.
  • projects/active/phase-2b-skills-audit/PHASE-2B-SKILLS-PLANNING.md#L542-L542: keep the item open until the workflow fails accordingly, or change the claim to warning-and-skip behaviour.
📍 Affects 2 files
  • .github/workflows/issue-fields-backfill.yml#L223-L227 (this comment)
  • projects/active/phase-2b-skills-audit/PHASE-2B-SKILLS-PLANNING.md#L542-L542
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/issue-fields-backfill.yml around lines 223 - 227, The
invalid project-number branch in the workflow must fail before any writes rather
than only issuing core.warning; update the validation around projNum to
propagate an actual failure while preserving the existing diagnostic context. In
projects/active/phase-2b-skills-audit/PHASE-2B-SKILLS-PLANNING.md:542, keep the
item open until this workflow failure behavior is implemented, or revise the
documented claim to explicitly describe warning-and-skip behavior.

} else {
try {
const projData = await github.graphql(`
query($owner: String!, $number: Int!) {
organization(login: $owner) {
projectV2(number: $number) {
id
fields(first: 30) {
nodes {
... on ProjectV2Field { id name dataType }
... on ProjectV2SingleSelectField { id name dataType options { id name } }
}
}
}
}
}
`, { owner, number: projNum });
projectId = projData.organization.projectV2.id;
fieldMeta = {};
for (const f of projData.organization.projectV2.fields.nodes) {
fieldMeta[f.name.toLowerCase()] = f;
}
`, { owner, number: projNum });
projectId = projData.organization.projectV2.id;
fieldMeta = {};
for (const f of projData.organization.projectV2.fields.nodes) {
fieldMeta[f.name.toLowerCase()] = f;
core.info(`Project "${projectId}" fields: ${Object.keys(fieldMeta).join(', ')}`);
} catch (err) {
core.warning(`Could not access project: ${err.message}`);
}
core.info(`Project "${projectId}" fields: ${Object.keys(fieldMeta).join(', ')}`);
} catch (err) {
core.warning(`Could not access project: ${err.message}`);
}

if (projectId) {
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Issue fields backfill workflow hardened** — Upgraded actions to current major versions, added safer project number extraction to avoid invalid `Int!` coercion, and improved diagnostics with guardrail warnings. ([PR #1405](https://github.com/lightspeedwp/.github/pull/1405), [#1404](https://github.com/lightspeedwp/.github/issues/1404))

- **Routine dependency updates** — Multiple rounds of security and feature updates across project dependencies including TypeScript (5.9.3 → 7.0.2), markdownlint-cli2 (0.19.0 → 0.23.1), Svelte ecosystem updates (5.56.3 → 5.56.6 in website), Astro framework (6.4.8 → 7.1.3), and GitHub Actions (setup-node 4 → 7, mergifyio/gha-mergify-ci 22 → 24). ([PR #1027](https://github.com/lightspeedwp/.github/pull/1027), [#1030](https://github.com/lightspeedwp/.github/pull/1030), [#1032](https://github.com/lightspeedwp/.github/pull/1032), [#1034](https://github.com/lightspeedwp/.github/pull/1034), [#1035](https://github.com/lightspeedwp/.github/pull/1035), [#1037](https://github.com/lightspeedwp/.github/pull/1037), [#1038](https://github.com/lightspeedwp/.github/pull/1038), [#1048](https://github.com/lightspeedwp/.github/pull/1048), [#1049](https://github.com/lightspeedwp/.github/pull/1049), [#1050](https://github.com/lightspeedwp/.github/pull/1050), [#1051](https://github.com/lightspeedwp/.github/pull/1051), [#1052](https://github.com/lightspeedwp/.github/pull/1052), [#1053](https://github.com/lightspeedwp/.github/pull/1053), [#1056](https://github.com/lightspeedwp/.github/pull/1056), [#1060](https://github.com/lightspeedwp/.github/pull/1060), [#1061](https://github.com/lightspeedwp/.github/pull/1061), [#1062](https://github.com/lightspeedwp/.github/pull/1062), [#1063](https://github.com/lightspeedwp/.github/pull/1063), [#1064](https://github.com/lightspeedwp/.github/pull/1064), [#1065](https://github.com/lightspeedwp/.github/pull/1065))
- **Gitleaks hardening — pinned version, checksum verification and least privilege** — `gitleaks-reusable.yml` no longer resolves the latest Gitleaks release at run time. The version (`8.30.1`) and its official `linux_x64` SHA-256 are pinned together in the workflow and the archive is verified with `sha256sum --check` before extraction, so a tampered or truncated download fails closed. `actions/checkout` is pinned to the immutable SHA for `v7.0.1` with the version in a same-line comment, and runs with `persist-credentials: false`. Added explicit `permissions: contents: read` at workflow and job level, `timeout-minutes`, `set -euo pipefail` in every run block, temporary-directory cleanup under `if: always()`, and a graceful fallback when a calling repository has no `.gitleaks.toml`. Replaced the undocumented `gitleaks detect` alias with the supported `gitleaks dir` and `gitleaks git` commands. Values are passed to shell through `env` rather than interpolated from `${{ }}`. Added `gitleaks-update.yml`, a weekly updater that resolves the latest non-draft, non-prerelease release, retrieves the official checksum manifest, updates version and checksum together, re-verifies the download and opens a pull request without merging. ([PR #1455](https://github.com/lightspeedwp/.github/pull/1455), [#1454](https://github.com/lightspeedwp/.github/issues/1454))

- **Routine dependency updates** — Multiple rounds of security and feature updates across project dependencies including TypeScript (5.9.3 → 7.0.2), markdownlint-cli2 (0.19.0 → 0.23.1), Svelte ecosystem updates (5.56.3 → 5.56.6 in website), Astro framework (6.4.8 → 7.1.3), and GitHub Actions (setup-node 4 → 7, mergifyio/gha-mergify-ci 22 → 24). ([PR #1027](https://github.com/lightspeedwp/.github/pull/1027) — *chore(deps-dev): bump lint-staged from 17.0.7 to 17.0.8*, [PR #1030](https://github.com/lightspeedwp/.github/pull/1030) — *chore(deps): bump svelte from 5.56.3 to 5.56.4 in /website*, [PR #1032](https://github.com/lightspeedwp/.github/pull/1032) — *chore(deps-dev): bump markdownlint-cli2 from 0.19.0 to 0.23.0*, [PR #1034](https://github.com/lightspeedwp/.github/pull/1034) — *chore(deps): bump @astrojs/svelte from 8.1.2 to 9.0.1 in /website*, [PR #1035](https://github.com/lightspeedwp/.github/pull/1035) — *chore(deps-dev): bump @typescript-eslint/parser from 8.61.1 to 8.62.1*, [PR #1037](https://github.com/lightspeedwp/.github/pull/1037) — *chore(deps): bump marked from 18.0.5 to 18.0.6 in /website*, [PR #1038](https://github.com/lightspeedwp/.github/pull/1038) — *chore(deps): bump astro from 6.4.8 to 7.0.7 in /website*, [PR #1048](https://github.com/lightspeedwp/.github/pull/1048) — *chore(deps): bump mergifyio/gha-mergify-ci from 22 to 23*, [PR #1049](https://github.com/lightspeedwp/.github/pull/1049) — *chore(deps): bump actions/setup-node from 4 to 7*, [PR #1050](https://github.com/lightspeedwp/.github/pull/1050) — *chore(deps-dev): bump typescript from 5.9.3 to 7.0.2*, [PR #1051](https://github.com/lightspeedwp/.github/pull/1051) — *chore(deps-dev): bump markdownlint-cli2 from 0.23.0 to 0.23.1*, [PR #1052](https://github.com/lightspeedwp/.github/pull/1052) — *chore(deps): bump svelte from 5.56.5 to 5.56.6 in /website*, [PR #1053](https://github.com/lightspeedwp/.github/pull/1053) — *chore(deps): bump astro from 7.0.9 to 7.1.0 in /website*, [PR #1056](https://github.com/lightspeedwp/.github/pull/1056) — *chore(deps): bump astro from 7.0.9 to 7.1.3 in /website*, [PR #1060](https://github.com/lightspeedwp/.github/pull/1060) — *chore(deps): bump marked from 18.0.6 to 18.0.7 in /website*, [PR #1061](https://github.com/lightspeedwp/.github/pull/1061) — *chore(deps-dev): bump @typescript-eslint/eslint-plugin from 8.64.0 to 8.65.0*, [PR #1062](https://github.com/lightspeedwp/.github/pull/1062) — *chore(deps-dev): bump prettier from 3.9.5 to 3.9.6*, [PR #1063](https://github.com/lightspeedwp/.github/pull/1063) — *chore(deps-dev): bump @typescript-eslint/parser from 8.64.0 to 8.65.0*, [PR #1064](https://github.com/lightspeedwp/.github/pull/1064) — *chore(deps-dev): bump lint-staged from 17.0.8 to 17.1.1*, [PR #1065](https://github.com/lightspeedwp/.github/pull/1065) — *chore(deps-dev): bump @stoplight/spectral-cli from 6.16.1 to 6.16.2*)
Expand All @@ -113,6 +116,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Branch cleanup automation** — Added reusable cleanup script, weekly scheduled workflow, and report generation for stale merged branches with safety guardrails. ([PR #1067](https://github.com/lightspeedwp/.github/pull/1067) — *Add scheduled branch cleanup automation and reporting*, [#1066](https://github.com/lightspeedwp/.github/issues/1066))

- **Issue fields backfill: Node 20 deprecation and project number coercion warnings** — Removed Node 20 deprecation warnings and resolved invalid project number coercion (`String` to `Int!`) in the backfill run path. Added fallback behaviour for org issue type sync when integration scope is insufficient. ([PR #1405](https://github.com/lightspeedwp/.github/pull/1405), [#1404](https://github.com/lightspeedwp/.github/issues/1404))

- **Changelog automation: Section headers destroyed on merge** — The merge-entries workflow was discarding section headers during deduplication, corrupting changelog structure on every PR merge. Fixed deduplication logic to preserve headers and limited scope to [Unreleased] section only. ([PR #1276](https://github.com/lightspeedwp/.github/pull/1276), [#1275](https://github.com/lightspeedwp/.github/issues/1275))
### Removed

- **`agents/playwright-testing-agent/checksums.sha256`** — Removed a manifest that provided the appearance of integrity without the substance: nothing in CI, `scripts/` or `hooks/` generated or verified it, and an audit of all 15 agents found twelve at 44–71% invalid, invalidated by the repo's own markdown normalisation sweeps and `lint-staged --fix`. Because it sat alongside the files it hashed it could not evidence tampering either. Note this diverges from the other 14 agents pending an org-wide decision. ([PR #1392](https://github.com/lightspeedwp/.github/pull/1392) — *feat(playwright-testing-agent): performance routing, a11y/SEO/console gates, and scope-exclusion discipline*, [#1394](https://github.com/lightspeedwp/.github/issues/1394))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This document consolidates Phase A audit findings into a strategic plan for Phas
- Titles now map to canonical task codes (W5-2 through W12-1) and use descriptive naming.
- Bodies now follow task template conventions, including Definition of Ready (DoR) and Definition of Done (DoD).
- Status labels updated to `status:ready` across the remediated issue set.
- Follow-up workflow remediation issue created: #1404 (validate:workflows multiline run-block control-flow fixes).

---

Expand Down Expand Up @@ -531,6 +532,19 @@ Per-agent cleanup (5-6 hours each due to higher complexity):
2. ✅ Request decision on PRD agents (Option A/B/C) — 2-3 day discussion window
3. ✅ Finalize Figma audit plan scope

### Active TODO: Workflow Backfill Debug Pack (Issue #1404)

- [x] Confirm action runtime compatibility for `actions/create-github-app-token@v2` and `actions/github-script@v7` under Node 24.
- [x] Review each action release note/changelog and pin to Node 24-compatible versions where available.
- [ ] If upstream action support is pending, document temporary mitigation and owner for follow-up.
- [ ] Reproduce GraphQL project lookup locally using the same project URL/number values from workflow inputs.
- [x] Patch project number parsing in `.github/workflows/issue-fields-backfill.yml` run script to avoid invalid `Int!` coercion.
- [x] Add guardrail validation that fails fast with explicit logging when project URL parsing cannot produce a valid numeric project ID.
- [ ] Verify GitHub App permissions for org issue types query and confirm required org/project scopes are granted.
- [x] Add fallback behavior for org issue type sync when integration scope is insufficient, including actionable summary output.
- [x] Re-run `Issue Fields • Bulk Backfill` after fixes and capture before/after metrics: native types set, project fields set, errors.
- [x] Post a run-summary update to issue #1404 and link final remediation PR.

### Phase C Kickoff (Week 5)

1. Create per-agent consolidation issues (1 per agent or batch)
Expand All @@ -542,4 +556,6 @@ Per-agent cleanup (5-6 hours each due to higher complexity):

**Phase 2B Planning Complete. Ready for team review & Phase C kickoff.**

*Built by 🧱 LightSpeedWP with ☕ & open-source spirit.*
---

*Audit generated by the LightSpeedWP Automation Team*
Loading