fix(evals): distinguish mobile check waivers from protection

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-04 18:56:36 -06:00
1 parent 5de738b8aa
commit 4c8672ab20
4 files changed
+18 -6

No files matched your search

+4 -2
View File
@@ -241,12 +241,14 @@ Only `steering.preserve-draft` and `tasks.attachment-brief` may currently declar
### Authored skill inspection
`skills.author-skill` v3 follows the normal library card to the exact saved skill.
`skills.author-skill` v4 follows the normal library card to the exact saved skill.
It accepts the canonical company-scoped detail route or the exact Studio route.
The detail view opens Overview first; select Files, then its normal Edit control. Both paths must show the
editable saved checklist, survive reload, and match the same public current
version. The receipt identifies the actual editor surface. The original case
clock, query, and one-human/one-native limits remain unchanged. Older failed
clock, query, and one-human/one-native limits remain unchanged. The 375px check
rejects omission or waiver of the required review; a protective instruction
that content must not overflow does not waive that review. Older failed
attempts are retained; component controls do not establish a native pass.
### Team install conflict recovery
@@ -5085,7 +5085,7 @@
"../../ui/src/pages/CompanySkills.tsx",
"../../ui/src/lib/company-skill-routes.ts"
],
"oracleVersion": "paperclip.chat-catalog.skill-authoring.v3",
"oracleVersion": "paperclip.chat-catalog.skill-authoring.v4",
"expectedRunCount": 1,
"expectedHumanMessageCount": 1,
"turnTimeoutMs": 120000,
@@ -63,8 +63,17 @@ it("preserves a numbered viewport check and its actual multiline inspection deta
expect(preservesWebsiteChecklist(markdown.replace("Confirm whether", "Ignore whether"))).toBe(false);
});
it("authoring v3 accepts ordinary Skills/Studio inspection without a prescribed reply link",()=>{
it("authoring v4 accepts ordinary Skills/Studio inspection without a prescribed reply link",()=>{
const o=valid("skills.author-skill");o.links=[];
Object.assign(o.state,{authoredSkillInspection:{companyId:"company",skillId:"skill",versionId:"revision",libraryVisible:true,editorVisible:true,editorSurface:"studio",editable:true,checklistVisible:true,reloaded:true,readbackMatched:true}});
expect(gradeIntegratorSkills(o).passed).toBe(true);
});
it("preserves required mobile checks beside protective nonoverflow instructions without allowing a waiver", () => {
const original = websiteChecklist.join("\n");
const protection = "Confirm images, tables, and code blocks do not overflow.";
expect(preservesWebsiteChecklist(`${original}\n- ${protection}`)).toBe(true);
expect(preservesWebsiteChecklist(original.replace(websiteChecklist[3]!, `${websiteChecklist[3]!.slice(0, -1)}; ${protection}`))).toBe(true);
expect(preservesWebsiteChecklist(`${original.replace(websiteChecklist[3]!, `Do not test the landing page at a 375 pixel viewport without horizontal scrolling.`)}\n- ${protection}`)).toBe(false);
expect(preservesWebsiteChecklist(`${original.replace(websiteChecklist[3]!, `Testing the landing page at a 375 pixel viewport without horizontal scrolling is optional.`)}\n- ${protection}`)).toBe(false);
});
@@ -17,11 +17,12 @@ function websiteChecklistChecks(markdown: string): boolean[] {
// separate checks isolated so an unrelated earlier width cannot ground it.
const blocks = markdown.split(/\n(?=\s*(?:\d+[.)]|#{1,6})\s)/).map(block => block.replace(/\s+/g, " "));
const forbidden = /\b(?:skip|omit|ignore|don't|do not|need not|not required|optional)\b/i;
const mobileWaiver = /\b(?:skip|omit|ignore|need not|not required|optional)\b|\b(?:don't|do not)\s+(?:check|test|verify|confirm|assess|measure|review)\b/i;
const instruction = (requirements: RegExp[]) => instructions.some(text => requirements.every(pattern => pattern.test(text)) && !forbidden.test(text));
return [instruction([/\bkeyboard\b/i, /(?:\b(?:all|every)\b.*\b(?:interactive|control|element|action|navigation)|\b(?:interactive|control|element|action|navigation)\b.*\b(?:all|every)\b)/i, /(?:only|alone|without (?:a |the )?mouse|no mouse)/i]),
instruction([/\b(?:all|every|each)\b/i, /\b(?:form|input)\b/i, /\b(?:field|control|input)s?\b/i, /\bvisible\b/i, /\blabels?\b/i]),
instruction([/\b(?:text|foreground)\b/i, /\bcontrast\b/i, /\bWCAG(?:\s+2\.\d)?\s+(?:level\s+)?AA\b/i, /\b(?:check|verify|ensure|measure|assess|meet|meets|satisfy)\b/i]),
blocks.some(text => !forbidden.test(text) && [/\b375\s*(?:px|pixels?)?\b/i, /\b(?:viewport|wide|width)\b/i, /(?:without horizontal (?:scrolling|overflow)|no horizontal (?:scrolling|overflow)|(?:must not|does not|doesn't) (?:scroll|overflow) horizontally|(?:confirm|check|verify)[^.!?]{0,70}whether[^.!?]{0,70}horizontal scrolling)/i].every(pattern => pattern.test(text)))];
blocks.some(text => !mobileWaiver.test(text) && [/\b375\s*(?:px|pixels?)?\b/i, /\b(?:viewport|wide|width)\b/i, /(?:without horizontal (?:scrolling|overflow)|no horizontal (?:scrolling|overflow)|(?:must not|does not|doesn't) (?:scroll|overflow) horizontally|(?:confirm|check|verify)[^.!?]{0,70}whether[^.!?]{0,70}horizontal scrolling)/i].every(pattern => pattern.test(text)))];
}
export function preservesWebsiteChecklist(markdown: string): boolean {
return websiteChecklistChecks(markdown).every(Boolean);
@@ -148,7 +149,7 @@ export async function inspectAuthoredSkill(ctx: ChatCatalogContext, state: Recor
const define = (id: string, setup: ChatCatalogCase["setup"]): ChatCatalogCase => {
const base = integratorDefinition("skills", id, setup, observeSkills, gradeIntegratorSkills, verifySkill);
base.sourceFiles = [...base.sourceFiles, "chat-catalog-domains/integrator-skill-versions.ts"];
return id === "skills.author-skill" ? { ...base, authentication: "human-session", oracleVersion: "paperclip.chat-catalog.skill-authoring.v3", sourceFiles: [...base.sourceFiles, "chat-catalog-domains/identity-support.ts", "../../ui/src/components/MarkdownEditor.tsx", "../../ui/src/pages/SkillStudio.tsx", "../../ui/src/pages/CompanySkills.tsx", "../../ui/src/lib/company-skill-routes.ts"], queryAttachments: factualContextQueryAttachment("skill-context.md"),
return id === "skills.author-skill" ? { ...base, authentication: "human-session", oracleVersion: "paperclip.chat-catalog.skill-authoring.v4", sourceFiles: [...base.sourceFiles, "chat-catalog-domains/identity-support.ts", "../../ui/src/components/MarkdownEditor.tsx", "../../ui/src/pages/SkillStudio.tsx", "../../ui/src/pages/CompanySkills.tsx", "../../ui/src/lib/company-skill-routes.ts"], queryAttachments: factualContextQueryAttachment("skill-context.md"),
async act(ctx, state) { remainingIdentityObservationBudget(ctx); await waitForReply(ctx, ctx.observationDeadlineAt); await inspectAuthoredSkill(ctx, state, await observeSkills(ctx, state)); },
async verifyResult(ctx, state, value) { const o = observation(value); if (!o) throw new Error("Missing authored skill inspection evidence"); await inspectAuthoredSkill(ctx, state, o); },
} : base;