Skip to content

OpenFloodAI V1 Data Contracts

1. Purpose

This document defines the first shared data records for OpenFloodAI V1.

The goal is simple: every part of the system should use the same words for the same things. A camera record, a risk result, and an alert candidate should mean the same thing everywhere.

This is a planning document. It does not add camera ingestion, ML, risk-engine logic, storage code, alerting code, dashboard code, or deployment code.

2. Plain-Language Summary

OpenFloodAI watches a river camera and creates records about what it sees.

Some records describe the place. Some describe the camera. Some describe video quality. Some describe water-change evidence. Some describe risk state and alert candidates.

Simple example: instead of writing “big water problem” in one place and “high river” somewhere else, the system should use a clear risk state like HIGH plus reason codes that explain why.

Quick Reference Table

Record Type What It Means Who Uses It
site The river monitoring place. Operators, dashboard, audit records.
camera One camera at a site. Camera setup, health checks, event tracing.
video_frame_metadata Information about a frame without storing the image itself. Vision module, audit/replay tools.
camera_health_output Whether the camera/video feed is usable. Risk engine, operators, audit records.
visual_signal_output Simple visual evidence from a frame or frame pair. Temporal analysis and risk engine.
temporal_analysis_output How evidence changes across time. Risk engine and audit review.
risk_engine_input Evidence sent into the risk engine. Risk engine tests and decision logic.
risk_engine_output The selected risk state and reasons. Alert candidate logic, audit records.
alert_candidate A request for human or approved local review. Operators or review workflow.
event_audit_record Long-term record of an important event. QA, debugging, field review.
delivery_result Whether a notification attempt worked. Audit and operations review.

State Meaning Table

State Simple Meaning
USABLE The camera/video evidence is good enough to use.
DEGRADED Evidence exists, but it is poor quality or partly blocked.
UNKNOWN The system cannot tell what is happening.
UNKNOWN_DEGRADED Risk must not be treated as normal because evidence is missing or unreliable.

3. Data Contract Principles

V1 records should follow these principles:

  • Records must be easy to understand.
  • Required fields must be clearly separated from optional fields.
  • Records must be traceable later for audit and review.
  • Missing or unreliable evidence must be represented clearly.
  • UNKNOWN / DEGRADED states must be explicit.
  • ML output must be treated as evidence, not ground truth.
  • Alert candidates must stay separate from official public warnings.
  • Raw video and exact location data must be handled carefully.
  • Camera credentials must never appear in records that may be logged or shared.

Simple example: if the camera is offline, the record should say the camera is offline. It should not leave the field blank and make people guess.

4. Common Fields Used Across Records

Many records should share these fields.

Required fields:

  • contract_version: version of this data contract.
  • record_id: unique ID for the record.
  • record_type: kind of record, such as camera_health_output or risk_engine_output.
  • site_id: stable ID for the river site.
  • camera_id: stable ID for the camera, when the record is camera-specific.
  • timestamp: time the record was created or observed, in ISO 8601 format.

Optional fields:

  • software_version: version of OpenFloodAI that produced the record.
  • config_version: version of the configuration used.
  • model_version: version of the model used, if a model was used.
  • source_record_ids: IDs of earlier records used to create this record.
  • notes: short human-readable note.

Example:

{
  "contract_version": "v1",
  "record_id": "event-20260827-bridge-01-0001",
  "record_type": "event_audit_record",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45"
}

Privacy or safety concern:

  • site_id and camera_id are needed for traceability.
  • Exact location details should be referenced through site and camera records instead of repeated in every event.

5. Site Record

A site record describes the river monitoring location.

What it is used for:

  • Helps operators know which river place needs attention.
  • Links cameras, events, and alert candidates to one monitoring site.

Required fields:

  • contract_version
  • record_id
  • record_type: site
  • site_id
  • site_name
  • river_name
  • timezone
  • country
  • location_visibility

Optional fields:

  • nearby_landmark
  • municipality
  • district
  • province_or_state
  • latitude
  • longitude
  • coordinate_precision
  • public_location_label
  • restricted_location_notes

Example:

{
  "contract_version": "v1",
  "record_id": "site-record-bridge-01",
  "record_type": "site",
  "site_id": "site-bridge-01",
  "site_name": "Bridge 01 River Watch",
  "river_name": "Example River",
  "timezone": "Asia/Kathmandu",
  "country": "Nepal",
  "location_visibility": "restricted",
  "nearby_landmark": "Old footbridge"
}

