LearnNewsExamplesServices
Frontmatter
id16289
titleCI scope classifier resolves package.json content from the base repo on fork PRs
stateClosed
labels
enhancementcontributor-experienceaitestingbuild
assigneesneo-kimi-iris
createdAtAug 1, 2026, 8:26 PM
updatedAtAug 1, 2026, 10:45 PM
githubUrlhttps://github.com/neomjs/neo/issues/16289
authorneo-kimi-iris
commentsCount0
parentIssue16248
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 1, 2026, 10:45 PM

CI scope classifier resolves package.json content from the base repo on fork PRs

Closed Backlog/active-chunk-11 enhancementcontributor-experienceaitestingbuild
neo-kimi-iris
neo-kimi-iris commented on Aug 1, 2026, 8:26 PM

Context

Surfaced by @neo-opus-grace's Approve+Follow-Up review on PR #16282 (2026-08-01T18:09:46Z): "the single durable finding (fork PRs can never take the optimised path) fails in the safe direction and is new scope, not a defect in this contract." Verified at source: the #16248 classifier helper resolves package.json content via github.rest.repos.getContent({owner: context.repo.owner, repo: context.repo.repo, ...}) — the base repository. A fork PR's head SHA does not exist in the base repo, so the fetch throws, the catch returns true (fail toward running), and the components/integration suites run on every fork PR forever.

The Problem

#16248 made package.json edits content-aware so metadata-only diffs skip irrelevant suites. On fork PRs the optimisation can never fire: the head-content lookup is pointed at the wrong repository by construction. The failure direction is safe (fail-open was designed for exactly this), but every external-contributor PR pays the full components + integration suites regardless of diff — the same waste class #16248 eliminated for same-repo PRs, plus a quiet contributor-experience tax (longer CI on the PRs least likely to touch components).

The Architectural Reality

  • The helper sits in the scope step of .github/workflows/test.yml (post-#16282 head).
  • context.payload.pull_request.head.repo.full_name identifies the fork (head.repo.fork === true); the base half must still read from context.repo (the PR's target), only the HEAD half needs the fork-aware repo.
  • Edge cases that must stay fail-open: deleted fork, private fork without token scope, API error on either side — the existing catch already covers them; the change is only WHERE the head fetch points.
  • The WorkflowScopeClassifier.spec.mjs harness already mocks repos.getContent per ref; fork variants need the mock keyed by repo as well.
  • Decision Record impact: none (CI suite selection has no ADR — same as #16248).

The Fix

  1. In packageJsonTouchesDependencies(): resolve the head-content repository from context.payload.pull_request.head.repo when head.repo.fork is true (and the event is pull_request); keep the base half on context.repo. Push events keep the existing same-repo path.
  2. Fail-open stance unchanged: any fetch/parse/ref failure returns true, exactly as today.
  3. Spec: a fork-head variant (mock head.repo.fork: true + head content served from the fork repo coordinates) resolving run_components=false on a metadata-only fork diff; a deleted-fork variant (head fetch throws) staying true; same-repo behavior pinned unchanged.

Acceptance Criteria

  • A fork PR whose only package.json change is metadata-kind resolves run_components=false when the fork head repo is readable; spec-pinned.
  • A fork PR whose head repo is deleted or unreadable resolves run_components=true (fail-open preserved); spec-pinned.
  • Same-repo PRs behave exactly as post-#16282 (existing specs unmodified).
  • The classifier log line names which repo the head content resolved from (fork-aware legibility).

Out of Scope

  • Changing the fail-open stance itself (the #15368 completeness property is untouched).
  • Any other whitelist predicate (src/, resources/scss/, lock atom) — unchanged.
  • Docs/content guards (requiresUnitForContent) — orthogonal.

Related

  • #16248 (parent contract — the two-kinds split this extends to fork heads)
  • #15368 (the completeness property the fail-open preserves)

Live latest-open sweep: checked latest 20 open issues (created-desc) at 2026-08-01T18:25Z; no equivalent found. A2A in-flight sweep (last ~60 min, all read-states): no claim on this scope (Ada is on the runbook repair, Grace reviewing, Emmy #16283/#16285, Vega #16278).

Origin Session ID: session_fdc69689-d147-442f-8e12-1a2bc72ae4ee

Retrieval Hint: query_raw_memories("CI scope classifier fork pull_request head repo package.json getContent fail-open")

Authored by Iris (@neo-kimi-iris, Kimi K3, Kimi Code CLI) 🌈

tobiu referenced in commit 8b5c751 - "feat(ci): resolve package.json head content from the fork repo on fork PRs (#16289) (#16296)" on Aug 1, 2026, 10:45 PM
tobiu closed this issue on Aug 1, 2026, 10:45 PM