Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
eed7b14
fix(skills): use latest check-run attempts
cv Jul 16, 2026
4f8e083
merge(main): refresh merge-gate checker
cv Jul 16, 2026
65cfba8
merge(main): refresh merge-gate checker
cv Jul 16, 2026
efa3d73
fix(skills): validate current check evidence
cv Jul 16, 2026
c4daf2d
fix(skills): harden workflow evidence checks
cv Jul 16, 2026
140031c
fix(skills): validate action evidence
cv Jul 16, 2026
802f650
fix(skills): bind required check evidence
cv Jul 16, 2026
3f9a867
merge(main): refresh merge-gate checker
cv Jul 16, 2026
f19952a
fix(skills): make merge-gate evidence fail closed
cjagwani Jul 16, 2026
e1b7a1c
merge: refresh from main
cjagwani Jul 16, 2026
f991988
fix(skills): reject stale pull request bases
cjagwani Jul 16, 2026
b2d0647
merge: refresh from main
cjagwani Jul 16, 2026
4aac159
test(skills): distinguish stale check runs
cjagwani Jul 16, 2026
f8439e0
Merge remote-tracking branch 'origin/main' into codex/fix-merge-gate-…
cv Jul 16, 2026
bd0ab2a
fix(ci): require successful merge gates
cjagwani Jul 16, 2026
07a1e32
merge: refresh from main
cjagwani Jul 16, 2026
fa82094
fix(ci): align dependabot DCO identities
cjagwani Jul 16, 2026
b4cf995
fix(skills): bind merge-gate evidence to immutable revisions
cv Jul 16, 2026
1354447
Merge remote-tracking branch 'origin/codex/fix-merge-gate-check-attem…
cv Jul 16, 2026
86d9fce
fix(skills): align comparator DCO bypass
cjagwani Jul 16, 2026
3ee05c8
merge: preserve concurrent gate hardening
cjagwani Jul 16, 2026
46263f1
merge: refresh from main
cjagwani Jul 16, 2026
43b07d4
Merge remote-tracking branch 'origin/main' into codex/fix-merge-gate-…
cv Jul 16, 2026
87812d0
Merge remote-tracking branch 'origin/codex/fix-merge-gate-check-attem…
cv Jul 16, 2026
26005ca
fix(ci): align DCO exemption contract
cv Jul 16, 2026
4fec1fc
fix(skills): order checks by workflow creation
cv Jul 16, 2026
17de16c
merge(main): refresh v0.0.85 gate base
cv Jul 16, 2026
9fee992
merge(main): refresh v0.0.85 gate base
cv Jul 17, 2026
0c2754d
merge(main): include v0.0.85 dcode validation
cv Jul 17, 2026
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
15 changes: 8 additions & 7 deletions .agents/skills/nemoclaw-maintainer-day/MERGE-GATE.md

Large diffs are not rendered by default.

