@zhuoyuezs/ml-platform 0.1.8 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +7 -3
  2. package/package.json +1 -1
  3. package/checksums.json +0 -155
  4. package/release.json +0 -52
  5. package/runtime/business-client/README.md +0 -78
  6. package/runtime/business-client/package-lock.json +0 -19
  7. package/runtime/business-client/package.json +0 -23
  8. package/runtime/business-client/src/catalog.js +0 -206
  9. package/runtime/business-client/src/cli.js +0 -533
  10. package/runtime/business-client/src/config.js +0 -56
  11. package/runtime/business-client/src/http.js +0 -254
  12. package/skills/feature-management/SKILL.md +0 -479
  13. package/skills/feature-management/agents/openai.yaml +0 -4
  14. package/skills/feature-management/assets/catalog-template/catalog.json +0 -23
  15. package/skills/feature-management/assets/catalog-template/datasets/example_temperature_training.v1.json +0 -40
  16. package/skills/feature-management/assets/catalog-template/feature_sets/example_temperature_core.v1.json +0 -14
  17. package/skills/feature-management/assets/catalog-template/features/example_temperature_mean_5m.v1.json +0 -28
  18. package/skills/feature-management/assets/catalog-template/operator_package/pyproject.toml +0 -12
  19. package/skills/feature-management/assets/catalog-template/operator_package/src/business_feature_operator_template/__init__.py +0 -39
  20. package/skills/feature-management/assets/catalog-template/operator_package/tests/test_operator.py +0 -83
  21. package/skills/feature-management/assets/catalog-template/operators/example_temperature_features.v1.json +0 -58
  22. package/skills/feature-management/assets/catalog-template/parameters/example_temperature.v1.json +0 -58
  23. package/skills/feature-management/references/commands.md +0 -358
  24. package/skills/feature-management/references/contracts.md +0 -719
  25. package/skills/feature-management/references/operator-authoring.md +0 -175
  26. package/skills/feature-management/references/platform-capability-guide.md +0 -75
  27. package/skills/feature-management/references/supervised-datasets.md +0 -101
  28. package/skills/model-lifecycle-management/SKILL.md +0 -38
  29. package/skills/model-lifecycle-management/agents/openai.yaml +0 -4
  30. package/skills/model-lifecycle-management/references/discovery.md +0 -89
  31. package/skills/model-lifecycle-management/references/evaluation.md +0 -172
  32. package/skills/model-lifecycle-management/references/packaging.md +0 -51
  33. package/skills/model-lifecycle-management/references/training-contracts.md +0 -139
  34. package/skills/model-lifecycle-management/references/training.md +0 -81
