1122 lines
43 KiB
Markdown
1122 lines
43 KiB
Markdown
# Lardon3D Project Database
|
|
|
|
## Authority and current state
|
|
|
|
This document defines the persistent Project Database architecture and the durable contracts that
|
|
span schema versions. Historical sections remain authoritative for the version they describe, but
|
|
present-tense statements in this document use the current schema head.
|
|
|
|
```text
|
|
CURRENT_PROJECT_DB_SCHEMA=v26
|
|
```
|
|
|
|
The current head is additive:
|
|
|
|
```text
|
|
v22 Selected scientific execution foundation PASS / FROZEN
|
|
v23 Generic optical-context overlay IMPLEMENTED / VALIDATED / REVIEWED
|
|
v24 raw.develop.batch/1 persistence IMPLEMENTED / VALIDATED
|
|
v25 features.extract.batch/1 persistence IMPLEMENTED / VALIDATED
|
|
v26 Capture geometric state and exact calibration applicability PASS / FROZEN
|
|
```
|
|
|
|
Project DB v26 preserves the scientific and persistence meaning of every earlier retained row. No
|
|
migration from v22 through v26 backfills scientific identity, infers camera/lens identity, invents
|
|
calibration, rewrites Capture identity, or changes historical Task payload interpretation.
|
|
|
|
The current real A6000 pre-SfM proof completed the v24 RAW-batch and v25 Feature-batch paths before
|
|
Geometric Verification and Track publication. Therefore v24 and v25 are not validation-in-progress
|
|
states.
|
|
|
|
## Database role
|
|
|
|
Project DB stores reconstruction metadata, durable execution payloads, immutable scientific
|
|
identities, provenance relationships and restart state. Large binary scientific artifacts remain
|
|
outside SQLite and are referenced through validated identities and paths.
|
|
|
|
The database is designed to remain:
|
|
|
|
- persistent;
|
|
- bounded in memory access;
|
|
- restartable;
|
|
- transactionally consistent;
|
|
- explicit about identity;
|
|
- independent from runtime-only resource reservations;
|
|
- compatible with sequential additive migration.
|
|
|
|
Project DB never persists pthread objects, callbacks, pointers, active Resource Reservations, worker
|
|
objects, ncurses state or other process-local runtime state.
|
|
|
|
## Identity invariants
|
|
|
|
The following identities are distinct unless a specific FROZEN contract explicitly relates them:
|
|
|
|
```text
|
|
Capture != file
|
|
Capture != Asset
|
|
Capture != image_id
|
|
Capture != SHA-256
|
|
Capture != path
|
|
Capture != basename
|
|
Capture != Task ID
|
|
Capture != campaign group ID
|
|
Task ID != scientific acquisition identity
|
|
campaign group ID != Capture identity
|
|
```
|
|
|
|
Specific meanings:
|
|
|
|
- SHA-256 identifies immutable asset bytes.
|
|
- `capture_id` identifies one physical acquisition representation in Project DB.
|
|
- `image_id` identifies one scientific image representation.
|
|
- `task_id` identifies durable execution work.
|
|
- campaign `group_id` identifies one stable operational group inside a campaign request.
|
|
- optical body, lens, optical configuration and calibration are separate identities.
|
|
|
|
No migration may recover or manufacture Capture identity from path, digest, basename, timestamp,
|
|
metadata, `image_id`, Task ID or campaign group ID unless an explicit future contract defines that
|
|
identity.
|
|
|
|
## Current additive head
|
|
|
|
### Project DB v23 - generic optical-context overlay
|
|
|
|
**IMPLEMENTED / VALIDATED / REVIEWED.**
|
|
|
|
The v22 -> v23 migration is transactional and additive. It creates nine empty tables:
|
|
|
|
- `camera_body_profiles`;
|
|
- `camera_body_aliases`;
|
|
- `lens_profiles`;
|
|
- `lens_profile_aliases`;
|
|
- `optical_configurations`;
|
|
- `acquisition_campaign_group_optics`;
|
|
- `capture_optical_configurations`;
|
|
- `optical_calibration_profiles`;
|
|
- `capture_calibration_selections`.
|
|
|
|
A camera body, lens, optical configuration and calibration are four separate identities. An optical
|
|
configuration binds exactly one body, one lens and an optional integer focal length in micrometers.
|
|
|
|
A manual lens without EXIF is a normal supported case. Its explicit lens profile requires no metadata
|
|
alias. The Meike fixture proves this generic path and does not introduce any brand- or model-specific
|
|
product branch.
|
|
|
|
Metadata aliases, when present, are exact `BINARY` make/model pairs. They are never fuzzy lookup,
|
|
case folding, spelling correction or implicit identity evidence.
|
|
|
|
A campaign may assign a different optical configuration to each group before materialization. When
|
|
S3-E returns a Capture, the same transaction retains the group-to-Capture mapping, copies the
|
|
campaign optical assignment and advances the campaign cursor. A late rollback reverts all three
|
|
mutations.
|
|
|
|
An explicit post-import optical assignment is allowed only for a Capture that is still unassigned.
|
|
Absence remains `NOT_FOUND`; no synthetic "unknown" profile is created.
|
|
|
|
A calibration profile references one existing immutable sparse calibration and exactly one optical
|
|
configuration. It does not copy, recompute, interpolate or fabricate coefficients. Compatible lists
|
|
filter only by exact optical-configuration identity.
|
|
|
|
Calibration selection on a Capture is always explicit and requires an exact configuration match.
|
|
Multiple compatible profiles remain ambiguous until one is selected. Exact retries converge. A
|
|
valid durable conflict returns `CONSTRAINT`; malformed persisted state returns `CORRUPT` before
|
|
comparison with an alternative request.
|
|
|
|
The v22 -> v23 migration does not inspect EXIF, path, basename, SHA-256, dimensions, device name or
|
|
historical calibration. `schema_version=23` is the durable publication point. Failure while creating
|
|
the overlay or updating a marker that does not target exactly v22 rolls the migration back.
|
|
|
|
### Project DB v24 - bounded selected RAW-batch persistence
|
|
|
|
**IMPLEMENTED / VALIDATED.**
|
|
|
|
The v23 -> v24 migration adds only:
|
|
|
|
```text
|
|
raw_development_batch_tasks(task_id, selected_execution_id)
|
|
```
|
|
|
|
`task_id` references generic Task state. `selected_execution_id` is unique. The row does not copy a
|
|
Capture, Asset, image, path, campaign group or selected-execution cursor.
|
|
|
|
The migration is transactional and DDL-only. It creates no historical association, and the v24
|
|
schema marker is its publication point.
|
|
|
|
`raw.develop.batch/1` reloads the durable `selected_executions` cursor for every admitted window.
|
|
Independent RAW items may be prepared concurrently by bounded participants. After all participants
|
|
join, only the owner Task publishes selected representations, in increasing selected-item order.
|
|
|
|
The durable ordering is:
|
|
|
|
```text
|
|
bounded independent RAW preparation
|
|
-> join all participants
|
|
-> owner-only selected representation publication
|
|
-> selected item + selected cursor commit
|
|
-> generic Task progress/checkpoint
|
|
```
|
|
|
|
Therefore a crash may leave generic Task progress behind durable selected execution state, but never
|
|
ahead of it. Restart resumes from durable selected state without guessing the identity of an already
|
|
published RAW-derived representation.
|
|
|
|
The historical `raw.develop/1` path remains valid. v24 does not modify RAW Policy v1, `L3DRAWD1`,
|
|
Capture identity, image identity or calibration.
|
|
|
|
The retained real A6000 proof completed this RAW-batch path.
|
|
|
|
### Project DB v25 - bounded selected Feature-batch persistence
|
|
|
|
**IMPLEMENTED / VALIDATED.**
|
|
|
|
The v24 -> v25 migration adds only `feature_extract_batch_tasks`.
|
|
|
|
A row binds one durable `features.extract.batch/1` Task to:
|
|
|
|
- one immutable selected execution;
|
|
- a monotonic `next_item_index` prefix;
|
|
- the exact ORB extractor kind/version/parameters/fingerprint domain.
|
|
|
|
The migration is transactional and DDL-only. It creates no scientific rows, converts no historical
|
|
`features.extract/1` Task, and infers no `image_id` or Feature Set.
|
|
|
|
Selected images may be prepared concurrently by bounded CPU participants without SQLite access.
|
|
After every participant joins, the owner publishes Feature Sets in selected order. An item advances
|
|
only after the exact READY Feature Set is durable; generic Task progress/checkpoint follows.
|
|
|
|
A crash may therefore leave generic progress or the Feature-batch cursor behind an immutable Feature
|
|
Set that is already durable. Restart revalidates that exact result and converges without inventing
|
|
identity.
|
|
|
|
ORB U8x32 format, keypoints, descriptor semantics, Feature Store identity and fingerprints remain
|
|
unchanged.
|
|
|
|
The retained real A6000 proof completed the v25 Feature-batch path and preserved:
|
|
|
|
```text
|
|
Feature Sets 689
|
|
Candidate Pairs 38,420
|
|
Match Results 38,420
|
|
```
|
|
|
|
No acquisition, RAW development, Candidate generation or Matcher replay was required during the
|
|
final A6000 GV/Tracks continuation.
|
|
|
|
### Project DB v26 - Capture geometric state and exact calibration applicability
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v25 -> v26 migration is transactional and additive. It creates empty
|
|
`capture_geometric_states`, `optical_calibration_applicabilities_v2` and
|
|
`capture_calibration_selections_v2` tables. It creates no observed state,
|
|
applicability, selection, calibration profile or scientific identity for a
|
|
historical row.
|
|
|
|
`capture_geometric_states` is Capture-owned evidence, not Capture identity. It
|
|
references the existing exact v23 body/lens/focal configuration and stores a
|
|
bounded, versioned tuple of geometry-relevant observations: focus, aperture,
|
|
stabilization, crop, geometric pipeline, representation and decoded geometry,
|
|
with explicit metadata or caller-explicit provenance. Unknown is durable but
|
|
never a default or a compatible value.
|
|
|
|
A v26 applicability binds an existing immutable calibration profile to one
|
|
complete exemplar state. A target Capture is compatible only when its exact
|
|
configuration and every state field match the exemplar. There is no
|
|
cross-body/lens/focal substitution, nearby-focus borrowing, interpolation or
|
|
extrapolation. An incomplete target is `CALIBRATION_REQUIRED`; zero, one and
|
|
multiple complete candidates resolve respectively to `CALIBRATION_REQUIRED`,
|
|
resolved and `SELECTION_REQUIRED`. An explicit v26 selection is immutable,
|
|
idempotent on exact retry, and rejected unless the selected applicability is
|
|
exactly compatible.
|
|
|
|
The v1 optical-profile and calibration-selection APIs/tables remain unchanged.
|
|
The v26 tables are a separate additive foundation; they do not attach a Sparse
|
|
calibration scope or transition a selected execution to `READY`.
|
|
|
|
## Optical TUI workflow
|
|
|
|
The optical TUI uses bounded public APIs and does not write SQLite directly. It can:
|
|
|
|
- inspect a Capture;
|
|
- resolve exact metadata aliases;
|
|
- page through bodies, lenses, configurations and compatible calibrations;
|
|
- create immutable body/lens/configuration profiles;
|
|
- explicitly assign campaign-group or Capture optics where the persistence contract permits;
|
|
- explicitly select an exactly compatible calibration.
|
|
|
|
A manual lens without electronics or metadata aliases is normal data. A lookup with no exact match
|
|
remains unresolved.
|
|
|
|
Absence, multiple candidates, incompatibility, `BUSY`, I/O failure and corruption remain explicit.
|
|
The TUI never fabricates an "unknown" profile, compatibility relationship or identity.
|
|
|
|
The TUI model borrows Project DB until unbind and must be detached before project close. See
|
|
[runtime.md](runtime.md).
|
|
|
|
## Project DB v22 - selected scientific execution snapshot
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v21 -> v22 additive migration adds:
|
|
|
|
- `capture_source_assets`;
|
|
- `selected_executions`;
|
|
- `selected_execution_items`;
|
|
- `raw_development_tasks`.
|
|
|
|
`capture_source_assets` retains the explicit relation published by S3-E between a Capture, one
|
|
`SOURCE` Asset and the already validated JPEG/RAW source kind. Publication of source association and
|
|
source kind is transactional. Exact retry converges; a different source kind conflicts.
|
|
|
|
Captures migrated from v21 remain honestly unmapped. Migration never derives a mapping from path,
|
|
name, SHA-256, attachment order or logical image.
|
|
|
|
An ordered selected-execution snapshot explicitly retains:
|
|
|
|
```text
|
|
quality_task_id/group_id
|
|
-> campaign_task_id/group_id
|
|
-> capture_id
|
|
```
|
|
|
|
The quality and campaign `group_id` namespaces remain independent. Numeric equality between them is
|
|
never identity.
|
|
|
|
Both Tasks must target the same ScanSet, triage must be complete and every retained result must be
|
|
included at snapshot creation. Snapshot contents and order are immutable afterward.
|
|
|
|
Each selected item receives an `image_id` only through an already durable explicit `capture_images`
|
|
association. Representation publication and selected cursor advance are transactional. Exact retry
|
|
converges; a different `image_id` conflicts.
|
|
|
|
After the final item, an existing `calibration_scope_id` may be attached only when every retained
|
|
image belongs to that scope. Terminal `READY` is exactly:
|
|
|
|
```text
|
|
QUALITY_SELECTED
|
|
INTERSECT REPRESENTATION_READY
|
|
INTERSECT CALIBRATION_ASSIGNED
|
|
```
|
|
|
|
No step infers Capture from Asset, SHA-256, path, basename, Task ID, group ID or `image_id`.
|
|
|
|
The snapshot also freezes the representation source. An A6000 selected item explicitly retains the
|
|
RAW `asset_id` already associated as `SOURCE`. An item using a source image explicitly retains the
|
|
absence of RAW identity. This discriminant/Asset pair is immutable and part of exact retry.
|
|
|
|
The selected execution is bounded to 4096 items and reserves no execution resources by itself.
|
|
|
|
Calibration Bootstrap v1 validates a bounded calibration artifact against this snapshot, creates or
|
|
reuses immutable calibrations/scopes and attaches the result through v22 persistence. It does not
|
|
extend the schema, solve calibration or perform RAW development.
|
|
|
|
### Durable single-RAW Task
|
|
|
|
The RAW developer also exposes a bounded entry point keyed by explicit `source_asset_id`. It
|
|
requires the Capture/SOURCE-RAW relation, resolves managed bytes through that Asset ID, verifies the
|
|
bytes against persisted SHA-256, then reuses RAW Policy v1 unchanged.
|
|
|
|
`raw_development_tasks` stores exactly:
|
|
|
|
- `task_id`;
|
|
- `capture_id`;
|
|
- `source_asset_id`;
|
|
- monotonic phase `PENDING -> PUBLISHED`;
|
|
- the derived `image_id` when published.
|
|
|
|
Generic Task snapshot, checkpoint reference and typed RAW payload are written transactionally.
|
|
Immutable Asset/image publication may precede the generic checkpoint if the process dies; exact S3-B1
|
|
retry converges by content and publishes the phase/image identity without guessing.
|
|
|
|
Persistent reads reject malformed SQLite storage classes, phases, nullability or inconsistent
|
|
Capture/RAW/image relations as durable corruption.
|
|
|
|
The v22 persistence boundary passed its retained normal, C17, targeted ASan/UBSan and review
|
|
evidence. The full sanitizer context retains the documented third-party RADV/LSan qualification.
|
|
This freeze concerns persistence, not real-camera calibration availability.
|
|
|
|
## Project DB v21 - Photo Quality Triage
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v20 -> v21 migration adds `photo_quality_triage_tasks` and
|
|
`photo_quality_triage_results` without changing v20 Capture/Asset/image identities.
|
|
|
|
Each result retains canonical plan `group_id` in `1..N`. `next_group_id` uses the same one-based
|
|
identity:
|
|
|
|
```text
|
|
initial 1
|
|
after group k k + 1
|
|
terminal N + 1
|
|
```
|
|
|
|
A private `group_index = group_id - 1` may exist inside the executor but is not durable identity.
|
|
|
|
Result and cursor become durable atomically before generic Task checkpoint. Reads validate SQLite
|
|
64-bit storage class, sign, range and relationships before narrowing to public C fields and require
|
|
the exact `photo_quality.triage/v1` dispatch.
|
|
|
|
Measured recommendation and explicit human override remain distinct.
|
|
|
|
## Project DB v20 - durable acquisition-campaign execution
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v19 -> v20 migration adds:
|
|
|
|
- `acquisition_campaign_tasks`;
|
|
- `acquisition_campaign_captures`.
|
|
|
|
It does not change v19 Capture/Asset identities.
|
|
|
|
`acquisition_campaign_tasks` has a one-to-one relation to generic Task through `task_id`, the
|
|
operational campaign identity. Creation and checkpoints transactionally retain generic Task snapshot,
|
|
checkpoint reference and typed campaign payload.
|
|
|
|
The campaign record stores:
|
|
|
|
- `scanset_id`;
|
|
- historical `next_group_id`;
|
|
- group count;
|
|
- immutable request bytes.
|
|
|
|
Despite its historical name, campaign-table `next_group_id` is a zero-based next-work position in
|
|
`0..N`; its value equals the number of one-based groups already retained.
|
|
|
|
An upsert may advance the cursor only when ScanSet, group count and immutable request bytes are exact
|
|
matches.
|
|
|
|
The request v1 codec is bounded and deterministic with explicit magic/version, fixed-width
|
|
little-endian integers, validated lengths/enums/indices and no trailing bytes. It persists explicit
|
|
confirmations as `CALLER_EXPLICIT`, never as inferred `STRONG`.
|
|
|
|
Plan groups keep one-based IDs. For every completed group,
|
|
`acquisition_campaign_captures` retains the unique relation:
|
|
|
|
```text
|
|
(task_id, group_id) -> capture_id
|
|
```
|
|
|
|
A Capture cannot represent two groups of the same campaign Task. Valid durable state contains exactly
|
|
the mapping prefix already retained, with no holes or mappings ahead of the cursor.
|
|
|
|
After S3-E returns, one transaction retains the current group mapping and advances the campaign
|
|
cursor before generic Task progress/checkpoint. Restart can then pass the retained `capture_id` back
|
|
to S3-E without rehoming or inference.
|
|
|
|
Malformed storage class, bounds, dispatch kind/version or persistent relationship returns `CORRUPT`
|
|
before mutation and never creates a false `resume_capture_id`.
|
|
|
|
Each Task sequence materializes one group. Pause/cancel are cooperative at group boundaries.
|
|
A non-terminal group sets `sequence_break`, releases admission and requires re-admission by the
|
|
existing Queue and Resource Governor.
|
|
|
|
The registry reconstructs the Task from Task ID and immutable request at project open. No parallel
|
|
runtime, Queue, Governor, Scheduler or persistence subsystem is introduced.
|
|
|
|
The accepted residual crash window is strictly after S3-E has created/returned a Capture but before
|
|
the campaign transaction has retained that Capture. In this window Project DB deliberately does not
|
|
infer `capture_id`; exactly-once is not claimed for that group.
|
|
|
|
## Project DB v19 - Capture / Asset Provenance v1
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v18 -> v19 migration adds:
|
|
|
|
- `captures`;
|
|
- `capture_images`;
|
|
- `capture_assets`;
|
|
- `capture_selections`;
|
|
- `asset_derivations`.
|
|
|
|
A Capture belongs to one ScanSet. It is not an `image_id` and does not redefine scientific image
|
|
identity.
|
|
|
|
Source and derived Assets may be attached without creating a logical image. An optional current
|
|
selection references exactly one image already attached to the Capture and never mutates the image,
|
|
its Asset or prior scientific results.
|
|
|
|
Explicit attachment of an existing `SOURCE` Asset to an existing Capture is idempotent. The same
|
|
association is accepted without a new row or migration. It creates no logical image, changes no
|
|
selection and performs no automatic pairing.
|
|
|
|
`asset_derivations` intentionally represents one parent Asset and one child Asset with kind/version
|
|
and canonical 32-byte fingerprint. It is not a generic DAG and does not perform RAW development or
|
|
video extraction.
|
|
|
|
The v18 -> v19 legacy migration creates one independent legacy Capture per image in increasing
|
|
`image_id`, attaches that image Asset as source and selects the same image. It does not interpret
|
|
filename, EXIF or sibling relationships.
|
|
|
|
### S3-E - Multi-Source Ingestion v1
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
S3-E receives one explicit ScanSet and at most 64 caller-supplied source paths. It never scans a
|
|
directory.
|
|
|
|
Every source is first published as an immutable managed Asset. S3-D evidence is then extracted from
|
|
those published bytes.
|
|
|
|
Automatic grouping creates a common Capture only for a unique mutual `SAME_ACQUISITION_STRONG`
|
|
relationship. Filename, path, SHA-256, timestamp, model, weak evidence, contradiction or ambiguity is
|
|
never Capture identity.
|
|
|
|
Duplicate Assets inside one request are rejected deterministically.
|
|
|
|
The caller may provide explicit groups. Their provenance is `CALLER_EXPLICIT`, distinct from
|
|
scientific `STRONG`. This basis is an operation result, not a new durable identity column.
|
|
|
|
Sibling relations are persisted through S3-C. A camera JPEG remains `SOURCE` and may become a
|
|
logical image of the Capture through transactional source-image publication. RAW remains `SOURCE`;
|
|
its logical image is produced only through deterministic S3-B1 derived publication.
|
|
|
|
RAW and JPEG from one acquisition do not create separate Captures. Selection changes only when the
|
|
caller explicitly requests the selected representation.
|
|
|
|
Once a Capture ID has been returned or durably retained, restart supplies that
|
|
`resume_capture_id`. Publication and attachments then converge without rehoming or duplicate logical
|
|
objects.
|
|
|
|
Before the caller durably retains that ID, v19 does not claim whole-request exactly-once and never
|
|
infers restart identity from SHA, path, basename, timestamp, metadata or `image_id`.
|
|
|
|
### S3-D - Acquisition Pairing Evidence v1
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
S3-D extracts a fixed bounded metadata evidence set from immutable source Assets.
|
|
|
|
- RAW metadata extraction uses LibRaw without pixel decoding.
|
|
- JPEG metadata extraction uses libexif.
|
|
- Evidence is not scientific identity and does not modify existing identity semantics.
|
|
|
|
Only a known, non-empty, exactly shared camera `ImageUniqueID` is strong evidence eligible for
|
|
automatic association between two distinct Assets.
|
|
|
|
Timestamp, make/model, matching body serial number, dimensions/exposure and basename are weak or
|
|
corroborating only. Known conflicting `ImageUniqueID` or known conflicting body serial numbers prove
|
|
that the Assets differ. Missing values are not conflicts. Ambiguity is never resolved by input order.
|
|
|
|
S3-D performs no DB write and no schema change. S3-C remains the persistence primitive; S3-E consumes
|
|
S3-D evidence without redefining it.
|
|
|
|
### Bounded discovery and campaign planning
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The bounded discovery layer accepts 1..64 explicit absolute lexically normalized roots. It does not
|
|
recurse. Each root and its immediate regular entries are inspected without following symlinks.
|
|
|
|
Supported discovery suffixes are `.arw`, `.jpg` and `.jpeg`, case-insensitive. Discovery sources,
|
|
groups and proposals are each bounded to 4096 elements. Paths use deterministic `strcmp` ordering.
|
|
|
|
Planning runs S3-D metadata-only. It does not decode pixels, write Project DB or materialize Assets
|
|
or Captures.
|
|
|
|
Automatic association requires one unique mutual strong relationship. A shared filename stem is only
|
|
a proposal. Contradictions, ambiguities, insufficient comparisons and unresolved inputs remain
|
|
singletons.
|
|
|
|
The caller may explicitly confirm groups; confirmed groups are `CALLER_EXPLICIT`.
|
|
|
|
The plan and its transient progress are caller-owned. The durable campaign identity/restart mechanism
|
|
is the v20 Task path above.
|
|
|
|
The retained A6000 dry-run evidence observed:
|
|
|
|
```text
|
|
ARW sources 953
|
|
JPEG sources 953
|
|
total sources 1906
|
|
metadata OK 1906
|
|
strong groups 0
|
|
stem candidate proposals 953
|
|
ambiguities 0
|
|
contradictions 0
|
|
```
|
|
|
|
The Sony JPEG files were valid MPF containers with one secondary JPEG and zero-only trailing fill.
|
|
The dry run did not write Project DB, materialize Captures or develop RAW. The 953 proposals required
|
|
explicit `CALLER_EXPLICIT` confirmation.
|
|
|
|
## Project DB v18 - Phase H v1 incremental reconstruction
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The v17 -> v18 migration:
|
|
|
|
- adds nullable `derivation_identity` to `sparse_reconstructions`;
|
|
- creates `incremental_reconstructions`;
|
|
- creates `incremental_reconstruction_tasks`.
|
|
|
|
Historical Gate F rows keep `derivation_identity IS NULL` and their exact
|
|
`parameter_fingerprint`. Their historical candidate tuple remains protected by its partial UNIQUE
|
|
index.
|
|
|
|
Derived Phase H rows retain the true H fingerprint in `parameter_fingerprint` and canonical H
|
|
scientific SHA-256 identity in `derivation_identity`, protected by a second partial UNIQUE index.
|
|
|
|
Transactional reconstruction of the parent and child tables preserves v17 IDs, rows, foreign keys
|
|
and cascades without rewriting scientific data.
|
|
|
|
`incremental_reconstructions` atomically binds one complete Sparse SfM snapshot to:
|
|
|
|
- its predecessor;
|
|
- extension Track Set;
|
|
- calibration scope;
|
|
- H kind/version;
|
|
- H fingerprint;
|
|
- unique H scientific identity.
|
|
|
|
`incremental_reconstruction_tasks` stores the immutable durable H Task payload.
|
|
|
|
No Resource Reservation state, resource snapshot, partial geometry, observation subset, component
|
|
remap or dependency graph is persisted. Restart recomputes from immutable inputs.
|
|
|
|
Phase H publication reuses the full-snapshot structural validator. Geometry and H metadata are
|
|
inserted in one transaction; late metadata failure rolls back the new geometry as well.
|
|
|
|
## Project DB v17 - Gate F durable Sparse SfM Task
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
The historical v15 -> v16 Sparse SfM persistence model remains unchanged. v16 -> v17 adds exactly one
|
|
typed durable Task table: `sparse_sfm_tasks`.
|
|
|
|
One row per `task_id` references generic `tasks(task_id)` with cascade deletion and retains:
|
|
|
|
- immutable `track_set_id`;
|
|
- immutable `calibration_scope_id`;
|
|
- `sfm_kind`;
|
|
- `sfm_version`;
|
|
- the 27 effective `Lardon3DSparseIncrementalParameters` values.
|
|
|
|
Finite floating-point values are stored as SQLite `REAL` and must round-trip exact binary64 bits.
|
|
Canonical C-width integers preserve their defined domains.
|
|
|
|
Task kind/version remain in `tasks` and version interpretation of the typed payload. The F0
|
|
fingerprint is derived on reload and is not duplicated.
|
|
|
|
Generic Task summary and typed payload are written in one transaction. Missing, incompatible or
|
|
invalid typed state prevents runtime reconstruction; defaults are never silently substituted.
|
|
|
|
The four full-domain `uint64_t` fields for observation/Track limits and relative-pose/PnP seeds are
|
|
stored as exact eight-byte little-endian BLOBs.
|
|
|
|
Global persisted metrics are Gate F diagnostics over the final retained Gate E observations. They do
|
|
not participate in scientific identity.
|
|
|
|
Gate F publication persists a component exactly when registered-image count and landmark count are
|
|
both positive. A component whose BA was rejected but whose Gate D geometry remains valid is
|
|
persisted unchanged; an unreconstructed graph component is omitted.
|
|
|
|
## Project DB v16 - Sparse SfM persistence model
|
|
|
|
**PASS / FROZEN.**
|
|
|
|
Sparse SfM v1 is atomically published through:
|
|
|
|
- `sparse_calibrations`;
|
|
- `sparse_calibration_scopes`;
|
|
- `sparse_calibration_scope_images`;
|
|
- `sparse_reconstructions`;
|
|
- `sparse_reconstruction_components`;
|
|
- `sparse_registered_images`;
|
|
- `sparse_landmarks`;
|
|
- `sparse_landmark_observations`.
|
|
|
|
Results are immutable. Large collections are accessed through bounded cursors; Project DB does not
|
|
load a complete reconstruction by default.
|
|
|
|
Coordinates remain in the arbitrary frame of each component. Observation references do not duplicate
|
|
descriptors or pixel coordinates.
|
|
|
|
Child indexes support foreign-key deletion/validation paths, including calibration members and
|
|
reconstruction calibration scopes.
|
|
|
|
Gate C adds pure numerical primitives outside Project DB and creates no extra v16 table or identity.
|
|
|
|
## Project DB v15 - durable Track Builder payload
|
|
|
|
The v14 -> v15 migration adds only `track_builder_tasks`. It changes no Track Model table and no
|
|
scientific identity.
|
|
|
|
The row references an atomically published scope file:
|
|
|
|
```text
|
|
.lardon3d/checkpoints/<task_id>.scope
|
|
```
|
|
|
|
The row retains size, SHA-256, format, exact selector, Builder fingerprint, `gvr_count` and
|
|
`input_scope_hash`.
|
|
|
|
The scope file contains `L3DTSCP1`, explicit version, ID count and sorted unique little-endian
|
|
`uint64` IDs. Restart replays from the beginning; no cursor may discard transient graph edges.
|
|
|
|
Reconstruction validates format, size, checksum, bounds, ordering, uniqueness, selector, fingerprint
|
|
and `L3DTSIS1` before creating a new callback. Corruption makes the Task unreconstructable without
|
|
creating a Track Set.
|
|
|
|
## Project DB v14 - Track Model v1
|
|
|
|
The v13 -> v14 migration creates:
|
|
|
|
- `track_sets`;
|
|
- `tracks`;
|
|
- `track_observations`.
|
|
|
|
The executable SQL remains canonical in `src/project_db.c`; the complete scientific contract is in
|
|
[tracks.md](tracks.md).
|
|
|
|
Core persistent invariants:
|
|
|
|
```text
|
|
Track Set identity:
|
|
(builder_kind, builder_version, parameter_fingerprint,
|
|
verifier_kind, verifier_version, verifier_fingerprint,
|
|
input_scope_hash)
|
|
|
|
track observation primary identity:
|
|
(track_set_id, feature_set_id, feature_index)
|
|
```
|
|
|
|
SQL and API jointly enforce:
|
|
|
|
- one observation occurrence per Track Set;
|
|
- `observation_count >= 2`;
|
|
- cascade Track/Track Set deletion semantics;
|
|
- unique `position_in_track` per Track;
|
|
- referenced Feature Set existence;
|
|
- Track observation parent/set consistency;
|
|
- at most one observation from one image in a Track;
|
|
- `feature_index < feature_count`;
|
|
- exact Track/observation counts.
|
|
|
|
Track identities are immutable after publication.
|
|
|
|
## Project DB v13 - durable Geometric Verifier Task
|
|
|
|
The v12 -> v13 migration adds only `geometric_verifier_tasks`.
|
|
|
|
It stores:
|
|
|
|
- `after_match_result_id`;
|
|
- the seven v1 scientific parameters;
|
|
- the verifier control fingerprint.
|
|
|
|
UPSERT may advance only the cursor. Configuration remains immutable. Transactional migration failure
|
|
leaves a true v12 and exact retry converges.
|
|
|
|
## Project DB v12 - Geometric Verification Result Model
|
|
|
|
The v11 -> v12 migration adds only `geometric_verification_results`.
|
|
|
|
Parent must be a `MATCHED` Match Result; cross-row validation remains in the API.
|
|
|
|
Stable values include:
|
|
|
|
```text
|
|
FUNDAMENTAL=1
|
|
GEOMETRIC_REJECTED=1
|
|
GEOMETRIC_VERIFIED=2
|
|
```
|
|
|
|
Identity is:
|
|
|
|
```text
|
|
(match_result_id, verifier_kind, verifier_version, parameter_fingerprint)
|
|
```
|
|
|
|
`parameter_fingerprint` is exactly 32 bytes. Inlier-mask representation is canonical and bounded.
|
|
REJECTED forbids a model; VERIFIED requires all nine finite 3x3 model values. Both states require
|
|
canonical mask size/padding/popcount and `inlier_count <= parent.match_count`.
|
|
|
|
See [geometric_verification.md](geometric_verification.md) for the complete bit-order contract.
|
|
|
|
## Project DB v11 - durable Matcher Task
|
|
|
|
The v10 -> v11 migration adds `matcher_tasks`.
|
|
|
|
It stores immutable matcher configuration and durable `after_candidate_pair_id`. UPSERT may only
|
|
advance the cursor.
|
|
|
|
The cursor identifies the last checkpointed Candidate Pair ID; it does not imply contiguous IDs or
|
|
a persisted Candidate Pair list.
|
|
|
|
## Project DB v10 - Match Result Model
|
|
|
|
The v9 -> v10 migration adds `match_results`.
|
|
|
|
Persistent scientific identity is the six-part tuple:
|
|
|
|
```text
|
|
(candidate_pair_id,
|
|
feature_set_id_a,
|
|
feature_set_id_b,
|
|
matcher_kind,
|
|
matcher_version,
|
|
parameter_fingerprint)
|
|
```
|
|
|
|
Core invariants:
|
|
|
|
- Candidate Pair exists.
|
|
- Feature Set A belongs to Candidate Pair image A.
|
|
- Feature Set B belongs to Candidate Pair image B.
|
|
- `NO_MATCH` requires count zero and no match artifact.
|
|
- `MATCHED` requires positive count and complete SHA/path/size artifact fields.
|
|
- execution failures remain Task Runtime state and create no Match Result row.
|
|
- `matcher_kind` text is bounded to 64 characters.
|
|
- `parameter_fingerprint` is exactly 32 bytes.
|
|
|
|
The executable SQL remains canonical in `src/project_db.c`.
|
|
|
|
## Project DB v9 - durable Candidate Pair Task
|
|
|
|
The v8 -> v9 migration adds `candidate_pair_generate_tasks`.
|
|
|
|
The durable payload stores:
|
|
|
|
- `visual_index_id`;
|
|
- non-negative `after_feature_set_id`;
|
|
- `top_k` in `1..256`;
|
|
- `minimum_evidence_count` in `0..1024`;
|
|
- `scanset_filter` in the defined enum domain;
|
|
- boolean `exclude_same_asset`.
|
|
|
|
UPSERT updates only the restart cursor under immutable configuration.
|
|
|
|
## Project DB v8 - Candidate Pair Model
|
|
|
|
The v7 -> v8 migration adds `candidate_pairs`.
|
|
|
|
Persistent invariants:
|
|
|
|
- `candidate_pair_id` is positive AUTOINCREMENT identity;
|
|
- `image_id_a < image_id_b`;
|
|
- self-pairs are impossible;
|
|
- `(image_id_a, image_id_b)` is UNIQUE;
|
|
- `created_at` is non-negative Unix seconds.
|
|
|
|
The API canonicalizes image order before INSERT, supports exact find/load and paginated listing.
|
|
|
|
## Project DB v7 - persistent project/runtime foundation
|
|
|
|
The v7 foundation contains the durable project, Task, checkpoint, catalog, Feature Store and Visual
|
|
Index state used by later migrations.
|
|
|
|
Important tables include:
|
|
|
|
- `metadata`;
|
|
- `project`;
|
|
- `tasks`;
|
|
- `checkpoints`;
|
|
- `artifacts`;
|
|
- `scansets`;
|
|
- `image_assets`;
|
|
- `images`;
|
|
- `image_import_tasks`;
|
|
- `feature_assets`;
|
|
- `feature_sets`;
|
|
- `feature_extract_tasks`;
|
|
- `sift_extract_tasks`;
|
|
- Feature Support tables;
|
|
- Visual Index tables and update Tasks.
|
|
|
|
`metadata` contains the schema version and durable next Task ID state.
|
|
|
|
`project` stores one stable logical Project identity.
|
|
|
|
`tasks` stores durable generic Task summary and exact Task kind/version when known.
|
|
|
|
`checkpoints` references `DURABLE` or `PUBLISHED_NOT_DURABLE` checkpoint publication state.
|
|
|
|
`artifacts` inventories external files as `STAGED` or `READY`.
|
|
|
|
`scansets`, `image_assets` and `images` store logical acquisition grouping, immutable content identity
|
|
and logical scientific image identity.
|
|
|
|
`UNIQUE(scanset_id, asset_id)` rejects duplicate content inside one ScanSet without merging distinct
|
|
ScanSets.
|
|
|
|
Feature assets remain external content-addressed files. SQLite stores Feature Set identity,
|
|
extractor/version/fingerprint, source hash, descriptor type/dimension/count and lightweight coverage
|
|
metrics.
|
|
|
|
Visual Index postings remain outside SQLite. SQLite stores index identity, segment identity,
|
|
membership and durable update cursor.
|
|
|
|
Published catalog, Feature Store and Visual Index identities use SQLite `AUTOINCREMENT` where the
|
|
contract requires non-reuse of committed identities. IDs from rolled-back transactions were never
|
|
published and may be reused.
|
|
|
|
SQLite configuration at this foundation is:
|
|
|
|
```text
|
|
foreign_keys=ON
|
|
journal_mode=DELETE
|
|
synchronous=FULL
|
|
busy_timeout=5000
|
|
```
|
|
|
|
DELETE journal mode matches the current single-owner design, avoids persistent WAL/SHM files and
|
|
preserves strong synchronization. The timeout bounds waiting on an external DB lock to five seconds.
|
|
|
|
## Historical migration ledger
|
|
|
|
The schema evolves only through sequential additive migrations.
|
|
|
|
```text
|
|
v1 -> v2 Task kind/version columns
|
|
v2 -> v3 durable image-import Task payload and next Task ID metadata
|
|
v3 -> v4 ScanSets, image Assets/images and legacy catalog marker
|
|
v4 -> v7 Feature Store, Feature Support and Visual Index foundations
|
|
v7 -> v8 Candidate Pair Model
|
|
v8 -> v9 Candidate Pair durable Task
|
|
v9 -> v10 Match Result Model
|
|
v10 -> v11 Matcher durable Task
|
|
v11 -> v12 Geometric Verification Result Model
|
|
v12 -> v13 Geometric Verifier durable Task
|
|
v13 -> v14 Track Model v1
|
|
v14 -> v15 Track Builder durable payload
|
|
v15 -> v16 Sparse SfM persistence
|
|
v16 -> v17 Sparse SfM durable Task
|
|
v17 -> v18 Phase H v1 persistence
|
|
v18 -> v19 Capture / Asset Provenance v1
|
|
v19 -> v20 durable acquisition-campaign execution
|
|
v20 -> v21 Photo Quality Triage
|
|
v21 -> v22 selected scientific execution + single RAW Task
|
|
v22 -> v23 generic optical-context overlay
|
|
v23 -> v24 selected RAW-batch Task persistence
|
|
v24 -> v25 selected Feature-batch Task persistence
|
|
v25 -> v26 Capture geometric state + exact calibration applicability
|
|
```
|
|
|
|
Historical version-specific contracts remain valid for the rows and checkpoints they describe.
|
|
An older version number is not stale when the text explicitly describes historical state.
|
|
|
|
## Opening and migration
|
|
|
|
An empty Project DB is created and migrated through the complete known chain to v26.
|
|
|
|
A supported historical DB is migrated sequentially to v26. Each migration is transactional. A
|
|
migration failure rolls back both newly created objects and the schema-version marker for that step,
|
|
leaving the prior version complete and retryable.
|
|
|
|
The implementation rejects:
|
|
|
|
- future schema versions;
|
|
- missing/invalid schema-version metadata;
|
|
- impossible version/storage-class combinations;
|
|
- malformed required durable relationships.
|
|
|
|
The migration implementation must recognize only the known sequential range through v26. It must not
|
|
skip an intermediate contract.
|
|
|
|
Important rollback properties retained from historical tests include:
|
|
|
|
- v12 failure leaves true v11;
|
|
- v13 failure leaves true v12 without `geometric_verifier_tasks`;
|
|
- v14 failure leaves true v13 without Track Model tables;
|
|
- v15 failure leaves true v14 without `track_builder_tasks`;
|
|
- the same transactional principle covers v16 through v22;
|
|
- v23 failure leaves no partial optical overlay and a true v22;
|
|
- v24 failure leaves no partial RAW-batch table/marker and a true v23;
|
|
- v25 failure leaves no partial Feature-batch table/marker and a true v24.
|
|
- v26 failure leaves no partial geometric-state/applicability tables or marker and a true v25.
|
|
|
|
Exact executable migration SQL is owned by `src/project_db.c`. Documentation may summarize it, but
|
|
must not contradict the source or the FROZEN migration tests.
|
|
|
|
## Task checkpoint boundary
|
|
|
|
The durable Task model and its checkpoint codec are implemented independently from Project DB.
|
|
|
|
Project DB stores a queryable logical summary and a checkpoint reference. The validated checkpoint
|
|
file remains the complete source for `lardon3d_task_restore()`; SQLite alone never reconstructs a
|
|
Task.
|
|
|
|
DB/checkpoint consistency compares the fields that `tasks` actually stores, including Task ID, name,
|
|
saved/recovery state, progress and sequence count. It does not pretend to compare fields that are not
|
|
stored in the DB summary.
|
|
|
|
A summary mismatch or invalid checkpoint prevents recovery.
|
|
|
|
Checkpoint paths are portable relative paths:
|
|
|
|
```text
|
|
.lardon3d/checkpoints/<task_id>.chk
|
|
```
|
|
|
|
Generic publication ordering is:
|
|
|
|
```text
|
|
write and durably publish .chk.next
|
|
-> commit SQLite checkpoint reference/summary
|
|
-> under .chk.lock, promote .chk.next to canonical .chk
|
|
```
|
|
|
|
`.chk.lock` is advisory and process-local synchronization, not recovery data.
|
|
|
|
Project inventory uses its DB page only for discovery. After acquiring `.chk.lock`, it reloads the DB
|
|
row because the row may have changed while waiting.
|
|
|
|
A valid canonical checkpoint whose summary matches DB wins over a stale/corrupt `.next`. If the
|
|
canonical file does not match, a valid matching `.next` may be promoted under the lock.
|
|
|
|
After copying the DB record outside the SQLite mutex, recovery consults the Task Kind Registry and
|
|
distinguishes:
|
|
|
|
- `LEGACY_UNTYPED`;
|
|
- `UNKNOWN_TASK_KIND`;
|
|
- `UNSUPPORTED_TASK_KIND_VERSION`.
|
|
|
|
No business reconstructor is called while holding the Project DB mutex.
|
|
|
|
Project open scans recoverable Tasks in pages of 8 by increasing Task ID. A full Queue window stops
|
|
the scan without mutating remaining Tasks. The bounded summary reports `inspected`, `resumed`,
|
|
`skipped`, `failed`, recovered `PUBLISHED_NOT_DURABLE` checkpoints and saturation.
|
|
|
|
Project/schema/identity migration errors are fatal to open. Per-Task legacy/unknown/future kind,
|
|
invalid checkpoint, unavailable source or reconstruction failure is non-fatal. SQLite `BUSY` after
|
|
the configured timeout stops the scan without a retry loop and leaves the Project open.
|
|
|
|
## Concurrency and ownership
|
|
|
|
One opaque Project DB connection is serialized by an internal mutex.
|
|
|
|
Each composite public operation owns its full transaction. No public transaction remains open across
|
|
API calls.
|
|
|
|
Artifact I/O does not occur under the SQLite mutex. The module verifies caller-published regular
|
|
files before setting durable READY state.
|
|
|
|
Closing Project DB concurrently with an active Project/DB API call is forbidden by owner contract.
|
|
|
|
Records and strings are copied into caller-provided buffers. No SQLite pointer escapes the call.
|
|
Statements are finalized before return.
|
|
|
|
Bounded list/recovery APIs use caller-owned pages, with the documented maximums for each interface.
|
|
|
|
## Project integration
|
|
|
|
`Lardon3DAppState` owns exactly one `Lardon3DProjectDb *` while `project_loaded` is true.
|
|
|
|
Canonical DB path:
|
|
|
|
```text
|
|
<project_root>/project.db
|
|
```
|
|
|
|
Project create/open opens the DB. `lardon3d_project_close()` closes it exactly once after the owning
|
|
runtime has respected the Queue/DB lifetime boundary.
|
|
|
|
Application shutdown destroys/joins the Task Queue before Project DB close.
|
|
|
|
`project.ini` v2 stores:
|
|
|
|
- project name;
|
|
- 128-bit hexadecimal `stable_id`;
|
|
- `version=2`.
|
|
|
|
The same stable identity exists in Project DB `project`. Divergence is an error.
|
|
|
|
A legacy INI without identity adopts an existing DB identity. If no DB exists, one identity is
|
|
generated once and the INI is atomically migrated before later opens use it. An existing DB with no
|
|
Project row may be initialized only when the INI already carries the identity required by the
|
|
migration contract.
|
|
|
|
## Real-data compatibility evidence
|
|
|
|
The generic optical overlay was validated on migrated copies of real S21 and A6000 projects with
|
|
scientific row counts preserved and the new optical tables empty until explicit use.
|
|
|
|
The current A6000 Project DB v25 proof retains the current selected scientific pipeline state without
|
|
identity rewriting:
|
|
|
|
```text
|
|
Selected images 689
|
|
Feature Sets 689
|
|
Candidate Pairs 38,420
|
|
Match Results 38,420
|
|
Applicable GVR 37,805
|
|
Verified GVR 10,952
|
|
Rejected GVR 26,853
|
|
Track Sets 1
|
|
Tracks 130,714
|
|
Track observations 318,944
|
|
Sparse SfM Tasks 0
|
|
Sparse reconstructions 0
|
|
Dense / MVS 0
|
|
```
|
|
|
|
The final continuation reused upstream durable work:
|
|
|
|
```text
|
|
ACQUISITION_REPLAY=0
|
|
RAW_REPLAY=0
|
|
FEATURE_REPLAY=0
|
|
VISUAL_INDEX_REPLAY=0
|
|
CANDIDATE_REPLAY=0
|
|
MATCHER_REPLAY=0
|
|
```
|
|
|
|
The restart proof traversed existing Match Results, produced no duplicate GVR mapping and reused
|
|
Track Set 1.
|
|
|
|
This evidence proves persistence/restart behavior through Tracks. It does not create calibration for
|
|
the historical A6000 campaign and does not authorize Sparse SfM or Dense/MVS execution.
|
|
|
|
## Deferred persistence integration work
|
|
|
|
The following items remain outside the acquired Project DB persistence boundary and must not be
|
|
misreported as complete:
|
|
|
|
- autosave wiring on every possible runtime transition;
|
|
- UI retry workflow for unavailable sources;
|
|
- legacy TUI migration cleanup;
|
|
- orphan-file reconciliation and safe scrub;
|
|
- Visual Index compaction or a future capacity evolution.
|
|
|
|
Feature Store and Visual Index v1 themselves are implemented. Visual Index v1 retains the historical
|
|
bounded capacity of 256 segments with 16 memberships per segment, for 4096 Feature Sets in one index.
|
|
Larger scopes require compaction or an explicitly authorized evolution; this operational capacity
|
|
must not be silently reinterpreted as a scientific dataset limit.
|
|
|
|
## Status summary
|
|
|
|
```text
|
|
CURRENT_PROJECT_DB_SCHEMA=v26
|
|
|
|
v22 selected scientific execution foundation PASS/FROZEN
|
|
v23 generic optical-context overlay IMPLEMENTED/VALIDATED/REVIEWED
|
|
v24 raw.develop.batch/1 persistence IMPLEMENTED/VALIDATED
|
|
v25 features.extract.batch/1 persistence IMPLEMENTED/VALIDATED
|
|
v26 Capture geometric state/applicability PASS/FROZEN
|
|
|
|
REAL_S21_TRACKS PASS/FROZEN
|
|
REAL_A6000_PRE_SFM PASS/FROZEN
|
|
```
|
|
|
|
Current Project DB opens and migrates supported historical databases through the sequential known
|
|
chain to v26.
|
|
|
|
Historical contracts for Candidate Pair, Matcher, Geometric Verification, Tracks, Sparse SfM,
|
|
Phase H, Capture/Asset Provenance, campaign execution, Photo Quality and selected scientific
|
|
execution remain valid at the versions where they were introduced.
|
|
|
|
No current Project DB migration:
|
|
|
|
- invents Capture identity;
|
|
- infers optical identity;
|
|
- fabricates calibration;
|
|
- turns metadata into scientific calibration;
|
|
- converts historical Task kinds into newer Task kinds;
|
|
- rewrites historical scientific rows;
|
|
- creates a generic dependency DAG;
|
|
- persists active Resource Governor reservations.
|
|
|
|
Future schema changes beyond v26 require explicit human authorization.
|