Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
e62ba2d
feat(types): add VerifierRejections schema and SkillReport field
CalebKAston Jul 21, 2026
a26339b
feat(sdk): compute verifierRejections in verifyFindings
CalebKAston Jul 21, 2026
210a6e1
chore(specs): regenerate jsonl-schema.json for VerifierRejections field
CalebKAston Jul 21, 2026
3f43f3d
feat(sdk): thread verifierRejections through postProcessFindings
CalebKAston Jul 21, 2026
30bb1d4
feat(sdk): set verifierRejections on the skill report
CalebKAston Jul 21, 2026
eee0bb5
feat(reporting): export verifierRejections on findings-file skills
CalebKAston Jul 21, 2026
03b058a
feat(cli): show verifierRejections warning in terminal report
CalebKAston Jul 21, 2026
1e12fe5
feat(cli): show aggregate verifierRejections in Reporter summary
CalebKAston Jul 21, 2026
373f077
feat(cli): surface verifierRejections in JSONL run log
CalebKAston Jul 21, 2026
5fd3544
feat(cli): surface verifierRejections in the finalized chunk stream
CalebKAston Jul 21, 2026
b744cf6
feat(cli): set verifierRejections on the report in runSkillTasks
CalebKAston Jul 21, 2026
c3981f2
docs(types): document verifierRejections field
CalebKAston Jul 21, 2026
56cc003
fix(sdk): cap verifier rejection reason length
CalebKAston Jul 21, 2026
1518ecb
feat(reporting): add configuredSkills roster to findings output
CalebKAston Jul 20, 2026
37f5844
feat(workflow): accept configuredSkills in writeFindingsOutput options
CalebKAston Jul 20, 2026
14299e2
feat(workflow): compute and publish configuredSkills roster in pr-wor…
CalebKAston Jul 20, 2026
c001e72
docs(reporting): document buildConfiguredSkillsList dedup behavior
CalebKAston Jul 21, 2026
16c36bb
fix(workflow): require matchedTriggers/resolvedTriggers in finalizeRe…
CalebKAston Jul 21, 2026
682a6ea
feat: add output schema v2 with split metadata/findings files
CalebKAston Jul 22, 2026
b77495c
fix(utils): resolve getVersion() correctly under the ncc-bundled action
CalebKAston Jul 22, 2026
5cf9b8d
docs: add output schema v2 migration guide
CalebKAston Jul 22, 2026
611f854
fix: address Cursor Bugbot findings on schema v2
CalebKAston Jul 22, 2026
864e795
fix: address second Bugbot review round on schema v2
CalebKAston Jul 22, 2026
d0b9e80
fix: address third Bugbot review round on schema v2
CalebKAston Jul 22, 2026
831ab8f
fix: address fourth Bugbot round and Fight Me findings on schema v2
CalebKAston Jul 22, 2026
b78ce01
fix: address fifth Bugbot review round on schema v2
CalebKAston Jul 22, 2026
fb56118
fix: address sixth Bugbot review round on schema v2
CalebKAston Jul 22, 2026
558d358
fix: address seventh Bugbot review round on schema v2
CalebKAston Jul 22, 2026
e62cc65
fix: address eighth Bugbot review round on schema v2
CalebKAston Jul 22, 2026
efed685
fix: address ninth Bugbot review round on schema v2
CalebKAston Jul 22, 2026
fc8d791
fix: address tenth Bugbot review round on schema v2
CalebKAston Jul 22, 2026
858d6b6
fix: address eleventh Bugbot review round on schema v2
CalebKAston Jul 22, 2026
6661beb
fix: address twelfth Bugbot review round on schema v2
CalebKAston Jul 23, 2026
7e727ac
fix: address thirteenth Bugbot review round on schema v2
CalebKAston Jul 23, 2026
a4bfccf
refactor: give findings a stable id, separate from continuity display id
CalebKAston Jul 23, 2026
57942cf
fix: close remaining structural gaps behind repeated Bugbot rounds
CalebKAston Jul 23, 2026
ce46ce7
fix: close remaining Bugbot findings from schema v2 round 15
CalebKAston Jul 23, 2026
17e91c4
fix: bring renderer attribution footer in sync with main's format
CalebKAston Jul 23, 2026
2bad9ec
fix: close sixteenth Bugbot round by giving corroboration a real key
CalebKAston Jul 23, 2026
cccf50a
fix: seventeenth Bugbot round - name-ambiguity guard was skill-scoped…
CalebKAston Jul 23, 2026
ff65538
refactor: split corroboration into two candidate maps instead of one …
CalebKAston Jul 23, 2026
9bf42ef
fix: eighteenth Bugbot round - root-cause both pending v2 output gaps
CalebKAston Jul 23, 2026
c7645d5
fix: independent re-validation round - restore v1 attribution parity,…
CalebKAston Jul 23, 2026
003752f
docs: document two residual corroboration edge cases found by adversa…
CalebKAston Jul 23, 2026
c47dbc5
build: commit patched dist/action for babylist fork validation
CalebKAston Jul 23, 2026
6177694
Revert "build: commit patched dist/action for babylist fork validation"
CalebKAston Jul 23, 2026
b1040de
fix: satisfy dcramer's two hard requirements for schema v2 merge
CalebKAston Jul 24, 2026
472e79e
fix: fight-me round on the schema-v2 CLI-replay/GitHub-consumable commit
CalebKAston Jul 24, 2026
f5a5e10
feat: add live logging support to schema-v2 output
CalebKAston Jul 24, 2026
65183a1
fix: address all findings from two fight-me reviews (focused + full d…
CalebKAston Jul 24, 2026
252d5e6
fix: roll observed-model fix (#439) into schema-v2 output
CalebKAston Jul 24, 2026
e2fd5dc
fix: rebase onto upstream main, match attribution footer to #443's fo…
CalebKAston Jul 24, 2026
6df8fc1
fix: address fight-me findings on schema v2 (phantom revision, doc ty…
CalebKAston Jul 24, 2026
01fdbd9
fix: paginate posted-review comment lookup past 100 inline comments
CalebKAston Jul 24, 2026
b0e8900
fix: stage schema-v2 metadata/findings writes before committing either
CalebKAston Jul 24, 2026
58aa7a9
fix: match posted review comments by path+line, not body text alone
CalebKAston Jul 24, 2026
2e89f0e
fix: close nine review findings on schema v2 (write atomicity, v1 dri…
CalebKAston Jul 24, 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
23 changes: 21 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ inputs:
required: false
default: 'run'
findings-file:
description: 'Path to structured JSON findings file to read in report mode.'
description: 'Path to structured JSON findings file to read in report mode. When output-schema-version is "2", this is the schema-v2 findings file.'
required: false
metadata-file:
description: 'Path to schema-v2 metadata JSON file to read in report mode, required alongside findings-file when output-schema-version is "2".'
required: false
base-config-path:
description: 'Path to the org-wide base warden.toml file to load before the repo config (relative to repo root)'
Expand Down Expand Up @@ -55,6 +58,13 @@ inputs:
description: 'Maximum number of concurrent trigger executions'
required: false
default: '5'
output-schema-version:
description: 'Output schema for findings-file. "2" additionally writes warden-metadata.json and warden-findings-v2.json.'
required: false
default: '1'
action-ref:
description: 'Pinned SHA/ref of this action as referenced by the calling workflow, recorded in schema v2 metadata as harness.actionRef'
required: false

outputs:
findings-count:
Expand All @@ -67,8 +77,14 @@ outputs:
description: 'Summary of the analysis'
value: ${{ steps.warden.outputs.summary }}
findings-file:
description: 'Path to structured JSON findings file (always written, use for upload to GCS/S3/etc.)'
description: 'Path to structured JSON findings file (always written, use for upload to GCS/S3/etc.). When output-schema-version is "2", this is the schema-v2 findings file, matching the findings-file input.'
value: ${{ steps.warden.outputs.findings-file }}
metadata-file:
description: 'Path to schema-v2 metadata JSON file (only written when output-schema-version is "2")'
value: ${{ steps.warden.outputs.metadata-file }}
findings-file-v2:
description: 'Path to schema-v2 findings JSON file (only written when output-schema-version is "2")'
value: ${{ steps.warden.outputs.findings-file-v2 }}
Comment thread
cursor[bot] marked this conversation as resolved.

runs:
using: 'composite'
Expand All @@ -81,6 +97,7 @@ runs:
INPUT_GITHUB_TOKEN: ${{ inputs.github-token }}
INPUT_MODE: ${{ inputs.mode }}
INPUT_FINDINGS_FILE: ${{ inputs.findings-file }}
INPUT_METADATA_FILE: ${{ inputs.metadata-file }}
INPUT_BASE_CONFIG_PATH: ${{ inputs.base-config-path }}
INPUT_BASE_SKILL_ROOT: ${{ inputs.base-skill-root }}
INPUT_CONFIG_PATH: ${{ inputs.config-path }}
Expand All @@ -90,4 +107,6 @@ runs:
INPUT_REQUEST_CHANGES: ${{ inputs.request-changes }}
INPUT_FAIL_CHECK: ${{ inputs.fail-check }}
INPUT_PARALLEL: ${{ inputs.parallel }}
INPUT_OUTPUT_SCHEMA_VERSION: ${{ inputs.output-schema-version }}
INPUT_ACTION_REF: ${{ inputs.action-ref }}
run: node ${{ github.action_path }}/dist/action/index.js
56 changes: 56 additions & 0 deletions packages/warden/src/action/inputs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,28 @@ describe('parseActionInputs', () => {
expect(() => parseActionInputs()).toThrow('Invalid mode "later"');
});
});

describe('output-schema-version handling', () => {
it('defaults to schema version 1', () => {
const inputs = parseActionInputs();
expect(inputs.outputSchemaVersion).toBe('1');
});

it('parses output-schema-version 2, metadata-file, and action-ref', () => {
process.env['INPUT_OUTPUT_SCHEMA_VERSION'] = '2';
process.env['INPUT_METADATA_FILE'] = 'warden-metadata.json';
process.env['INPUT_ACTION_REF'] = 'abc123';
const inputs = parseActionInputs();
expect(inputs.outputSchemaVersion).toBe('2');
expect(inputs.metadataFile).toBe('warden-metadata.json');
expect(inputs.actionRef).toBe('abc123');
});

it('rejects an invalid output-schema-version', () => {
process.env['INPUT_OUTPUT_SCHEMA_VERSION'] = '3';
expect(() => parseActionInputs()).toThrow('Invalid output-schema-version "3"');
});
});
});

