Skip to content

Data Lifecycle Specification

Version: 1.0 Last Updated: 2026-01-03 Status: Canonical


Overview

This specification defines the canonical data lifecycle for all ESG data submissions (quantitative metrics, qualitative narratives, and supporting evidence) from initial capture through final lock and audit retention.

The lifecycle is designed to be:

  • Implementation-neutral – mappable to Flutter offline collector, Laravel backend, and web review UI
  • Audit-ready – defensible for external assurance and ESG verification
  • ISO-aligned – supports ISO 27001, ISO 9001, and GRI assurance requirements
  • Immutable-first – edits create new versions, never overwrite
  • Offline-friendly – mobile state is authoritative until submission

Lifecycle States

stateDiagram-v2
    [*] --> Draft
    Draft --> Queued: Submit
    Queued --> Received: Upload
    Received --> Validated: Validation Pipeline
    Validated --> Rejected: Validation Failed
    Validated --> Processed: Validation Passed
    Rejected --> Draft: Correction
    Processed --> Approved: Review Approved
    Approved --> Locked: Finalize
    Locked --> [*]

1. Draft (Mobile Only)

State

Data exists only on the mobile device and is not yet visible to backend or reviewers.

Characteristics

  • ✏️ Editable without restriction
  • ⚠️ May be incomplete or partially validated
  • 📱 Evidence files stored locally on device
  • 🔄 Can be saved and resumed multiple times

Controls

Control Implementation
Client-side validation Required fields, format checks, range validation
Local storage SQLite/Hive/Drift with encrypted storage
No audit record Pre-system state (not tracked centrally)
Data persistence Auto-save to prevent data loss

Exit Condition

User explicitly submits the record for upload via the mobile application.

Implementation Notes

  • Flutter: Use local database (Drift/Hive) with offline-first architecture
  • Validation: Light validation only (required fields, basic formats)
  • UX: Clear visual indicator of draft vs submitted state

2. Queued (Mobile → Backend)

State

Submission is finalized locally and queued for transmission. Device may be offline.

Characteristics

  • 🔒 Immutable on device (edit requires reverting to Draft)
  • 🆔 Assigned a UUID for idempotency
  • 📦 Evidence packaged or referenced for upload
  • 📡 Awaiting network connectivity

Controls

Control Implementation
Payload hashing SHA-256 hash of submission data
File integrity Individual file hashes for evidence
Idempotency UUID prevents duplicate processing
Retry logic Exponential backoff with retry queue
Network detection Auto-resume on connectivity

Exit Condition

Successful delivery to backend ingestion endpoint with HTTP 2xx response.

Implementation Notes

  • Flutter: Background sync service with work manager
  • Laravel: Idempotent ingestion endpoint (POST /api/submissions)
  • Network resilience: Queue persists across app restarts
  • Conflict resolution: UUID-based deduplication on server

3. Received (Backend Ingestion)

State

Backend has acknowledged receipt of the submission.

Characteristics

  • 💾 Stored as raw, immutable input
  • ⏳ No business validation applied yet
  • 🔍 Evidence files stored but not trusted
  • 📋 Ingestion event logged

Controls

Control Implementation
Append-only log Immutable ingestion record in audit_logs table
Malware scanning ClamAV or cloud-based scan for uploaded files
Duplicate detection Check for existing UUID, return idempotent response
Raw storage Preserve original payload for forensics
Tenant isolation Ensure submission belongs to correct tenant

Exit Condition

Submission enters validation pipeline (may be asynchronous queue job).

Implementation Notes

  • Laravel: Queue job ProcessSubmissionJob dispatched
  • Storage: Raw JSON stored in submissions.raw_data JSONB column
  • Evidence: S3/MinIO with versioning enabled
  • See: Backend Domain Models § Submission

4. Validated

State

Automated checks have been executed against the submission.

Validation Types

Type Description Examples
Schema Fields, data types, structure Required fields present, correct JSON schema
Logical Ranges, units, dates, calculations Positive numbers, valid date ranges, unit consistency
Referential Foreign keys, entity existence Valid site ID, valid metric ID, period exists
Evidence Presence, format, integrity File extension matches MIME type, hash matches
Business rules Domain-specific constraints Scope 1 emissions require fuel type evidence

Characteristics

  • ✅ Validation results are recorded as structured data
  • 🤖 No human judgment applied (purely deterministic)
  • 🔁 Validation rules are versioned and reproducible
  • 📊 Pass/fail status with detailed error messages

Controls

Control Implementation
Deterministic rules Same input always produces same result
Rule versioning validation_rules table with effective dates
Result logging Store validation output in submissions.validation_results
Error reporting Field-level error messages returned to mobile

Exit Condition

  • All checks pass → proceed to Processed
  • One or more checks fail → transition to Rejected

