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
59 changes: 56 additions & 3 deletions build/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80453,9 +80453,27 @@ async function findProjectItem(client, project, itemId, fieldName) {
if (item)
return item;
}
const message = `Project item ${itemId} was not found while updating field '${fieldName}'.`;
(0, logger_1.logError)(message);
throw new Error(message);
const result = await client.graphql(`query($itemId: ID!, $fieldName: String!) {
node(id: $itemId) {
id __typename
... on ProjectV2Item {
project { id }
fieldValueByName(name: $fieldName) {
... on ProjectV2ItemFieldSingleSelectValue { optionId }
}
}
}
}`, { itemId, fieldName });
const item = result.node;
if (item?.id !== itemId || item.__typename !== 'ProjectV2Item' || item.project?.id !== project.id
|| !Object.prototype.hasOwnProperty.call(item, 'fieldValueByName')) {
throw new Error(`Project item ${itemId} has no verified field evidence in project ${project.id}.`);
}
const value = item.fieldValueByName;
if (value !== null && (typeof value?.optionId !== 'string' || !value.optionId.trim())) {
throw new Error('The direct Project item single-select value is malformed.');
}
return { id: itemId, fieldValues: { nodes: value === null ? [] : [{ field: { name: fieldName }, optionId: value.optionId }] } };
}


Expand Down Expand Up @@ -80535,6 +80553,41 @@ async function findProjectItemId(client, project, contentId) {
if (item)
return item.id;
}
return findContentSideProjectItemId(client, project, contentId);
}
/** Recover a missing board-list entry from the exact content's own memberships. */
async function findContentSideProjectItemId(client, project, contentId) {
for await (const page of (0, github_pagination_adapter_1.paginateCursor)(async (after) => {
const result = await client.graphql(`query($contentId: ID!, $after: String) {
node(id: $contentId) {
id __typename
... on Issue { projectItems(first: 100, after: $after, includeArchived: true) {
pageInfo { hasNextPage endCursor }
nodes { id project { id } content { ... on Issue { id } ... on PullRequest { id } } }
} }
... on PullRequest { projectItems(first: 100, after: $after, includeArchived: true) {
pageInfo { hasNextPage endCursor }
nodes { id project { id } content { ... on Issue { id } ... on PullRequest { id } } }
} }
}
}`, { contentId, after });
const node = result.node;
const membership = node?.projectItems;
if (node?.id !== contentId || !['Issue', 'PullRequest'].includes(node.__typename ?? '')
|| !membership || !Array.isArray(membership.nodes)
|| typeof membership.pageInfo?.hasNextPage !== 'boolean'
|| !(membership.pageInfo.endCursor === null || typeof membership.pageInfo.endCursor === 'string')) {
throw new Error('The content-side Project membership evidence is unavailable or malformed.');
}
return membership;
}, { description: 'content-side project membership', maxPages: 2 })) {
const item = page.nodes.find(candidate => candidate.project?.id === project.id && candidate.content?.id === contentId);
if (item) {
if (typeof item.id !== 'string' || !item.id.trim())
throw new Error('The content-side Project item identity is malformed.');
return item.id;
}
}
return undefined;
}

Expand Down
59 changes: 56 additions & 3 deletions build/github_action/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74080,9 +74080,27 @@ async function findProjectItem(client, project, itemId, fieldName) {
if (item)
return item;
}
const message = `Project item ${itemId} was not found while updating field '${fieldName}'.`;
(0, logger_1.logError)(message);
throw new Error(message);
const result = await client.graphql(`query($itemId: ID!, $fieldName: String!) {
node(id: $itemId) {
id __typename
... on ProjectV2Item {
project { id }
fieldValueByName(name: $fieldName) {
... on ProjectV2ItemFieldSingleSelectValue { optionId }
}
}
}
}`, { itemId, fieldName });
const item = result.node;
if (item?.id !== itemId || item.__typename !== 'ProjectV2Item' || item.project?.id !== project.id
|| !Object.prototype.hasOwnProperty.call(item, 'fieldValueByName')) {
throw new Error(`Project item ${itemId} has no verified field evidence in project ${project.id}.`);
}
const value = item.fieldValueByName;
if (value !== null && (typeof value?.optionId !== 'string' || !value.optionId.trim())) {
throw new Error('The direct Project item single-select value is malformed.');
}
return { id: itemId, fieldValues: { nodes: value === null ? [] : [{ field: { name: fieldName }, optionId: value.optionId }] } };
}