describe('setupAuthEnv', () => {
Expand All @@ -173,6 +195,7 @@ describe('setupAuthEnv', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
});
expect(process.env['ANTHROPIC_API_KEY']).toBe('sk-ant-api-key');
expect(process.env['WARDEN_ANTHROPIC_API_KEY']).toBe('sk-ant-api-key');
Expand All @@ -188,6 +211,7 @@ describe('setupAuthEnv', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
});
expect(process.env['CLAUDE_CODE_OAUTH_TOKEN']).toBe('sk-ant-oat-oauth-token');
expect(process.env['ANTHROPIC_API_KEY']).toBeUndefined();
Expand All @@ -206,6 +230,7 @@ describe('setupAuthEnv', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
});

expect(process.env['CLAUDE_CODE_OAUTH_TOKEN']).toBe('sk-ant-oat-oauth-token');
Expand All @@ -226,6 +251,7 @@ describe('setupAuthEnv', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
});

expect(process.env['CLAUDE_CODE_OAUTH_TOKEN']).toBeUndefined();
Expand All @@ -245,6 +271,7 @@ describe('validateInputs', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
})).toThrow('base-skill-root requires base-config-path');
});

Expand All @@ -257,6 +284,35 @@ describe('validateInputs', () => {
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
})).toThrow('findings-file is required when mode is report');
});