Implementation Notes

  • Laravel: ValidateSubmissionJob with rule engine
  • Rules: Laravel validation rules + custom business rule classes
  • Extensibility: Plugin architecture for custom validators per metric
  • See: GRI/ESG Scope § Evidence Types

5. Rejected / Needs-Fix

State

Submission failed validation or reviewer pre-checks.

Characteristics

  • ❌ Explicit rejection reasons recorded
  • 💬 Feedback linked to specific fields and/or evidence
  • ✏️ Data remains editable after correction
  • 🔄 Supports resubmission workflow

Controls

Control Implementation
Traceability Full linkage to original submission UUID
Preservation Original data preserved (no overwrite)
Feedback mechanism Structured error messages per field
Version control Old version archived with superseded_by relationship

Exit Condition

Corrected submission is re-queued as a new revision. Old version remains archived with status rejected or superseded.

Implementation Notes

  • Mobile: Display validation errors inline with form fields
  • Laravel: Return structured validation errors via API
  • Versioning: submissions.supersedes_id foreign key for tracking corrections
  • User workflow: "Fix and Resubmit" button creates new draft pre-filled with data

6. Processed

State

Submission has passed validation and is transformed into canonical form.

Processing Activities

Activity Purpose Example
Unit normalization Standardize measurement units kWh → MWh, kg → tonnes
Period alignment Align to reporting calendar Fiscal year → calendar year pro-rating
Aggregation/decomposition Match reporting granularity Site-level → facility-level rollup
Metric mapping Map to GRI/ESRS/SASB frameworks Internal KPI → GRI 305-1 Scope 1
Currency conversion Standardize financial data USD → EUR at period-end rates

Characteristics

  • 📊 Canonical data stored separately from raw input
  • 🔗 Linkage preserved between raw and processed
  • 🔄 Deterministic transformation logic
  • 📈 Ready for aggregation and reporting

Controls

Control Implementation
Transformation rules versioning transformation_rules table with effective dates
Raw → processed linkage metric_values.submission_id foreign key
Reproducibility Processing logic is idempotent and versioned
Audit trail Transformation job logged in audit_logs

Exit Condition

Data is ready for human review and appears in reviewer work queue.

Implementation Notes

  • Laravel: ProcessMetricsJob applies transformations
  • Storage: Processed values in metric_values table
  • Calculations: Formula engine for computed metrics (e.g., emissions factors)
  • See: Backend Domain Models § MetricValue

7. Approved

State

Human reviewer has approved the processed data.

Characteristics

  • ✅ Reviewer identity and timestamp recorded
  • 🔐 Approval applies to a specific revision
  • 📊 Data becomes report-eligible
  • 🎯 May require dual-approval for high-risk metrics

Controls

Control Implementation
Role-based authority Only users with review_esg_data permission
Maker-checker separation Submitter cannot approve own data (optional)
Approval logging Record in audit_logs with reviewer user ID
Time-bound validity Approval linked to specific reporting period lock state

Exit Condition

Data is finalized for reporting and moves to Locked state when period is closed.

Implementation Notes


8. Locked

State

Submission is frozen and immutable (terminal state).

Characteristics

  • 🔒 No edits or deletions allowed
  • 🔄 May only be superseded by a new version (with explicit unlocking)
  • 📑 Used for reporting, exports, and assurance
  • ♾️ Indefinite retention (subject to retention policy)

Controls

Control Implementation
Cryptographic integrity SHA-256 checksum of final data snapshot
Legal hold Integration with retention policies
Audit trail preservation Full history immutable and exported
Access control Read-only access via reporting APIs
Unlocking procedure Requires unlock_reporting_period permission + audit log

Exit Condition

None – this is a terminal state. Data can only be superseded by reopening the reporting period (exceptional case).

Implementation Notes

  • Laravel: Reporting period state = LOCKED prevents modifications
  • Database: Foreign key constraints prevent cascade deletes
  • Checksums: Stored in reporting_periods.data_checksum column
  • See: Backend Domain Models § ReportingPeriod

Key Design Principles

1. Immutability by Default

  • Edits create new versions, never overwrite
  • Original submissions always preserved
  • Enables full audit trail and forensic analysis

2. Clear Separation of Concerns

Separation States Involved
Draft vs Submitted Draft → Queued/Received
Raw vs Processed Received → Processed
Automated vs Human Validated → Approved
Mutable vs Immutable Approved → Locked

3. Audit-First Architecture

  • Every state transition generates an audit log entry
  • Audit logs are append-only and immutable
  • Full traceability from draft to locked state
  • Supports ISO 27001 and GRI assurance requirements

4. Offline-First Friendly

  • Mobile device is authoritative until submission
  • Queued state supports intermittent connectivity
  • Idempotent operations prevent duplicate processing
  • Sync conflicts resolved via UUID-based deduplication

