Skip to content

feat(api-sync): enum and nested-object coverage checks, delivery manifest verification - #38

Merged
ericviana merged 1 commit into
mainfrom
eric/api-sync-hardening
Aug 4, 2026
Merged

ericviana merged 1 commit into
mainfrom
eric/api-sync-hardening

Conversation

@ericviana

Copy link
Copy Markdown
Member

Summary

Two new blocking coverage checks in scripts/api-sync.php, wired into the existing
php scripts/api-sync.php --check CI job (no separate job needed since it shares the
same gate), plus a manifest-verification step in api-sync.yml.

a) Enum coverage (blocking)

Every enum-constrained spec property (enum, items.enum on an array, or an
anyOf/oneOf member carrying enum) on a schema mapped in spec-map.json must
resolve to a mapped enum symbol (a PHP backed-enum class that also appears in
spec-map.json's enums list), or have a recorded exclusion in
known-divergences.json (fields, situation (a): "backed by a constrained enum but
modeled as a plain untyped field").

b) Nested-object coverage (blocking)

Recursively enumerates every inline object or array-of-object shape reachable under a
mapped schema (skipping $ref'd shapes, which are separately reachable/mapped). Each
found shape must have its own spec-map.json path entry, or a recorded intentional
omission in unmodeled.json ({"schema":..,"path":..,"field":"*"} -- a new
whole-shape-omission convention, documented in that file's own $schema).

What running both checks found

Real, previously invisible gaps:

  • Several enum-constrained fields modeled as plain string/?array instead of a
    backed enum (PayinOut.currency, .payment_method, .type, .pse_document_type;
    PayoutOut/PayoutOnEvmOut's tracking_*.status/estimated_time_of_arrival/
    provider_status/provider_name; OfframpWallet.network; VirtualAccountOut.kyc_status
    and .us.account_type; WebhookEndpoint(In).events; BankAccountOut.spei_protocol;
    PayoutOut.ach_cop_document_type) -- recorded as known-divergences.json exclusions,
    visibility only, every spec value still parses.
  • Two enums missing spec-added cases: LimitIncreaseRequestSupportingDocumentType
    (7 missing values) and ManualExecutionStatus (pending, concluded) -- added.
  • Several nested shapes never modeled at all: TrackingTransaction was missing most
    of PayinOut/PayoutOut's tracking_transaction fields (sender_, provider_,
    ledger_*, trace/reference numbers); TrackingComplete was missing
    provider_transaction_id/refund_reason (payout) and error_message/gas_fee
    (transfer); BankDetails was missing swift_account_number/swift_receiving_bank;
    PayerRules was missing 8 PSE/transfers fields; VirtualAccountUsDetails was
    missing swift_intermediary_bank; Transfers' tracking_paymaster/
    tracking_bridge_swap/tracking_transaction_monitoring were step-only stubs.
    Added the cheap, safe (nullable, optional) fields directly. Genuinely unmodeled
    deeper shapes (pse_instruction, ted_instruction, transfers_instruction,
    payer_rules on the payin response side, owners[], contract, jpm_track_data,
    etc.) got honest unmodeled.json omission entries with reasons instead.

c) Delivery manifest verification

api-sync.yml now verifies .api-sync/delivery.json on the api-sync-data branch
before touching anything:

  • Fails loudly if spec-current.json or delivery.json is missing.
  • Fails if sha256sum of spec-current.json doesn't match the manifest's
    spec_sha256.
  • Fails with "stale delivery" if the triggering repository_dispatch's
    client_payload.spec_sha256 is non-empty and disagrees with the manifest.

pipeline-alert.yml sanity check (report only, no changes)

Present on main, and injection-safe: event fields go through env:, JSON is built
with jq --arg (not string interpolation), the webhook URL is quoted in the curl
call, and a missing SLACK_WEBHOOK_URL exits non-zero.

No version bump

This is CI/tooling hardening plus mechanical backfill of fields the new checks made
visible, not a deliberate feature release, per the task brief.

Test plan

  • composer run test -- 118 passed, 729 assertions
  • composer run lint:check -- clean
  • php scripts/api-sync.php --check -- exit 0
  • Map validity check (inline snippet from main.yaml's job) -- OK
  • Determinism proof (apply into two scratch copies, diff byte-identical) -- OK
  • php scripts/contract-check.php -- OK
  • .github/workflows/api-sync.yml YAML validated with yaml.safe_load

https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs

…fest verification

Hardens the deterministic spec-driven sync patcher with two new blocking CI checks and
strengthens the api-sync workflow's trust boundary with the api-sync-data branch:

- Enum coverage: every enum-constrained spec property (enum, items.enum, or an
  anyOf/oneOf member carrying enum) on a mapped schema must resolve to a mapped enum
  symbol in spec-map.json, or have a recorded known-divergences.json exclusion. Wired
  into the existing php scripts/api-sync.php --check gate, so it runs in the same CI
  job as the rest of state reconciliation.
- Nested-object coverage: recursively enumerates every inline object and
  array-of-object shape reachable under a mapped schema; each must have its own
  spec-map.json path entry or a recorded intentional omission in unmodeled.json
  (field "*" for a whole-shape omission).
- Running both checks against the current spec surfaced real, previously invisible
  gaps: several enum-constrained fields modeled as plain strings/arrays instead of
  backed enums, two enums missing spec-added cases (LimitIncreaseRequestSupportingDocumentType,
  ManualExecutionStatus), and several nested shapes (tracking_transaction,
  blindpay_bank_details, VirtualAccount's us.*, Transfers' tracking_paymaster/
  tracking_bridge_swap/tracking_transaction_monitoring, PayerRules) that were never
  modeled at all. Added the missing enum cases and optional fields where cheap and
  safe to do so; recorded honest ledger entries (with reasons) for the rest.
- api-sync.yml now verifies the delivery manifest published on the api-sync-data
  branch (.api-sync/delivery.json) before touching anything: fails loudly if either
  spec-current.json or delivery.json is missing, fails if spec-current.json's sha256
  doesn't match the manifest's spec_sha256, and fails with "stale delivery" if the
  triggering repository_dispatch's spec_sha256 disagrees with the manifest.

No version bump -- this is CI/tooling hardening plus mechanical backfill of fields
the new checks made visible, not a deliberate feature release.

Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs
@BernardoSM

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@ericviana
ericviana merged commit 783c676 into main Aug 4, 2026
6 checks passed
@ericviana
ericviana deleted the eric/api-sync-hardening branch August 4, 2026 13:01
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.

2 participants