Privacy or safety concern:

  • Public views may show river_name or nearby_landmark.
  • Exact coordinates should be restricted when they expose homes, private property, camera positions, or critical infrastructure.

6. Camera Record

A camera record describes one camera at a site.

What it is used for:

  • Helps the system know which camera produced a frame, signal, or event.
  • Stores non-secret camera setup details.

Required fields:

  • contract_version
  • record_id
  • record_type: camera
  • site_id
  • camera_id
  • camera_name
  • camera_status
  • timezone

Optional fields:

  • stream_type, such as file, rtsp, or cctv
  • view_description
  • installation_notes
  • expected_scene
  • public_view_allowed
  • has_people_or_road_in_view
  • retention_policy_id

Example:

{
  "contract_version": "v1",
  "record_id": "camera-record-bridge-01-main",
  "record_type": "camera",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "camera_name": "Main bridge camera",
  "camera_status": "active",
  "timezone": "Asia/Kathmandu",
  "stream_type": "rtsp",
  "has_people_or_road_in_view": true
}

Privacy or safety concern:

  • Camera URLs, usernames, passwords, and tokens must not be stored in general camera records.
  • Secrets should be stored only in a protected secrets system.

7. Location and GPS Fields

Location fields help people understand where an event happened.

Required traceability fields:

  • site_id
  • camera_id

Optional location fields:

  • site_name
  • river_name
  • nearby_landmark
  • municipality
  • district
  • province_or_state
  • country
  • latitude
  • longitude
  • coordinate_precision
  • timezone
  • location_visibility

Recommended values for location_visibility:

  • public: safe to show publicly.
  • restricted: only trusted users should see it.
  • private: highly sensitive; do not expose in public records.

Example:

{
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "river_name": "Example River",
  "nearby_landmark": "Old footbridge",
  "latitude": 27.7172,
  "longitude": 85.324,
  "coordinate_precision": "approximate",
  "location_visibility": "restricted"
}

Privacy or safety concern:

  • Exact GPS coordinates are optional.
  • Exact GPS coordinates and camera placement details should not be public by default.
  • Public records should use a coarse location, such as river name or nearby landmark, when exact coordinates are not needed.

Simple example: the public may need to know “near Old footbridge.” They may not need the exact camera pole location.

8. Video Frame Metadata Record

A video frame metadata record describes a frame without storing the image itself.

What it is used for:

  • Helps trace which frame or time window produced later signals.
  • Helps detect stale or delayed frames.

Required fields:

  • contract_version
  • record_id
  • record_type: video_frame_metadata
  • site_id
  • camera_id
  • timestamp
  • frame_id

Optional fields:

  • video_time_seconds
  • frame_width
  • frame_height
  • frame_rate
  • source_timestamp
  • frame_hash
  • clip_reference
  • snapshot_reference
  • dropped_frame_count

Example:

{
  "contract_version": "v1",
  "record_id": "frame-meta-000123",
  "record_type": "video_frame_metadata",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "video_time_seconds": 12.3,
  "frame_id": "frame-000123",
  "frame_width": 1280,
  "frame_height": 720
}

Privacy or safety concern:

  • This record should point to a clip or snapshot only when storage is allowed by site policy.
  • A frame hash can help detect repeated frames without storing the image.
  • video_time_seconds helps compare human labels with machine records from the same part of a local video.

9. Camera / Feed Health Output

A camera/feed health output says whether the video is usable.

What it is used for:

  • Prevents the system from treating missing or bad video as normal river conditions.
  • Gives the risk engine input quality evidence.

Required fields:

  • contract_version
  • record_id
  • record_type: camera_health_output
  • site_id
  • camera_id
  • timestamp
  • input_quality_state
  • is_usable
  • reason_codes

Optional fields:

  • frame_id
  • evidence_window
  • visibility_score
  • stale_frame_detected
  • obstruction_detected
  • failure_detail

Example:

{
  "contract_version": "v1",
  "record_id": "health-bridge-01-140500",
  "record_type": "camera_health_output",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "input_quality_state": "DEGRADED",
  "is_usable": false,
  "reason_codes": ["STALE_FRAMES"]
}