5. Assurance-Ready

  • Deterministic validation and processing
  • Versioned transformation rules
  • Cryptographic integrity checks
  • Full lineage from raw input to final report
  • Supports external ESG verification (ISAE 3000, AA1000)

State Transition Rules

Allowed Transitions

From To Trigger Permission Required
Draft Queued User submits submit_esg_data
Queued Received Upload success (system)
Received Validated Validation job (system)
Validated Processed Validation passed (system)
Validated Rejected Validation failed (system)
Rejected Draft User corrects submit_esg_data
Processed Approved Reviewer approves review_esg_data
Approved Locked Period closed lock_reporting_period

Exceptional Transitions

From To Trigger Permission Required
Locked Approved Period reopened unlock_reporting_period + CFO approval
Approved Processed Approval revoked revoke_approval (audit logged)

Forbidden Transitions

  • Locked → Draft – cannot un-lock and edit directly
  • Approved → Rejected – must revoke approval first
  • Processed → Received – cannot undo processing (reprocess instead)

Implementation Mapping

Flutter Mobile App (Offline Collector)

Lifecycle State Flutter Component
Draft DraftSubmissionModel (local database)
Queued SubmissionQueue (background sync service)
Validation errors ValidationErrorWidget (inline form feedback)

Laravel Backend

Lifecycle State Laravel Component
Received SubmissionController@store
Validated ValidateSubmissionJob
Processed ProcessMetricsJob
Approved ReviewSubmissionAction
Locked ReportingPeriod->lock()

Web Review UI

Lifecycle State UI Component
Processed (pending) Review queue dashboard
Approved Reporting dashboard (read-only)
Locked Archive/export interface

Audit Log Requirements

Every state transition must log:

{
  "event_type": "submission.state_changed",
  "timestamp": "2026-01-03T14:23:45Z",
  "actor": {
    "user_id": 123,
    "role": "esg_data_reviewer",
    "tenant_id": 5
  },
  "subject": {
    "submission_id": "uuid-here",
    "old_state": "processed",
    "new_state": "approved"
  },
  "metadata": {
    "validation_errors": [],
    "reviewer_comment": "Data verified against source documents",
    "ip_address": "192.168.1.1"
  }
}

See: Backend Domain Models § Audit Logging


Data Retention Policy

State Retention Period Rationale
Draft 30 days (mobile) Auto-delete abandoned drafts
Queued Until uploaded Retry until success or user cancels
Received → Approved 7 years ISO 9001, GRI assurance requirements
Locked Indefinite Legal and regulatory retention
Audit logs 10 years Extended regulatory compliance

Security Considerations

Tenant Isolation

  • All states enforce tenant_id scoping
  • Row-level security policies in PostgreSQL
  • API endpoints validate tenant membership

See: Tenancy and Roles § Multi-Tenancy

Role-Based Access Control

Action Required Permission
Submit data submit_esg_data
Review data review_esg_data
Approve data approve_esg_data (optional dual control)
Lock period lock_reporting_period
Unlock period unlock_reporting_period
View locked data view_reporting_data

See: RBAC Implementation

Data Integrity

  • Checksums: SHA-256 for all evidence files and locked submissions
  • Versioning: S3/MinIO versioning enabled for evidence storage
  • Encryption: At-rest (database/storage) and in-transit (TLS 1.3)

Validation Rules Reference

Standard Validations

Applies to all submissions:

  • required: Field must be present and non-null
  • date_format: ISO 8601 format (YYYY-MM-DD)
  • numeric: Valid number (int or float)
  • positive: Greater than zero
  • exists: Foreign key references valid entity

Metric-Specific Validations

Example for GRI 305-1 (Scope 1 Emissions):

[
    'value' => ['required', 'numeric', 'min:0'],
    'unit' => ['required', 'in:tonnes_co2e,kg_co2e'],
    'calculation_method' => ['required', 'in:direct_measurement,emission_factors'],
    'evidence' => ['required', 'array', 'min:1'],
    'evidence.*.type' => ['in:fuel_invoice,meter_reading,emission_calculation']
]

See: Validation rules stored in validation_rules table (backend-domain-models.md)


Future Enhancements

  • Blockchain integration: Immutable audit trail with distributed ledger
  • AI validation: Machine learning models for anomaly detection
  • Real-time streaming: Event-driven architecture for instant validation
  • Multi-language support: Localized validation messages
  • Advanced versioning: Git-like branching for scenario modeling


Changelog

Version Date Changes Author
1.0 2026-01-03 Initial canonical lifecycle specification Claude

Glossary

  • Assurance: Independent verification of ESG data by external auditor
  • Canonical form: Standardized, normalized representation of data
  • Idempotency: Operation that produces same result when repeated
  • Immutability: Data cannot be modified after creation
  • Lineage: Traceable history from source to final report
  • Tenant: Isolated customer organization in multi-tenant system

Document Control Approved by: [Pending] Next Review: 2026-07-03 Classification: Internal Use