Expand Down Expand Up @@ -74162,6 +74180,41 @@ async function findProjectItemId(client, project, contentId) {
if (item)
return item.id;
}
return findContentSideProjectItemId(client, project, contentId);
}
/** Recover a missing board-list entry from the exact content's own memberships. */
async function findContentSideProjectItemId(client, project, contentId) {
for await (const page of (0, github_pagination_adapter_1.paginateCursor)(async (after) => {
const result = await client.graphql(`query($contentId: ID!, $after: String) {
node(id: $contentId) {
id __typename
... on Issue { projectItems(first: 100, after: $after, includeArchived: true) {
pageInfo { hasNextPage endCursor }
nodes { id project { id } content { ... on Issue { id } ... on PullRequest { id } } }
} }
... on PullRequest { projectItems(first: 100, after: $after, includeArchived: true) {
pageInfo { hasNextPage endCursor }
nodes { id project { id } content { ... on Issue { id } ... on PullRequest { id } } }
} }
}
}`, { contentId, after });
const node = result.node;
const membership = node?.projectItems;
if (node?.id !== contentId || !['Issue', 'PullRequest'].includes(node.__typename ?? '')
|| !membership || !Array.isArray(membership.nodes)
|| typeof membership.pageInfo?.hasNextPage !== 'boolean'
|| !(membership.pageInfo.endCursor === null || typeof membership.pageInfo.endCursor === 'string')) {
throw new Error('The content-side Project membership evidence is unavailable or malformed.');
}
return membership;
}, { description: 'content-side project membership', maxPages: 2 })) {
const item = page.nodes.find(candidate => candidate.project?.id === project.id && candidate.content?.id === contentId);
if (item) {
if (typeof item.id !== 'string' || !item.id.trim())
throw new Error('The content-side Project item identity is malformed.');
return item.id;
}
}
return undefined;
}

Expand Down
9 changes: 9 additions & 0 deletions docs/issues/assignees-and-projects.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ the normal lifecycle transition selects the configured in-progress Status.
If initialization fails, retrying reads the same item and can fill its unset
Status without adding another item.

If a subsequent priority or size update cannot find a newly linked item in the
board listing, Copilot checks the exact issue or PR's own Project memberships
before reporting a failure. This recovery reads at most 200 memberships. If a
known item's field is absent from the listing, one direct item read verifies its
Project and current field value. Matching values stay unchanged on replay.
Permission errors, incomplete evidence and persistent absence remain visible
failures; rerun after checking access and membership. Successful linking and
other completed updates are retained.

### How many boards?

You can link each issue to **multiple** boards by listing several IDs:
Expand Down
11 changes: 11 additions & 0 deletions docs/security-operations/operations/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,17 @@ directory. The lock contains no PAT or other credential.
- Failed workflow runs

**Solutions:**
The hosted Windows verification-environment fixture reports a fixed
`verification-powershell` stage with the child status and error code when
its real PowerShell command fails. Use that evidence to distinguish command
failure from a timeout; `exit 2` alone does not identify a cause. This
literal-argv fixture also retries only the removal of its own temporary
root up to five times when the filesystem reports a retryable error; an
exhausted cleanup still fails the test. The PowerShell
fixture imports the trusted system hash/path modules explicitly and keeps
the existing execution deadlines. Its success certifies the fixture only,
and does not certify real Codex tools in Windows services.

1. **Timeouts:** Identify the phase before changing a job timeout. A pending
promotion, environment approval, merge queue, or reconciliation PR is an
external GitHub wait and has no release runner to extend. npm visibility
Expand Down
Loading
Loading