forked from chiki2bum2/SniperGold_ML
1429 lines
73 KiB
Markdown
1429 lines
73 KiB
Markdown
# P3_DATA_ENGINE_V1_SPEC — SNIPERGOLD DATA ENGINE v1
|
|
## Data Engine Architecture & Clean Reprocessing Specification
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Document ID | P3_DATA_ENGINE_V1_SPEC |
|
|
| Version | 1.1.0 (amended by P3-DATA-ENGINE-004; Appendix C authoritative for the amended clauses) |
|
|
| Status | DESIGN FROZEN + CONTROLLED AMENDMENT APPLIED (P3-DE-004); implemented & qualified |
|
|
| Session type | Amendment: source-grammar correction + implementation re-qualification |
|
|
| Authorized execution | Qualification only (NO real-data ingestion; NO G-14) |
|
|
| Classification | Internal research infrastructure |
|
|
|
|
---
|
|
|
|
## 0. Executive Summary
|
|
|
|
This document defines SNIPERGOLD DATA ENGINE v1 ("the Engine"): a standalone,
|
|
headless Python system that performs a clean, deterministic, resumable full
|
|
reprocessing of the authoritative XAUUSD Tickstory tick source
|
|
(`XAUUSD_mt5_ticks.csv`, current known size 34,473,661,010 bytes).
|
|
|
|
The Engine is designed so that the entire source processing lifecycle can
|
|
complete with ChatGPT closed. The AI Assistant role is limited to research
|
|
design, governance, audit, and interpretation. Python is the deterministic
|
|
data processor.
|
|
|
|
This session produced this specification only. No real-data ingestion was
|
|
performed, no checkpoint was modified, and no pipeline code was changed.
|
|
|
|
---
|
|
|
|
## 1. Purpose
|
|
|
|
Produce an implementation-ready architecture for a data engine that:
|
|
|
|
1. certifies the identity of the 34.5 GB raw tick source before any processing;
|
|
2. performs a clean full reprocessing of that source into a canonical,
|
|
columnar, versioned, resumable dataset;
|
|
3. builds deterministic canonical time bars (M1, M5, M15, M30, H1);
|
|
4. builds independently reproducible research datasets from canonical data
|
|
without ever re-reading the raw source;
|
|
5. runs headlessly, survives interruption (including Windows restarts),
|
|
and resumes without AI assistance;
|
|
6. exposes full auditability: every layer has a deterministic identity and
|
|
every artifact is independently verifiable.
|
|
|
|
## 2. Scope
|
|
|
|
The Engine covers:
|
|
|
|
- source certification (size, mtime, full SHA-256, format facts);
|
|
- deterministic line-safe chunking of the raw source;
|
|
- canonical tick parsing, classification, serialization, hashing;
|
|
- parallel canonical tick conversion with deterministic merge semantics;
|
|
- canonical bar aggregation (M1, M5, M15, M30, H1);
|
|
- persistent canonical storage (Parquet by default; see Human Decision Gate G-1);
|
|
- standalone checkpoint / resume with atomic fail-closed transitions;
|
|
- headless CLI (`sniper-data`), config, manifests, evidence and hashing;
|
|
- independent verification tooling, golden test corpus, failure model,
|
|
resource management, completion protocol, clean reprocessing protocol,
|
|
legacy comparison protocol;
|
|
- research dataset builder driven strictly by research configuration.
|
|
|
|
## 3. Non-Goals
|
|
|
|
The Engine MUST NOT:
|
|
|
|
- require an AI Assistant session (or any human) to remain active;
|
|
- make trading decisions, optimize a trading strategy, or select models;
|
|
- chase a "successful" timeframe or perform forecasting;
|
|
- read the legacy checkpoint as its own execution state (legacy checkpoint 759
|
|
is preserved and untouched; the new Engine starts its own lifecycle);
|
|
- reuse the legacy parser/aggregator code as its implementation;
|
|
- silently downgrade certification requirements;
|
|
- auto-recover from material failures by reasoning;
|
|
- use CSV as a default intermediate storage format;
|
|
- write to, delete, or modify any legacy pipeline directories or evidence.
|
|
|
|
## 4. Architecture
|
|
|
|
### 4.1 Logical layers
|
|
|
|
| # | Layer | Responsibility | Immutable contract |
|
|
|---|---|---|---|
|
|
| 0 | RAW SOURCE | immutable Tickstory CSV | never modified by the Engine |
|
|
| 1 | SOURCE CERTIFICATION | identity + format facts | certificate JSON |
|
|
| 2 | CANONICAL TICK REPRESENTATION | raw row → canonical record | canonical schema + hash |
|
|
| 3 | CHUNKING | line-safe byte ranges | chunk map |
|
|
| 4 | PROCESSING MODEL | parallel deterministic conversion | chunk output files |
|
|
| 5 | AGGREGATION | tick → M1/M5/M15/M30/H1 bars | bar files + carries |
|
|
| 6 | PERSISTENT CANONICAL DATASET | Parquet layer | dataset manifest |
|
|
| 7 | CHECKPOINT / RESUME | atomic state | checkpoint + commit journal |
|
|
| 8 | HEADLESS CLI | operator interface | exit codes |
|
|
| 9 | RESEARCH DATASET BUILDER | config-driven derived data | research dataset + manifest |
|
|
| 10 | DATASET MANIFEST | reproducibility record | manifest JSON |
|
|
| 11 | EVIDENCE & HASHING | identity at every layer | evidence manifest |
|
|
| 12 | INDEPENDENT VERIFICATION | non-producer verification | verification report |
|
|
| 13 | GOLDEN TEST CORPUS | adversarial micro-inputs | expected outputs |
|
|
| 14 | FAILURE MODEL | three-class fail-closed behavior | state transitions |
|
|
| 15 | RESOURCE MANAGEMENT | bounded local execution | telemetry |
|
|
| 16 | RUN COMPLETION PROTOCOL | machine-verifiable completion | RUN_COMPLETE.json |
|
|
| 17 | CLEAN REPROCESSING PROTOCOL | the authorized future procedure | step gates |
|
|
|
|
### 4.2 Design invariants (binding)
|
|
|
|
1. **Determinism**: canonical output is a pure function of
|
|
(source bytes, engine version, parser version, algorithm version, config
|
|
snapshot). Worker count, scheduling, result arrival order, disk layout and
|
|
wall-clock time NEVER affect output.
|
|
2. **Fail-closed**: every transition is atomic; on doubt the system stops in a
|
|
documented state. There is no silent fallback and no autonomous
|
|
recovery-by-reasoning.
|
|
3. **Resumable**: any chunk is either fully committed (file + journal
|
|
record + digest) or not committed at all. A hard power-loss leaves at most
|
|
an orphan `.tmp` file that resume deletes and re-dispatches.
|
|
4. **Source immutability**: the Engine opens the source read-only (`rb`),
|
|
never writes it, and re-checks size+mtime at every resume.
|
|
5. **Integer-only canonical math**: the canonical path (parse → serialization →
|
|
aggregation) uses integer arithmetic only. No floating point anywhere in
|
|
canonical ingestion or bar aggregation.
|
|
6. **Append-only outputs**: committed chunk/bar files are never rewritten in
|
|
place. Resume appends or skips; it never mutates committed data.
|
|
7. **Independent verification**: the verifier is a separate implementation
|
|
(different parsing and aggregation code), not a re-run of producer code.
|
|
|
|
### 4.3 Execution topology
|
|
|
|
```
|
|
sniper-data CLI (parent/operator process)
|
|
├── chunkmap scanner (reads source bytes for newline boundaries)
|
|
├── certification (size/mtime/SHA-256 + format sniff)
|
|
├── dispatcher (assigns chunk indices to workers)
|
|
│ ├── worker process 1..N (parse chunk -> chunk parquet + digests)
|
|
│ └── ... (spawn context; no shared state)
|
|
├── commit journal (append-only hash-chained commit records)
|
|
├── checkpoint writer (atomic tmp+rename)
|
|
├── sequential aggregator (canonical ticks -> bars, carry-aware)
|
|
├── research builder (canonical bars -> research datasets)
|
|
├── verifier (independent implementation)
|
|
└── completion/finalization (evidence + RUN_COMPLETE.json)
|
|
```
|
|
|
|
No component talks to a network. No component requires an interactive session.
|
|
|
|
## 5. Data Flow
|
|
|
|
```
|
|
RAW SOURCE (34.5 GB CSV, read-only)
|
|
│ [certify] size, mtime, FULL SHA-256, format facts
|
|
▼
|
|
source_certificate.json (Layer 1)
|
|
│ [chunkmap] newline-scan only
|
|
▼
|
|
chunk_map.json (Layer 3)
|
|
│ [parse+canonicalize, parallel]
|
|
▼
|
|
ticks/chunk_%06d.parquet + malformed/malformed_%06d.jsonl (Layer 2/4/6)
|
|
│ [ordered merge; sequential aggregator, carry-aware]
|
|
▼
|
|
bars/<TF>/wl_%03d.parquet (Layer 5)
|
|
│ [dataset manifest + content digests]
|
|
▼
|
|
dataset manifest (canonical) (Layer 6/10)
|
|
│ [research config]
|
|
▼
|
|
datasets/<dataset_id>/… (Layer 9)
|
|
│ [evidence + hash]
|
|
▼
|
|
evidence.json + RUN_COMPLETE.json (Layer 11/16)
|
|
```
|
|
|
|
Every layer records its inputs (`source_id`, versions) so each stage can be
|
|
recomputed and compared. Research datasets are built from the canonical bar
|
|
layer ONLY — never re-reading the 34 GB raw source.
|
|
|
|
## 6. Component Design
|
|
|
|
### 6.1 Module map (implementation target)
|
|
|
|
```
|
|
engine/
|
|
cli.py # argument parsing, command dispatch, exit codes
|
|
config.py # config load + strict schema validation
|
|
versions.py # canonical version constants (see §26)
|
|
certify.py # Layer 1
|
|
chunkmap.py # Layer 3 (newline scanning; pure function of bytes)
|
|
parse.py # Layer 2 producer parser (optimized, integer-only)
|
|
canonical.py # canonical record serialize/hash (Layer 2)
|
|
worker.py # Layer 4 worker entry point (spawn-safe)
|
|
dispatcher.py # Layer 4 dispatch + retry + telemetry
|
|
journal.py # commit journal (append-only, hash-chained)
|
|
checkpoint.py # Layer 7 atomic checkpoint read/write
|
|
aggregate.py # Layer 5 sequential deterministic aggregator
|
|
storage.py # Layer 6 parquet writers, naming, schema metadata
|
|
dataset_builder.py # Layer 9 research dataset builder
|
|
manifest.py # Layer 10 manifest build/verify
|
|
evidence.py # Layer 11 hashing + evidence manifest
|
|
run_complete.py # Layer 16 completion protocol
|
|
verify/ # Layer 12 INDEPENDENT verifier (separate impl)
|
|
__init__.py
|
|
vparse.py # separate CSV/line parser written from spec
|
|
vaggregate.py # separate aggregator written from spec
|
|
vinvariants.py # invariant checks
|
|
vcompare.py # differential comparison
|
|
tests/golden/ # Layer 13 corpus + expected outputs
|
|
cases/ expected/
|
|
tests/mutations/ # mutation test driver + cases
|
|
```
|
|
|
|
### 6.2 Responsibilities matrix
|
|
|
|
| Component | Reads | Writes | Fails closed by |
|
|
|---|---|---|---|
|
|
| certifier | source (rb) | `certification/source_certificate.json` | never writing source; recording status |
|
|
| chunkmapper | source (rb) | `chunkmap/chunkmap-<id>.json` | deterministic scan; no parsing |
|
|
| worker | source range, chunk map | `staging/chunk_%06d.parquet.tmp` → `ticks/chunk_%06d.parquet`; `malformed/...` | one chunk per process; tmp+rename; digest computed before rename |
|
|
| dispatcher | chunk map, journal | journal entries, `progress.json` | bounded retries then FAILED |
|
|
| aggregator | ticks parquet (ordered) | `bars/<TF>/wl_%03d.parquet`; carries in checkpoint | append-only; carry continuity check on resume |
|
|
| verifier | source ranges, artifacts | `verification/...` | never reusing producer parse/aggregate code |
|
|
| finalizer | everything | `evidence.json`, `RUN_COMPLETE.json` | only from COMPLETED + verified state |
|
|
|
|
### 6.3 Configuration
|
|
|
|
Config is a strict-schema JSON file passed at `init`. Fields relevant to data
|
|
identity: `source_path`, `source_tz_offset_minutes`, `has_volume`,
|
|
`timeframes`. Fields relevant to operation: `workers_requested`,
|
|
`chunk_bytes_nominal`, `workload_bytes_nominal`, `checkpoint_every_workload`,
|
|
`retry_limit`, `memory_limits`, `disk_floor_bytes`, `timeouts`. Every config
|
|
field that affects output is captured in the dataset manifest (`config_snapshot`
|
|
+ `config_sha256`). Operational-only fields are excluded from `config_sha256`.
|
|
|
|
## 7. Source Certification (Layer 1)
|
|
|
|
### 7.1 Purpose
|
|
|
|
Before any processing, the Engine certifies that the configured source file is
|
|
the authorized, unmodified Tickstory source.
|
|
|
|
### 7.2 Certificate schema
|
|
|
|
`certification/source_certificate.json`:
|
|
|
|
```
|
|
schema_version "CERT_V1"
|
|
certification_timestamp ISO-8601 UTC
|
|
certification_status FULLY_VERIFIED | VERIFIED_WITH_LIMITATION | FAILED
|
|
source_path absolute path as configured
|
|
source_size_bytes integer, stat size
|
|
source_mtime_ns integer, stat mtime nanoseconds (Windows st_mtime_ns)
|
|
sha256_full hex (only present when the full file was hashed)
|
|
sha256_mode "full" | "not_computed"
|
|
limitation_reason null | enum(see 7.4)
|
|
source_format "csv_tickstory_mt5" | "unrecognized" | "mismatch"
|
|
grammar_id "tickstory_mt5_v1" | "dotted_v1" | null (P3-DE-004: pins the source-adapter preset; init cross-check §8.1)
|
|
line_ending "CRLF" | "LF" | "CR" | "MIXED" (with ratio record)
|
|
encoding detected (BOM-aware) ; "utf8" | "ascii" | other
|
|
timestamp_format_detected "compact_date_time" | "dotted_ms" | "dotted_s" | "unrecognized"
|
|
delimiter "," (only comma supported in v1)
|
|
has_header bool (detection rule §8.3)
|
|
column_layout {"fields":N,"names":[...]} observed; 3/4 => [datetime,bid,ask(,vol)]; 6 => [date,time,bid,ask,last,volume]
|
|
parser_version (constants §26)
|
|
engine_version (constants §26)
|
|
```
|
|
|
|
### 7.3 Certification procedure
|
|
|
|
1. Verify path exists and is a regular file.
|
|
2. Record `size` and `mtime_ns`.
|
|
3. Stream the file once, `rb`, in fixed 8 MiB buffers, computing SHA-256 over
|
|
ALL bytes, and simultaneously sniffing: first 1 MiB (line terminator
|
|
classification), first 1000 parseable line-shaped rows (timestamp/delimiter/
|
|
header detection), BOM presence.
|
|
4. On success: write certificate with status `FULLY_VERIFIED` and the full hash.
|
|
5. If the configurable time budget (`certify_timeout_sec`, default 3600; config
|
|
option) elapses before the hash completes, the process stops cleanly and
|
|
records `VERIFIED_WITH_LIMITATION` with `sha256_mode="not_computed"` and
|
|
`limitation_reason="timeout"`. It MUST NOT claim a full hash.
|
|
6. Write is atomic (`*.tmp` + fsync + rename). Certificate path is derived
|
|
from source identity fields; re-certification is an allowed overwrite,
|
|
always recorded with a new timestamp.
|
|
|
|
### 7.4 Certification statuses
|
|
|
|
| Status | Meaning | Consequences |
|
|
|---|---|---|
|
|
| FULLY_VERIFIED | full file hashed; format facts consistent | canonical pipeline eligible (subject to Gate G-3) |
|
|
| VERIFIED_WITH_LIMITATION | full SHA-256 not completed within budget; only size/mtime/format facts guaranteed | pipeline MUST NOT claim full certification; starting a run requires explicit human authorization (Gate G-3) |
|
|
| FAILED | size/mtime changed mid-read, unreadable, or format facts self-contradictory | RESUME_BLOCKED; no processing |
|
|
|
|
A size or mtime change detected between the pre-stat and post-stat is an
|
|
automatic FAILED with `limitation_reason="source_changed_during_certify"`.
|
|
|
|
### 7.5 Operational requirement
|
|
|
|
FULL SOURCE SHA-256 VERIFIED is the required precondition of the canonical
|
|
pipeline. Any decision to proceed with `VERIFIED_WITH_LIMITATION` is a HUMAN
|
|
DECISION GATE (G-3). The Engine never silently downgrades this requirement.
|
|
|
|
## 8. Canonical Tick Representation (Layer 2)
|
|
|
|
### 8.1 CSV interpretation (deterministic rules)
|
|
|
|
- **Encoding**: UTF-8 (a leading UTF-8 BOM is stripped). Any undecodable byte
|
|
sequence marks the row `MALFORMED_ENCODING`. ASCII is a strict subset and is
|
|
processed identically.
|
|
- **Delimiter**: comma only in v1. Detection result is recorded in the
|
|
certificate; a mismatch between certificate and config blocks init.
|
|
- **Line endings**: the row scanner splits on any of `\r\n`, `\n`, `\r`
|
|
(deterministic; independent of the recorded classification). The recorded
|
|
`line_ending` is diagnostic.
|
|
- **Blank lines**: skipped, counted in `malformed.blank_line` (expected class).
|
|
- **Header**: after BOM strip, if the first non-blank row has a first field
|
|
matching `^(datetime|date|time)([^0-9]|$)` (case-insensitive), that row is
|
|
the header and is skipped with `has_header=true` recorded in the certificate.
|
|
The header row boundary is deterministic.
|
|
- **Grammar presets (P3-DATA-ENGINE-004 source adapter)**: exactly two named,
|
|
fully-specified, versioned grammar presets exist: `G_TICKSTORY_MT5`
|
|
(six-column `YYYYMMDD,HH:MM:SS,bid,ask,last,volume`; the authoritative
|
|
production grammar, derived from the P3-DATA-ENGINE-003 pilot evidence) and
|
|
`G_DOTTED_V1` (dotted 3/4-column corpus grammar, retained for the synthetic
|
|
G01–G17 fixtures). The certificate pins `grammar_id` from the observed
|
|
facts; the operator config `source_grammar` MUST equal the certified
|
|
`grammar_id` or `init` is BLOCKED (certificate/config mismatch, spec 8.1).
|
|
There is no silent per-row format guessing.
|
|
- **Timestamp**: accepted forms (recorded at certification):
|
|
- `YYYYMMDD` + `HH:MM:SS` (compact date + time fields; G_TICKSTORY_MT5) — PRIMARY;
|
|
- `YYYY.MM.DD HH:MM:SS.mmm` (dotted, 3-digit milliseconds; G_DOTTED_V1) — corpus preset;
|
|
- `YYYY.MM.DD HH:MM:SS` (no ms) — corpus preset, ms = 0.
|
|
Any other shape → `MALFORMED_TIMESTAMP_PARSE`.
|
|
- **Timezone**: source text is a wall-clock in the trader's server time.
|
|
Canonical `ts_ms` = epoch milliseconds UTC computed as:
|
|
`ts_ms = julian_day_ms(wall_clock) - source_tz_offset_minutes * 60000`.
|
|
`source_tz_offset_minutes` is an explicit config value recorded in the
|
|
manifest; DST is NOT modeled in v1 (see Open Decisions, risk R-TZ). The
|
|
parse→canonical mapping is therefore a pure, documented function of the
|
|
source text, so any future re-interpretation is a re-derivation with a new
|
|
dataset version, never an in-place mutation.
|
|
- **Timestamp range**: valid only if
|
|
`2000-01-01T00:00:00Z <= ts_ms <= certify_time+7d`; outside → `MALFORMED_TIMESTAMP_RANGE`.
|
|
|
|
### 8.2 Price, bid/ask, volume semantics
|
|
|
|
- Canonical prices are **scaled integers**: `PRICE_SCALE = 1_000_000`
|
|
(micro-USD units). `bid_u = int(decimal_text * PRICE_SCALE)` computed by
|
|
exact decimal-string parsing (no floats).
|
|
- `ask_u` likewise. `spread_u = ask_u - bid_u`.
|
|
- Invalid relationships:
|
|
- `bid_u <= 0` or `ask_u <= 0` → `MALFORMED_PRICE_NONPOSITIVE`;
|
|
- `spread_u < 0` → `MALFORMED_BID_ASK_RELATION`;
|
|
- fractional digits beyond the scale → `MALFORMED_PRICE_PRECISION`.
|
|
- **Volume (amended P3-DATA-ENGINE-004)**: the grammar presets fix the volume
|
|
semantics — for `G_TICKSTORY_MT5` the source HAS an explicit mandatory
|
|
volume column so `has_volume=true` is enforced by config validation;
|
|
`vol = 1` substitution (`has_volume=false`) is FORBIDDEN for this source.
|
|
Volume is a non-negative integer token; `0` is VALID (observed in the real
|
|
source: many canonical rows carry volume 0); negative or non-integer
|
|
values are `MALFORMED_VOLUME`. The dotted preset retains `has_volume=false`
|
|
(default) → `vol = 1` tick-volume semantics. Any violation of the
|
|
certificate's `column_layout` vs config → init block.
|
|
- Duplicate detection: identical (`ts_ms,bid_u,ask_u,vol`) records within one
|
|
chunk are flagged `MALFORMED_DUPLICATE` (bounded in-memory set per chunk).
|
|
- Monotonicity: within a chunk, `ts_ms` must be non-decreasing; otherwise the
|
|
offending row is `MALFORMED_NON_MONOTONIC`. Across a chunk boundary the
|
|
first `ts_ms` of chunk N must be >= the last `ts_ms` of chunk N-1 (carried
|
|
in checkpoint); a violation is counted as
|
|
`MALFORMED_NON_MONOTONIC_BOUNDARY` and, if > threshold, becomes material (§20).
|
|
|
|
### 8.3 Malformed-record taxonomy (complete)
|
|
|
|
| Class | Triggers |
|
|
|---|---|
|
|
| MALFORMED_EMPTY_LINE | blank row |
|
|
| MALFORMED_ENCODING | undecodable bytes |
|
|
| MALFORMED_FIELD_COUNT | field count ≠ grammar-expected (G_DOTTED_V1: 3/4; G_TICKSTORY_MT5: exactly 6) |
|
|
| MALFORMED_TIMESTAMP_PARSE | timestamp text unrecognized |
|
|
| MALFORMED_TIMESTAMP_RANGE | ts outside validity window |
|
|
| MALFORMED_PRICE_PARSE | bid/ask non-numeric |
|
|
| MALFORMED_PRICE_PRECISION | decimals exceed PRICE_SCALE precision |
|
|
| MALFORMED_PRICE_NONPOSITIVE | bid<=0 or ask<=0 |
|
|
| MALFORMED_BID_ASK_RELATION | ask < bid |
|
|
| MALFORMED_VOLUME | volume invalid (when has_volume) |
|
|
| MALFORMED_DUPLICATE | exact duplicate within chunk |
|
|
| MALFORMED_NON_MONOTONIC | ts regress within chunk |
|
|
| MALFORMED_NON_MONOTONIC_BOUNDARY | ts regression across chunk boundary |
|
|
|
|
Malformed rows are NEVER fabricated, skipped silently, or injected into the
|
|
canonical stream. Each is appended to `malformed/malformed_%06d.jsonl`
|
|
(`chunk_index`, `byte_start`, `global_line_no`, `class`, `raw_sha256`,
|
|
`reason`). Counters per class accumulate in the checkpoint and in
|
|
`malformed_summary.json` at completion. If malformed rows exceed
|
|
`malformed_rate_limit` (default 0.001 of parsed rows in a workload) OR the
|
|
absolute cap (default 10,000 in a workload), the run enters FAILED (§20).
|
|
|
|
### 8.4 Canonical record (CTS_V1)
|
|
|
|
| Column | Type | Semantics |
|
|
|---|---|---|
|
|
| ts_ms | int64 | epoch ms UTC |
|
|
| bid_u | int64 | micro-USD × 1e6 |
|
|
| ask_u | int64 | micro-USD × 1e6 |
|
|
| spread_u | int64 | ask_u − bid_u (≥ 0) |
|
|
| vol | int64 | source volume or 1 |
|
|
| src_line | int64 | global 1-based raw line number (audit anchor) |
|
|
| rec_ord | int64 | 0-based record ordinal within the chunk |
|
|
|
|
All values are integers. No floats, no nulls, no strings in the canonical tick
|
|
table.
|
|
|
|
### 8.5 Canonical serialization & hashing
|
|
|
|
The canonical serialization of a record r is the single line:
|
|
|
|
```
|
|
ts_ms|bid_u|ask_u|vol
|
|
```
|
|
|
|
(no spaces, `\n` terminated). The **chunk content digest** is:
|
|
|
|
```
|
|
chunk_content_id = SHA-256( concat(serialize(r_0), serialize(r_1), …, serialize(r_{n-1})) )
|
|
```
|
|
|
|
in `rec_ord` order. The chunk content id is the reproducibility identity of the
|
|
chunk (Parquet physical bytes are not part of the reproducibility contract,
|
|
because physical writer metadata may differ run-to-run; the logical
|
|
serialization is fixed by this spec). The chunk physical SHA-256 is also
|
|
recorded for integrity evidence.
|
|
|
|
### 8.6 LAST-field treatment and source-preservation layer (P3-DE-004)
|
|
|
|
The G_TICKSTORY_MT5 source carries a fourth price column `last` (last traded/
|
|
last quote price) that does NOT map into CTS_V1 (bid/ask/spread/vol). Decision
|
|
chain, based on preservation, reproducibility, future research usefulness and
|
|
schema integrity:
|
|
|
|
1. **Validated** at parse time with the same decimal micro-price grammar as
|
|
bid/ask (`PRICE_SCALE`; non-positive or over-precision → the corresponding
|
|
MALFORMED_PRICE_* class). Bid/ask relationship validation remains
|
|
mandatory; `last` is deliberately NOT cross-validated against bid/ask
|
|
(a last price may legally equal bid, ask or any value; imposing an
|
|
invariant would be an unsupported assumption).
|
|
2. **Deliberately EXCLUDED from CTS_V1**: the canonical tick schema
|
|
(and its serialization/content-id contract) is UNCHANGED — P3-DE-004 does
|
|
not re-open G-4/G-5. Promoting `last` into the canonical schema would be a
|
|
new CTS version and a new dataset version (human gate G-5).
|
|
3. **Preserved in a source-preservation layer**:
|
|
`source_preservation/chunk_%06d.jsonl`, one line per canonical row in
|
|
rec_ord order, `src_line|last_u` (e.g. `12|340345000`). The canonical preservation
|
|
serialization is the single line `src_line|last_u\n`; the preservation
|
|
chunk content id is SHA-256 over the concatenation (micro-units, integer
|
|
math only). The sidecar content id and physical SHA-256 are recorded in
|
|
the evidence manifest and independently re-verified (spec 18).
|
|
4. **Evidence basis**: the immutable raw source (certified FULL SHA-256)
|
|
remains the ultimate preservation record; the sidecar makes `last`
|
|
recoverable from canonical-run artifacts without re-reading the 34 GB
|
|
source, and keeps future research (e.g., last-price labels) derivable
|
|
under the frozen canonical identity. Dotted-grammar runs produce no
|
|
sidecars.
|
|
|
|
## 9. Chunking (Layer 3)
|
|
|
|
### 9.1 Evaluation of legacy values
|
|
|
|
- Legacy `atomic chunk = 24 MiB`, `workload = 512 MiB nominal`. Evaluation:
|
|
- 24 MiB ⇒ ~1,370 chunks for the 34.5 GB source; each chunk holds
|
|
~0.6–0.9 M tick lines (~20–40 MB RSS per worker) — excellent
|
|
checkpoint granularity, bounded worker memory, fine-grained resume.
|
|
- 512 MiB nominal workloads ⇒ ~67 workloads; matches legacy differential
|
|
comparison granularity (chunk 759/760 boundary lies on the same grid).
|
|
- Both values are RETAINED as nominal defaults for v1. Actual boundaries are
|
|
line-safe (below). These remain a human decision gate (G-7/G-8) because
|
|
they affect the differential comparison surface.
|
|
|
|
### 9.2 Chunk map (deterministic)
|
|
|
|
A single newline-scan pass over the source (no parsing) produces
|
|
`chunkmap/chunkmap-<source_id>.json`:
|
|
|
|
```
|
|
{"source_id", "chunk_bytes_nominal":25165824, "workload_bytes_nominal":536870912,
|
|
"chunks":[ {"index":i, "byte_start":b, "byte_end":e, "line_count":n,
|
|
"workload_index":w} , …],
|
|
"total_bytes", "total_chunks", "total_workloads", "chunkmap_sha256"}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- `byte_start` is 0-based from file start. Chunk 0 starts at byte 0.
|
|
- A chunk's nominal end = `byte_start + chunk_bytes_nominal`; the actual
|
|
`byte_end` = first line-terminator position at or after the nominal end,
|
|
INCLUSIVE of the terminator bytes. Consequence: `byte_end` is always a line
|
|
boundary; no chunk ever splits a CSV record.
|
|
- Chunk N+1 `byte_start` = chunk N `byte_end`. Boundaries are contiguous and
|
|
cover `[0, total_bytes)` exactly.
|
|
- If the source does not end with a newline, the final line is terminated by
|
|
EOF; the last chunk ends at `total_bytes`.
|
|
- A file smaller than one chunk yields exactly one chunk.
|
|
- Workload grouping: accumulate whole chunks whose cumulative `byte_end −
|
|
byte_start` first reaches or exceeds `workload_bytes_nominal`; the final
|
|
workload contains the remaining chunks (partial final workload) and is
|
|
recorded as `partial=true`. No padding.
|
|
- `chunkmap_sha256` = SHA-256 of the deterministic JSON serialization (sorted
|
|
keys, fixed field order). The chunk map is cached per `source_id`; on resume
|
|
it is recomputed cheaply only if the source identity changed (which blocks).
|
|
|
|
### 9.3 Byte cursor semantics
|
|
|
|
- Checkpoint records `next_chunk` and `next_byte_start` (= byte_start of the
|
|
next uncommitted chunk). They always agree with the chunk map; the chunk map
|
|
is the sole authority for boundaries.
|
|
|
|
## 10. Processing Model (Layer 4)
|
|
|
|
### 10.1 Model selection
|
|
|
|
- **Canonical tick conversion**: parallel multiprocessing (spawn context,
|
|
Windows-safe), one chunk per worker task.
|
|
- **Aggregation**: sequential deterministic pass over ordered canonical ticks
|
|
(see §11). Parallel aggregation is explicitly NOT in v1; it is a documented
|
|
future optimization that must preserve identical content ids and pass the
|
|
reordered-execution determinism test before adoption.
|
|
- Rationale: conversion is CPU/parse-bound (parallel-friendly, pure per
|
|
chunk); aggregation is stateful and order-dependent (sequential is the
|
|
safest deterministic design for v1).
|
|
|
|
### 10.2 Worker & dispatch model
|
|
|
|
- `workers_requested` (config, default 24 — Gate G-2);
|
|
- `workers_created` = min(requested, `os.cpu_count()` or 8, memory
|
|
estimates) — recorded, never assumed;
|
|
- `workers_with_non_empty_partitions` = workers that actually received ≥ 1
|
|
chunk before the queue drained;
|
|
- `cpu_utilization_pct` = measured (parent-side polling), reported but never
|
|
tuned automatically.
|
|
- Dispatch: parent keeps a deterministic ordered work list (chunk indices
|
|
0..N−1) and a simple job queue (process pool). A worker pulls exactly one
|
|
chunk at a time, processes it, writes `staging/chunk_%06d.parquet.tmp`,
|
|
computes digests, fsyncs, renames to `ticks/chunk_%06d.parquet`, returns
|
|
(chunk_index, stats). The parent records the commit in the journal
|
|
(hash-chained). Completion order NEVER affects outputs: each chunk file is
|
|
independently named and content-identified; the canonical reading layer
|
|
iterates chunk indices in ascending order.
|
|
- No shared memory between workers; no queues carrying tick data; workers
|
|
only communicate via files.
|
|
|
|
### 10.3 Deterministic merge
|
|
|
|
"Merge" is a sorted concatenation by `chunk_index` (and by `rec_ord` within a
|
|
chunk). The aggregator reads chunk files in ascending index order. Because
|
|
chunk files are immutable once committed, merged output is identical for every
|
|
arrival order.
|
|
|
|
### 10.4 Worker failure handling (bounded)
|
|
|
|
| Event | Action (exactly one predefined recovery) |
|
|
|---|---|
|
|
| worker exits non-zero before rename | redispatch chunk (attempt counter); after `retry_limit=2` → FAILED |
|
|
| worker killed (crash/OOM) | redispatch (attempt counter as above); stale `.tmp` deleted by dispatcher before redispatch |
|
|
| chunk file exists but journal has no record | journal is truth; file re-verified; if digest matches → journal repaired to record commit (recorded `repaired=commit`); else re-dispatched |
|
|
| journal has record but file missing/corrupt | FAILED (material; no silent re-issue) |
|
|
| worker OOM repeatedly | attempt counter applies; then FAILED with `reason=worker_oom` |
|
|
|
|
Commit is defined as: chunk parquet renamed and fsynced AND journal line
|
|
appended and fsynced. Only both count.
|
|
|
|
## 11. Aggregation (Layer 5)
|
|
|
|
### 11.1 Timeframes and justification
|
|
|
|
Included: **M1, M5, M15, M30, H1**. Justification:
|
|
|
|
- M1 is the finest bar technically justified by tick input and is the base
|
|
unit for all coarser frames;
|
|
- M5..H1 form the standard ladder required by the research plans for
|
|
horizons of 15/30/60 min and by the legacy differential protocol (M15/M30
|
|
are legacy comparison anchors);
|
|
- no other timeframe is added; "interesting" frames are explicitly out of
|
|
scope until a research decision demands one (then a new dataset version).
|
|
|
|
Periods (ms): M1=60_000, M5=300_000, M15=900_000, M30=1_800_000, H1=3_600_000.
|
|
|
|
### 11.2 Boundary definition
|
|
|
|
- Bar boundaries are UTC clock boundaries: `period_id = floor(ts_ms / period_ms)`;
|
|
`start_ms = period_id * period_ms`; `end_ms = start_ms + period_ms`.
|
|
- A tick belongs to the bar of its `period_id`.
|
|
- Ticks are consumed in canonical order `(ts_ms ascending, then src_line
|
|
ascending)` — stable and deterministic.
|
|
- Within identical `ts_ms`, order is by `src_line` (raw file order).
|
|
|
|
### 11.3 OHLC / volume / spread (integer-exact)
|
|
|
|
- The representative bar price is the mid: `mid_u2 = bid_u + ask_u`
|
|
(units: 2×PRICE_SCALE = 2,000,000 per USD). This keeps all OHLC math in
|
|
integers with no truncation:
|
|
- `open_u2` = first mid_u2 in the bar;
|
|
- `high_u2` = max mid_u2; `low_u2` = min mid_u2; `close_u2` = last mid_u2;
|
|
- `PRICE_SCALE_BAR = 2_000_000` is recorded in the bar schema metadata.
|
|
- Volume: `vol_sum` = Σ tick vol; `tick_count` = number of ticks.
|
|
- Spread: `spread_min_u`, `spread_max_u`, `spread_sum_u`,
|
|
`spread_avg_u = spread_sum_u // tick_count` (floor division; defined).
|
|
- Audit anchors: `first_src_line`, `last_src_line`.
|
|
|
|
### 11.4 Carry state
|
|
|
|
For each timeframe the aggregator keeps exactly one in-progress bar (the
|
|
partial bar) as the carry:
|
|
|
|
```
|
|
TF carry = {period_id, start_ms, open_u2, high_u2, low_u2, close_u2,
|
|
tick_count, vol_sum, spread_min_u, spread_max_u, spread_sum_u,
|
|
last_ts_ms, last_src_line}
|
|
```
|
|
|
|
Carries are checkpointed with every commit (see §13). On resume the aggregator
|
|
verifies that its first input `ts_ms` is ≥ the carried `last_ts_ms`; otherwise
|
|
FAILED (carry mismatch = material).
|
|
|
|
### 11.5 Finalization rules
|
|
|
|
- A bar is finalized when the first tick of a later period_id arrives. It is
|
|
written to `bars/<TF>/wl_%03d.parquet` with `is_final=true`.
|
|
- At EOF, all carried partial bars are flushed with `is_final=false` and the
|
|
dataset manifest records `final_bar_partial=true` for each timeframe.
|
|
- Bar uniqueness key: `(TF, period_id)`. Append-only: a period_id is written
|
|
at most once per run. On resume, the aggregator validates that the last
|
|
written record for each TF equals the carried period_id (continuity check);
|
|
mismatch → FAILED.
|
|
- Bar global ordinal `bar_idx` (0-based per timeframe, contiguous across
|
|
parts) makes reading order-independent.
|
|
|
|
### 11.6 Bar schema (CBS_V1)
|
|
|
|
| Column | Type | Semantics |
|
|
|---|---|---|
|
|
| bar_idx | int64 | global ordinal per timeframe |
|
|
| period_id | int64 | start_ms / period_ms |
|
|
| start_ms / end_ms | int64 | UTC boundaries |
|
|
| open_u2, high_u2, low_u2, close_u2 | int64 | units = 2 × PRICE_SCALE |
|
|
| tick_count, vol_sum | int64 | counts |
|
|
| spread_min_u, spread_max_u, spread_sum_u, spread_avg_u | int64 | spread in PRICE_SCALE units |
|
|
| is_final | bool | false only for EOF partial bars |
|
|
| first_src_line, last_src_line | int64 | audit anchors |
|
|
|
|
## 12. Storage Format & Persistent Canonical Dataset (Layer 6)
|
|
|
|
### 12.1 Format decision
|
|
|
|
Default: **Apache Parquet**, compression **zstd** level 3, `write_page` layout
|
|
with pinned writer version (see §26 determinism table).
|
|
|
|
Rationale: columnar, typed (no float in canonical tabsh), compressed,
|
|
stream-appendable by immutable files, universally readable, and row-group
|
|
scannable. CSV is explicitly rejected as the default intermediate format
|
|
(no types, slow, no random access, ambiguous escaping). PyArrow version is
|
|
pinned and recorded in every manifest.
|
|
|
|
### 12.2 Layout
|
|
|
|
```
|
|
engine_output/
|
|
certification/source_certificate.json
|
|
chunkmap/chunkmap-<source_id>.json
|
|
ticks/chunk_%06d.parquet # final per-chunk columnar files
|
|
staging/… # .tmp intermediates (prunable after verification)
|
|
bars/M1/wl_%03d.parquet , bars/M5/… , bars/M15/… , bars/M30/… , bars/H1/…
|
|
malformed/malformed_%06d.jsonl
|
|
state/checkpoint.json, state/commits.jsonl, state/lock, state/control.json
|
|
logs/*.jsonl
|
|
progress.json
|
|
evidence/evidence.json
|
|
RUN_COMPLETE.json
|
|
datasets/<dataset_id>/…
|
|
verification/…
|
|
```
|
|
|
|
### 12.3 Naming rules
|
|
|
|
- `chunk_%06d` (0-padded index), `wl_%03d` (workload index), `part` derived
|
|
deterministically from workload index.
|
|
- Every parquet file embeds schema key-value metadata: `schema_version`,
|
|
`engine_version`, `parser_version`, `algorithm_version`, `source_id`,
|
|
`chunk_index` (ticks) / `timeframe` (bars), `price_scale`,
|
|
`content_id` is NOT embedded (it is computed over logical rows; see §12.4).
|
|
|
|
### 12.4 Dataset identity (content ids, not physical bytes)
|
|
|
|
- `content_id(ticks chunk i)` = `chunk_content_id` from §8.5.
|
|
- `content_id(bars wl w, TF)` = SHA-256 over canonical bar serialization:
|
|
each row as `bar_idx|period_id|open_u2|high_u2|low_u2|close_u2|tick_count|vol_sum|spread_min_u|spread_max_u|spread_sum_u|is_final|first_src_line|last_src_line\n`
|
|
concatenated in ascending `bar_idx`.
|
|
- The **canonical dataset hash** = SHA-256 over the canonical concatenation
|
|
(fixed field order) of
|
|
`content_id(ticks chunk 0) ‖ content_id(chunk 1) ‖ … ‖ content_id(bars files…)`
|
|
in sorted path order. Ordering is defined by the manifest file list.
|
|
- Physical file SHA-256s are recorded alongside content ids in `evidence.json`;
|
|
content ids are the reproducibility identity; physical hashes are integrity
|
|
evidence. This distinction is explicit and non-negotiable (Parquet physical
|
|
bytes are writer-dependent).
|
|
|
|
### 12.5 Schema metadata & versioning
|
|
|
|
Manifest record per dataset: `{"dataset_version": "DS_V1.0.0", schema_version,
|
|
engine_version, parser_version, algorithm_version, source_id, config snapshot}`.
|
|
Dataset version bumps are HUMAN DECISION GATES (G-5).
|
|
|
|
## 13. Checkpoint / Resume (Layer 7)
|
|
|
|
### 13.1 Files
|
|
|
|
- `state/checkpoint.json` — atomic (tmp+fsync+rename+dir fsync).
|
|
- `state/commits.jsonl` — append-only commit journal, each line
|
|
hash-chained to the previous:
|
|
`{"seq":n,"chunk":i,"content_id":"…","file_sha256":"…","prev":"<hash of line n-1>","ts_utc":"…"}`
|
|
- `state/lock` — PID, run_id, heartbeat. Stale detection §21.
|
|
|
|
### 13.2 Checkpoint schema (CP_V1)
|
|
|
|
```
|
|
schema_version, run_id, status,
|
|
source_identity {path,size,mtime_ns,sha256_full,cert_status,cert_path},
|
|
versions {engine, parser, algorithm, dataset, manifest schema},
|
|
chunkmap_sha256,
|
|
journal_tail {seq, hash},
|
|
last_completed_chunk, next_chunk, next_byte_start,
|
|
cumulative {rows_parsed, rows_canonical, bytes_read, malformed:{class:count}, chunks_committed,
|
|
bars_per_timeframe:{TF:count}},
|
|
carries {TF: carry object (§11.4)},
|
|
update_ts_utc,
|
|
status_reason (nullable),
|
|
checkpoint_hash (SHA-256 over the canonical JSON serialization of all fields
|
|
EXCEPT checkpoint_hash — non-self-referential)
|
|
```
|
|
|
|
### 13.3 Status machine
|
|
|
|
```
|
|
INITIALIZED → RUNNING → CHUNK_COMMITTED* → PAUSED → RUNNING → … → COMPLETED
|
|
│ │
|
|
└──────────── FAILED ──────────────────────┘
|
|
INITIALIZED / RUNNING / PAUSED / FAILED ──(resume guard)──▶ RESUME_BLOCKED
|
|
```
|
|
|
|
- `INITIALIZED` — after `init`, before first dispatch.
|
|
- `RUNNING` — while workers are dispatched.
|
|
- `CHUNK_COMMITTED` — recorded transition on every journal append (the durable
|
|
state of progress; the file is rewritten with this status after each
|
|
workload, see 13.4).
|
|
- `PAUSED` — graceful stop (signal/control file), clean checkpoint, exit 0.
|
|
- `FAILED` — material condition; processing stopped; resume is permitted only
|
|
after operator resolution and explicit re-run (see §20).
|
|
- `COMPLETED` — final after verification; final checkpoint written.
|
|
- `RESUME_BLOCKED` — a resume attempt refused entry; no state mutation.
|
|
|
|
Transitions are atomic (single rename) and fail-closed: any exception during a
|
|
checkpoint write leaves the previous valid checkpoint untouched.
|
|
|
|
### 13.4 Checkpoint frequency
|
|
|
|
Checkpoint + journal fsync after **every workload** (~67 writes for the full
|
|
run) and always before graceful pause/stop and after any worker failure that
|
|
causes re-dispatch. Chunk commit (journal append) is the fine-grained durable
|
|
record; the checkpoint file is the coarse summary. On resume, journal replay
|
|
(from the recorded tail) determines actual committed chunks — including chunks
|
|
committed after the last checkpoint write.
|
|
|
|
### 13.5 Resume procedure (deterministic)
|
|
|
|
1. Load checkpoint; verify `checkpoint_hash`.
|
|
2. Verify source identity: recompute size+mtime; require certificate present
|
|
and matching (`sha256_full` if FULLY_VERIFIED). Any mismatch →
|
|
**RESUME_BLOCKED** (never auto-recovery).
|
|
3. Verify versions (engine/parser/algorithm/dataset/schema) match exactly →
|
|
mismatch = RESUME_BLOCKED.
|
|
4. Verify `chunkmap_sha256` matches the cached map → mismatch = RESUME_BLOCKED.
|
|
5. Replay journal from `journal_tail.seq`; validate chain; derive
|
|
`next_chunk`; verify each committed chunk file exists and its content id
|
|
matches the journal → any anomaly = FAILED (material), not auto-fixed.
|
|
6. Restore carries from checkpoint; verify aggregator continuity (§11.5).
|
|
7. Resume dispatch from `next_chunk`.
|
|
|
|
### 13.6 Source identity mismatch
|
|
|
|
`source_identity` mismatch → `RESUME_BLOCKED` with `status_reason`
|
|
(`source_size_changed` | `source_mtime_changed` | `certificate_missing` |
|
|
`certificate_mismatch` | `sha256_mismatch`). The operator must re-certify and
|
|
decide (human gate) whether to start a new run.
|
|
|
|
## 14. Headless CLI (Layer 8)
|
|
|
|
### 14.1 Commands (semantics, not implemented this session)
|
|
|
|
```
|
|
sniper-data --version
|
|
sniper-data certify --source <path> [--timeout-sec N] [--output <cert>]
|
|
sniper-data init --config <engine_config.json> [--run-id <id>]
|
|
sniper-data ingest [--limit-chunks N] [--pause-after <workload|chunk> N]
|
|
sniper-data resume [--limit-chunks N]
|
|
sniper-data pause | stop
|
|
sniper-data status [--json]
|
|
sniper-data verify --mode golden | range | dataset | run-complete | legacy
|
|
[--start-byte N --end-byte M] [--samples N]
|
|
sniper-data manifest [--dataset <id>] [--print]
|
|
sniper-data build-bars [--timeframes M1,M5,M15,M30,H1]
|
|
sniper-data build-dataset --config <research_cfg.json>
|
|
sniper-data test [--quick]
|
|
sniper-data audit-dataset --dataset <id>
|
|
sniper-data diff-legacy --legacy-evidence <path> --report <out.json>
|
|
```
|
|
|
|
### 14.2 Semantics and safety properties
|
|
|
|
| Command | Meaning | Safety property |
|
|
|---|---|---|
|
|
| certify | compute certificate (read-only on source) | never opens source writable; atomic cert write |
|
|
| init | create a new run (new run_id) from a certified source; refuses if a live run/lock exists; archives prior run on explicit `--new-run` | refuses to touch legacy evidence; validates cert + config |
|
|
| ingest | start processing from INITIALIZED (or resume-equivalent allowed states) | idempotent dispatch; journal-before-checkpoint durability |
|
|
| resume | continue from PAUSED/FAILED-after-resolution; RESUME_BLOCKED otherwise | verifies every guard in §13.5; changes nothing on refusal |
|
|
| pause / stop | graceful stop at next chunk/workload boundary; checkpoint; exit | never kills a worker mid-chunk without completing its rename |
|
|
| status | print checkpoint, progress, lock, journal tail; exit code indicates completion | read-only |
|
|
| verify | run the independent verifier | refuses to run pipeline stages; only reads source/artifacts |
|
|
| manifest | build/print the dataset manifest | refuses to list missing/corrupt files |
|
|
| build-dataset | research dataset builder (§15) | reads canonical layers only; never the raw source |
|
|
| diff-legacy | differential comparison vs legacy evidence | read-only on legacy evidence |
|
|
| test | golden corpus runner | tiny synthetic inputs only |
|
|
|
|
### 14.3 Exit codes
|
|
|
|
```
|
|
0 success / COMPLETED
|
|
1 operational error (I/O, config invalid)
|
|
2 usage error
|
|
3 not completed (status: RUNNING/PAUSED/INITIALIZED)
|
|
4 BLOCKED (RESUME_BLOCKED / lock held / certification missing)
|
|
5 verification or audit failed
|
|
```
|
|
|
|
The CLI is designed so start / pause / resume / verify / status / completion
|
|
detection require no human conversation.
|
|
|
|
## 15. Research Dataset Builder (Layer 9)
|
|
|
|
### 15.1 Separation of concerns
|
|
|
|
```
|
|
DATA ENGINE → CANONICAL DATA → RESEARCH DATASET → FORECASTING EXPERIMENT → MODEL → TRADING DECISION
|
|
```
|
|
|
|
The Engine provides canonical ticks+bars and a fixed library of deterministic
|
|
feature/label primitives. It holds NO forecasting hypothesis. Examples such as
|
|
"M15 + 15m direction", "M15 + 30m return", "M30 + 60m return" are research
|
|
configurations, never ingestion rules.
|
|
|
|
### 15.2 Research config schema (strict; hash → config_sha256)
|
|
|
|
```
|
|
{
|
|
"dataset_id": "xau_m15_15m_dir_v1",
|
|
"source_dataset_version": "DS_V1.0.0",
|
|
"timeframe": "M15",
|
|
"prediction_horizon_min": 15,
|
|
"target": {"type": "direction|cls_3|return_reg", "horizon": "15m", "from": "close"},
|
|
"features": [{"name":..., "primitive": "return|log_return|volatility|range|spread_stats|rolling_*",
|
|
"window": k, "args": {...}}],
|
|
"date_range": ["2015-01-01","2020-12-31"],
|
|
"split": {"policy": "time_ordered", "train":0.7,"val":0.15,"test":0.15,
|
|
"oos_barrier": true, "barrier_gap_min": 15},
|
|
"cost_model": {"type":"fixed_spread","spread_units":…,"commission_per_lot":…,"lot_size":…},
|
|
"label_rules": {"min_abs_move_units":…,"neutral_zone":…}}
|
|
}
|
|
```
|
|
|
|
Rules:
|
|
|
|
- Inputs: the canonical bar layer of `source_dataset_version` ONLY.
|
|
- Feature/label primitives are deterministic pure functions of bars
|
|
(integer or pinned-float operations, fixed evaluation order).
|
|
- Leakage rule: the label for row t uses bars `(t, t+h]` only; rows lacking
|
|
`h` future bars are excluded and counted in the manifest
|
|
(`rows_excluded_no_future`).
|
|
- Split: strictly time-ordered; `oos_barrier` demands a gap of
|
|
≥ horizon between the last training bar and the first test bar (in bars),
|
|
recorded in the manifest.
|
|
- All boundaries are (row_id, bar_idx) based, never shuffled at row level.
|
|
- Float math is allowed HERE (research layer), but the pipeline is
|
|
deterministic: pinned library versions, fixed operation order, no hash
|
|
randomization, no set-iteration in serialization paths.
|
|
|
|
### 15.3 Outputs
|
|
|
|
```
|
|
datasets/<dataset_id>/
|
|
config.json, config_sha256,
|
|
features.parquet, labels.parquet, dataset.parquet (row_id, ts, features, label, split),
|
|
split.parquet (row_id → split), manifest.json
|
|
```
|
|
|
|
## 16. Dataset Manifest (Layer 10)
|
|
|
|
`manifest.json` schema (MS_V1) — applies to the canonical dataset and to every
|
|
research dataset:
|
|
|
|
```
|
|
schema_version, dataset_id, dataset_version, engine_version, parser_version,
|
|
algorithm_version, source_identity {certificate reference},
|
|
timeframe (research only), prediction_horizon_min, target_definition,
|
|
features (list), date_range, split_definition, oos_barrier, cost_model,
|
|
row_counts {total, per_split, excluded_no_future, malformed…},
|
|
files [ {relpath, bytes, sha256_physical, content_id} ], # sorted by relpath
|
|
config_snapshot, config_sha256,
|
|
creation_ts_utc,
|
|
manifest_hash, # H(canonical JSON of manifest WITHOUT manifest_hash and dataset_hash)
|
|
dataset_hash # H(canonical concat of (relpath|content_id) list) — excludes manifest_hash
|
|
```
|
|
|
|
Non-self-referential hashing: `manifest_hash` explicitly excludes both hash
|
|
fields; `dataset_hash` is computed over file content ids only (plus version
|
|
identity fields), never over the manifest that contains it. No hash cycle.
|
|
|
|
## 17. Evidence & Hashing Strategy (Layer 11)
|
|
|
|
### 17.1 Evidence hierarchy
|
|
|
|
```
|
|
SOURCE
|
|
↓ certification (FULLY_VERIFIED gate)
|
|
CANONICAL TICKS content_id per chunk; dataset hash
|
|
CANONICAL BARS content_id per workload/TF file
|
|
DERIVED FEATURES content_id per dataset file
|
|
LABELS content_id per dataset file
|
|
RESEARCH DATASET dataset_hash per manifest
|
|
```
|
|
|
|
### 17.2 Identity definitions
|
|
|
|
- **run_id**: `RUN-<YYYYMMDD-HHMMSS>-<uuid4 first 8>`. Assigned at `init`.
|
|
- **file hash**: SHA-256 of physical bytes (`sha256_physical`).
|
|
- **content_id**: layer-specific canonical logical digest (§8.5, §12.4).
|
|
- **aggregate/layer hash**: SHA-256 over the canonical concatenation of the
|
|
layer's content ids in manifest-sorted order.
|
|
- **manifest hash** / **dataset hash**: see §16 (no self-reference).
|
|
- **checkpoint hash**: SHA-256 of the checkpoint payload excluding the hash
|
|
field itself (§13.2).
|
|
- **evidence manifest** (`evidence/evidence.json`): lists every produced file
|
|
with both hashes, plus `evidence_manifest_hash` computed over itself minus
|
|
the hash field. `RUN_COMPLETE.json` records the `evidence_manifest_hash`;
|
|
no cycle (RUN_COMPLETE.json is not hashed by evidence.json).
|
|
|
|
### 17.3 Rules
|
|
|
|
- All hash inputs use fixed canonical serialization (sorted keys at each JSON
|
|
level, `\n` line endings, no trailing whitespace).
|
|
- No hash is ever computed over a blob that contains that same hash.
|
|
- `PYTHONHASHSEED` is irrelevant by construction (no set/dict iteration in any
|
|
serialization path); the Engine additionally pins `PYTHONHASHSEED=0` as a
|
|
belt-and-braces control.
|
|
|
|
## 18. Independent Verification (Layer 12)
|
|
|
|
### 18.1 Principle
|
|
|
|
The verifier (`engine/verify/`) is a SEPARATE implementation written from this
|
|
specification's tables — its own line splitter, timestamp parser, price
|
|
parser, and aggregator. It does NOT import or call producer parsing,
|
|
aggregation, or serialization code. Producer and verifier may share only the
|
|
version constant tables.
|
|
|
|
### 18.2 Verification modes
|
|
|
|
| Mode | What it does |
|
|
|---|---|
|
|
| golden | runs the golden corpus through BOTH implementations; byte-equal expected outputs |
|
|
| range | re-parses selected source byte ranges with the verifier parser; compares canonical rows, ts, bid/ask against chunk parquet logical rows |
|
|
| dataset | recomputes every content_id and dataset_hash from artifacts (independent of producer); checks manifest consistency |
|
|
| run-complete | validates RUN_COMPLETE.json plus evidence manifest against artifacts |
|
|
| legacy | differential comparison vs legacy evidence (§24) |
|
|
|
|
### 18.3 Verification techniques
|
|
|
|
- **Golden vectors**: hand-computed expected outputs for tiny inputs.
|
|
- **Deterministic invariants** (at least): OHLC validity
|
|
(low ≤ open,close ≤ high; counts ≥ 1), ts_ms non-decreasing across merged
|
|
stream, Σ per-chunk tick counts == total tick count, bar continuity
|
|
(close of bar k == first tick of bar k+1 order boundary, carry continuity),
|
|
content_id stability across producer/verifier, append-only uniqueness of
|
|
(TF, period_id).
|
|
- **Byte-range verification**: independent parse+aggregate over chosen ranges,
|
|
compared to chunk artifacts.
|
|
- **Hash verification**: full recomputation from files.
|
|
- **Differential comparison** vs legacy (§24).
|
|
- **Mutation tests**: flip a byte / duplicate a row / drop a row / alter a
|
|
timestamp in a golden corpus COPY (never the source) → engine must change
|
|
digests or classify per taxonomy.
|
|
- **Boundary tests**: chunk boundary exactly at a terminator; boundary inside
|
|
a line; CRLF/LF/CR/mixed inputs.
|
|
- **Resume tests**: kill+resume cycles must reproduce identical content ids.
|
|
- **Reordered-worker tests**: same input dispatched in different orders →
|
|
identical content ids.
|
|
- **Duplicated/missing data tests**: injected duplicates/missing rows must be
|
|
detected by invariants (counters/hash changes).
|
|
|
|
### 18.4 Verdicts
|
|
|
|
`verification/report_<run>.json` with per-check
|
|
`PASS|FAIL|SKIP(reason)|DIFF(classification)` and an overall
|
|
`VERIFIER_ACCEPTED | REJECTED`. A FAILED invariant or a failed independent
|
|
content-id comparison is material (§20).
|
|
|
|
## 19. Golden Test Corpus (Layer 13)
|
|
|
|
Small, permanent, adversarial; each input < 100 KiB; full run < 10 s.
|
|
|
|
| Case | Input characteristics | Expected effect |
|
|
|---|---|---|
|
|
| G01 CRLF | terminator `\r\n` everywhere | parse ok; counts exact |
|
|
| G02 LF | `\n` only | parse ok; identical canonical ids to G01 |
|
|
| G03 header present | first row header | skipped; has_header=true |
|
|
| G04 header absent | data only | all rows data |
|
|
| G05 boundary at exact byte | chunk cut lands exactly on terminator | one line per chunk |
|
|
| G06 boundary inside line | chunk cut lands mid-line | chunk map extends boundary to line end; no split record |
|
|
| G07 malformed timestamp | `2023.01.01 12:00:00` variants / garbage | MALFORMED_TIMESTAMP_PARSE count |
|
|
| G08 malformed price | `abc` / signed / extra decimals | MALFORMED_PRICE_* classes |
|
|
| G09 invalid bid/ask | ask < bid | MALFORMED_BID_ASK_RELATION |
|
|
| G10 duplicated row | exact duplicate in chunk | MALFORMED_DUPLICATE + digest sensitivity |
|
|
| G11 dropped row | verifier-independent count mismatch | invariant FAIL (verification-time) |
|
|
| G12 non-monotonic | regressed ts | MALFORMED_NON_MONOTONIC |
|
|
| G13 worker reorder | shuffled dispatch of same input | identical content ids |
|
|
| G14 missing input carry | resume without carry state | RESUME_BLOCKED / continuity FAIL |
|
|
| G15 altered state | corrupt checkpoint hash / journal chain | RESUME_BLOCKED / FAILED |
|
|
| G16 repeated resume | pause+resume+resume | identical content ids (determinism proof) |
|
|
| G17 already-transformed data | canonical-shaped input fed as raw | format mismatch → certification fails / blocked |
|
|
|
|
Source-grammar golden cases SG01–SG15 (P3-DATA-ENGINE-004) extend the corpus
|
|
with dedicated synthetic fixtures for the authoritative six-column
|
|
G_TICKSTORY_MT5 grammar: SG01 grammar acceptance, SG02 CRLF handling,
|
|
SG03 valid six-column row, SG04 wrong field count, SG05 invalid date,
|
|
SG06 invalid time, SG07 invalid bid, SG08 invalid ask, SG09 invalid last,
|
|
SG10 invalid volume, SG11 bid>ask, SG12 zero/negative volume, SG13 timestamp
|
|
conversion (regression anchor `20030505,00:01:03` → `1_052_092_863_000` ms),
|
|
SG14 decimal precision, SG15 header absence/presence semantics. Like G01–G17,
|
|
every SG case requires producer == independent verifier == expected values.
|
|
|
|
Each case has an `expected/` JSON (counters, classes, content ids) that both
|
|
the producer and the verifier must reproduce.
|
|
|
|
## 20. Failure Model (Layer 14)
|
|
|
|
Three classes. The Engine's explicit action set is: continue (with counter),
|
|
apply ONE predefined recovery, or STOP.
|
|
|
|
| # | Condition | Class | Action | Result state |
|
|
|---|---|---|---|---|
|
|
| F01 | malformed row (any taxon) | EXPECTED | classify + counter + sidecar | continue |
|
|
| F02 | malformed rate > 0.001 or >10,000 in a workload | MATERIAL | STOP; record reason | FAILED |
|
|
| F03 | blank lines / BOM / header | EXPECTED | deterministic skip | continue |
|
|
| F04 | worker non-zero exit | BOUNDED | ONE recovery: redispatch, ≤2 attempts | continue/FAILED after limit |
|
|
| F05 | worker OOM | BOUNDED | ONE recovery: redispatch ≤2 | FAILED(`worker_oom`) after limit |
|
|
| F06 | chunk file present, no journal | BOUNDED | ONE recovery: verify digest, repair journal OR redispatch | continue |
|
|
| F07 | journal says committed, file missing/digest mismatch | MATERIAL | STOP | FAILED |
|
|
| F08 | checkpoint hash mismatch | MATERIAL | STOP; no auto-recovery | RESUME_BLOCKED |
|
|
| F09 | source size/mtime/cert mismatch | MATERIAL | STOP | RESUME_BLOCKED |
|
|
| F10 | version mismatch (engine/parser/algorithm/dataset/schema) | MATERIAL | STOP | RESUME_BLOCKED |
|
|
| F11 | carry continuity / bar append mismatch | MATERIAL | STOP | FAILED |
|
|
| F12 | disk free below floor at runtime | BOUNDED | ONE recovery: graceful pause | PAUSED(reason=disk_space); resume after human frees space |
|
|
| F13 | disk free below floor at init | MATERIAL | STOP | init blocked |
|
|
| F14 | lock held by live process | MATERIAL | STOP (no kill) | exit 4 (locked) |
|
|
| F15 | unknown exception in canonical writer/aggregator | MATERIAL | STOP | FAILED |
|
|
| F16 | verifier verdict REJECTED | MATERIAL | STOP before COMPLETED | FAILED(verification) |
|
|
| F17 | malformed threshold inside chunk (pre-workload) | EXPECTED | carry counters | continue (workload check at end) |
|
|
|
|
Rules:
|
|
|
|
- No autonomous recovery-by-reasoning. No silent fallback. No broad self-repair.
|
|
- Every STOP writes a checkpoint (atomic), a `status_reason`, and a
|
|
`logs/failure_<run>.jsonl` entry with the exact condition code.
|
|
- `FAILED` runs resume only after an operator (human) resolves the root cause
|
|
and re-invokes `resume` or starts a new run; the Engine never decides to
|
|
bypass F07–F16.
|
|
|
|
## 21. Resource Management (Layer 15)
|
|
|
|
| Resource | Requirement |
|
|
|---|---|
|
|
| Max memory, parent | ≤ 512 MB RSS (dispatcher/coordinator only) |
|
|
| Max memory, worker | ≤ 1 GiB RSS per worker (chunk-bounded; one chunk in memory) |
|
|
| Total memory target | requested workers × per-worker bound; never exceeds measured machine memory / 2 |
|
|
| Worker isolation | separate processes (`spawn`); worker crash cannot corrupt parent |
|
|
| Logging | JSONL, one operator log + per-worker logs; rotation at 100 MB, 5 files; logs never influence outputs |
|
|
| Progress | `progress.json`: run_id, status, chunks done/total, bytes, workloads, workers{requested, created, with_data, busy, cpu_util_pct}, started/updated ts, eta_sec |
|
|
| Checkpoint frequency | journal per chunk commit; checkpoint file per workload; always at pause/fail |
|
|
| Graceful shutdown | SIGINT/SIGTERM or `state/control.json` → finish current chunk, join workers (≤300 s), checkpoint PAUSED, exit 0 |
|
|
| OS restart recovery | any interruption leaves only orphan `.tmp`; resume (§13.5) restores exactly |
|
|
| Disk-space checks | preflight: free ≥ max(2×estimated_canonical_size, 20 GiB); per-workload: free ≥ 4 GiB else PAUSED(disk_space) |
|
|
| Output-space checks | staging cleaned after successful verification; `--prune-staging` explicit only |
|
|
| Process cleanup | atexit + signal handlers remove lock; children joined; no zombie policy beyond retry |
|
|
| Stale-process detection | lock heartbeat every 60 s; stale if >300 s old; resume with a stale lock → exit 4 and `status` reports `lock_stale`; releasing requires explicit `--force-release-lock` (human gate G-11) — default: refuse |
|
|
|
|
The Engine never relies on AI session lifetime; all of the above executes
|
|
under the OS scheduler.
|
|
|
|
## 22. Run Completion Protocol (Layer 16)
|
|
|
|
`RUN_COMPLETE.json` is produced ONLY after: all chunks committed, aggregator
|
|
flushed, evidence manifest computed, and the independent verifier
|
|
(`verify --mode dataset` and `--mode run-complete`) returned ACCEPTED.
|
|
|
|
Schema (RC_V1):
|
|
|
|
```
|
|
status: "COMPLETED",
|
|
run_id, source_identity {ref to certificate},
|
|
versions {engine, parser, algorithm, dataset, manifest schema},
|
|
rows {parsed, canonical, per-chunk-min/max},
|
|
malformed {per class, total},
|
|
outputs {ticks_files, bar_files_per_TF, dataset_files},
|
|
checkpoint {last_completed_chunk, next_chunk(=total), status},
|
|
hashes {canonical_dataset_hash, manifest_hash, evidence_manifest_hash,
|
|
per-layer aggregate hashes},
|
|
completion_ts_utc, verifier_report (path + verdict)
|
|
```
|
|
|
|
Machine-verifiability: `sniper-data verify --mode run-complete` re-checks every
|
|
hash and file from the disk; exit code 0 ⇔ genuine completion.
|
|
|
|
Operator flow: run the Engine headlessly; hours later, independently of any AI
|
|
session, run `sniper-data status` (→ completion detection), then hand the
|
|
result package (RUN_COMPLETE.json + evidence + manifests) to ChatGPT for audit.
|
|
|
|
## 23. Clean Reprocessing Protocol (Layer 17)
|
|
|
|
Exact future procedure (NOT executed in this session; each step has an
|
|
authorization requirement):
|
|
|
|
| # | Step | Entry condition | Authority |
|
|
|---|---|---|---|
|
|
| 1 | Freeze legacy pipeline | approved; legacy remains reference-only, evidence preserved | G-12 |
|
|
| 2 | Certify source | step 1; certificate FULLY_VERIFIED preferred | G-3 |
|
|
| 3 | Initialize NEW engine | certificate accepted; new run_id (not checkpoint 759) | G-4 |
|
|
| 4 | Run golden tests | `sniper-data test` green (producer + verifier) | — |
|
|
| 5 | Run small real-data pilot | e.g., first 2 workloads or a configurable 512 MiB window | G-13 (pilot scope) |
|
|
| 6 | Independently verify pilot | verifier PASS on pilot ranges | — |
|
|
| 7 | START full 34 GB processing | step 6 accepted | G-14 (full reprocessing) |
|
|
| 8 | Allow Python to run independently | no AI required; resume as needed | — |
|
|
| 9 | Resume if interrupted | §13.5; no AI required | — |
|
|
| 10 | Complete full dataset | status COMPLETED + RUN_COMPLETE.json | — |
|
|
| 11 | Independently certify final dataset | verifier ACCEPTED on full dataset | — |
|
|
| 12 | Freeze canonical research dataset | mark frozen in manifest; publish hashes | G-15 (freeze) |
|
|
|
|
The legacy pipeline is not extended during or after this procedure. Legacy
|
|
checkpoint 759 and all workload evidence remain untouched.
|
|
|
|
## 24. Legacy Comparison Protocol
|
|
|
|
### 24.1 Role of the legacy pipeline
|
|
|
|
The P3-S25 pipeline is a REFERENCE, not an unquestionable oracle. Legacy cursor
|
|
(chunks 738–759 / workload 45 completed, next chunk 760 at byte
|
|
18,442,355,762) is preserved and read-only. The new Engine does not inherit it.
|
|
|
|
### 24.2 Differential verification scope
|
|
|
|
```
|
|
NEW ENGINE ── selected byte ranges ──▶ canonical ticks (ts, bid, ask)
|
|
── selected ticks ─────────▶ row-level samples
|
|
── timestamps ─────────────▶ M15/M30 bar boundaries
|
|
── M15/M30 bars ───────────▶ OHLC/tick counts
|
|
── cumulative counters ────▶ parsed/canonical/malformed totals
|
|
── hashes ─────────────────▶ layer digests
|
|
▼
|
|
LEGACY EVIDENCE
|
|
```
|
|
|
|
Selection set (deterministic): first 64 KiB; the 24 MiB range preceding legacy
|
|
chunk 759/760 boundary; and 8 evenly spaced sample ranges across the file
|
|
(config-driven; each ≤ 24 MiB).
|
|
|
|
### 24.3 Discrepancy classification (mandatory taxonomy)
|
|
|
|
| Class | Meaning | Auto-action |
|
|
|---|---|---|
|
|
| EXPECTED_SEMANTIC | documented spec difference (e.g., mid-based OHLC, UTC alignment, carry policy) | record, no alarm |
|
|
| IMPLEMENTATION_DIFFERENCE | same semantics, different internals | record; verify both |
|
|
| LEGACY_DEFECT | legacy evidence contradicts the source within known legacy scope | record; escalate (G-16) |
|
|
| NEW_ENGINE_DEFECT | new Engine deviates from its own spec | FAILED; fix before proceeding |
|
|
| UNRESOLVED | cannot classify from available evidence | record; escalate (G-16); never auto-assume |
|
|
|
|
Output: `verification/legacy_diff_<run>.json` with per-item
|
|
range/tick/bar/counter/hash evidence and a classification. The Legacy
|
|
Comparison stage never modifies legacy files and never chooses an answer on
|
|
the operator's behalf; UNRESOLVED entries are G-16 material.
|
|
|
|
## 25. Security & Integrity Controls
|
|
|
|
1. Source is opened `rb` only; the Engine never holds a writable handle to the
|
|
raw source and fails if `stat` changes during certification (`F09`).
|
|
2. All outputs live under `engine_output/`; path validation (resolved absolute
|
|
paths under the configured write root) rejects `..`, symlink/junction
|
|
escape, and absolute paths outside the root.
|
|
3. No network access, no credentials, no account data, no secrets, no eval of
|
|
config (config validated against JSON schema).
|
|
4. Checkpoint and journal are hash-chained (`checkpoint_hash`, per-line
|
|
`prev`); optional HMAC layer (key file outside repo) is a NON-BLOCKING
|
|
future hardening (G-17).
|
|
5. Every produced artifact carries `sha256_physical` + `content_id`
|
|
(§17); evidence manifest enumerates all files with both.
|
|
6. Legacy evidence directories are excluded from every access path the Engine
|
|
may write; a runtime guard refuses any write whose target path is not under
|
|
the Engine's own output root.
|
|
7. Logs are sanitized (no raw bid/ask values, no account identifiers).
|
|
8. Malformed sidecar rows contain hashes (`raw_sha256`), not raw text.
|
|
|
|
## 26. Versioning
|
|
|
|
| Constant | Value (v1) | Bump rule |
|
|
|---|---|---|
|
|
| ENGINE_VERSION | 1.1.0 | any Engine behavior change (semver) |
|
|
| PARSER_VERSION | PARSER_V1.1 | any change to CSV/timestamp/price parsing rules (P3-DE-004: extended with G_TICKSTORY_MT5; unambiguous vs the failed PARSER_V1) |
|
|
| GRAMMAR REGISTRY | G_TICKSTORY_MT5 / G_DOTTED_V1 | named presets, certificate-pinned (P3-DE-004; §8.1) |
|
|
| ALGORITHM_VERSION | ALG_V1 | any change to canonical serialization, chunking rule, or aggregation semantics (unchanged by P3-DE-004) |
|
|
| SCHEMA_VERSION (ticks) | CTS_V1 | schema change |
|
|
| SCHEMA_VERSION (bars) | CBS_V1 | bar schema change |
|
|
| DATASET_VERSION | DS_V1.0.0 | human decision gate only (G-5) |
|
|
| MANIFEST_SCHEMA_VERSION | MS_V1 | manifest schema change |
|
|
| CHECKPOINT_SCHEMA_VERSION | CP_V1 | checkpoint schema change |
|
|
| RUN_COMPLETE_SCHEMA_VERSION | RC_V1 | completion schema change |
|
|
|
|
Determinism pins (recorded in every manifest): Python ≥ 3.11, pyarrow exact
|
|
version, pandas (research layer) exact version, `PYTHONHASHSEED=0`, Parquet
|
|
writer version string. Any pin change bumps ENGINE_VERSION and requires
|
|
re-running the golden corpus and determinism tests before further runs.
|
|
|
|
Version mismatch on resume = RESUME_BLOCKED (F10). No automatic data migration;
|
|
schema migrations are new dataset versions (G-5).
|
|
|
|
## 27. Acceptance Criteria
|
|
|
|
The Engine is accepted for full reprocessing only when ALL hold:
|
|
|
|
1. Golden corpus G01–G17 passes under both producer and verifier.
|
|
1a. Source-grammar corpus SG01–SG15 (G_TICKSTORY_MT5 six-column grammar) passes
|
|
under both producer and verifier with the regression timestamp anchor
|
|
(`20030505,00:01:03` → `1_052_092_863_000` ms at tz offset 0), the
|
|
compact-grammar end-to-end pipeline run, and the certificate/grammar
|
|
fail-closed cross-check all green.
|
|
1b. CLI headless E2E includes the run-less-directory regression: `status` and
|
|
`resume` on an uninitialized output directory MUST both exit 4 (BLOCKED)
|
|
without a traceback.
|
|
2. Independent verifier accepts the pilot (step 6).
|
|
3. Resume invariance: content ids of a run interrupted 3× and resumed are
|
|
identical to an uninterrupted run of the same range.
|
|
4. `verify --mode dataset` recomputes all content ids and the dataset hash
|
|
from disk independently and matches.
|
|
5. `verify --mode run-complete` returns exit 0 only on a genuine COMPLETED.
|
|
6. Differential report vs legacy is produced with every item classified.
|
|
7. Source certificate: FULL_SOURCE_SHA256_VERIFIED achieved or the run did not
|
|
start without G-3 authorization.
|
|
8. Malformed rates within limits; malformed sidecar complete.
|
|
9. Research dataset regeneration: two runs of `build-dataset` with the same
|
|
config produce identical dataset hashes without touching the raw source.
|
|
10. Engine completes a full 34.5 GB pass headlessly (AI closed), including at
|
|
least one forced resume.
|
|
11. Legacy evidence directories byte-identical before/after full run.
|
|
12. Source file size and mtime unchanged by the full run.
|
|
|
|
## 28. Risks & Mitigations
|
|
|
|
| # | Risk | Mitigation |
|
|
|---|---|---|
|
|
| R-TZ | Server-time/timezone ambiguity (DST) shifts bar boundaries | canonical ts is a pure function of text; re-derivation with new offset = new dataset version; manifest records offset; G-6 |
|
|
| R-DISK | 34.5 GB source + canonical outputs exceed free space | preflight floor; per-workload floor; PAUSED(disk_space) |
|
|
| R-HW | Disk/CPU failure mid-run | atomic commits; journal truth; resume |
|
|
| R-AV | Antivirus/OneDrive locking files | rename+fsync pattern; documented deployment requirement; retry on transient EACCES (bounded) |
|
|
| R-DETS | Library/dependency drift breaks determinism | pinned versions recorded; golden corpus gate; upgrade bumps ENGINE_VERSION |
|
|
| R-MEM | Parent or worker memory blowup | chunk-bounded workers; memory telemetry; retry/FAILED policy |
|
|
| R-DUP | Duplicate/overlapping data in source (Tickstory known cases) | taxonomy counters; verifier invariants; research layer documents dedup policy per dataset config |
|
|
| R-LEG | Legacy evidence drift/incomplete | legacy dirs read-only; differential classification taxonomy |
|
|
| R-GAP | Weekend/illiquid gaps produce zero-tick bars | bars exist only when ticks exist (no empty-bar inflation); gap analysis is research-layer |
|
|
| R-CERT | Full SHA-256 too slow on target hardware | certification budget; VERIFIED_WITH_LIMITATION is explicit and gated (G-3) |
|
|
| R-SRC | Source replaced/moved by another process | size/mtime re-checks; cert binding; RESUME_BLOCKED |
|
|
| R-SPEC | Spec ambiguity discovered at implementation | every ambiguity must be filed as an Open Decision, not silently assumed |
|
|
|
|
## 29. Open Decisions Requiring Human Authorization
|
|
|
|
Every item below is a HUMAN DECISION GATE. The Engine must NOT proceed past
|
|
the gate without an explicit owner decision recorded in
|
|
`docs/CURRENT_DECISION_GATE.md` (or successor register).
|
|
|
|
| # | Decision | Recommended default | Consequence if not authorized |
|
|
|---|---|---|---|
|
|
| G-1 | Final storage format (Parquet + zstd-3 + pinned pyarrow) | accept default | init blocked |
|
|
| G-2 | Default worker count (24; created = min(24, cpu_count, mem-constrained)) | accept default | ingest uses 1 worker until decided |
|
|
| G-3 | Source certification acceptance: require FULLY_VERIFIED; allow VERIFIED_WITH_LIMITATION only with explicit authorization | FULLY_VERIFIED only | no run under limitation |
|
|
| G-4 | Canonical schema CTS_V1/CBS_V1 (columns, PRICE_SCALE=1e6, mid-based OHLC with PRICE_SCALE_BAR=2e6, vol semantics) | accept default | canonicalization blocked |
|
|
| G-5 | Dataset version identity & bump policy (DS_X.Y.Z, human-only bumps) | accept default | freeze/dataset publish blocked |
|
|
| G-6 | Source timezone offset (default config `source_tz_offset_minutes`; DST not modeled in v1) | record + document | canonicalization blocked |
|
|
| G-7 | Atomic chunk size (24 MiB nominal, line-safe) | accept default | chunkmap frozen with legacy grid |
|
|
| G-8 | Workload size (512 MiB nominal) | accept default | workload grouping frozen |
|
|
| G-9 | Aggregation semantics (UTC clock boundaries, mid OHLC, spread min/max/avg floor, carry policy) | accept default | bar layer blocked |
|
|
| G-10 | Bar partial policy at EOF (flush as is_final=false) & no empty-bar inflation | accept default | bar layer publish blocked |
|
|
| G-11 | Stale-lock release policy (default: refuse; `--force-release-lock` explicit) | refuse by default | blocked while stale lock present |
|
|
| G-12 | Freeze legacy pipeline as reference-only | after G-1..G-10 | reprocessing not authorized |
|
|
| G-13 | Pilot scope (default first 512 MiB window) | accept default | pilot not run |
|
|
| G-14 | FULL 34 GB REPROCESSING authorization | after pilot verification | step 7 never starts |
|
|
| G-15 | Freeze of canonical research dataset + publish hashes | after independent certification | step 12 not executed |
|
|
| G-16 | Legacy-vs-new discrepancy resolution (per UNRESOLVED item) | adjudicate per item | run stays paused |
|
|
| G-17 | Optional checkpoint HMAC hardening | non-blocking future | no impact on v1 |
|
|
|
|
Additionally: any research target definition (Layer 9 configs such as
|
|
M15+15m direction) is owner-authorized research configuration and is out of
|
|
engine scope until G-14 completes. No gate is decided by the Engine.
|
|
|
|
Approved governance clarifications (P3-DATA-ENGINE-001 GOV-CLOSE, human decision baseline 2026-09-07):
|
|
- G-14 remains NOT AUTHORIZED even after design freeze.
|
|
- G-6 approval does not define the actual UTC offset value; `source_tz_offset_minutes` must be established explicitly at source initialization.
|
|
- G-12 is a legacy freeze/reference decision and does not mean the legacy checkpoint becomes a new-engine checkpoint.
|
|
|
|
---
|
|
|
|
## Appendix A — Final Review Gate (design self-check)
|
|
|
|
| # | Question | Answer |
|
|
|---|---|---|
|
|
| 1 | Works with ChatGPT completely closed? | YES — headless CLI, no AI dependency anywhere in the pipeline (§4.3, §14) |
|
|
| 2 | Windows restart interrupts without corrupting state? | YES — atomic tmp+rename, journal truth, orphan-tmp cleanup (§13) |
|
|
| 3 | Resume without AI intervention? | YES — §13.5 procedure is deterministic `sniper-data resume` |
|
|
| 4 | Source identity immutable? | YES — read-only handles, cert binding, F09 (§7, §25) |
|
|
| 5 | Full source SHA requirement explicit? | YES — required; limitation is explicit + gated (§7.4–7.5, G-3) |
|
|
| 6 | Canonical dataset reproducible? | YES — content-id contract independent of physical bytes (§12.4) |
|
|
| 7 | Research datasets regenerable without re-reading raw ticks? | YES — canonical bar layer only (§15) |
|
|
| 8 | Verifier independent of producer validation? | YES — separate implementation, no shared parse/aggregate code (§18) |
|
|
| 9 | Discrepancy old vs new diagnosable? | YES — legacy comparison taxonomy (§24) |
|
|
| 10 | Human decisions clearly identified? | YES — §29 gates G-1..G-17 |
|
|
| 11 | Hidden dependency on future AI interaction? | NO — none |
|
|
| 12 | Any path silently changing semantics? | NO — version gates, RESUME_BLOCKED, fail-closed states (§13, §20, §26) |
|
|
|
|
Non-conformance items, if any, are filed in §29 rather than silently assumed.
|
|
This table records DESIGN COMPLETE.
|
|
|
|
## Appendix B — Glossary
|
|
|
|
| Term | Definition |
|
|
|---|---|
|
|
| chunk | line-safe byte range of the raw source (§9) |
|
|
| workload | deterministic group of consecutive chunks (§9.2) |
|
|
| content_id | canonical logical digest of a layer file (§12.4) |
|
|
| journal | append-only hash-chained commit record (Layer 4/7) |
|
|
| carry | in-progress partial bar per timeframe (§11.4) |
|
|
| material condition | a failure that forces STOP and operator resolution (§20) |
|
|
| expected class | a deterministic, counted, non-fatal data anomaly (§20) |
|
|
|
|
---
|
|
|
|
*End of specification. This document is the only artifact produced by the design session. Nothing in it authorizes execution.*
|
|
|
|
---
|
|
|
|
# Appendix C — P3-DATA-ENGINE-004 CONTROLLED AMENDMENT RECORD
|
|
|
|
Session type: CONTROLLED SPECIFICATION AMENDMENT + IMPLEMENTATION
|
|
RE-QUALIFICATION ONLY (authorized by the owner after the P3-DATA-ENGINE-003
|
|
pilot finding; DESIGN remains FROZEN; no unrelated governance decision is
|
|
re-opened). All amendments below are traceable to the P3-DATA-ENGINE-003
|
|
evidence: source `XAUUSD_mt5_ticks.csv` (size 34,473,661,010 bytes; FULL
|
|
SHA-256 `5252ce8f5b0e7d286b71a3172c7ef9c1009c8e4b1254ac67400a803fcd85cba1`;
|
|
CRLF; ASCII; no header; first raw row `20030505,00:01:03,…`).
|
|
|
|
## C.1 Source grammar (authoritative; supersedes the 8.1 dotted primary)
|
|
|
|
G_TICKSTORY_MT5 :: YYYYMMDD , HH:MM:SS , bid , ask , last , volume
|
|
field count :: exactly 6 (else MALFORMED_FIELD_COUNT)
|
|
date :: 8 digits, month 01..12, day 01..31
|
|
time :: HH:MM:SS, hh 00..23, mi 00..59, ss 00..59 (no ms)
|
|
bid/ask/last :: decimal micro-price (PRICE_SCALE = 1e6; 0..6 fraction
|
|
digits); non-positive -> MALFORMED_PRICE_NONPOSITIVE;
|
|
ask < bid -> MALFORMED_BID_ASK_RELATION (mandatory)
|
|
volume :: non-negative integer; 0 VALID; negative/non-integer
|
|
-> MALFORMED_VOLUME (has_volume=true enforced)
|
|
delimiter :: comma only
|
|
line ending :: CRLF/LF/CR accepted deterministically; recorded
|
|
header :: none in the real source; keyword-detected header
|
|
(date|time|datetime prefix) skipped + has_header=true;
|
|
absence verified by evidence
|
|
|
|
Grammar selection is certificate-bound: `grammar_id` in
|
|
`source_certificate.json` must equal the configured `source_grammar`, else
|
|
`init` is BLOCKED (spec 8.1 certificate/config mismatch). No silent generic
|
|
format acceptance.
|
|
|
|
## C.2 Timestamp
|
|
|
|
`ts_ms = wallclock_epoch_ms(YYYYMMDD, HH:MM:SS, ms=0) -
|
|
source_tz_offset_minutes * 60000`, integer civil-day math, deterministic.
|
|
Production `source_tz_offset_minutes = 0` preserved (P3-DE-003 evidence
|
|
chain: first row `20030505,00:01:03` -> epoch `1,052,092,863` s equivalent
|
|
`1,052,092,863,000` ms == legacy chunk0 first_timestamp; G-6 policy; DST not
|
|
modeled in v1). Regression-anchored by SG13.
|
|
|
|
## C.3 Volume / last-field / versions
|
|
|
|
- Volume: see 8.2 amendment (mandatory source volume for G_TICKSTORY_MT5;
|
|
`vol = 1` substitution FORBIDDEN for this source; zero valid).
|
|
- `last`: validated; EXCLUDED from CTS_V1; preserved in
|
|
`source_preservation/chunk_%06d.jsonl` (spec 8.6); promotion to the
|
|
canonical schema = new CTS version + G-5 gate.
|
|
- Versioning: ENGINE_VERSION 1.1.0; PARSER_VERSION PARSER_V1.1;
|
|
ALGORITHM_VERSION ALG_V1 (unchanged); SCHEMA_VERSION CTS_V1/CBS_V1
|
|
(unchanged); DATASET_VERSION DS_V1.0.0 (unchanged; G-5).
|
|
- G-9 (aggregation semantics) and G-10 (partial-bar policy): UNCHANGED.
|
|
- G-14: UNCHANGED — BLOCKED / NOT AUTHORIZED.
|
|
- Legacy state: checkpoint `759 -> 760 @ byte 18,442,355,762` UNCHANGED;
|
|
workload 46 NOT prepared; legacy pipeline byte-identical and read-only.
|
|
|
|
## C.4 Implementation-visible changes
|
|
|
|
- Parser/source-adapter: two named grammar presets (8.1 registry); compact
|
|
timestamp parse; `last` validation + preservation sidecar; volume
|
|
semantics; certificate `grammar_id` + `compact_date_time` sniffing.
|
|
- CLI defect repair: `status`/`resume` on a run-less directory now exit 4
|
|
(BLOCKED) instead of exit 1 + traceback (P3-DE-003 finding;
|
|
`NEW_ENGINE_DEFECT`, non-data-affecting; regression in CLI E2E).
|
|
- Qualification suite extended: SG01–SG15 source-grammar golden cases +
|
|
compact-grammar end-to-end + dependent acceptance criteria (27).
|
|
|
|
## C.5 Scope exclusions (unchanged)
|
|
|
|
No pilot execution, no 34 GB source processing, no full-source hashing for
|
|
ingestion purposes, no workload 46, no chunk 760+ preparation, no legacy
|
|
checkpoint/pipeline modification, no research dataset generation, no
|
|
forecasting, no G-14 authorization.
|