Define the local evaluation task
A synthetic drafting rubric supplies Boolean outcomes per dimension and explicitly names the required dimensions. Every required dimension must exist and pass; other dimensions are retained but cannot compensate. A missing required dimension is unresolved. No numeric averaging or weighted compromise is defined.
- Retain independent dimension labels
- Select explicitly required dimensions
- Require complete passing required evidence
Absent or malformed required dimensions need rubric evidence before grading.
Use the filled cases
| Case | Supplied input | Expected structured output | Decision and reason |
|---|---|---|---|
| Fluency cannot offset required format failure | {"required":["content","format"],"outcomes":{"content":true,"format":false,"fluency":true}} | {"missing":[],"failed":["format"],"invalid":[],"status":"fail"} | The required format fails even though two dimensions pass. |
| Required pass, optional fail | {"required":["content"],"outcomes":{"content":true,"style":false}} | {"missing":[],"failed":[],"invalid":[],"status":"pass"} | Style is retained but not required under this supplied rubric. |
| Required dimension missing | {"required":["content","format"],"outcomes":{"content":true}} | {"missing":["format"],"failed":[],"invalid":[],"status":"unresolved"} | Missing format is not silently passed or failed. |
Work the missing-required case. Which output is required, and why?
Show the worked answer
{"missing":["format"],"failed":[],"invalid":[],"status":"unresolved"} — Missing format is not silently passed or failed.
Run the self-contained evaluator
import assert from 'node:assert/strict';
function evaluate(x){
const missing=x.required.filter(k=>!Object.hasOwn(x.outcomes,k)),failed=x.required.filter(k=>Object.hasOwn(x.outcomes,k)&&x.outcomes[k]===false),invalid=x.required.filter(k=>Object.hasOwn(x.outcomes,k)&&typeof x.outcomes[k]!=='boolean');
return {missing,failed,invalid,status:missing.length+invalid.length?'unresolved':failed.length?'fail':'pass'};
}
const cases = [
{
"id": "fluency-no-compensation",
"input": {
"required": [
"content",
"format"
],
"outcomes": {
"content": true,
"format": false,
"fluency": true
}
},
"expected": {
"missing": [],
"failed": [
"format"
],
"invalid": [],
"status": "fail"
}
},
{
"id": "required-pass-optional-fail",
"input": {
"required": [
"content"
],
"outcomes": {
"content": true,
"style": false
}
},
"expected": {
"missing": [],
"failed": [],
"invalid": [],
"status": "pass"
}
},
{
"id": "missing-required",
"input": {
"required": [
"content",
"format"
],
"outcomes": {
"content": true
}
},
"expected": {
"missing": [
"format"
],
"failed": [],
"invalid": [],
"status": "unresolved"
}
}
];
for (const c of cases) assert.deepEqual(evaluate(c.input), c.expected, c.id);
console.log(JSON.stringify({checked:cases.length,caseIds:cases.map(c=>c.id)}));
Keep the required-dimension list with each revision of the acceptance rubric. Changing a blocker is a policy change, not a harmless score formatting adjustment.