1,195 changes: 1,173 additions & 22 deletions .agents/skills/nemoclaw-maintainer-day/scripts/check-gates.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .agents/skills/nemoclaw-maintainer-day/scripts/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ export interface StatusCheck {
__typename?: string;
name?: string; // CheckRun field
context?: string; // StatusContext field
workflowName?: string; // CheckRun workflow identity
startedAt?: string; // CheckRun/StatusContext RFC3339 timestamp
completedAt?: string; // CheckRun RFC3339 timestamp
detailsUrl?: string; // CheckRun workflow-run identity when Actions produced it
status?: string; // CheckRun: COMPLETED, IN_PROGRESS, QUEUED, etc.
conclusion?: string; // CheckRun: SUCCESS, FAILURE, NEUTRAL, SKIPPED, etc.
state?: string; // StatusContext: SUCCESS, FAILURE, PENDING, ERROR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ else
fi

raw=$(gh pr view "$pr" "${repo_args[@]}" \
--json number,state,body,headRefOid,statusCheckRollup,mergeable,mergeStateStatus,reviewDecision \
--json number,state,body,author,headRefOid,statusCheckRollup,mergeable,mergeStateStatus,reviewDecision \
2>/dev/null) || {
emit_error "fetch_failed"
exit 0
Expand Down Expand Up @@ -106,6 +106,12 @@ if printf '%s' "$raw" | jq -r '.body // ""' | grep -Eq '^Signed-off-by:[[:space:
else
dco_declaration_present=false
fi
author_login=$(printf '%s' "$raw" | jq -r '.author.login // ""' | tr '[:upper:]' '[:lower:]')
if [ "$author_login" = "app/dependabot" ] || [ "$author_login" = "dependabot[bot]" ]; then
dco_declaration_bypassed=true
else
dco_declaration_bypassed=false
fi

commits_fetch_failed=false
commit_parse_failed=false
Expand All @@ -122,7 +128,8 @@ elif commits_json=$(printf '%s\n' "$commits_raw" | jq -s '.' 2>/dev/null); then
unverified_commits=$(printf '%s' "$commits_json" | jq '[.[] | select(.verified != true) | {sha, reason}]')
unverified_count=$(printf '%s' "$unverified_commits" | jq 'length')
gate_contributor_compliance=$(
[ "$dco_declaration_present" = "true" ] && [ "$unverified_count" = "0" ] && echo true || echo false
{ [ "$dco_declaration_present" = "true" ] || [ "$dco_declaration_bypassed" = "true" ]; } \
&& [ "$unverified_count" = "0" ] && echo true || echo false
)
else
commit_count=0
Expand Down Expand Up @@ -166,6 +173,7 @@ jq -n \
--arg mergeable "$mergeable" \
--arg merge_state "$merge_state" \
--argjson dco_declaration_present "$dco_declaration_present" \
--argjson dco_declaration_bypassed "$dco_declaration_bypassed" \
--argjson commit_count "$commit_count" \
--argjson unverified_commits "$unverified_commits" \
--argjson commit_fetch_failed "$commits_fetch_failed" \
Expand All @@ -192,6 +200,7 @@ jq -n \
mergeable: $mergeable,
merge_state_status: $merge_state,
dco_declaration_present: $dco_declaration_present,
dco_declaration_bypassed: $dco_declaration_bypassed,
commit_count: $commit_count,
unverified_commits: $unverified_commits,
commit_fetch_failed: $commit_fetch_failed,
Expand Down
14 changes: 6 additions & 8 deletions .github/workflows/dco-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,24 @@ concurrency:

jobs:
dco-check:
if: github.event.pull_request.user.login != 'dependabot[bot]'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

- name: Check DCO bypass list
- name: Check Dependabot DCO bypass
id: dco-bypass
env:
USERNAME: ${{ github.event.pull_request.user.login }}
run: |
if grep -Fxq "$USERNAME" .github/dco-bypass.txt; then
# GitHub exposes the same trusted app as dependabot[bot] in event
# payloads and app/dependabot through some GraphQL PR surfaces.
if [[ "$USERNAME" == "dependabot[bot]" || "$USERNAME" == "app/dependabot" ]]; then
echo "bypass=true" >> "$GITHUB_OUTPUT"
echo "Author is in dco-bypass.txt; skipping DCO sign-off requirement."
echo "Dependabot is exempt from the PR-body DCO declaration."
else
echo "bypass=false" >> "$GITHUB_OUTPUT"
echo "Author is not in dco-bypass.txt; DCO sign-off is required."
echo "The PR-body DCO declaration is required."
fi

- name: Check PR body for Signed-off-by
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/installer-hash-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@

name: Security / Installer Hash Check

run-name: >-
${{ github.event_name == 'pull_request' &&
format('Installer Hash PR #{0} head {1} base {2} gate true',
github.event.pull_request.number, github.event.pull_request.head.sha,
github.event.pull_request.base.sha) ||
format('Installer Hash {0} {1}', github.event_name, github.sha) }}

on:
pull_request:
types: [opened, synchronize, reopened]
types: [opened, synchronize, reopened, edited]
push:
branches: [main]
schedule:
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/pr-e2e-gate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

name: E2E / PR Gate Controller

run-name: >-
${{ github.event_name == 'pull_request_target' &&
format('E2E Gate PR #{0} head {1} base {2} gate {3}',
github.event.pull_request.number, github.event.pull_request.head.sha,
github.event.pull_request.base.sha,
github.event.action != 'edited' || github.event.changes.base != null) ||
format('E2E Gate {0} {1}', github.event_name, github.run_id) }}

on:
workflow_run:
workflows: ["CI / Pull Request"]
Expand Down
10 changes: 10 additions & 0 deletions ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@
"test": "publishes coverage only from same-repository code (#6692)",
"category": "security"
},
{
"file": "test/pr-workflow-contract.test.ts",
"test": "records the Dependabot DCO bypass as a successful required job",
"category": "security"
},
{
"file": "test/pr-workflow-contract.test.ts",
"test": "reruns installer hash verification after a pull request base retarget",
"category": "security"
},
{
"file": "test/pr-workflow-contract.test.ts",
"test": "reuses the same shared CI actions in PR and main workflows",
Expand Down
6 changes: 6 additions & 0 deletions test/pr-e2e-gate-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type CoordinatorJob = WorkflowJob & {

type TriggeredWorkflow = Omit<Workflow, "jobs"> & {
name: string;
"run-name": string;
on: {
workflow_run: { workflows: string[]; types: string[] };
pull_request_target: { types: string[] };
Expand Down Expand Up @@ -304,6 +305,11 @@ describe("PR E2E gate workflow", () => {
const recordForkSkip = workflow.jobs["record-fork-e2e-skip"];

expect(workflow.name).toBe("E2E / PR Gate Controller");
expect(workflow["run-name"]).toContain("E2E Gate PR #{0} head {1} base {2} gate {3}");
expect(workflow["run-name"]).toContain("github.event.pull_request.number");
expect(workflow["run-name"]).toContain("github.event.pull_request.head.sha");
expect(workflow["run-name"]).toContain("github.event.pull_request.base.sha");
expect(workflow["run-name"]).toContain("github.event.changes.base != null");
expect(workflow.on).toEqual({
workflow_run: {
workflows: ["CI / Pull Request"],
Expand Down
42 changes: 42 additions & 0 deletions test/pr-workflow-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ function codeFilterMatchesChangedPaths(workflow: CiWorkflow, paths: string[]): b
describe("pull request and main workflow contracts", () => {
const prWorkflow = readYaml<CiWorkflow>(".github/workflows/pr.yaml");
const mainWorkflow = readYaml<CiWorkflow>(".github/workflows/main.yaml");
const dcoWorkflow = readYaml<CiWorkflow>(".github/workflows/dco-check.yaml");
const installerHashWorkflow = readYaml<CiWorkflow>(".github/workflows/installer-hash-check.yaml");
const installerHashAction = readYaml<InstallerHashAction>(
".github/actions/ci-installer-hash-check/action.yaml",
Expand All @@ -299,6 +300,35 @@ describe("pull request and main workflow contracts", () => {
".github/actions/ci-installer-integration/action.yaml",
),
};

// source-shape-contract: security -- Base retargets must rerun trusted installer verification without minting skipped required evidence
it("reruns installer hash verification after a pull request base retarget", () => {
expect(installerHashWorkflow.on?.pull_request?.types).toEqual([
"opened",
"synchronize",
"reopened",
"edited",
]);
expect(installerHashWorkflow.jobs["check-hash"].if).toBe(
"github.repository == 'NVIDIA/NemoClaw'",
);
});

// source-shape-contract: security -- Dependabot's bounded DCO exemption must report an explicit successful required check
it("records the Dependabot DCO bypass as a successful required job", () => {
const job = dcoWorkflow.jobs["dco-check"];
const bypass = requiredWorkflowStep(job, "Check Dependabot DCO bypass");
const declaration = requiredWorkflowStep(job, "Check PR body for Signed-off-by");

expect(job.if).toBeUndefined();
expect(job.steps?.some((step) => step.uses?.startsWith("actions/checkout@"))).toBe(false);
expect(bypass.env?.USERNAME).toBe("${{ github.event.pull_request.user.login }}");
expect(bypass.run).toContain('"$USERNAME" == "dependabot[bot]"');
expect(bypass.run).toContain('"$USERNAME" == "app/dependabot"');
expect(bypass.run).not.toContain(".github/dco-bypass.txt");
expect(declaration.if).toBe("${{ steps.dco-bypass.outputs.bypass != 'true' }}");
});

// source-shape-contract: security -- Installer hashes must be verified by base-trusted or immutable bootstrap code
it("runs pull request installer verification from immutable trusted code", () => {
const job = installerHashWorkflow.jobs["check-hash"];
Expand Down Expand Up @@ -338,6 +368,18 @@ describe("pull request and main workflow contracts", () => {
);

expect(installerHashWorkflow.on?.pull_request?.paths).toBeUndefined();
expect(installerHashWorkflow.on?.pull_request?.types).toEqual([
"opened",
"synchronize",
"reopened",
"edited",
]);
expect(installerHashWorkflow["run-name"]).toContain(
"Installer Hash PR #{0} head {1} base {2} gate true",
);
expect(installerHashWorkflow["run-name"]).toContain("github.event.pull_request.base.sha");
expect(installerHashWorkflow["run-name"]).not.toContain("github.event.changes.base");
expect(job.if).toBe("github.repository == 'NVIDIA/NemoClaw'");
expect(installerHashWorkflow.permissions).toEqual({ contents: "read" });
expect(parserRuntimeSetup.uses).toBe(trustedSetupNodeAction);
expect(parserRuntimeSetup.with?.["node-version"]).toBe("22.19.0");
Expand Down
Loading