Privacy or safety concern:

  • Failure messages should not include camera passwords, tokens, or private stream URLs.

Implementation note:

  • Local video health helpers live in src/openfloodai/ingestion/feed_health.py.
  • The first helper checks whether a local video file exists, opens, and has at least one readable frame.
  • It does not inspect river conditions or decide flood risk.

10. Visual / ML Signal Output

A visual/ML signal output contains visual evidence from the image.

What it is used for:

  • Describes simple measurements from the vision module.
  • Sends evidence to temporal analysis and the risk engine.

Early POC helpers may produce simple values such as brightness_score, sharpness_score, and frame_change_score. Region-based helpers may also produce reference_region_used, region_x, region_y, region_brightness_score, region_sharpness_score, region_change_score, region_width, region_height, upper_region_change_score, middle_region_change_score, lower_region_change_score, strongest_changed_area, and water_level_evidence_state. These are test signals only. They do not detect floods.

Simple example: if a user watches a bridge pillar, the helper can say the lower part of that watched box changed more than the upper part. That may be useful water-level evidence, but a person still needs to review it.

Current water_level_evidence_state values:

  • useful_water_level_evidence: the watched area changed in a way that may help human water-level review.
  • weak_visual_evidence: the change is small or not clearly useful.
  • cannot_judge_whole_region_changed: the whole watched area changed, so it may be lighting, blur, or camera movement.
  • cannot_judge_region_too_small: the watched area is too small to judge safely.

Required fields:

  • contract_version
  • record_id
  • record_type: visual_signal_output
  • site_id
  • camera_id
  • timestamp
  • input_quality_state
  • signal_state
  • reason_codes

Optional fields:

  • frame_id
  • model_version
  • water_coverage_ratio
  • relative_level
  • confidence
  • uncertainty
  • region_reference
  • reference_region_used
  • region_x
  • region_y
  • region_brightness_score
  • region_sharpness_score
  • region_change_score
  • region_width
  • region_height
  • upper_region_change_score
  • middle_region_change_score
  • lower_region_change_score
  • strongest_changed_area
  • water_level_evidence_state
  • debug_artifact_reference

Example:

{
  "contract_version": "v1",
  "record_id": "visual-bridge-01-140500",
  "record_type": "visual_signal_output",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "input_quality_state": "USABLE",
  "signal_state": "WATER_COVERAGE_INCREASED",
  "water_coverage_ratio": 0.72,
  "confidence": 0.81,
  "reason_codes": ["WATER_REGION_VISIBLE"]
}

Privacy or safety concern:

  • This is evidence, not ground truth.
  • Debug images should follow the site retention policy.

Simple example: the vision record may say, “water covers about 72 percent of the watched river area.” It does not say, “evacuate.”

11. Temporal Analysis Output

A temporal analysis output summarizes change across time.

What it is used for:

  • Helps avoid decisions based on one odd frame.
  • Shows whether water evidence persisted or changed quickly.

Required fields:

  • contract_version
  • record_id
  • record_type: temporal_analysis_output
  • site_id
  • camera_id
  • timestamp
  • evidence_window
  • temporal_state
  • reason_codes

Optional fields:

  • source_record_ids
  • rate_of_change
  • persistence_duration_seconds
  • missing_frame_count
  • confidence
  • uncertainty

Example:

{
  "contract_version": "v1",
  "record_id": "temporal-bridge-01-140500",
  "record_type": "temporal_analysis_output",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "evidence_window": {
    "start": "2026-08-27T13:59:00+05:45",
    "end": "2026-08-27T14:05:00+05:45",
    "duration_seconds": 360
  },
  "temporal_state": "SUSTAINED_RISE",
  "reason_codes": ["PERSISTENT_WATER_INCREASE"]
}

Privacy or safety concern:

  • Time windows must be accurate enough for audit.
  • Bad clocks should produce degraded time-integrity records.

12. Risk-Engine Input

A risk-engine input is the package of evidence sent to the risk engine.

What it is used for:

  • Keeps the risk engine separate from the ML module.
  • Allows tests to pass fake evidence to the risk engine without using a real camera.

Required fields:

  • contract_version
  • record_id
  • record_type: risk_engine_input
  • site_id
  • camera_id
  • timestamp
  • config_version
  • input_quality_state
  • component_signals
  • evidence_window

