flightopsDocumentation
Reference: history

Local history catalog

9 min readdocs/reference/history-catalog.md (source repository)

flightops_tools.history_catalog.HistoryCatalog(path) is the SQLite history index for Standard mode. axikit.history_contracts owns the record shapes. The catalog is authority for new native history records; existing task JSON, experiment attempts, and retained-results manifests stay authoritative in their original files. The catalog never executes a check or grants evidence access. It uses SQLite schema version 1 and refuses a newer user_version.

Public API

MethodBehavior
put_record(record) -> strValidate and store a native record by its content digest. Repeated identical input is idempotent. Changed evaluator ID/revision or installation ID/revision association is rejected transactionally.
`get(record_id) -> dictNone`Return a validated native record by its 64-character digest.
`get_entry(identifier) -> dictNone`Return indexed detail for a native or external entry, including source state/path and source SHA.
`get_artifact(identifier) -> dictNone`Return a referenced artifact's path, expected SHA, byte size, and availability state.
source_use_decision(artifact_id, use)Return {allowed, policy_ids, reason} for the latest local source-use policy and every upstream derivation. A requested use also requires retain permission; callers must still supply current external authorization where required.
query(*, project_id, robot_id, asset_id, unit_id, installation_id, attempt_id, kind, status, limit=100)Return exact indexed rows in stable ID order. Filters are optional; limit is 1–100. Rows include authority, record_digest, and source_state.
search(text, *, scope=None, limit=100)Bounded literal-token FTS5 search, with optional native, task, or retained scope.
import_task(path) / import_retained_manifest(path)Explicitly import one bounded source file. Return {source_key, state, items} and an error when stale. No filesystem crawl.
source_status(path, authority)Inspect current/stale/restored/unavailable source state and its last good source digest.
rebuild_projections()Rebuild exact and FTS rows from native records and last good external projections.
backup(destination, *, permitted_artifact_ids=(), authorize=None, max_recording_bytes=20 GiB, max_recording_seconds=300)Create a new directory containing a SQLite snapshot and complete bounded inventory. Selected MCAPs require current caller authorization and current source-use retention permission; MCAP bytes remain excluded by default.
HistoryCatalog.restore(backup_dir, target_db, *, permitted_artifact_ids=(), authorize=None, max_recording_bytes=20 GiB, max_recording_seconds=300)Verify the complete inventory and copied bytes, restore into a new database path, and install selected authorized MCAPs under a target-owned sibling directory. Restored collector scopes are inert and owned-source payloads unavailable.

HistoryCatalog supports with and close(). Rows have one kind and one status: for assessments, status is the execution axis; the other six outcome axes remain in the native record. Exact project/robot/asset/unit/installation filters apply to snapshots, and attempt ID applies to assessments and task attempt projections. Task project_id is the project root path stored by its own authority. FTS indexes bounded titles, identifiers, and selected contract text, not arbitrary evidence bytes.

External projection IDs are task:<task-id>, task-attempt:<attempt-id>, and retained:<result-id>. Their source-file SHA-256 is tracked separately, so a valid changed task refreshes its existing IDs. A corrupt, missing, or unknown schema file marks its registered source stale while its last good rows remain queryable with source_state=stale. A retained artifact whose bytes are missing or changed gets status=missing or changed when its manifest is imported again. A projectless retained manifest can have null project_root. Rebuild preserves these states; it never writes an external authority.

Source-use evaluation fails closed when policy or receipt records are missing, malformed, unresolved, denied, or cyclic. It examines at most 10,000 source-use and receipt records, with 64 KiB per record and a 64 MiB combined body limit, and follows derivation chains to a maximum depth of 64. Larger inputs return a typed denial reason instead of unbounded traversal.

The installation binding fixes an installation ID/revision to project, robot, asset, and physical unit IDs. Firmware or other configuration changes can add new snapshots for that same installation. Replacing the physical unit requires a new installation revision or ID. An evaluator ID/revision binds to exactly one evaluator digest, including its declared measurement and isolation boundaries. Parent identity revision changes alone do not imply a physical replacement; the installation binding intentionally uses their stable IDs. These constraints are enforced in the same transaction as native record publication.

Backup and recovery boundary

A backup inventory names every registered task/manifest source and referenced artifact as copied, missing, changed, or excluded. Source files are copied when their expected digest still matches. Artifact bytes require both selection in permitted_artifact_ids and a latest local source-use policy with retain=allowed; upstream derivative references also have to permit retention. An absent policy, an unknown upstream reference, or a later denial excludes the bytes. The caller must also enforce current authoritative permission before passing artifact IDs: historical records alone do not grant current use.

The catalog database alone is a catalog backup, not a complete evidence backup. restore verifies the database digest, copied file digests, and the complete inventory against the database, and never overwrites original task or artifact paths. Copied bytes stay in the backup directory, and restored source paths point to those copies. Preserve that directory alongside the restored database; unavailable/excluded bytes remain explicit in inventory.json, get_artifact, and retained-result status. A source that was missing or changed at backup time is not made current by restoring its old projection.