@@ -1,719 +0,0 @@
1
- # Current Contract Reference
2
-
3
- Use this reference for the implemented V1 schemas. Prefer the running CLI and `src/data_platform_demo/schemas.py` when they disagree with historical design documents.
4
-
5
- ## Contents
6
-
7
- 1. Naming and versioning
8
- 2. Projects and namespacing
9
- 3. Parameter
10
- 4. Operator
11
- 5. Feature
12
- 6. FeatureSet
13
- 7. DatasetManifest
14
- 8. Catalog
15
- 9. Cross-object validation
16
-
17
- ## Naming And Versioning
18
-
19
- - Use identifiers matching `^[A-Za-z0-9][A-Za-z0-9_-]*$` and at most 128 characters.
20
- - Prefer lowercase snake_case business identifiers.
21
- - Use explicit immutable versions such as `v1`, `v2`.
22
- - Create a new version when any semantic field changes.
23
- - Keep credentials in the configured profile or server Secret, never in an asset.
24
-
25
- ## Projects And Namespacing
26
-
27
- - Every registry asset is namespaced by a `project`. Its true registry key is
28
- `project/name:version`, so `name:version` alone is unique only within one
29
- project; the same `name:version` may exist independently under other projects.
30
- - The platform ships a built-in `default` project. When no project is given,
31
- assets are created and read under `default`.
32
- - Conceptually a project is a namespace, not an attribute of the asset. In the
33
- current implementation, however, the project a Parameter/Operator/Feature/
34
- FeatureSet/DatasetManifest is **created** under comes from the optional
35
- `project` field in its own JSON spec (it defaults to `default` when omitted).
36
- So to place an asset in a non-default project, set `"project": "<name>"` in the
37
- spec before `apply`.
38
- - For a confirmed non-default target, include that field in every draft
39
- Parameter, Operator, Feature, Feature input, FeatureSet, and DatasetManifest;
40
- do not rely on Pydantic's `default` fallback during authoring.
41
- - Reads and filters, by contrast, take the project out-of-band: `list-* --project`
42
- and the leading project argument of the artifact commands. `apply --project`
43
- scopes the comparison snapshot that publication is validated against.
44
- - All references inside one catalog resolve within a single project. Cross-project
45
- references (a Feature in project A depending on a Parameter in project B) are
46
- rejected by the platform.
47
- - Immutability is per project: publishing `name:v1` into project A never touches
48
- `name:v1` in project B.
49
-
50
- ## Parameter
51
-
52
- Required shape:
53
-
54
- ```json
55
- {
56
- "schema_version": "ml_data_platform.parameter/v1",
57
- "name": "hot_blast_pressure",
58
- "display_name": "Hot blast pressure",
59
- "version": "v1",
60
- "data_type": "time_series",
61
- "unit": "kPa",
62
- "expected_frequency": "5s",
63
- "source": {},
64
- "time_semantics": {
65
- "event_time_field": "event_time",
66
- "ingested_at_field": "ingested_at",
67
- "timezone": "Asia/Shanghai",
68
- "availability": {
69
- "strategy": "source_field",
70
- "field": "ingested_at",
71
- "accuracy": "exact"
72
- }
73
- },
74
- "availability_sla": {
75
- "max_delay": "PT10M"
76
- },
77
- "value_field": "value",
78
- "quality_rules": {
79
- "valid_range": [0.0, 450.0],
80
- "allow_missing": true,
81
- "rules": [
82
- {
83
- "id": "finite_source_values",
84
- "stage": "normalized_source",
85
- "check": {"type": "finite"},
86
- "acceptance": {"max_violation_rate": 0.0},
87
- "enforcement": "fail"
88
- }
89
- ]
90
- },
91
- "owner": "business_owner"
92
- }
93
- ```
94
-
95
- Supported source modes are `direct_column`, `sql`, and `derived`. Prefer `direct_column` for ordinary business parameters.
96
-
97
- For `sql`, `source.sql` must contain one statement without a trailing semicolon,
98
- and named placeholders must be supplied through `source.params`; `start` and
99
- `end` are reserved runtime window parameters. `source.parameters` is not a
100
- supported field.
101
-
102
- `availability.strategy` 支持 `source_field`、`fixed_delay` 和 `unsupported`。只有一个
103
- 事件时间列时,可以使用经过数据 owner 确认的固定延迟上界,例如
104
- `{"strategy":"fixed_delay","delay":"PT10M","accuracy":"upper_bound"}`。
105
- 当前严格 `as_of` 只接受 `exact` 或有合同支撑的 `upper_bound`;修改这些语义需要发布
106
- 新的 Parameter 业务版本。
107
-
108
- An optional `rounding` section states the fixed-point precision the platform
109
- enforces at the Parameter boundary:
110
-
111
- ```json
112
- "rounding": {"mode": "half_up", "decimals": 2}
113
- ```
114
-
115
- Fixing only the decimal count leaves the tie case to whichever engine evaluates
116
- it: PostgreSQL `ROUND` on `numeric` rounds halves away from zero (5.625 becomes
117
- 5.63) while Python and NumPy round halves to even (5.625 becomes 5.62). A single
118
- 0.01 difference spreads through rolling windows into many derived Features, so
119
- declare both fields. `mode` is `half_up` or `half_even`.
120
-
121
- The platform applies the rule with deterministic decimal quantization after the
122
- adapter produces the standard metric frame and before quality checks, source
123
- cache publication/replay, and Operator execution. Prefer an unrounded source
124
- expression. A source that already applies the same rule is accepted because the
125
- second application is idempotent, but an upstream path that already discarded a
126
- tie with a different rule cannot be repaired downstream. Do not repeat the same
127
- Parameter-level rounding in an Operator. Changing the mode, decimals, or stage
128
- requires a new Parameter version and the affected reverse-dependency closure.
129
-
130
- PostgreSQL direct-column example:
131
-
132
- ```json
133
- {
134
- "adapter": "postgresql_direct",
135
- "mode": "direct_column",
136
- "schema": "process_data",
137
- "table": "sensor_readings",
138
- "time_column": "event_time",
139
- "value_column": "hot_blast_pressure",
140
- "metric_name": "hot_blast_pressure",
141
- "unit_column": "unit",
142
- "filters": {"furnace_id": "BF12"}
143
- }
144
- ```
145
-
146
- InfluxDB direct-column example:
147
-
148
- ```json
149
- {
150
- "adapter": "influxdb_direct",
151
- "mode": "direct_column",
152
- "measurement": "blast_furnace",
153
- "field": "hot_blast_pressure",
154
- "metric_name": "hot_blast_pressure",
155
- "tags": {"furnace_id": "BF12"}
156
- }
157
- ```
158
-
159
- Do not put DSNs, access keys, tokens, passwords, or connection endpoints containing credentials in `source`.
160
-
161
- `quality_rules.rules` defines immutable single-Parameter assertions. Supported
162
- checks are `not_null`, `finite`, `range`, and `max_gap`. `stage` is one of
163
- `normalized_source`, `post_preprocess`, or `aligned_grid`; execution follows
164
- that order. `acceptance` controls evidence thresholds, while `enforcement`
165
- chooses `observe`, `warn`, or `fail`. Keep cross-Parameter assertions out of a
166
- Parameter version and declare them in DatasetManifest `parameter_relationships`.
167
-
168
- `quality_rules.missing_detection` controls source-observation gap detection and
169
- is independent from value validity. It defaults to `disabled`, which is the
170
- safe behavior for irregular event Parameters such as tapping events or silicon
171
- samples. Use `fixed_frequency` only when `expected_frequency` is a real fixed
172
- duration. For irregular Parameters, use an explicit `max_gap` or an
173
- `interval_multiplier` over a `mean` or `median` observed interval:
174
-
175
- ```json
176
- "missing_detection": {
177
- "mode": "interval_multiplier",
178
- "multiplier": 10,
179
- "baseline": "median",
180
- "min_observations": 2
181
- }
182
- ```
183
-
184
- `max_gap` accepts positive short durations including second precision (for
185
- example `30s`, `10min`, or `2h`). Detection runs on the normalized,
186
- post-preprocess source frame before target-grid interpolation, and its source
187
- windows are recorded in the build's `missing_windows.json` artifact.
188
-
189
- ## Operator
190
-
191
- Feature Operator draft shape:
192
-
193
- ```json
194
- {
195
- "schema_version": "ml_data_platform.operator/v1",
196
- "name": "pressure_features",
197
- "version": "v1",
198
- "type": "feature",
199
- "function_hash": "pressure_features.formulas.v1",
200
- "entrypoint": "pressure_features:compute_features",
201
- "code_hash": null,
202
- "package_uri": null,
203
- "code_artifact": null,
204
- "input_schema": {
205
- "parameters": ["hot_blast_pressure:v1"]
206
- },
207
- "output_schema": {
208
- "columns": ["event_time", "pressure_mean_5m"]
209
- },
210
- "config_schema": {
211
- "properties": {
212
- "window": {"type": "string"}
213
- },
214
- "required": ["window"]
215
- },
216
- "runtime": {"engine": "python_entrypoint", "network": "none"},
217
- "resources": {"timeout_seconds": 60},
218
- "deterministic": true,
219
- "supports_batch": true,
220
- "supports_online": false,
221
- "owner": "business_owner"
222
- }
223
- ```
224
-
225
- For a catalog wheel entry, `catalog.json` supplies the package file. Publication calculates and stores `code_hash`, `package_uri`, and `code_artifact`.
226
-
227
- Treat `pythonpath://` as platform-owned example behavior. Publish formal business Operators as immutable pure-Python wheels.
228
-
229
- ## Feature
230
-
231
- ```json
232
- {
233
- "schema_version": "ml_data_platform.feature/v1",
234
- "name": "pressure_mean_5m",
235
- "version": "v1",
236
- "inputs": [
237
- {"parameter": "hot_blast_pressure", "version": "v1"}
238
- ],
239
- "operator": "pressure_features",
240
- "operator_version": "v1",
241
- "config": {"window": "5min"},
242
- "output_column": "pressure_mean_5m",
243
- "output_dtype": "float64",
244
- "offline_online_supported": false,
245
- "owner": "business_owner",
246
- "description": "Mean pressure over the causal five-minute window."
247
- }
248
- ```
249
-
250
- Rules:
251
-
252
- - Define exactly one public output column per Feature.
253
- - Keep `inputs` nonempty, unique, versioned, and formula-exact.
254
- - Require the referenced Operator to have `type=feature`.
255
- - Require `output_column` to appear in the Operator output schema.
256
- - Use `name` as the final dataset column; use `output_column` as the Operator's physical result column.
257
-
258
- ## FeatureSet
259
-
260
- ```json
261
- {
262
- "schema_version": "ml_data_platform.feature_set/v1",
263
- "name": "pressure_core",
264
- "version": "v1",
265
- "features": [
266
- {"name": "pressure_mean_5m", "version": "v1"}
267
- ],
268
- "owner": "business_owner",
269
- "description": "Ordered pressure model inputs."
270
- }
271
- ```
272
-
273
- The list must be nonempty, unique, and ordered exactly as consumers expect.
274
-
275
- ## DatasetManifest
276
-
277
- ```json
278
- {
279
- "schema_version": "ml_data_platform.dataset_manifest/v1",
280
- "dataset_id": "pressure_training_20260701_20260702",
281
- "dataset_version": "v1",
282
- "mode": "training",
283
- "read_policy": "snapshot",
284
- "time_range": {
285
- "start": "2026-07-01T00:00:00+08:00",
286
- "end": "2026-07-02T00:00:00+08:00",
287
- "grid": "10min"
288
- },
289
- "prediction": {
290
- "horizon": "10min"
291
- },
292
- "parameters": [],
293
- "parameter_missing_policies": [
294
- {
295
- "parameter": "hot_blast_pressure",
296
- "version": "v1",
297
- "project": "default",
298
- "policy": "report_only"
299
- }
300
- ],
301
- "feature_set": {"name": "pressure_core", "version": "v1"},
302
- "preprocess": [],
303
- "output": {
304
- "format": "parquet",
305
- "include_stats": true,
306
- "include_lineage": true,
307
- "engine": "chronon"
308
- }
309
- }
310
- ```
311
-
312
- Current schema rules:
313
-
314
- - Accept exactly one `feature_set`.
315
- - Reject old top-level `features` and `recipe` fields.
316
- - Use a half-open `[start, end)` target grid. `start` must be earlier than
317
- `end`, the window must span at least one grid step, both bounds must agree on
318
- timezone awareness, and `grid` must be one of `1min`, `5min`, `10min`,
319
- `15min`, `20min`, `30min`, `1h`, `2h`, `1d`. Violations are rejected at
320
- submission with a field-level error on `time_range`; no Job is created.
321
- - Require `as_of` only with `read_policy=as_of`.
322
- - Allow `snapshot_id` only with `read_policy=snapshot`.
323
- - Use `parameters` only to request explicit Parameter output columns.
324
- - Declare one dataset-wide `prediction` contract for forecast Features. `horizon`
325
- accepts non-negative whole-minute durations such as `0min`, `10min`, or `2h`.
326
- The platform always derives `cutoff_times = target_times - horizon` and records
327
- that derivation in resolved lineage; it is not a user-configurable policy.
328
-
329
- ### Optional sections
330
-
331
- These ten sections may be omitted: `source_read`, `realtime_fetch`,
332
- `abnormal_windows`, `target`, `prediction`, `parameter_relationships`,
333
- `parameter_missing_policies`, `rowset`, `rowset_splits`, and `endpoint_policy`.
334
- Omitting a section keeps older manifest hashes stable.
335
- Sections containing only inert empty values are also treated as unset, but a
336
- declared `realtime_fetch` policy contains meaningful defaults and changes the
337
- manifest hash. Declaring `endpoint_policy` also changes the manifest hash.
338
-
339
- Each explicit `parameters` request may set `missing_policy`. A
340
- `parameter_missing_policies` entry is reserved for a Parameter used only through
341
- the selected FeatureSet's input closure; it must not also appear in
342
- `parameters`. Its exact V1 shape is `{"parameter": "<name>", "version":
343
- "<version>", "project": "<project>", "policy": "<policy>"}`. Do not use
344
- the `ParameterRequest` field names `name` or `missing_policy` in this list. For
345
- a confirmed non-default target, set `project` explicitly instead of relying on
346
- its `default` fallback. The policies are:
347
-
348
- - `report_only`: retain all target rows and report source gaps.
349
- - `fail_on_requested_range_gap`: fail when a detected gap affects the requested
350
- target range.
351
- - `drop_target_and_lookback`: remove the target rows in the detected window and
352
- the following rows whose resolved Feature input lookback still reaches into
353
- that window. The same merged row mask is applied to both
354
- `feature_dataset.parquet` and `parameter_dataset.parquet`.
355
-
356
- The default `report_only` value is omitted from canonical manifest identity so
357
- manifests published before `missing_policy` existed keep their artifact hash.
358
- When `endpoint_policy` is declared, `drop_target_and_lookback` is rejected:
359
- endpoint eligibility requires the canonical datasets and
360
- `rowset_membership.parquet` to retain the same complete candidate rowset. Use
361
- `report_only` or `fail_on_requested_range_gap` for those manifests.
362
-
363
- The resolved per-Parameter windows, policy, affected rows, and dropped rows are
364
- written to `missing_windows.json` and summarized in `validation.json`. A
365
- partitioned build with a drop policy must be materialized as one full source
366
- window so the same global gap contract is used for every rowset.
367
-
368
- `source_read` pages the source reads. A long target window read in one request
369
- can exceed the driver read timeout; chunking keeps one manifest covering the
370
- whole window instead of splitting it into per-day manifests. Chunk windows,
371
- attempts, rows, and elapsed time land in
372
- `latency_stats.fetch_timing_summary`.
373
-
374
- ```json
375
- "source_read": {
376
- "chunk_days": 1,
377
- "timeout_seconds": 60,
378
- "max_retries": 3,
379
- "retry_sleep_seconds": 2
380
- }
381
- ```
382
-
383
- `realtime_fetch` declares the policy for one low-latency causal-cutoff read:
384
-
385
- ```json
386
- "realtime_fetch": {
387
- "max_workers": 4,
388
- "min_lookback": "1h",
389
- "validate_freshness": true,
390
- "max_source_lag_hours": 3.0,
391
- "max_source_lag_hours_by_parameter": {},
392
- "allow_missing": false,
393
- "allow_tail_edge_missing": true,
394
- "max_tail_edge_missing": 1,
395
- "tail_edge_missing_max_age_minutes": 65.0,
396
- "allow_internal_gap_fill": true,
397
- "internal_gap_fill_max_age_hours": 5.0
398
- }
399
- ```
400
-
401
- `POST /inference-data/fetch` serves one cutoff without running a batch build,
402
- writing parquet, or publishing a DatasetArtifact. It derives
403
- `target_time = cutoff_time + prediction.horizon`, filters every source to
404
- `timestamp <= cutoff_time`, and derives each Parameter read window from the
405
- selected Operators' `input_schema.history_requirements`. The response carries
406
- `contract.manifest_hash` and a replayable `read_policy=as_of` manifest instead
407
- of an artifact.
408
-
409
- Keep the default strict: `allow_missing=false`. Tail-edge and bounded internal
410
- gap-fill tolerance may return a row, but the response must mark
411
- `freshness.degraded=true` and include validation evidence. A freshness or
412
- missing-data condition that may recover returns HTTP `503`; an invalid contract
413
- returns HTTP `400`. Declaring `realtime_fetch` changes the manifest hash; leaving
414
- it unset preserves older contract hashes and uses environment fallbacks.
415
-
416
- `abnormal_windows` excludes target windows whose data must not be trusted. The
417
- policy and the lookback apply to the whole window set, not to individual
418
- windows: a dataset is either a filtered rowset or a marked full rowset.
419
-
420
- ```json
421
- "abnormal_windows": {
422
- "policy": "drop_target_and_lookback",
423
- "windows": [
424
- {"start": "2026-05-25T19:30:00+08:00",
425
- "end": "2026-05-26T09:50:00+08:00",
426
- "reason": "shutdown"}
427
- ]
428
- }
429
- ```
430
-
431
- | policy | Meaning |
432
- |-|-|
433
- | `drop_target_and_lookback` | Drop rows in a window, and rows after it whose feature input lookback reaches back in. Default. |
434
- | `drop_target_only` | Drop only rows inside a window. |
435
- | `mark_only` | Drop nothing; record what is affected in lineage and validation. |
436
-
437
- How far past a window to drop is derived from the widest resolved Operator history
438
- requirement. Cutoff-relative history contributes `prediction.horizon + lookback`;
439
- target-time-relative history contributes its declared lookback directly. The
440
- maximum is used for source fetch and abnormal-window propagation. Do not restate
441
- that number unless you want it asserted: an optional `input_lookback_hours` is
442
- checked against the derived value and a mismatch is rejected. Note that a window
443
- near the end of the target range can have its lookback clipped by the range end,
444
- and a window close to the end can remove most remaining rows.
445
-
446
- `target` states which instant the label is aligned to, so reviewing a manifest
447
- shows the target definition rather than only a Parameter version string. When
448
- the target is a Parameter, keep its complete versioned reference **inside**
449
- `target.parameter`; do not flatten `version` or `project` onto `target`.
450
- `si_time_source` is `weight_time` or `dispatch_time`. `interpolation` is an
451
- object with a `method`, never a bare string.
452
-
453
- ```json
454
- "target": {
455
- "parameter": {
456
- "parameter": "hot_metal_si",
457
- "version": "v1",
458
- "project": "replace_with_project"
459
- },
460
- "si_time_source": "weight_time",
461
- "offset_minutes": 0,
462
- "interpolation": {"method": "linear"}
463
- }
464
- ```
465
-
466
- Allowed interpolation methods are `none`, `linear`, and `forward_fill`. A
467
- business phrase such as "allow and report target nulls" belongs in an existing
468
- Parameter missing policy, Feature configuration, or the semantic review; it is
469
- not a free-form `target` or DatasetManifest field.
470
-
471
- The declaration is checked against the selected Si Parameter, but only for
472
- `direct_column` reads, which name their time column in the contract. A `sql` or
473
- `derived` Parameter can apply an offset inside its query text, so those are
474
- recorded as unverified with a reason and a warning — do not read the absence of
475
- an error as confirmation that the label instant matches.
476
-
477
- `parameter_relationships` evaluates manifest-scoped assertions over exact
478
- Parameter versions after preprocessing and grid alignment:
479
-
480
- ```json
481
- "parameter_relationships": [
482
- {
483
- "id": "temperature_pair_consistency",
484
- "inputs": [
485
- {"alias": "left", "parameter": "temperature_left", "version": "v1"},
486
- {"alias": "right", "parameter": "temperature_right", "version": "v1"}
487
- ],
488
- "alignment": {"method": "last_before_or_at", "tolerance": "PT5M"},
489
- "rules": [
490
- {
491
- "id": "bounded_difference",
492
- "stage": "aligned_grid",
493
- "check": {
494
- "type": "absolute_difference",
495
- "left": "left",
496
- "right": "right",
497
- "max_difference": 10.0
498
- },
499
- "acceptance": {"min_comparable_rate": 0.9},
500
- "enforcement": "fail"
501
- }
502
- ]
503
- }
504
- ]
505
- ```
506
-
507
- Relationship checks are `compare`, `absolute_difference`, `ratio_range`,
508
- `sum_equals`, and `co_presence`. `exact` compares only exact grid timestamps.
509
- `last_before_or_at` is causal. With a tolerance, each relationship input fetch
510
- starts at `manifest.start - tolerance`; without one, lookup is bounded to data
511
- inside the manifest target window and does not trigger an unbounded history read.
512
-
513
- `rowset` selects which instants become dataset rows:
514
-
515
- ```json
516
- "rowset": {
517
- "strategy": "fixed_grid",
518
- "grid": "10min"
519
- }
520
- ```
521
-
522
- Supported strategies are `event_driven`, `fixed_grid`, and
523
- `exact_horizon_measured`. `fixed_grid` requires `grid`;
524
- `exact_horizon_measured` requires `horizon` and accepts a `tolerance`. An
525
- optional rowset `time_range` overrides the manifest range for that strategy.
526
- Use `rowset_splits` instead of `rowset` when training needs named strategies
527
- such as `support`, `validation`, and `test`; it is mutually exclusive with the
528
- single `rowset`. Split ranges must not overlap and must cover every emitted
529
- target row. A split-level `fixed_grid.grid` is an explicit assertion and must
530
- equal `DatasetManifest.time_range.grid`; it cannot silently resample one split.
531
-
532
- `rowset_splits` defines labels inside one dataset contract. It does not by
533
- itself publish three DatasetManifests or three DatasetArtifacts. A build emits
534
- one canonical Feature dataset and one Parameter dataset, with `rowset_split`
535
- identifying each candidate row. Publish separate manifests only when separate
536
- artifact identities, permissions, retention, or build lifecycles are required.
537
-
538
- ### Endpoint eligibility and membership
539
-
540
- Use `endpoint_policy` when the platform must retain the complete canonical
541
- rowset while declaring which target endpoints are safe for model training or
542
- evaluation. Candidate construction and eligibility evaluation are two stages:
543
-
544
- ```text
545
- time_range + rowset_splits -> candidate target rows and rowset labels
546
- candidate rows + endpoint_policy -> eligibility membership and reasons
547
- ```
548
-
549
- The policy is scoped to named rowsets, so one manifest can apply it to
550
- `validation` and `test` while leaving `training` as pass-through:
551
-
552
- ```json
553
- {
554
- "prediction": {"horizon": "2h"},
555
- "rowset_splits": {
556
- "training": {
557
- "strategy": "fixed_grid",
558
- "grid": "10min",
559
- "time_range": {
560
- "start": "2025-01-01T00:00:00+08:00",
561
- "end": "2026-01-01T00:00:00+08:00",
562
- "grid": "10min"
563
- }
564
- },
565
- "validation": {
566
- "strategy": "fixed_grid",
567
- "grid": "10min",
568
- "time_range": {
569
- "start": "2026-01-01T00:00:00+08:00",
570
- "end": "2026-04-01T00:00:00+08:00",
571
- "grid": "10min"
572
- }
573
- },
574
- "test": {
575
- "strategy": "fixed_grid",
576
- "grid": "10min",
577
- "time_range": {
578
- "start": "2026-04-01T00:00:00+08:00",
579
- "end": "2026-06-01T00:00:00+08:00",
580
- "grid": "10min"
581
- }
582
- }
583
- },
584
- "abnormal_windows": {
585
- "policy": "mark_only",
586
- "windows": [
587
- {
588
- "start": "2026-03-20T00:00:00+08:00",
589
- "end": "2026-03-28T00:00:00+08:00",
590
- "reason": "shutdown"
591
- }
592
- ]
593
- },
594
- "endpoint_policy": {
595
- "scope": ["validation", "test"],
596
- "policy": "exclude_target_or_input_overlap",
597
- "anchor": "cutoff",
598
- "context_lookback": "28h",
599
- "window_source": "abnormal_windows",
600
- "membership_column": "endpoint_eligible",
601
- "emit_reason": true
602
- }
603
- }
604
- ```
605
-
606
- `endpoint_policy` requires `prediction.horizon`, at least one abnormal window,
607
- and `abnormal_windows.policy=mark_only`. Every `scope` name must exist in
608
- `rowset_splits`; without splits, the only valid scope is `default`. The
609
- membership column must not collide with reserved or materialized dataset
610
- columns.
611
-
612
- Do not derive `context_lookback` from an Operator automatically. Operator
613
- history requirements answer how much source history is needed to compute a
614
- Feature. `endpoint_policy.context_lookback` answers how much model input context
615
- must be free of abnormal observations before an endpoint is usable. They can be
616
- different and are versioned independently in the manifest contract.
617
-
618
- For target time `t`, prediction horizon `h`, context lookback `L`, and abnormal
619
- window `[a, b)`, evaluation uses:
620
-
621
- ```text
622
- cutoff c = t - h
623
- model context = [c - L, c]
624
- target overlap = a <= t < b
625
- context overlap = c >= a and c - L < b
626
- eligible = not (target overlap or context overlap), within policy scope
627
- ```
628
-
629
- The abnormal window is half-open and the cutoff side is closed. Its effective
630
- exclusion tail ends at `b + h + L`; a target exactly at that instant is
631
- eligible. A scoped row receives `target_in_abnormal_window`,
632
- `input_context_overlap`, or both in `reason_codes`. Overlapping abnormal windows
633
- do not duplicate a reason code, but all matching `window_id` values are retained.
634
-
635
- The build does not delete ineligible rows. It writes
636
- `rowset_membership.parquet` with one row per candidate target:
637
-
638
- | Column | Meaning |
639
- |-|-|
640
- | `target_time` | Candidate model target time. |
641
- | `cutoff_time` | `target_time - prediction.horizon`. |
642
- | `rowset_name` | Named split, or `default` without `rowset_splits`. |
643
- | `candidate` | Always true for a row emitted by the current candidate resolver. |
644
- | `policy_applied` | Whether `rowset_name` belongs to `endpoint_policy.scope`. |
645
- | `eligible` | Overlap result; pass-through rows outside scope remain true. |
646
- | `reason_codes` | Stable list of exclusion reasons; empty when eligible or `emit_reason=false`. |
647
- | `matched_window_ids` | Stable IDs of all matching abnormal windows. |
648
- | `effective_exclusion_end` | Latest matched window end plus horizon and context lookback. |
649
- | `policy_hash` | Hash of the resolved endpoint policy and normalized windows. |
650
- | `manifest_hash` | Dataset contract identity used for the build. |
651
-
652
- For compatibility, `feature_dataset.parquet` and `parameter_dataset.parquet`
653
- also receive the configured `membership_column`. Its value is:
654
-
655
- ```text
656
- endpoint_eligible = policy_applied AND eligible
657
- ```
658
-
659
- Consequently, a row outside `scope` is a pass-through membership row with
660
- `eligible=true`, but its compatibility column is false. Use the membership
661
- artifact when consumers need to distinguish "not evaluated" from "evaluated
662
- and rejected"; do not interpret the compatibility column alone as a global
663
- data-quality flag.
664
-
665
- The resolved manifest, `quality_report.json`, `validation.json`, and lineage
666
- record the resolved policy and counts. Reconcile at least
667
- `candidate_rows`, `policy_applied_rows`, `evaluated_eligible_rows`,
668
- `ineligible_rows`, `pass_through_rows`, `reason_counts`, and per-rowset counts.
669
- `eligible_rows` includes true pass-through rows, whereas
670
- `evaluated_eligible_rows` counts only scoped eligible rows. Single and
671
- partitioned builds of the same manifest must produce equivalent merged
672
- membership, counts, lineage, and `policy_hash`.
673
-
674
- For the EXP301 validation contract, `h=2h` and `L=28h`, so the exclusion tail is
675
- 30 hours after each abnormal-window end. The fixed 10-minute validation rowset
676
- contains 20,853 candidates: 19,263 are evaluated eligible and 1,590 are
677
- ineligible. Treat those figures as an example-specific regression contract,
678
- not platform defaults.
679
-
680
- `endpoint_policy` selects historical dataset rows and is not applied to
681
- `POST /inference-data/fetch`. Realtime inference reports it as not applicable
682
- and omits it from the replay manifest.
683
-
684
- The current public schema still records `output.engine=chronon`, while the first-phase bridge executes Python Feature Operators and does not run Chronon compile/backfill. Report this limitation accurately.
685
-
686
- ## Catalog
687
-
688
- ```json
689
- {
690
- "schema_version": "ml_data_platform.catalog/v1",
691
- "name": "pressure_feature_catalog",
692
- "version": "v1",
693
- "parameters": ["parameters/hot_blast_pressure.v1.json"],
694
- "operators": [
695
- {
696
- "spec": "operators/pressure_features.v1.json",
697
- "package": "operator_package/dist/pressure_features-1.0.0-py3-none-any.whl"
698
- }
699
- ],
700
- "features": ["features/pressure_mean_5m.v1.json"],
701
- "feature_sets": ["feature_sets/pressure_core.v1.json"],
702
- "datasets": ["datasets/pressure_training_20260701_20260702.v1.json"]
703
- }
704
- ```
705
-
706
- All paths must be unique, relative, remain inside the catalog directory, exist, and have the expected `.json` or `.whl` suffix.
707
-
708
- ## Cross-Object Validation
709
-
710
- Require:
711
-
712
- - every Feature input Parameter to exist in the target Registry or the same catalog;
713
- - every Feature Operator to exist and declare `type=feature`;
714
- - every Feature input to be allowed by the Operator input schema when that schema declares parameters;
715
- - every Feature physical output to be declared by the Operator output schema;
716
- - every FeatureSet reference to exist;
717
- - every Dataset FeatureSet and preprocess Operator to exist;
718
- - every Dataset relationship input Parameter to exist;
719
- - every new immutable version to differ by key from conflicting registered content.