Optional fields:

  • source_record_ids
  • model_version
  • confidence
  • uncertainty
  • operator_context

Example:

{
  "contract_version": "v1",
  "record_id": "risk-input-bridge-01-140500",
  "record_type": "risk_engine_input",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "config_version": "config-2026-08-27-a",
  "input_quality_state": "USABLE",
  "component_signals": {
    "water_coverage_ratio": 0.72,
    "temporal_state": "SUSTAINED_RISE"
  },
  "evidence_window": {
    "start": "2026-08-27T13:59:00+05:45",
    "end": "2026-08-27T14:05:00+05:45",
    "duration_seconds": 360
  }
}

Privacy or safety concern:

  • The input should reference site and camera IDs instead of repeating exact restricted location details.

13. Risk-Engine Output

A risk-engine output is the risk state and explanation produced by the risk engine.

What it is used for:

  • Tells the rest of the system what risk state was selected.
  • Explains why the state changed or stayed the same.

Required fields:

  • contract_version
  • record_id
  • record_type: risk_engine_output
  • site_id
  • camera_id
  • timestamp
  • risk_state
  • reason_codes
  • evidence_window
  • config_version

Optional fields:

  • source_record_ids
  • previous_risk_state
  • confidence
  • uncertainty
  • recommended_next_step

Allowed initial values for risk_state:

  • NORMAL
  • ELEVATED
  • HIGH
  • CRITICAL
  • UNKNOWN_DEGRADED

Example:

{
  "contract_version": "v1",
  "record_id": "risk-output-bridge-01-140500",
  "record_type": "risk_engine_output",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "risk_state": "HIGH",
  "previous_risk_state": "ELEVATED",
  "reason_codes": ["SUSTAINED_RISE", "HIGH_WATER_COVERAGE"],
  "config_version": "config-2026-08-27-a",
  "evidence_window": {
    "start": "2026-08-27T13:59:00+05:45",
    "end": "2026-08-27T14:05:00+05:45",
    "duration_seconds": 360
  }
}

Privacy or safety concern:

  • A risk-engine output is not an official public warning.

Test-Only Risk-State Output Skeleton

Early OF-011 code may also return risk_state_output records for simple rule-based tests.

Allowed test states:

  • NORMAL: test signals are below the watch threshold.
  • WATCH: test signals crossed a cautious threshold.
  • WARNING_CANDIDATE: test signals crossed a stronger threshold and need human review.
  • UNKNOWN: camera/feed health is not OK, so the system cannot judge risk.

Simple example: if camera health is BROKEN, the result should be UNKNOWN, not NORMAL.

Safety boundary: WARNING_CANDIDATE is not a public warning. It only means a person or later review system should check the result.

14. Alert-Candidate Record

An alert-candidate record says that a human or approved local process should review the situation.

What it is used for:

  • Routes a concerning risk state to review.
  • Keeps alert candidates separate from official public warnings.

Required fields:

  • contract_version
  • record_id
  • record_type: alert_candidate
  • site_id
  • camera_id
  • timestamp
  • risk_state
  • reason_codes
  • evidence_window
  • input_quality_state
  • software_version
  • config_version

Optional fields:

  • model_version
  • component_signals
  • confidence
  • uncertainty
  • candidate_status
  • operator_notes
  • delivery_result_id

Example:

{
  "contract_version": "v1",
  "record_id": "candidate-bridge-01-140500",
  "record_type": "alert_candidate",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "risk_state": "HIGH",
  "reason_codes": ["SUSTAINED_RISE", "HIGH_WATER_COVERAGE"],
  "input_quality_state": "USABLE",
  "software_version": "0.1.0",
  "config_version": "config-2026-08-27-a",
  "evidence_window": {
    "start": "2026-08-27T13:59:00+05:45",
    "end": "2026-08-27T14:05:00+05:45",
    "duration_seconds": 360
  },
  "candidate_status": "needs_review"
}

Privacy or safety concern:

  • This record must not say an evacuation is required.
  • Public-warning decisions stay outside OpenFloodAI V1 authority during development and early pilots.

Simple example: it can say “please review this camera now.” It should not say “everyone must leave now.”

15. Event / Audit Record

An event/audit record is the long-term record of something important that happened.

What it is used for:

  • Supports later review, debugging, field-pilot analysis, and accountability.
  • Helps answer why an alert candidate was or was not created.

