Skip to content

[finding][spec] SkillTriggerConditionSchema.value is not operator-constrained either — the consumer coerces instead (dormant twin of #6227) #7113

Description

@os-zhuang

Out-of-scope observation from implementing #6227, recorded per PD #10. Unassigned, not queued — filed for triage grading. Duplicate search: no open issue matches SkillTriggerCondition or triggerConditions operator value shape.

Fact

SkillTriggerConditionSchema (packages/spec/src/ai/skill.zod.ts:20-29) has the same operator/value decoupling #6227 fixes on ViewFilterRuleSchema:

operator: z.enum(['eq', 'neq', 'in', 'not_in', 'contains']),
value: z.union([z.string(), z.array(z.string())]),

{ field: 'userRole', operator: 'in', value: 'admin' } — a set operator with a scalar — parses today, exactly like the #6227 shape did.

Why this is an OBSERVATION and not the same defect

Measured in the consumer rather than assumed. SkillRegistry.evaluateCondition (cloud repo, packages/service-ai/src/skill-registry.ts:171-181) normalizes the scalar itself:

case 'in': {
  const list = Array.isArray(expected) ? expected : [expected];
  return list.includes(fieldValue as string);
}

So there is no two-stage failure here — nothing 400s or 500s, and the scalar evaluates to the predicate an author would expect. This is the key difference from #6227, whose runtime half genuinely refuses the shape (assertListComparandShapes, 400 INVALID_FILTER). Nothing a user hits today, hence finding and no pm:queue.

Why it is still worth recording

It is a consumer-side lenient coercion standing in for a contract the producer surface never declared — the Array.isArray(x) ? x : [x] shape the repo's contract-first rule treats as the wrong side of the fix. The cost is not a broken query but a second dialect: triggerConditions accepts two spellings of one predicate, and only one of them is what an author reading the schema would write. For a surface whose authors are increasingly AI-generated, "declared = enforced" is the property that keeps generated metadata honest.

Note the two surfaces are genuinely independent — triggerConditions is evaluated by the cloud agent runtime (skill.zod.ts:212: "evaluated by the cloud agent runtime only"), NOT lowered through AST_OPERATOR_MAP into an objectql filter — so #6227's fix does not reach it and no shared helper covers both today.

Options, if graded live

No recommendation pre-empted — the "real business need" axis (who actually authors triggerConditions, and whether any of them write the scalar form) is not measurable from this repo, and that measurement should drive the call.

Refs: #6227, PR for #6227, #5869.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions