{
  "schemaVersion": "1",
  "source": {
    "title": "Offline Mode for the Field Inspections App",
    "path": "examples/mobile-offline-mode.md",
    "sha256": "3af60c7aa5063fe921f45a9d855b54ba69f66035e0bef50e04942b45a4e31a37"
  },
  "generator": {
    "name": "heuristic"
  },
  "requirements": [
    {
      "id": "REQ-c3081d",
      "text": "Inspectors must be able to download assigned inspections for offline use before leaving coverage.",
      "kind": "functional",
      "priority": "must",
      "details": [
        "The download includes checklists, prior findings and site documents."
      ],
      "source": {
        "line": 27,
        "endLine": 28,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-e3f064",
      "text": "The app must let inspectors fill in checklists, add notes and capture photos while offline.",
      "kind": "functional",
      "priority": "must",
      "details": [
        "All offline changes are stored locally and encrypted at rest."
      ],
      "source": {
        "line": 30,
        "endLine": 31,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-5199d9",
      "text": "When connectivity returns, the app must sync queued changes to the server automatically in the background.",
      "kind": "functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 33,
        "endLine": 34,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-327858",
      "text": "Sync must resume after interruption without duplicating photos.",
      "kind": "functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 33,
        "endLine": 34,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-bc70e9",
      "text": "If the same inspection was edited on the server while the inspector was offline, the app must detect the conflict and let the inspector choose which version of each conflicting field to keep.",
      "kind": "functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 36,
        "endLine": 37,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-7d05f0",
      "text": "The app should show a clear offline indicator and the number of changes waiting to sync.",
      "kind": "functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 39,
        "endLine": 39,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Must have"
        ]
      }
    },
    {
      "id": "REQ-0d2dcf",
      "text": "Inspectors could pre-fetch map tiles for assigned sites so directions work offline.",
      "kind": "functional",
      "priority": "could",
      "details": [],
      "source": {
        "line": 43,
        "endLine": 43,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Scope",
          "Nice to have"
        ]
      }
    },
    {
      "id": "REQ-8aae2b",
      "text": "Offline storage must support at least 50 inspections and 2 GB of photos per device.",
      "kind": "non-functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 47,
        "endLine": 47,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Non-functional requirements"
        ]
      }
    },
    {
      "id": "REQ-34651f",
      "text": "Local data must be encrypted with a key stored in the platform keystore.",
      "kind": "non-functional",
      "priority": "must",
      "details": [],
      "source": {
        "line": 48,
        "endLine": 48,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Non-functional requirements"
        ]
      }
    },
    {
      "id": "REQ-6fe87c",
      "text": "Sync of a 20-photo inspection should complete within 3 minutes on a 3G connection.",
      "kind": "non-functional",
      "priority": "should",
      "details": [],
      "source": {
        "line": 49,
        "endLine": 49,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Non-functional requirements"
        ]
      }
    }
  ],
  "outOfScope": [
    {
      "text": "Offline creation of new sites or new inspection templates.",
      "source": {
        "line": 53,
        "endLine": 53,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Out of scope"
        ]
      }
    },
    {
      "text": "Real-time collaboration between inspectors on the same inspection.",
      "source": {
        "line": 54,
        "endLine": 54,
        "section": [
          "Offline Mode for the Field Inspections App",
          "Out of scope"
        ]
      }
    }
  ],
  "epics": [
    {
      "id": "EP-5715ce",
      "title": "Offline Mode for the Field Inspections App",
      "description": "Field inspectors use the mobile app at construction sites and rural facilities where signal is unreliable. When the connection drops, the app shows a spinner and inspectors lose notes and photos. Inspectors fall back to paper and re-type reports later.",
      "requirementIds": [
        "REQ-c3081d",
        "REQ-e3f064",
        "REQ-5199d9",
        "REQ-327858",
        "REQ-bc70e9",
        "REQ-7d05f0",
        "REQ-0d2dcf"
      ]
    },
    {
      "id": "EP-bfd533",
      "title": "Offline Mode for the Field Inspections App: non-functional requirements",
      "description": "Requirements under \"Offline Mode for the Field Inspections App > Non-functional requirements\".",
      "requirementIds": [
        "REQ-8aae2b",
        "REQ-34651f",
        "REQ-6fe87c"
      ]
    }
  ],
  "stories": [
    {
      "id": "ST-c3081d",
      "epicId": "EP-5715ce",
      "title": "Download assigned inspections for offline use before leaving coverage",
      "asA": "field inspector",
      "iWant": "to download assigned inspections for offline use before leaving coverage",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-c3081d-1",
          "given": "a field inspector",
          "when": "the field inspector downloads assigned inspections for offline use before leaving coverage",
          "then": "the download includes checklists, prior findings and site documents",
          "origin": "prd"
        }
      ],
      "requirementIds": [
        "REQ-c3081d"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (offline) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-e3f064",
      "epicId": "EP-5715ce",
      "title": "Fill in checklists, add notes and capture photos while offline",
      "asA": "field inspector",
      "iWant": "to fill in checklists, add notes and capture photos while offline",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-e3f064-1",
          "given": "a field inspector",
          "when": "the field inspector fills in checklists, add notes and capture photos while offline",
          "then": "all offline changes are stored locally and encrypted at rest",
          "origin": "prd"
        }
      ],
      "requirementIds": [
        "REQ-e3f064"
      ],
      "estimate": {
        "points": 5,
        "rationale": "base 1; +2 distributed-state complexity (offline); +1 security or compliance (encrypted) = score 4 -> 5 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-5199d9",
      "epicId": "EP-5715ce",
      "title": "Sync queued changes to the server automatically in the background",
      "asA": "field inspector",
      "iWant": "the system to sync queued changes to the server automatically in the background",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-5199d9-1",
          "given": "a field inspector",
          "when": "connectivity returns",
          "then": "the app must sync queued changes to the server automatically in the background",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-5199d9"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (sync, queued, background) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-327858",
      "epicId": "EP-5715ce",
      "title": "Sync must resume after interruption without duplicating photos",
      "asA": "field inspector",
      "iWant": "sync to resume after interruption without duplicating photos",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-327858-1",
          "given": "a field inspector",
          "when": "the behaviour is exercised",
          "then": "sync must resume after interruption without duplicating photos",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-327858"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (sync, resume) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-bc70e9",
      "epicId": "EP-5715ce",
      "title": "Detect the conflict and let the inspector choose which version of each...",
      "asA": "field inspector",
      "iWant": "the system to detect the conflict and let the inspector choose which version of each conflicting field to keep",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-bc70e9-1",
          "given": "a field inspector",
          "when": "the same inspection was edited on the server while the inspector was offline",
          "then": "the app must detect the conflict and let the inspector choose which version of each conflicting field to keep",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-bc70e9"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (offline, conflict, conflicting) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-7d05f0",
      "epicId": "EP-5715ce",
      "title": "Show a clear offline indicator and the number of changes waiting to sync",
      "asA": "field inspector",
      "iWant": "the system to show a clear offline indicator and the number of changes waiting to sync",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-7d05f0-1",
          "given": "a field inspector",
          "when": "the system shows a clear offline indicator and the number of changes waiting to sync",
          "then": "the app should show a clear offline indicator and the number of changes waiting to sync",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-7d05f0"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (offline, sync) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-0d2dcf",
      "epicId": "EP-5715ce",
      "title": "Pre-fetch map tiles for assigned sites",
      "asA": "field inspector",
      "iWant": "to pre-fetch map tiles for assigned sites",
      "soThat": "directions work offline",
      "acceptanceCriteria": [
        {
          "id": "AC-0d2dcf-1",
          "given": "a field inspector",
          "when": "the field inspector pre-fetches map tiles for assigned sites",
          "then": "inspectors could pre-fetch map tiles for assigned sites so directions work offline",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-0d2dcf"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (offline) = score 3 -> 3 pts"
      },
      "priority": "could",
      "labels": []
    },
    {
      "id": "ST-8aae2b",
      "epicId": "EP-bfd533",
      "title": "Offline storage must support at least 50 inspections and 2 GB of photos per...",
      "asA": "field inspector",
      "iWant": "offline storage to support at least 50 inspections and 2 GB of photos per device",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-8aae2b-1",
          "given": "a field inspector",
          "when": "the behaviour is exercised",
          "then": "offline storage must support at least 50 inspections and 2 GB of photos per device",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-8aae2b"
      ],
      "estimate": {
        "points": 3,
        "rationale": "base 1; +2 distributed-state complexity (offline) = score 3 -> 3 pts"
      },
      "priority": "must",
      "labels": [
        "non-functional",
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-34651f",
      "epicId": "EP-bfd533",
      "title": "Local data must be encrypted with a key stored in the platform keystore",
      "asA": "field inspector",
      "iWant": "local data to be encrypted with a key stored in the platform keystore",
      "soThat": "it supports the goal \"No inspection data is lost because of connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-34651f-1",
          "given": "a field inspector",
          "when": "the behaviour is exercised",
          "then": "local data must be encrypted with a key stored in the platform keystore",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-34651f"
      ],
      "estimate": {
        "points": 2,
        "rationale": "base 1; +1 security or compliance (encrypted, keystore) = score 2 -> 2 pts"
      },
      "priority": "must",
      "labels": [
        "non-functional",
        "benefit-inferred"
      ]
    },
    {
      "id": "ST-6fe87c",
      "epicId": "EP-bfd533",
      "title": "Sync of a 20-photo inspection should complete within 3 minutes on a 3G...",
      "asA": "field inspector",
      "iWant": "sync of a 20-photo inspection to complete within 3 minutes on a 3G connection",
      "soThat": "it supports the goal \"Inspectors can complete a full inspection with no connectivity\"",
      "acceptanceCriteria": [
        {
          "id": "AC-6fe87c-1",
          "given": "a field inspector",
          "when": "the behaviour is exercised",
          "then": "sync of a 20-photo inspection should complete within 3 minutes on a 3G connection",
          "origin": "inferred"
        }
      ],
      "requirementIds": [
        "REQ-6fe87c"
      ],
      "estimate": {
        "points": 5,
        "rationale": "base 1; +2 distributed-state complexity (sync); +1 measurable performance target (within 3 minutes) = score 4 -> 5 pts"
      },
      "priority": "should",
      "labels": [
        "non-functional",
        "benefit-inferred"
      ]
    }
  ],
  "dependencies": [
    {
      "from": "ST-5199d9",
      "to": "ST-327858",
      "kind": "inferred",
      "reason": "acts on \"sync\", which ST-5199d9 introduces"
    },
    {
      "from": "ST-5199d9",
      "to": "ST-6fe87c",
      "kind": "inferred",
      "reason": "acts on \"sync\", which ST-5199d9 introduces"
    }
  ],
  "risks": [
    {
      "id": "RISK-af9cfd",
      "description": "Conflict resolution UX may confuse inspectors and lead to the wrong version being kept.",
      "likelihood": "medium",
      "impact": "medium",
      "mitigation": "",
      "relatedIds": [
        "ST-bc70e9"
      ],
      "origin": "prd"
    },
    {
      "id": "RISK-62f445",
      "description": "Low-end Android devices may run out of storage with large photo sets.",
      "likelihood": "medium",
      "impact": "medium",
      "mitigation": "",
      "relatedIds": [
        "ST-8aae2b"
      ],
      "origin": "prd"
    },
    {
      "id": "RISK-8d44b1",
      "description": "Background sync is restricted by iOS and Android battery policies.",
      "likelihood": "medium",
      "impact": "medium",
      "mitigation": "",
      "relatedIds": [
        "ST-5199d9"
      ],
      "origin": "prd"
    },
    {
      "id": "RISK-bce6d8",
      "description": "Unresolved question: How long should synced inspections remain cached on the device?",
      "likelihood": "medium",
      "impact": "medium",
      "mitigation": "Resolve with the PRD owner before the related stories enter a sprint.",
      "relatedIds": [
        "ST-6fe87c",
        "ST-327858",
        "ST-5199d9"
      ],
      "origin": "derived"
    }
  ]
}
