Skip to content

feat(sdk): data-processor host contract: typed create, window and instant evaluations (0.37.0) - #207

Merged
facontidavide merged 4 commits into
mainfrom
feat/data-processor-host-contract
Oct 18, 2026
Merged

facontidavide merged 4 commits into
mainfrom
feat/data-processor-host-contract

Conversation

@facontidavide

@facontidavide facontidavide commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor
Lines Added Removed
Production (headers, sources) 974 53
Tests 1253 374
Docs 94 5
Other (CMake, floors) 21 2

Base: main (0.36.0). Replaces the draft #202 as the data-processor host contract; #202 stays open as a reference only. Shares the 0.37.0 minor with #205 (widget family): whichever merges second rebases its VERSION, CHANGELOG and feature floors, a mechanical change.

Goals

The host contract that lets a plugin create an on-demand data processor (a script evaluated at a consumer-requested time, PJ4's derived objects) and run explicit window or instant evaluations of it. Data processors only: catalog v2, plot-tab scene tabs and dialog scene views wait for a later minor.

  • Four vtable tail slots on PJ_data_processors_host_vtable_t: create_data_processor_v2, submit_evaluation, poll_evaluation, release_evaluation. Appended and guarded by struct_size; hasTypedRequests() is true only when all four are present and non-NULL.
  • PJ_data_processor_request_t: typed outputs, time_flags (WINDOW or INSTANT, mutually exclusive), dataset_id, window and instant times. PJ_evaluation_budget_t: time, evaluations, memory (interpreter plus native objects) and report bytes.
  • Host-side decoders in pj_base/sdk/data_processors_host.hpp: readDataProcessorRequest and readEvaluationBudget read the size prefix first, then validate every count, size, pointer and flag before a host touches it. A count or size that cannot be represented is rejected before any traversal.
  • parseEvaluationReport in pj_plugins/sdk/evaluation_report.hpp: the one report shape (coverage.{candidates, evaluated, stopped, error?, gaps} and the bundles), with int64 timestamps as JSON integers, evaluated equal to the bundle count, no input later than its requested_ns, and the inferred-output type vocabulary checked.
  • Rules by kind, documented in plugin_data_api.h: transform takes no time flags; markers take WINDOW on create_data_processor_v2 (a generator clipped to the window, coverage_ns in its config) and no submit; on_demand takes INSTANT on create (a pinned finding) and WINDOW or INSTANT on submit. EPHEMERAL with INSTANT on create is rejected (an instant preview is submit_evaluation's job). A scripted submit is ephemeral by definition; HISTORY_EXEMPT on submit is rejected. The additive config keys of on_demand (state, detail, requested_ns, lag_ns, upstream, findings) and markers (computing, error, coverage_ns) are documented.

Not in this PR (no host consumer before the PJ4 slice that needs them): a label field, an INFER flag (inference applies when no output is declared), typed marker scope, the non-blocking marker create flag, the marker metadata constants.

How to evaluate

  • Read the contract block in pj_base/include/pj_base/plugin_data_api.h (the DATA PROCESSORS section and the request/budget structs), then data_processors_host.hpp and evaluation_report.hpp.
  • Tests: pj_plugins/tests/data_processors_api_test.cpp (decoders, wrappers, report parser, the NULL-slot and short-prefix cases) and the ABI layout sentinels; check_feature_floors.py lists the new surfaces.
  • Verified: 98/98 on the Debug + ASAN lane and the default lane. A Codex review of the whole contract found six issues (report consistency, input stamps after the request, the output-type vocabulary, the input-resolution wording, EPHEMERAL with INSTANT, NULL slots), all fixed test-first; an earlier pass found an unchecked input_count that overran under ASAN, now guarded.
  • Release plan: this PR sets VERSION to 0.37.0-dev.1 (the changelog stays under [Unreleased]), and its merge commit is tagged v0.37.0-dev.1. Pre-releases v0.37.0-dev.N (Conan to JFrog, GitHub pre-release, no conda; tooling in ci(release): development pre-releases X.Y.Z-dev.N #206) while PJ4 builds its bridge against it; v0.37.0 is cut once when PJ4's bridge has passed its walkthrough. No ABI promise between dev builds; the final tag freezes it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01E228Y7uZ8QdznAjEnrXKSz

facontidavide and others added 3 commits October 18, 2026 18:44
….37.0)

The host contract PJ4's on-demand plugin bridge (slice 8a-2) consumes,
ported from #202 onto 0.36.0 with the agreed review changes. Data
processors only: catalog v2, plot-tab slots, markers on v2 and dialog
scene views wait for the release their consumers need.

- Four tail slots on PJ_data_processors_host_vtable_t:
  create_data_processor_v2, submit_evaluation, poll_evaluation,
  release_evaluation; hasTypedRequests() detects them.
- PJ_data_processor_request_t (read-prefix struct_size rule, a typed
  dataset_id, 0 = name resolution), the output element and the budget
  (a nonzero reserved field and a cap below the report envelope are
  rejected; NULL means host defaults). WINDOW and INSTANT are exclusive.
- States PENDING, COMPLETED, FAILED; 3 is reserved and never emitted.
  FAILED carries the COMPLETED report shape with stopped "error";
  parseEvaluationReport() validates types and coverage and rejects a raw
  NUL.
- A scripted submit implies EPHEMERAL and rejects HISTORY_EXEMPT; a
  submit naming an installed node takes its stored recipe and rejects
  recipe fields in the request.
- readDataProcessorRequest() / readEvaluationBudget(): the host-side
  read-prefix decoders in one place; null string views with a size are
  rejected.
- INFER_OUTPUTS and the v1 "<name>:<type>" on_demand route are dropped:
  v1 create rejects on_demand with a hint to use v2.

Every member offset is pinned on x64; the fake host enforces the
contract through the raw C slots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N9J3ZtWHDRzRsiJTi2o861
- Time flags by kind: markers accept WINDOW on create_data_processor_v2
  (coverage_ns echoed in the config, start > end rejected) and reject
  INSTANT; transform accepts none.
- Document the keys a host adds to data_processor_config for on_demand
  and markers, and where a create's failures are reported.
- Declared outputs: single value, positional values or a table keyed by
  output name.
- max_bytes bounds the run's whole memory, not only the interpreter's.
- readDataProcessorRequest rejects a count or string size that cannot be
  an object before reading the array or string (red first: an ASAN abort
  on a UINT64_MAX count); pinned with a decoder test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E228Y7uZ8QdznAjEnrXKSz
The first development pre-release of the data-processor host contract;
changelog entries stay under [Unreleased] until the final 0.37.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E228Y7uZ8QdznAjEnrXKSz
@facontidavide
facontidavide force-pushed the feat/data-processor-host-contract branch from f6b84ad to fe25418 Compare October 18, 2026 16:46
The floor check read CHANGELOG.md, VERSION, the surface snapshot and the
headers with the platform default encoding, which is cp1252 on Windows and
fails on the changelog's UTF-8 text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E228Y7uZ8QdznAjEnrXKSz
@facontidavide
facontidavide merged commit d4ee6cc into main Oct 18, 2026
7 checks passed
@facontidavide
facontidavide deleted the feat/data-processor-host-contract branch October 18, 2026 17:13
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.

1 participant