Managed recording segments use stable selection IDs of recording-segment:<segment-id>. They are excluded by default. A selected segment can be copied only when it is finalized, its frozen catalog SHA-256 and size match a regular source file, the latest local retain decision allows it, and the supplied authorize(source_artifact_id, "retain") callback returns exactly True for each policy source in that decision. The callback is checked again during copying and immediately before backup publication. It represents current caller authorization; the historical recording-policy digest is capture-time provenance and does not grant permission. The frozen policy decision and its sorted policy IDs are recorded in the inventory for later validation.

MCAP copying streams in 1 MiB chunks and checks source identity, size, and digest before and after reading. The default aggregate copy limit is 20 GiB, the hard per-segment limit is 1 GiB, and the copy-time budget is 300 seconds. Callers may lower the aggregate byte and time limits. The copy also preserves a 512 MiB free-space reserve. Active, interrupted, unavailable, missing, changed, unverified, over-budget, or no-longer-authorized segments remain explicitly excluded or unavailable. A selected source that changes during the copy is never published as a copied payload.

When selected recording bytes or newer metadata are present, the manifest uses flightops.history-backup/3; older /1 and /2 backups remain readable. The /3 inventory still contains one row for every segment and validates the entire inventory against the copied SQLite catalog before restoring. The historical segment lifecycle, original path, digest, and size remain unchanged.

Optional knowledge and learning tables contain private derived content that this portable backup cannot authorize per row. If either extension is present, the manifest uses flightops.history-backup/4. It lists each frozen extension table, bounded row count, and SHA-256 digest of every row under extensions, with an explicit excluded disposition. The copied SQLite database deletes all knowledge case/strategy/event/status rows and all learning record/event rows, then runs VACUUM before it is published. Only the empty table schemas and the knowledge_schema version row survive. Procedure JSON beside the catalog is never copied. A bound marker inside the scrubbed database lets restore detect altered exclusion inventory. Restored knowledge and learning tables are empty: this bundle does not recover their historical content, reviews, routes, or training decisions. Unknown catalog tables, extension columns, triggers, views, or schema versions make backup or restore refuse instead of silently copying private bytes. /1–/3 bundles with extension tables are refused; older bundles without them remain readable. Restore defaults to leaving all segment bytes excluded. A selected payload is copied into <target_db>.recording-payloads/<session_id>/<segment_id>.mcap only after another current authorization check; the separate availability is then restored with restored_path set to that target-owned copy. This avoids resolving an old machine's original path. A /1 backup that contains recording tables is refused because it cannot account for those payloads.

Recording schema v2 backups inventory collector scopes, epochs, and gaps. Restore preserves session-to-scope links and collector history, clears every lease field, sets the desired state to stopped, and marks the service restored-inert with an explicit local re-enable reason. No collector service or local policy path is started or resolved during restore. Independent retention tables are also inventoried row by row with stable IDs and metadata digests. Selected recording copies acquire a retention read lease before opening bytes and hold it through the verified copy, so concurrent expiry cannot remove the source mid-copy. Restore marks retention policies, active read leases, and pending or committed pins inert; pending deletions become interrupted tombstones. Derivative lineage remains historical and is invalidated when source payload is excluded. Already expired segments keep their expired availability and immutable finalized digest. No restored metadata reactivates capture, deletion, or source-use permission. Owned-source tables are inventoried by stable item/revision/event IDs and metadata digests, while their payloads remain excluded. Restore preserves revisions and events but marks each owned source unavailable, records restored_payload_excluded, and clears its original root, relative path, and rights review so old-machine files cannot resolve as current bytes.

Legacy task/manifest source files and artifacts retain their existing backup behavior: copied files remain in the backup directory, and restored paths refer to those backup copies. The combined inventory is bounded to 10,000 entries and 4 MiB of manifest JSON; collector and owned-source metadata are bounded before materialization to 64 KiB per row and 64 MiB total. Catalog freeze for restore streams a regular SQLite file of at most 20 GiB, with a 300-second limit and a 512 MiB free-space reserve before any payload validation.

Reopen an investigation in Standard mode

flightops history --database /path/catalog.sqlite3 investigate task:TASK_ID returns selected historical details after comparing the source with its imported fingerprint. Changed, stale or missing sources retain their indexed identity and explicit failure state; their new contents are not silently substituted.

Add --recording-session SESSION_ID to include a recording, and -o investigation.html to write a standalone dark desktop view. Up to eight records and eight recordings can be selected. Existing output files are never replaced. No model is required.

Task event lanes use the existing bounded timeline composer (five-minute default window with an explicit count of omitted events). Recording log times remain in separate clock lanes. Neither common project identity nor nearby timestamps establishes an association. Current applicability is unevaluated. Recording availability and artifact links are metadata; inspecting this view does not read payloads, grant reuse permission or recheck their bytes.