Skip to content

[trajectory-grader] Implement exploitation-error - #57152

Merged
pelikhan merged 3 commits into
mainfrom
copilot/trajectory-grader-implement-exploitation-error
Aug 30, 2026
Merged

[trajectory-grader] Implement exploitation-error#57152
pelikhan merged 3 commits into
mainfrom
copilot/trajectory-grader-implement-exploitation-error

Conversation

Copilot AI commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Implements exploitation-error (Tier 2, rank 4) from the catalog in .github/workflows/shared/graders/README.md. Where exploration-error attributes objective failure to insufficient search, exploitation-error isolates the opposite case: the run gathered enough evidence but still missed objectives, scoring how much of that evidence no later action ever consumed.

Changes

  • .github/workflows/shared/graders/exploitation-error.md (new) — importable graders: fragment, unit: ratio, direction: lower_is_better, range [0, 1]. Resolves the Trajectory IR with the same candidate-selection logic as exploration-error (preferring the objective-bearing candidate over unrelated agentOutput).
  • .github/workflows/shared/graders/README.md — rank 4 Status flipped to Implemented (9 of 25).
  • .github/workflows/shared/graders/exploration-error.md — drops the now-stale "exploitation-error (not yet implemented)" cross-references in the script comment and trailing doc block.
  • actions/setup/js/trace_graders.test.cjs — 8 cases covering every branch, mirroring the existing exploration-error suite.

Scoring

Condition Result
All objectives satisfied 0
No objectives / no states+events / no observations not-applicable
observations < distinctStatesVisited not-applicable — defers to exploration-error
Otherwise unusedObservations / observations, clamped

An observation counts as used iff consumedByActionIds is a non-empty array. distinctStatesVisited is the distinct-ref count over state_change events, falling back to declared states[].

The deferral guard is what makes the two graders mutually exclusive on the same trace:

if (observations.length < distinctStatesVisited) {
  return {
    value: null, unit: "ratio", passed: null,
    message: `not applicable: exploration was insufficient (observations=${observations.length} < distinctStatesVisited=${distinctStatesVisited}); see exploration-error`,
  };
}

No workflow imports the fragment yet, so no .lock.yml regeneration is involved.

Copilot AI and others added 2 commits August 30, 2026 12:37
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement exploitation-error grader [trajectory-grader] Implement exploitation-error Aug 30, 2026
Copilot AI requested a review from pelikhan August 30, 2026 12:41
@pelikhan
pelikhan marked this pull request as ready for review August 30, 2026 12:50
Copilot AI balanced review requested due to automatic review settings August 30, 2026 12:50
@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ Security scanning failed for Ponytail Reviewer. Review the logs for details.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by Ponytail Reviewer for #57152

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

Copilot AI left a comment

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.

🔵 Needs a closer look

The protected grader and catalog changes explicitly require final human scrutiny before merge.

Pull request overview

Adds the exploitation-error trajectory grader for failures caused by unused evidence.

Changes:

  • Implements and documents exploitation-error scoring.
  • Adds eight JavaScript test cases.
  • Updates catalog status and exploration-error references.
File summaries
File Description
.github/workflows/shared/graders/exploitation-error.md Defines the new grader.
.github/workflows/shared/graders/exploration-error.md Updates complementary-grader documentation.
.github/workflows/shared/graders/README.md Marks the grader implemented.
actions/setup/js/trace_graders.test.cjs Adds behavioral coverage.
pkg/workflow/schemas/github-workflow.json Reorders an existing permission definition.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-30T12:53:39Z
review_event: COMMENT
top_themes:
  - no actionable blocking issues found
files_reviewed:
  - .github/workflows/shared/graders/README.md
  - .github/workflows/shared/graders/exploitation-error.md
  - .github/workflows/shared/graders/exploration-error.md
  - actions/setup/js/trace_graders.test.cjs
  - pkg/workflow/schemas/github-workflow.json
comment_count: 0

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 27.8 AIC · ⌖ 7.33 AIC · ⊞ 21.8K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

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.

Verdict

Non-blocking review: I didn't find a changed-line issue here that clearly breaks grader behavior or leaves the new metric under-tested.

Themes checked
  • The new grader mirrors the existing trajectory-IR candidate selection used by exploration-error, so it does not introduce a divergent trace-resolution path.
  • The mutual-exclusion guard with exploration-error is explicit and covered by tests.
  • The added tests hit the null-applicability branches, the satisfied-objective fast path, the state fallback, and the candidate-preference behavior.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 27.8 AIC · ⌖ 7.33 AIC · ⊞ 21.8K
Comment /review to run again

@github-actions github-actions Bot left a comment

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.

Review: exploitation-error grader

Overall: The implementation is well-structured and the test suite covers all declared branches (not-applicable conditions, all-satisfied, deferred-to-exploration, unused-fraction, fallback to states[]).

One non-blocking correctness issue found:

In exploitation-error.md line 90, unmetDescriptions can emit the string literal "undefined" when an objective has neither a valid string id nor a description property, because objective.description evaluates to undefined and .join() coerces it. A simple three-way fallback to "(unnamed)" would harden this.

Everything else looks correct: the satisfiedAtEventIndex check correctly treats index 0 as satisfied; the Set deduplication for distinctStatesVisited is right; the mutual-exclusivity invariant with exploration-error is properly maintained; and the schema reorder of vulnerability-alerts is cosmetic/ordering only.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet46 · 27 AIC · ⌖ 13.8 AIC · ⊞ 6.2K

