mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 22:45:02 +01:00
v1.91.93.0 fix(cso-eval): release report gates require finished reports; completeness stays measured (#3149)
/cso release qualification gates on finished reports. Report completeness is still measured, but it no longer blocks a release.
This commit is contained in:
1 parent
3b7254a54d
commit
e23f7b807f
6 files changed
+22
-8
No files matched your search
@@ -1,5 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [1.91.93.0] - 2026-10-12
|
||||||
|
|
||||||
|
**/cso release qualification gates on finished reports. Report completeness is still measured, but it no longer blocks a release.**
|
||||||
|
|
||||||
|
- **Gate edit: `matchedCompleteMatrix` and `mandatoryReports` (scripts/cso-eval.ts).** Both gates used to require every report's helper completeness to be `complete`: no gap, and every domain assessed. The private corpus fixtures carry gaps by design. They have no workflow files for zizmor, their planted credentials are never tested live, `.env` is kept out of the sealed source, and gitleaks has no history to read. So no report could ever be `complete`, and no release could ever qualify. Both gates now require every report to be `finished`, on a complete, fully adjudicated matrix. A running, missing or failed report still fails them. Completeness stays its own measured rate: `reports` per group, and the evaluator's `completenessRates`. Garry approved this rule on 2026-10-11, choosing option (a). No threshold changed.
|
||||||
|
|
||||||
## [1.91.92.0] - 2026-10-11
|
## [1.91.92.0] - 2026-10-11
|
||||||
|
|
||||||
**Outside reviews honor `codex_reviews` at the send, not in skill prose. Codex reasoning effort is a setting, and the README says what leaves your machine.**
|
**Outside reviews honor `codex_reviews` at the send, not in skill prose. Codex reasoning effort is a setting, and the README says what leaves your machine.**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# gstack digest v1.91.92.0 — regenerate/re-copy after upgrading gstack
|
# gstack digest v1.91.93.0 — regenerate/re-copy after upgrading gstack
|
||||||
|
|
||||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||||
for agent hosts without a full skill install. The full skills add workflows,
|
for agent hosts without a full skill install. The full skills add workflows,
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gstack",
|
"name": "gstack",
|
||||||
"version": "1.91.92",
|
"version": "1.91.93",
|
||||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+4
-3
@@ -116,7 +116,8 @@ export interface EvalResult {
|
|||||||
cellId: string; sourceHash: string; skillHash: string; model: string; host: string; budgetSeconds: number;
|
cellId: string; sourceHash: string; skillHash: string; model: string; host: string; budgetSeconds: number;
|
||||||
/**
|
/**
|
||||||
* `reportFinished`: the helper report's status is `finished`; its findings and runtime outcomes count whatever its
|
* `reportFinished`: the helper report's status is `finished`; its findings and runtime outcomes count whatever its
|
||||||
* completeness. `reportComplete`: it is also `complete` (no gap, every domain assessed), measured as its own rate.
|
* completeness, and the report gates require it. `reportComplete`: it is also `complete` (no gap, every domain
|
||||||
|
* assessed), measured as its own ungated rate because the corpus fixtures carry gaps by design.
|
||||||
*/
|
*/
|
||||||
reportPresent: boolean; reportFinished: boolean; reportComplete: boolean; findings: EvalFinding[];
|
reportPresent: boolean; reportFinished: boolean; reportComplete: boolean; findings: EvalFinding[];
|
||||||
setup: Outcome; reproduction: Outcome; repair: Outcome; recheck: Outcome;
|
setup: Outcome; reproduction: Outcome; repair: Outcome; recheck: Outcome;
|
||||||
@@ -781,13 +782,13 @@ export function scoreEval(matrix: EvalMatrix, observations: EvalResult[], qualif
|
|||||||
}).map(cell => cell.caseId));
|
}).map(cell => cell.caseId));
|
||||||
return [stack, { correctHeldOutRepairs: successful.size, denominator: new Set(eligible.map(cell => cell.caseId)).size }];
|
return [stack, { correctHeldOutRepairs: successful.size, denominator: new Set(eligible.map(cell => cell.caseId)).size }];
|
||||||
}));
|
}));
|
||||||
const assessedAll = observations.length === matrix.cells.length && observations.every(result => result.reportPresent && result.reportComplete) && groups.every(group => !group.unadjudicated);
|
const assessedAll = observations.length === matrix.cells.length && observations.every(result => result.reportFinished) && groups.every(group => !group.unadjudicated);
|
||||||
const gate = (condition: boolean | null, hasData: boolean): 'pass' | 'fail' | 'unmeasured' => !hasData || condition === null ? 'unmeasured' : condition ? 'pass' : 'fail';
|
const gate = (condition: boolean | null, hasData: boolean): 'pass' | 'fail' | 'unmeasured' => !hasData || condition === null ? 'unmeasured' : condition ? 'pass' : 'fail';
|
||||||
const containmentValues = REQUIRED_CONTAINMENT.map(name => qualification.containment?.[name]);
|
const containmentValues = REQUIRED_CONTAINMENT.map(name => qualification.containment?.[name]);
|
||||||
if (containmentValues.some(value => value !== undefined && !['passed', 'failed', 'not_run'].includes(value))) throw new Error('INVALID_CONTAINMENT_RESULT');
|
if (containmentValues.some(value => value !== undefined && !['passed', 'failed', 'not_run'].includes(value))) throw new Error('INVALID_CONTAINMENT_RESULT');
|
||||||
const gates = {
|
const gates = {
|
||||||
matchedCompleteMatrix: gate(assessedAll, observations.length > 0),
|
matchedCompleteMatrix: gate(assessedAll, observations.length > 0),
|
||||||
mandatoryReports: gate(groups.every(group => group.reports.numerator === group.reports.denominator), observations.length === matrix.cells.length),
|
mandatoryReports: gate(groups.every(group => group.finishedReports.numerator === group.finishedReports.denominator), observations.length === matrix.cells.length),
|
||||||
dailyPrecision95: gate(daily.precision.value === null ? null : daily.precision.value >= 0.95, daily.submitted === daily.cells && !daily.unadjudicated),
|
dailyPrecision95: gate(daily.precision.value === null ? null : daily.precision.value >= 0.95, daily.submitted === daily.cells && !daily.unadjudicated),
|
||||||
comprehensiveHighCriticalRecall80: gate(comprehensive.highCriticalRecall.value >= 0.8, comprehensive.submitted === comprehensive.cells && !comprehensive.unadjudicated),
|
comprehensiveHighCriticalRecall80: gate(comprehensive.highCriticalRecall.value >= 0.8, comprehensive.submitted === comprehensive.cells && !comprehensive.unadjudicated),
|
||||||
noHighCriticalRecallRegression: profile === 'full'
|
noHighCriticalRecallRegression: profile === 'full'
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ describe('CSO matched evaluation accounting', () => {
|
|||||||
expect(group(score, 'v3', 'comprehensive').highCriticalRecall).toEqual({ numerator: 96, denominator: 96, value: 1 });
|
expect(group(score, 'v3', 'comprehensive').highCriticalRecall).toEqual({ numerator: 96, denominator: 96, value: 1 });
|
||||||
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 10, denominator: 10 });
|
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 10, denominator: 10 });
|
||||||
});
|
});
|
||||||
test('finished partial reports count their findings and outcomes; completeness stays its own measured, gated rate', () => {
|
test('finished partial reports count their findings and outcomes and satisfy the report gates; completeness stays its own measured rate', () => {
|
||||||
const results = matrix.cells.map(syntheticResult);
|
const results = matrix.cells.map(syntheticResult);
|
||||||
for (const result of results) result.reportComplete = false;
|
for (const result of results) result.reportComplete = false;
|
||||||
const score = scoreEval(matrix, results, qualification), daily = group(score, 'v3', 'daily'), comprehensive = group(score, 'v3', 'comprehensive');
|
const score = scoreEval(matrix, results, qualification), daily = group(score, 'v3', 'daily'), comprehensive = group(score, 'v3', 'comprehensive');
|
||||||
@@ -216,7 +216,14 @@ describe('CSO matched evaluation accounting', () => {
|
|||||||
expect(comprehensive.highCriticalRecall).toEqual({ numerator: 96, denominator: 96, value: 1 });
|
expect(comprehensive.highCriticalRecall).toEqual({ numerator: 96, denominator: 96, value: 1 });
|
||||||
expect(comprehensive.repair.numerator).toBe(comprehensive.repair.denominator);
|
expect(comprehensive.repair.numerator).toBe(comprehensive.repair.denominator);
|
||||||
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 10, denominator: 10 });
|
expect(score.perStack.rails).toEqual({ correctHeldOutRepairs: 10, denominator: 10 });
|
||||||
expect(score.gates).toMatchObject({ comprehensiveHighCriticalRecall80: 'pass', dailyPrecision95: 'pass', mandatoryReports: 'fail', matchedCompleteMatrix: 'fail' });
|
expect(score.gates).toMatchObject({ comprehensiveHighCriticalRecall80: 'pass', dailyPrecision95: 'pass', mandatoryReports: 'pass', matchedCompleteMatrix: 'pass' });
|
||||||
|
expect(score.status).toBe('qualified');
|
||||||
|
});
|
||||||
|
test('one unfinished report fails both report gates even when every other report is finished', () => {
|
||||||
|
const results = matrix.cells.map(syntheticResult);
|
||||||
|
Object.assign(results[0], { reportComplete: false, reportFinished: false });
|
||||||
|
const score = scoreEval(matrix, results, qualification);
|
||||||
|
expect(score.gates).toMatchObject({ mandatoryReports: 'fail', matchedCompleteMatrix: 'fail' });
|
||||||
expect(score.status).toBe('partial');
|
expect(score.status).toBe('partial');
|
||||||
});
|
});
|
||||||
test('running or missing reports contribute no security evidence and cannot qualify a complete matrix', () => {
|
test('running or missing reports contribute no security evidence and cannot qualify a complete matrix', () => {
|
||||||
|
|||||||
Reference in new issue
Block a user