it('requires metadata-file in report mode when output-schema-version is 2', () => {
expect(() => validateInputs({
anthropicApiKey: 'sk-ant-api-key',
oauthToken: '',
githubToken: 'test',
mode: 'report',
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '2',
findingsFile: 'warden-findings-v2.json',
})).toThrow("metadata-file is required when mode is report and output-schema-version is '2'");
});

it('does not require metadata-file in report mode when output-schema-version is 1', () => {
expect(() => validateInputs({
anthropicApiKey: 'sk-ant-api-key',
oauthToken: '',
githubToken: 'test',
mode: 'report',
configPath: 'warden.toml',
maxFindings: 50,
parallel: 4,
outputSchemaVersion: '1',
findingsFile: 'warden-findings.json',
})).not.toThrow();
});
});
22 changes: 22 additions & 0 deletions packages/warden/src/action/inputs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export interface ActionInputs {
mode: ActionMode;
/** Structured findings file used by report mode */
findingsFile?: string;
/** Schema-v2 metadata file used by report mode when outputSchemaVersion is '2' */
metadataFile?: string;
/** Optional org-wide base config that is loaded before the repo config */
baseConfigPath?: string;
/** Optional repo root containing org-shared local skills for the base config */
Expand All @@ -39,6 +41,10 @@ export interface ActionInputs {
failCheck?: boolean;
/** Max concurrent trigger executions */
parallel: number;
/** Output schema for the written artifacts. '2' additionally writes warden-metadata.json and warden-findings-v2.json. */
outputSchemaVersion: '1' | '2';
/** Pinned SHA/ref of the calling workflow's `uses:` line, for harness.actionRef in schema v2. */
actionRef?: string;
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -77,6 +83,14 @@ function parseModeInput(value: string): ActionMode {
throw new Error(`Invalid mode "${mode}". Expected run, analyze, or report.`);
}

function parseOutputSchemaVersionInput(value: string): '1' | '2' {
const version = value || '1';
if (version === '1' || version === '2') {
return version;
}
throw new Error(`Invalid output-schema-version "${version}". Expected 1 or 2.`);
}

/**
* Parse action inputs from the GitHub Actions environment.
* Runtime-specific auth can be absent here; runtime setup validates it when needed.
Expand Down Expand Up @@ -112,12 +126,15 @@ export function parseActionInputs(): ActionInputs {
const requestChanges = parseBooleanInput(getInput('request-changes'));
const failCheck = parseBooleanInput(getInput('fail-check'));

const outputSchemaVersion = parseOutputSchemaVersionInput(getInput('output-schema-version'));

return {
anthropicApiKey,
oauthToken,
githubToken: getInput('github-token') || process.env['GITHUB_TOKEN'] || '',
mode: parseModeInput(getInput('mode')),
findingsFile: getInput('findings-file') || undefined,
metadataFile: getInput('metadata-file') || undefined,
baseConfigPath: getInput('base-config-path') || undefined,
baseSkillRoot: getInput('base-skill-root') || undefined,
configPath: getInput('config-path') || 'warden.toml',
Expand All @@ -127,6 +144,8 @@ export function parseActionInputs(): ActionInputs {
requestChanges,
failCheck,
parallel: Number.isNaN(parallelParsed) ? DEFAULT_CONCURRENCY : parallelParsed,
outputSchemaVersion,
actionRef: getInput('action-ref') || undefined,
Comment thread
cursor[bot] marked this conversation as resolved.
};
}

Expand All @@ -144,6 +163,9 @@ export function validateInputs(inputs: ActionInputs): void {
if (inputs.mode === 'report' && !inputs.findingsFile) {
throw new Error('findings-file is required when mode is report');
}
if (inputs.mode === 'report' && inputs.outputSchemaVersion === '2' && !inputs.metadataFile) {
throw new Error('metadata-file is required when mode is report and output-schema-version is \'2\'');
}
}

/**
Expand Down
16 changes: 14 additions & 2 deletions packages/warden/src/action/reporting/outcomes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ export type FindingOutcome =

export type DedupeSource = 'warden' | 'external';
export type DedupeMatchType = 'hash' | 'semantic';
export type SkippedReason = 'max_findings' | 'duplicate_in_batch' | 'no_inline_location';
export type SkippedReason = 'max_findings' | 'duplicate_in_batch' | 'no_inline_location' | 'review_not_posted';
export type ResolvedReason = 'fix_evaluation' | 'stale_check';

export const DedupeDetailSchema = z.object({
source: z.enum(['warden', 'external']),
matchType: z.enum(['hash', 'semantic']),
existingFindingId: z.string().optional(),
existingSkillExecutionId: z.string().optional(),
existingCommentId: z.number().int().positive().optional(),
existingThreadId: z.string().optional(),
existingResolved: z.boolean().optional(),
existingSkills: z.array(z.string()).optional(),
actor: z.string().optional(),
});

Expand All @@ -29,10 +31,13 @@ export type DedupeDetail = z.infer<typeof DedupeDetailSchema>;
interface BaseFindingObservation {
finding: Finding;
skill?: string;
skillExecutionId?: string;
}

export interface PostedFindingObservation extends BaseFindingObservation {
outcome: 'posted';
githubCommentId?: number;
githubCommentUrl?: string;
}

export interface DedupedFindingObservation extends BaseFindingObservation {
Expand Down Expand Up @@ -66,29 +71,36 @@ export const FindingObservationSchema = z.discriminatedUnion('outcome', [
outcome: z.literal('posted'),
finding: FindingSchema,
skill: z.string().optional(),
skillExecutionId: z.string().optional(),
githubCommentId: z.number().int().positive().optional(),
githubCommentUrl: z.string().optional(),
}),
z.object({
outcome: z.literal('deduped'),
finding: FindingSchema,
skill: z.string().optional(),
skillExecutionId: z.string().optional(),
dedupe: DedupeDetailSchema,
}),
z.object({
outcome: z.literal('skipped'),
finding: FindingSchema,
skill: z.string().optional(),
skippedReason: z.enum(['max_findings', 'duplicate_in_batch', 'no_inline_location']),
skillExecutionId: z.string().optional(),
skippedReason: z.enum(['max_findings', 'duplicate_in_batch', 'no_inline_location', 'review_not_posted']),
}),
z.object({
outcome: z.literal('resolved'),
finding: FindingSchema,
skill: z.string().optional(),
skillExecutionId: z.string().optional(),
resolvedReason: z.enum(['fix_evaluation', 'stale_check']),
}),
z.object({
outcome: z.literal('failed'),
finding: FindingSchema,
skill: z.string().optional(),
skillExecutionId: z.string().optional(),
}),
]);

Expand Down
Loading
Loading