return {
value,
unit: "ratio",
details: `objectives=${objectives.length} unmet=${unmet.length} observations=${observations.length} unused=${unused.length} distinctStatesVisited=${distinctStatesVisited} (from ${source})${unmetDescriptions.length === 0 ? "" : `; unmet objectives: ${unmetDescriptions.join(", ")}`}`,

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.

unmetDescriptions can produce "undefined" in the details string

If an objective object has neither a truthy string id nor any description property, the expression objective.description is undefined. That value is coerced to the string "undefined" via Array.prototype.join, producing misleading details like "unmet objectives: undefined, ...".

Consider a string-safe fallback:

const unmetDescriptions = unmet.slice(0, 5).map(objective => {
  if (typeof objective.id === 'string' && objective.id !== '') return objective.id;
  if (typeof objective.description === 'string' && objective.description !== '') return objective.description;
  return '(unnamed)';
});

@copilot please address this.

@github-actions github-actions Bot left a comment

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.

A couple of spots are carrying extra abstraction and test volume without buying much. net: -24 lines possible.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • ab.chatgpt.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"

See Network Configuration for more information.

Generated by ✂️ Ponytail Reviewer for #57152 · codex · mai10 · 5.04 AIC · ⌖ 0.579 AIC · ⊞ 13.5K
Comment /ponytail to run again

min: 0.0
max: 1.0
script: |
const isRecord = value => value !== null && typeof value === "object" && !Array.isArray(value);

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.

L20-39: yagni: multi-branch trace-shape fallback for trajectoryIR, trajectoryIr, ir, and agentOutput variants. Use one canonical trajectoryIR payload and let the producer emit it.

@@ -757,6 +775,121 @@ describe("trace_graders", () => {
});

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.

L775-890: shrink: eight near-duplicate cases for one grader. Two smoke tests plus a table-driven case would cover the same branches with less noise.

@github-actions github-actions Bot left a comment

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.

Skills-Based Review 🧠

Applied /tdd — commenting on test coverage gaps; no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Boundary condition untested: the observations.length === distinctStatesVisited equality case is the exact threshold between "defer" and "score", but no test pins it.
  • Unsafe undefined interpolation: objective.description can be undefined, producing literal "undefined" in the details output string.
  • Test readability: the duplicate "repo-root" ref in the scoring test silently relies on Set deduplication; without a comment it reads as a copy-paste bug.

Positive Highlights

  • ✅ Full branch coverage across 8 test cases, mirroring the exploration-error suite structure
  • ✅ Deferral guard makes the two graders mutually exclusive — clean complementary design
  • ✅ Candidate-selection priority ordering is correct (objective-bearing IR over bare agentOutput)
  • ✅ Frontmatter comment block is thorough and matches the implementation

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet46 · 48.2 AIC · ⌖ 15.3 AIC · ⊞ 7.6K
Comment /matt to run again

objectives: [{ id: "goal", description: "Inspect repo", satisfiedAtEventIndex: null }],
events: [
{ kind: "state_change", ref: "repo-root" },
{ kind: "state_change", ref: "repo-readme" },

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.

[/tdd] Missing boundary test: observations.length === distinctStatesVisited is the exact threshold between deferral and scoring, but no test exercises equality — only < (defers) and > (scores) are covered.

💡 Suggested test
it("scores when observations exactly equal distinctStatesVisited (boundary)", () => {
  const result = runExploitationError({
    trajectoryIR: {
      objectives: [{ id: "goal", description: "Inspect repo", satisfiedAtEventIndex: null }],
      events: [{ kind: "state_change", ref: "s1" }],
      observations: [{ id: "obs-1", consumedByActionIds: [] }],
    },
  });

  // observations (1) === distinctStatesVisited (1): should score, not defer
  expect(result.value).toBe(1);
  expect(result.details).toContain("observations=1 unused=1");
});

The < guard means equality falls through to scoring — this test pins that semantics and prevents a future <= regression.

@copilot please address this.

const value = helpers.clamp(unused.length / observations.length, 0, 1);
const unmetDescriptions = unmet.slice(0, 5).map(objective => (typeof objective.id === "string" && objective.id !== "" ? objective.id : objective.description));

return {

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.

[/tdd] objective.description may be undefined, causing "undefined" to appear literally in the details string when an objective has neither id nor description.

💡 Suggested fix
const unmetDescriptions = unmet.slice(0, 5).map(objective =>
  (typeof objective.id === "string" && objective.id !== "" ? objective.id :
   typeof objective.description === "string" && objective.description !== "" ? objective.description :
   "(unnamed)")
);

No test currently exercises this path. A companion test with { satisfiedAtEventIndex: null } (no id, no description) would lock in the safe fallback.

@copilot please address this.

});

it("scores the unused fraction of observations when exploration was sufficient", () => {
const result = runExploitationError({

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.

[/tdd] The duplicate "repo-root" ref is intentional (to force distinctStatesVisited=1 via Set deduplication), but looks like a copy-paste error without a comment.

💡 Suggestion

Add an inline comment explaining the intent:

// Two events with the same ref → Set deduplication → distinctStatesVisited=1
// so observations(2) > distinctStatesVisited(1): exploitation-error applies.
events: [
  { kind: "state_change", ref: "repo-root" },
  { kind: "state_change", ref: "repo-root" },
],

@copilot please address this.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot this PR has completed review activity and would benefit from a final pass. Please address any remaining reviewer feedback, refresh the branch if needed, and run the pr-finisher skill before handing back for maintainer review.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • github.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "github.com"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · pi · gpt54 · 13.2 AIC · ⌖ 8.63 AIC · ⊞ 9.2K ·
Comment /souschef to run again

@pelikhan
pelikhan merged commit 0ce1b53 into main Aug 30, 2026
115 of 127 checks passed
@pelikhan
pelikhan deleted the copilot/trajectory-grader-implement-exploitation-error branch August 30, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[trajectory-grader] Implement exploitation-error

4 participants