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
Related Documentation
- Backend Domain Models – Database schema and Laravel implementation
- Tenancy and Roles – RBAC and tenant isolation
- GRI/ESG Scope – Reporting framework and evidence types
- Laravel RBAC Implementation – Security controls
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
ProcessSubmissionJobdispatched - Storage: Raw JSON stored in
submissions.raw_dataJSONB 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:
ValidateSubmissionJobwith 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_idforeign 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:
ProcessMetricsJobapplies transformations - Storage: Processed values in
metric_valuestable - 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
- Laravel:
ReviewSubmissionActionwith permission check - RBAC: See RBAC Matrix for approval permissions
- Dual control: Configurable per metric or tenant
- See: Tenancy and Roles § Review Workflow
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 =
LOCKEDprevents modifications - Database: Foreign key constraints prevent cascade deletes
- Checksums: Stored in
reporting_periods.data_checksumcolumn - 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-nulldate_format: ISO 8601 format (YYYY-MM-DD)numeric: Valid number (int or float)positive: Greater than zeroexists: 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
Related Specifications
- Backend Domain Models – Database implementation
- GRI/ESG Scope v1 – Reporting framework
- RBAC Matrix – Permission model
- Laravel RBAC Implementation – Security implementation
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