Skip to content

feat(training-agent): implement provenance enforcement (closes #3777)#3792

Open
bokelley wants to merge 2 commits intomainfrom
bokelley/training-prov-enforce
Open

feat(training-agent): implement provenance enforcement (closes #3777)#3792
bokelley wants to merge 2 commits intomainfrom
bokelley/training-prov-enforce

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 1, 2026

Summary

Brings the reference training agent up to the provenance spec landed in #3468 so creative_sales_agent/provenance_enforcement passes against it. Closes #3777.

Wire contract enforced:

  • get_products round-trips creative_policy.{provenance_required, provenance_requirements, accepted_verifiers} from comply_test_controller-seeded products (previously only create_media_buy saw fixtures)
  • sync_creatives runs structural enforcement against the session's effective creative_policy before persisting; emits per-creative failures with action: 'failed' + errors[] carrying:
    • PROVENANCE_REQUIRED
    • PROVENANCE_DIGITAL_SOURCE_TYPE_MISSING
    • PROVENANCE_DISCLOSURE_MISSING
    • PROVENANCE_EMBEDDED_MISSING
    • PROVENANCE_VERIFIER_NOT_ACCEPTED — buyer's verify_agent.agent_url cross-checked (canonicalized) against the seller's accepted_verifiers allowlist before any outbound call. Off-list URLs reject without contacting them, closing the buyer-controlled-URL trust gap that drove the seller-publishes / buyer-represents / seller-confirms pattern in feat(provenance): add embedded_provenance and watermarks to provenance schema #3468.

PROVENANCE_CLAIM_CONTRADICTED (truth-of-claim — requires calling get_creative_features against an on-list verifier) is out of scope for this pass; the structural codes are sufficient to exercise the wire contract end to end.

Other changes

  • backfillProductDefaults fills in spec-required Product fields (name, description, publisher_properties, format_ids, pricing_options, reporting_capabilities + sub-fields) for fixture-seeded products. Historical fixtures only carried what create_media_buy validation needed; once seeded products begin round-tripping through get_products, missing required fields fail response schema validation. Defensive backfill so fixture-driven products serialize as valid Product objects without forcing every fixture to repeat boilerplate.
  • Removes creative_sales_agent/provenance_enforcement from KNOWN_FAILING_STORYBOARDS in server/tests/manual/run-storyboards.ts (passes 5/5 in both legacy and framework dispatches).
  • Bumps min_clean_storyboards (53→65) and min_passing_steps (388→444 legacy, 401→462 framework) in .github/workflows/training-agent-storyboards.yml.
  • Storyboard fixture gains a unique name/description so brief-mode scoring places the seeded product at products[0]. Per-creative error assertions switched to field_value paths (the runner's error_code validator only inspects top-level errors[] but sync_creatives carries per-item failures inside creatives[]).

Test plan

Local run, both modes:

  • npx tsx server/tests/manual/run-storyboards.ts (framework) — 65/65 clean, 462 passing steps, 0 failures
  • TRAINING_AGENT_USE_FRAMEWORK=0 npx tsx server/tests/manual/run-storyboards.ts (legacy) — 65/65 clean, 444 passing steps, 0 failures
  • npm run typecheck — clean
  • npm run test:unit — 849/849 pass, 45 files
  • Pre-commit hook passes (test:unit, test:test-dynamic-imports, test:callapi-state-change, typecheck)

🤖 Generated with Claude Code

Bring the reference training agent up to the spec landed in #3468 so the
creative_sales_agent/provenance_enforcement storyboard passes against it.

handleGetProducts:
- Overlay comply_test_controller-seeded products onto the response so
  storyboard-seeded creative_policy fields (provenance_required,
  provenance_requirements, accepted_verifiers) round-trip through
  get_products. Previously only handleCreateMediaBuy saw seeded fixtures.
- New backfillProductDefaults fills in spec-required Product fields
  (name, description, publisher_properties, format_ids, pricing_options,
  reporting_capabilities and its required sub-fields) for fixture-seeded
  products that historically carried only the fields create_media_buy
  validation needed. Closes the response-schema gap exposed once seeded
  products began round-tripping through get_products.

handleSyncCreatives:
- Aggregate creative_policy across session-seeded products and run
  structural enforcement before persisting each creative. Emits per-
  creative failures with action: 'failed' + errors[]:
    PROVENANCE_REQUIRED
    PROVENANCE_DIGITAL_SOURCE_TYPE_MISSING
    PROVENANCE_DISCLOSURE_MISSING
    PROVENANCE_EMBEDDED_MISSING
    PROVENANCE_VERIFIER_NOT_ACCEPTED — buyer's verify_agent.agent_url
      cross-checked (canonicalized) against the seller's accepted_verifiers
      allowlist before any outbound call; off-list URLs are rejected
      without contacting them, closing the buyer-controlled-URL trust gap.
- PROVENANCE_CLAIM_CONTRADICTED (truth-of-claim, requires calling
  get_creative_features against an on-list verifier) is out of scope for
  this pass — the structural codes are sufficient to exercise the wire
  contract.

Storyboard cleanup:
- Remove creative_sales_agent/provenance_enforcement from
  KNOWN_FAILING_STORYBOARDS — passes 5/5 in both legacy and framework.
- Bump min_clean_storyboards (53→65) and min_passing_steps (388→444
  legacy, 401→462 framework) in
  .github/workflows/training-agent-storyboards.yml.
- Update the scenario fixture with a unique name/description so brief-
  mode scoring places it at products[0], and switch per-creative error
  assertions to field_value paths (the storyboard runner's error_code
  validator only inspects top-level errors[] but sync_creatives carries
  per-item failures inside creatives[]).

Local conformance: 65/65 storyboards clean in both modes; 444 passing
steps legacy / 462 framework — matching the new floors.

Refs: #3468, #3777.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion, storyboard rigor)

Addresses all expert-review items from #3792's three-pass read
(code-reviewer, security-reviewer, nodejs-testing-expert).

Catalog safety + symmetric backfill (code-review):
- Restrict backfillProductDefaults to seeded-product IDs only by folding
  it into overlaySeededProducts. The previous integration mutated nested
  objects (format_ids[], reporting_capabilities) on every product
  including catalog ones; getCatalog() returns a shallow copy whose
  nested fields alias the cached singleton, so mutations would leak
  across requests once a partial catalog product appeared.
- Both handleGetProducts and handleCreateMediaBuy now go through the
  same overlay path, so backfill is applied symmetrically. Closes the
  asymmetric-backfill divergence the reviewer flagged.
- Rename backfillProductDefaults -> backfillTrainingProductDefaults to
  make the training-only intent obvious at every call site.

Sanitization (security-review):
- Add sanitizeForError helper that strips C0/C1 controls and caps
  length; apply to buyer-controlled strings (verify_agent.agent_url,
  creative_id) before interpolating into TaskError.message and
  error.field. Defense against log/transcript poisoning by attacker-
  shaped values.
- Confirmed via review (and grep): no outbound HTTP touches the buyer-
  supplied verify_agent.agent_url anywhere in the enforcement path. The
  trust invariant from #3468 holds.

Helper documentation:
- enforceProvenancePolicy gains a cascade-order docstring (storyboard
  assertions on errors[0] rely on stable ordering).
- aggregateCreativePolicy gains a comment explaining the deliberate
  asymmetry: requirement booleans are intersected (most-restrictive
  wins), accepted_verifiers are unioned (allowlist semantics).

Storyboard rigor (testing-review):
- Add reject_no_provenance phase (PROVENANCE_REQUIRED) — the cheapest,
  most likely buyer mistake had zero coverage.
- Add reject_missing_digital_source_type phase
  (PROVENANCE_DIGITAL_SOURCE_TYPE_MISSING). Fixture now requires
  digital_source_type so the policy gate fires.
- Tighten accept-phase assertion from field_present to
  field_value allowed_values: [created, updated]. field_present would
  have silently passed on action: failed.
- Comment the errors[0] indexing as a stable contract tied to
  enforceProvenancePolicy's cascade order, with a TODO to switch to a
  field_contains predicate when the SDK ships one (adcp#3803).
- Comment the brief-mode coupling for products[0] selection.

Workflow comment correction:
- True origin/main baseline was 64 storyboards / 439 legacy steps / 457
  framework steps — the previous 53/388/401 floors had drifted. New
  floors: 65 / 446 / 464, lifting only the new
  creative_sales_agent/provenance_enforcement scenario (six phases).
  Comment corrected so the next reader doesn't reverse-engineer the
  baseline.

Truth-of-claim follow-up:
- Add static/compliance/source/protocols/media-buy/scenarios/
  provenance_truth_of_claim.yaml as a single-phase skeleton.
- Register it in KNOWN_FAILING_STORYBOARDS pointing at adcp#3802 (the
  follow-up issue tracking PROVENANCE_CLAIM_CONTRADICTED implementation
  in the training agent).

Conformance rigor follow-up:
- adcp#3803 tracks: (1) required-clean storyboard allowlist alongside
  the floors, (2) errors[*] field_contains predicate in the storyboard
  validator, (3) storyboard run in pre-push hook.

Local conformance both modes: 65/65 clean; 446 passing steps legacy /
464 framework — exactly matching the new floors.

Refs: #3468, #3777, #3792.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley
Copy link
Copy Markdown
Contributor Author

bokelley commented May 2, 2026

Issue #3803 proposes three conformance-rigor follow-ups surfaced in this PR's review: required_clean_storyboards allowlist (item 1 — same workflow file as your floor-bump), errors[*].code wildcard predicate for field_value (item 2 — extends the per-creative positional assertion approach introduced here), and a pre-push storyboard hook (item 3 — same workflow file). Items 1 and 3 have direct file overlap with this PR's diff; consider folding before merge or confirm as a follow-up.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Training agent: implement provenance enforcement (provenance_requirements + accepted_verifiers + PROVENANCE_* codes)

1 participant