Required fields:

  • contract_version
  • record_id
  • record_type: event_audit_record
  • site_id
  • camera_id
  • timestamp
  • event_type
  • software_version
  • config_version
  • input_quality_state
  • risk_state
  • reason_codes
  • evidence_window

Optional fields:

  • model_version
  • source_record_ids
  • component_signals
  • alert_action_taken
  • delivery_result
  • local_storage_state
  • public_fields
  • restricted_fields

Example:

{
  "contract_version": "v1",
  "record_id": "event-bridge-01-140500",
  "record_type": "event_audit_record",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:00+05:45",
  "event_type": "risk_state_changed",
  "software_version": "0.1.0",
  "config_version": "config-2026-08-27-a",
  "input_quality_state": "USABLE",
  "risk_state": "HIGH",
  "reason_codes": ["SUSTAINED_RISE", "HIGH_WATER_COVERAGE"],
  "evidence_window": {
    "start": "2026-08-27T13:59:00+05:45",
    "end": "2026-08-27T14:05:00+05:45",
    "duration_seconds": 360
  },
  "alert_action_taken": "alert_candidate_created"
}

Privacy or safety concern:

  • Use site/camera references instead of copying exact GPS details into every event.
  • Raw video references should be included only when allowed by site policy.

16. Delivery Result Record

A delivery result record is used if a notification is attempted.

What it is used for:

  • Records whether an alert candidate was delivered to an operator or review channel.
  • Supports audit when delivery fails.

Required fields:

  • contract_version
  • record_id
  • record_type: delivery_result
  • site_id
  • camera_id
  • timestamp
  • alert_candidate_id
  • delivery_channel
  • delivery_status

Optional fields:

  • recipient_role
  • attempt_count
  • provider_message_id
  • failure_reason
  • acknowledged_at
  • acknowledged_by_role

Example:

{
  "contract_version": "v1",
  "record_id": "delivery-bridge-01-140501",
  "record_type": "delivery_result",
  "site_id": "site-bridge-01",
  "camera_id": "camera-bridge-01-main",
  "timestamp": "2026-08-27T14:05:01+05:45",
  "alert_candidate_id": "candidate-bridge-01-140500",
  "delivery_channel": "operator_dashboard",
  "delivery_status": "delivered",
  "recipient_role": "operator"
}

Privacy or safety concern:

  • Avoid storing personal phone numbers, emails, or names unless a future access-control design allows it.
  • Prefer role labels such as operator when possible.

17. Offline / Local Storage Expectations

The edge node should store important records locally when cloud or network access is unavailable.

Expected local records:

  • Camera/feed health outputs.
  • Vision/ML signal outputs when useful for audit.
  • Temporal analysis outputs when useful for audit.
  • Risk-engine outputs.
  • Alert-candidate records.
  • Delivery results, if notification is attempted.
  • System degraded/failure events.

During network loss:

  • Local detection can continue if video, power, storage, and local configuration are available.
  • Records should be queued locally for later upload if upload is configured.
  • Upload may resume after connectivity returns.

Early POC runs may write simple JSON Lines (.jsonl) files for debugging and review. Each line is one record. This is only a local file format, not a production database.

During low disk:

  • The system should report degraded storage health.
  • It should preserve the most important audit records where possible.
  • It should avoid claiming that audit storage is healthy.

Simple example: if the internet is down, the edge node should still remember that a HIGH alert candidate happened at 14:05.

18. Privacy and Sensitive-Data Rules

General privacy and retention rules are defined in Privacy And Retention, including the public-versus-restricted field split for event/audit records. This document's schemas (Sections 14-15) follow that split: public fields stay coarse and non-sensitive; restricted fields (exact GPS, camera placement, raw video references, private contact details) require future access control.

19. Versioning Rules

Records should include versions so future reviewers can understand how the record was produced.

Version fields:

  • contract_version: the data contract version, such as v1.
  • software_version: the OpenFloodAI software version.
  • config_version: the configuration version used for thresholds and settings.
  • model_version: the model version, if a model is used.

Why this matters:

  • A future reviewer can see which code, model, and configuration produced an event.
  • Tests can replay old events with the correct versions.
  • If a model or config was wrong, maintainers can find affected records.

Simple example: if a bad config caused too many false alert candidates, config_version helps identify which events used that config.

20. Validation and Testability Expectations

Future implementation stories should test these contracts.

Validation expectations:

  • Required fields are present.
  • record_type has an expected value.
  • risk_state uses the allowed V1 values.
  • Timestamps are valid and include timezone information.
  • site_id and camera_id are present for camera-specific records.
  • Required version fields are present where relevant.
  • UNKNOWN_DEGRADED is allowed and testable.
  • Sensitive fields do not appear in public records or logs.

Future test examples:

  • A risk-engine output without reason_codes should fail validation.
  • An alert candidate without evidence_window should fail validation.
  • A camera-offline event should use UNKNOWN_DEGRADED, not NORMAL.
  • A public event record should not include a camera password or exact private GPS field.
  • A delivery result should record failure if notification delivery fails.

Machine-readable schema:

  • src/openfloodai/schemas/event.schema.json provides a first simple schema for event/audit records, packaged as importable data via importlib.resources.
  • Example records live in examples/events/.
  • Validation tests live in tests/schema/.
  • Shared validation helpers live in src/openfloodai/contracts/event_validation.py.
  • Local JSON Lines record helpers live in src/openfloodai/contracts/local_store.py.
  • Local video health helpers live in src/openfloodai/ingestion/feed_health.py.
  • Local video frame metadata helpers live in src/openfloodai/ingestion/video_file.py.
  • Simple visual signal helpers live in src/openfloodai/vision/simple_signals.py.
  • The local POC pipeline lives in src/openfloodai/pipeline/local_poc.py.
  • The schema is intentionally focused on event/audit records and should grow only when implementation stories need stricter validation.

21. Known Unknowns

The following are not settled yet:

  • Final field names may change after implementation starts.
  • The exact list of reason codes is not defined yet.
  • The exact evidence-window format may need adjustment during replay testing.
  • Public versus restricted field rules need a full access-control design.
  • Raw video, snapshot, and clip reference formats are not finalized.
  • Model metadata fields may change after model packaging is designed.
  • Location privacy rules may need site-specific policy review.
  • Offline queue ordering and retry behavior are not defined in detail yet.
  • Final JSON schemas for every record type are not created yet.

These unknowns should become future issues, ADRs, schemas, or tests.

Future issue candidates:

  • Add stricter schemas for more record types.
  • Add a public-event schema that blocks exact GPS, camera secrets, and private details from public output.

Reason-code details now live in reason-codes.md.

Architect Review Notes

Architecture decisions supported by these contracts:

  • Events are traceable to site, camera, time, software, config, model, evidence, risk state, and reason codes.
  • Risk-engine records are separate from vision/ML records.
  • Alert candidates are separate from official public warnings.
  • Degraded and unknown states are explicit.
  • Raw video and exact location fields are handled as sensitive data.

Main architecture caution:

  • Do not let future implementation hide sensitive details in logs or repeat exact GPS and camera-placement details in every event.

QA Challenge Notes

QA concerns to test in future stories:

  • Missing required fields must fail validation.
  • Bad or missing timestamps must be visible because event order matters.
  • Camera-offline, stale-frame, and obstruction records must clearly become degraded evidence.
  • Risk-engine tests must use fake inputs so they do not depend on ML.
  • Event replay must preserve software, config, model, and contract versions.
  • Public records must be checked for sensitive fields.
  • Delivery failure must be recorded when notification is attempted.

QA recommendation:

  • These contracts are a good V1 starting point, but they are not complete production schemas. Future work should add stricter schemas, validation tests, reason-code lists, and replay evidence.

Local Sampled Evidence (Issue #104)

These additive fields belong to local POC records. They do not change the public event/audit schema.

Every decoded frame metadata record includes frame_index and mean_brightness. The sampling pipeline adds input_quality_state, reason_codes, and minimum_brightness. Unusable decoded frames retain their metadata.

Each sampled visual_signal_output and its derived risk_state_output includes:

  • video_time_seconds: the later frame time.
  • comparison_start_seconds and comparison_end_seconds: both actual frame times.
  • baseline_frame_index and changed_frame_index: exact decoded frame indices.
  • evidence_window_seconds: the requested period, start included and end excluded.
  • coverage_sufficient: whether the requested period passed prototype coverage rules.
  • source_record_ids: the two frame records for a visual signal, or its visual signal record for a risk result.

A local evidence_window_output record carries the common record identity fields and time_window_seconds, usable/unusable frame counts, unusable_reasons, first_usable_second, last_usable_second, largest_gap_seconds, usable_coverage_fraction, coverage_sufficient, and coverage_reason. It records sample_interval_seconds, max_samples, minimum_brightness, sampled_frame_indices, sampled_video_times, and actual_max_sample_gap_seconds for repeatable review.

An empty period still has an evidence-window record. It has no visual comparison record. Label comparison reports cannot_compare rather than treating missing measurements as zero change.

See the design decision for defaults and limitations.

Confirmed Riverbank Reference (Issue #163)

This record is stored on the site config alongside reference_region, tied to it rather than replacing it: region and every marker's region must fit inside the site's own reference_region. Only the current state is kept, the same as reference_region itself — reconfirming or invalidating overwrites it rather than appending to a history.

  • status: draft (machine-suggested or unconfirmed), confirmed (a human confirmed it), or invalid (no longer trustworthy).
  • region: the confirmed riverbank rectangle, in the same percentage-of-frame shape as reference_region.
  • video_id and video_time_seconds: which video and moment the reference was drawn from.
  • site_id and camera_id: duplicated from the parent config onto the record itself for self-contained provenance, matching this project's other evidence records (for example the run-export receipt).
  • normal_condition: whether the source footage was normal-condition, per the ML readiness plan.
  • notes: free text.
  • markers: a list of {label, region} stable extra references (bridge pillars, rocks, wall edges), each also constrained to fit inside the site's watched area.
  • confirmed_at, invalidated_at, invalidation_reason: server-set timestamps and reason (one of camera_moved, view_changed, bank_changed, visibility_unreliable, other); never client-supplied.

Confirming a reference is not required before running validation — see the ML readiness plan for why a visible riverbank is the agreed first reference, and windowed video evidence for how this record relates to the still-proposed moving-overlay display.

Riverbank/Reference Quality Checks On Validation Samples (Issue #164)

Adds five optional fields to the existing human-reviewed label record (see Local Sampled Evidence and openfloodai.review.human_labels), rather than a new record type — a labeled time window already is the natural unit of "one reviewed sample":

  • riverbank_visible, stable_marker_visible, water_boundary_visible, camera_stable: "yes" | "no" | "unsure". Tri-state on purpose — a reviewer is not forced to claim certainty they do not have.
  • visibility_condition: one of clear, dark, glare, rain, fog, blur, obstruction.

normal_baseline_confirmed is not stored on the record. It is derived at read time as True iff the site's Confirmed Riverbank Reference has both status == "confirmed" and normal_condition == True — a confirmed reference drawn from non-normal-condition footage is not a trustworthy baseline. This is the explicit link between OF-082 and OF-083.

failure_reason is also derived, never reviewer-supplied, from a fixed priority order over the fields above (see openfloodai.review.sample_quality.compute_failure_reason): one of riverbank_not_visible, obstructed_view, camera_moved, water_boundary_unclear, poor_visibility, baseline_not_confirmed, or None if no known problem is recorded. An explicit "no" always beats "unsure" or a missing answer, so a sample is never penalized just for a reviewer's uncertainty. stable_marker_visible is extra, optional evidence (a pillar, rock, or similar) rather than a requirement, so a "no" there never contributes to failure_reason or baseline_ready on its own.

A sample is baseline_ready when it has no failure_reason and the reviewer explicitly answered "yes" (not "unsure") for both riverbank_visible and water_boundary_visible. Everything else is practice_only — never rejected, just not counted toward the trusted baseline. Validation reports (render_site_validation_report) and each site's scorecard summarize baseline_ready vs practice_only counts per site, cross-referenced against the site's confirmed reference — see the ML readiness plan.

Report presentation (Issue #165 / OF-084). render_site_validation_report now presents this data per comparison, rather than only aggregating it: a ## Confirmed Reference section once per report naming the site's confirmed video/time (or a plain "No confirmed reference yet." line when absent), and per comparison window, whether that window's reference evidence was usable (via compute_failure_reason/friendly_failure_reason). The report is honest about a real limitation: the pipeline has no signal for whether water covers more or less of the reference area, only whether a change was seen at all, so a water_change_seen result says a change was seen without claiming a direction. See openfloodai.validation.result_explanation. explain_confirmed_reference and explain_riverbank_evidence.