1262 lines
108 KiB
Markdown
1262 lines
108 KiB
Markdown
# nigig-site — Security and Reliability Remediation Plan
|
||
|
||
**Plan date:** 2026-09-12 · **Last verified:** 2026-09-26
|
||
**Audit baseline:** `e899a271c69efca0e11ae274b879378d2f26485f` (`main`)
|
||
**Scope:** `crates/apps/nigig-site`, its UI-free core crate `crates/apps/nigig-site-core`, their domain/storage/media/export code, and the protocol boundary with `nimanyatta`
|
||
**Status:** **Not complete — see the tranche ledger in §1a for the exact per-tranche state. There is no production transport between `nigig-site` and `nimanyatta`, and none of the three existing protocol definitions is shared by both sides.** SITE-00 and SITE-01 are complete and published. SITE-02's implementation is complete and unit-verified but stays a **blocked candidate**: activation requires an independent cryptography review that has not happened, and no one working in the repository can self-grant it. SITE-03 through SITE-11 and SITE-13 through SITE-19 hardening contracts are implemented **and now actually compiled and unit-tested** (they were previously dead files; see §1a). Feature tranches SITE-20, SITE-21, SITE-22, SITE-23, SITE-25, SITE-26, SITE-27, SITE-28, SITE-29 and SITE-30 have implemented, tested **domain** layers but no screens and no server-side enforcement; the SITE-18 notification contract and the SITE-19 multi-site contract are likewise tested domain code, while their screen work and release evidence are not done. SITE-12, SITE-24, SITE-31 and SITE-32 have implemented, tested **domain and contract** layers (interop evidence, chat, integrations, NFR/i18n harnesses); their transport, server, provider and device-lab halves remain open as stated in §1a. Product scope (`construction-site-app-scope.md` + `construction-site-app-scope2.md`) is mapped to feature tranches SITE-20–SITE-32 with an R1–R5 release roadmap (§13); anything in the scope docs without a tranche row in §13 is not covered — report it as a plan gap.
|
||
**Published evidence:** SITE-00 `f49d8b16ac8f1245ba1e80ac19588825f91a79f4`; SITE-01 `5d2d890f701444a3ff7e9c227e41e4608406ee89`. SITE-03 is published on `main` as `834755205b6c0cc63f506435cd8edbdec2422024` — that commit is **not** an ancestor of this branch, so the SITE-03 evidence lives on a diverged line and must be reconciled before release (see §1a, "branch divergence").
|
||
**Local verification on 2026-09-26:** `cargo test -p nigig-site-core --locked` → **228 passed, 0 failed, 1 ignored**, and `cargo test -p nimanyatta-protocol` → **13 passed, 0 failed** (that crate is a workspace member as of this pass; its 8 pre-existing tests had never compiled) (the ignored test needs a live unlocked Secret Service session); `cargo clippy -p nigig-site-core --locked --all-targets --no-deps -- -D warnings` → **clean**; `cargo check -p nigig-site --locked` → **clean**; all 6 `nigig-site.yml` Python contract gates re-executed against the tree → **pass**. `cargo test -p nigig-site` (the Makepad-dependent target) was **killed by SIGKILL compiling `makepad-widgets`** on a 2 GB / 2 vCPU host — the same infrastructure/resource failure recorded in §2.1, not a test failure.
|
||
**Release posture:** **Do not enable current multi-device sync. Do not call current storage confidential. Do not claim real OCR, safe media processing, or lossless photo reports.**
|
||
|
||
This is the authoritative forward execution plan for this crate. `README.md` and `NIGIG_SITE_ASSESSMENT_AND_PLAN.md` contain useful history but do not satisfy the security, migration, and release gates here.
|
||
|
||
---
|
||
|
||
## 1. Executive verdict
|
||
|
||
`nigig-site` handles high-risk construction and identity data—worker ID numbers, names, phones, client/site details, reports, meetings, approvals, locations, photos, and chat—but its storage and sync designs are not safe for that data.
|
||
|
||
The two most urgent defects are destructive storage recovery and plaintext whole-store sync:
|
||
|
||
- If the keystore is unavailable, saving falls back to plaintext by design.
|
||
- If the data key is missing, a new key can be created, decryption fails, the file is classified as corrupt, and the app backs it up then seeds a demo site. Tampering, key loss, unsupported data, and JSON corruption all collapse into the same destructive path.
|
||
- Save APIs return no failure, discard write/sync/rename errors, queue unbounded full-store clones, and production never drains the queue on shutdown.
|
||
- Sync serializes the entire multi-site store—including worker identity/contact data—into ordinary text messages in the selected site's normal chat room. HTTPS protects a network hop; it does not hide plaintext from the chat server, room members, logs, backups, or moderation systems.
|
||
- Snapshot chunks split raw UTF-8, grow after JSON escaping, have no encoded/decompressed size contract, integrity, trusted sender identity, site binding, replay defense, tombstones, or reliable concurrent request correlation.
|
||
|
||
The rest of the app repeats the same architectural pattern: public mutable structs and screen-level code bypass central authorization, state transitions, reference validation, audit history, finite numeric rules, and site scoping. Media processing trusts stored paths, decodes before limits, buffers every frame repeatedly, and writes collision-prone files non-atomically. OCR does no text recognition. PDF/DOCX print photo paths instead of images, contain Unicode panic paths, and DOCX assembly unwraps production errors. AI constructs Claude while labelling it OpenAI, can accept a timed-out partial response as success, and has no request IDs.
|
||
|
||
**Current grade:** security F, privacy F, data durability F, sync correctness F, domain integrity D-, media safety F, export fidelity D-, architecture D, test credibility D+.
|
||
|
||
---
|
||
|
||
## 1a. Tranche status ledger (2026-09-26)
|
||
|
||
**This ledger is the authority on completion.** A tranche is `done` only when its
|
||
exit criteria in §8 are met by evidence recorded here. `blocked` means the
|
||
remaining work requires something that cannot be produced inside this
|
||
repository; the blocker and its owner are named. `domain-only` means the
|
||
enforceable core logic exists and is unit-tested, while the screen layer and/or
|
||
the server-side enforcement the scope demands do not exist. Nothing below may be
|
||
upgraded without re-running the command named in its evidence column.
|
||
|
||
Legend: ✅ done · 🟩 domain-only · ⛔ blocked externally · ❌ not started
|
||
|
||
### Structural change made in this pass
|
||
|
||
`crates/apps/nigig-site-core` is a new UI-free crate holding the domain
|
||
aggregates, typed ids, explicit site context, the fail-closed encrypted
|
||
repository, and every SITE-04…SITE-19 hardening contract. `nigig-site` depends
|
||
on it and re-exports it, so there is exactly **one copy** of each module source:
|
||
the app and the core's unit tests compile the same files. This is the scope's
|
||
"Rust core crate … shared across platforms; testable; safe" (§5) and the target
|
||
architecture in §6.
|
||
|
||
Before this change, **13 hardening modules totalling 3,079 lines were present in
|
||
`src/` but declared in no `mod` statement.** They had never been compiled or
|
||
tested; `cargo check` never saw them. Compiling them for the first time found
|
||
four real defects, all fixed:
|
||
|
||
| Defect | Where | Fix |
|
||
|---|---|---|
|
||
| `Role`/`Capability` lacked `Ord`, so every `BTreeSet` of them failed to compile | `auth.rs` | derived `PartialOrd, Ord` |
|
||
| `CaptureResult::empty` never initialised `site_id` from its `site` parameter | `ocr_policy.rs` | field initialised |
|
||
| `negotiate_version(2, 5)` agreed on a protocol the peer never offered — the exact silent downgrade the function exists to prevent | `sync_protocol.rs` | requires genuine version-range overlap |
|
||
| An absurd frame size returned `Overflow` instead of the actionable budget breach | `media_bounds.rs` | saturating arithmetic, `TooLarge("decoded-pixels")`, named `MAX_DECODED_PIXELS` |
|
||
|
||
A further four were test-only compile failures in the same never-built code
|
||
(`export_policy`, `net_client`, `commands`). All 228 core tests now pass (175 before this pass; 53 added by SITE-18/19/26/27/29/30).
|
||
|
||
### Branch divergence — must be reconciled before release
|
||
|
||
This branch (`feature/daily-reports/nimanyatta`, from `feature/daily-reports`
|
||
`fd8b063`) and `main` (`97f03fd`) diverged at `b3a9005`:
|
||
|
||
- `main` has SITE-03 published (`8347552`) and its own `aggregates.rs` /
|
||
`site_context.rs`.
|
||
- This branch has the two scope documents and the hardening modules that `main`
|
||
does not have.
|
||
|
||
Neither is a superset. The SITE-03 evidence SHA in this plan's header therefore
|
||
does not exist in this branch's history. **Owner: repository maintainer.**
|
||
Decision required: merge `main` into this line (or rebase this line onto `main`)
|
||
and re-run the SITE-03 sentinel tests against the merged result before any
|
||
release gate is claimed for SITE-03.
|
||
|
||
### Ledger
|
||
|
||
| Tranche | State | Evidence / blocker |
|
||
|---|---|---|
|
||
| SITE-00 truthful CI | ✅ | `.forgejo/workflows/nigig-site.yml`; 6/6 Python contract gates re-executed locally 2026-09-26. Extended this pass with `core-contracts` and `core-clippy` lanes and a "≥100 tests collected" check so a lane cannot pass vacuously. |
|
||
| SITE-01 containment | ✅ | Published `5d2d890`. Structural gate re-run locally: pass. |
|
||
| SITE-02 encrypted repository | ⛔ | Implementation complete and unit-verified (`crypto::tests`, `repository::tests`, `store::tests` all pass). **Blocker: independent cryptography review — reviewer `UNASSIGNED`, decision `NOT APPROVED`, activation `BLOCKED`.** No one working in the repo may self-approve. Owner: security reviewer. |
|
||
| SITE-03 typed ids + context | 🟩 | `ids.rs`, `site_context.rs`, `aggregates.rs`, `store.rs` implemented; 5+5+8+10 unit tests pass. **See branch divergence above.** UI call sites still reach the store directly rather than through a command service. |
|
||
| SITE-04 command service + audit | 🟩 | `commands.rs` (381 lines, hash-chained `AuditLog`, finite/range/date/duration validators) compiles and its 6 tests pass. **Gap: domain structs still expose public mutable fields, so the service is not the only mutation path.** |
|
||
| SITE-05 auth, roles, PII policy | 🟩 | `auth.rs` deny-by-default `authorize`, PII masking/redaction; 5 tests pass. Now extended by SITE-20 `organisation.rs`. **Gap: nothing enforces `authorize` at the screen layer yet.** |
|
||
| SITE-06 workflow transition tables | ✅ | `workflows.rs` tables + tests pass; extended this pass with the FR-1.14 `Locked` state and its transition tests. |
|
||
| SITE-07 managed asset lifecycle | 🟩 | `assets.rs` preflight/quota/atomic-publish contract; 4 tests pass. **Gap: no screen ingests through it; production still has no media path.** |
|
||
| SITE-08 shared sync protocol | 🟩 | `sync_protocol.rs` offline-envelope codec + budgets; 5 tests pass (downgrade bug fixed this pass). **Gap: this codec is shared with nothing, and three encodings coexist.** Corrected attribution (2026-09-26, read from source): (a) `nimanyatta/src/routes/sync.rs` serves `POST /sync` with `Json<SyncRequest>`/`Json<SyncResponse>` whose types come from **`nigig_common`**, a sibling repository that is not present here — so the server's HTTP sync shapes are not visible from this checkout; (b) `nimanyatta/crates/nimanyatta-protocol` is a **WebSocket chat** protocol (postcard, `ClientToServerMsg`/`ServerToClientMsg`), and the server imports exactly one item from it (`is_guest` in `routes/realtime.rs`) — it is *not* the `/sync` protocol; (c) `sync_protocol.rs` is a third thing: a JSON offline snapshot envelope. Its doc-comment claimed "one canonical encoding is used by app, server, and tests"; that was false and has been corrected. **Reconciling them from here would be a guess** — the server's field definitions live in the unreachable `nigig_common`. Blocked on SITE-12. |
|
||
| SITE-09 authenticated E2EE | 🟩 | `e2ee.rs` envelope contract, replay/reorder/downgrade rejection; 4 tests pass. **Blocker: group protocol + device lifecycle need external review; transport stays disabled.** |
|
||
| SITE-10 journal + tombstones | 🟩 | `journal.rs` operation log, deterministic conflict resolution; 5 tests pass. **Gap: not wired to the repository writer.** |
|
||
| SITE-11 correlated network client | 🟩 | `net_client.rs` bounded concurrent requests, per-request correlation; 4 tests pass. **New 2026-09-27:** `RetryPolicy` — exponential backoff with deterministic equal jitter, attempt budget, `Retry-After` honor within the cap, and `SYNC_DRAIN_BATCH` pacing for bulk replays (thundering-herd defense; see `SITE_31_SERVER_ADR.md` §6); 3 tests pass. **Gap: no production transport uses it.** |
|
||
| SITE-12 real server interop | 🟨 | **Update 2026-09-27 (hermetic evidence, no server):** `interop.rs` — pinned server commit `84fe56d` plus file-level drift detection (`assert_pinned_tree` fails closed on a moved tree/member), a two-user/three-device/two-site field scenario with mid-scenario removal and a ten-revision offline fork converging deterministically, cross-site rejection, and a routing-metadata ciphertext-freedom proof; 4 tests pass. **Still needed from the server side:** completed `cargo check`/`cargo test` for `nimanyatta --features b_server`, the `[workspace]` membership decision, the dedicated live job actually executing, and database inspection. **Substantially unblocked by commits `dfffe9e` + `13ae501` (landed from elsewhere, 2026-09-26; 654 files, ~106k insertions).** `nigig-lite/crates/common` (package `nigig-common`) is now **present in this repository**. A vendored `xitca-web` briefly landed in `dfffe9e` and was then dropped in `f9c1235` in favour of crates.io `xitca-web = "0.8"`; `nigig-common` moved to crates.io at the same time. The `/sync` DTOs I previously called unreconstructible are in the tree and readable: `nigig-lite/crates/common/src/sync.rs` defines `SyncRequest`, `SyncFilter`, `JoinedRoomSync`, `SyncResponse`; `rooms.rs` defines `TimelineEvent`, `PaginationDirection`, `MessageContent`, `SendMessageRequest`. **Two architecture facts this settles.** (1) There are two `MessageContent` types, deliberately: `nigig_common::MessageContent::Text { body, formatted_body, mentions }` for HTTP/JSON — its own doc-comment says it "serialises externally tagged, e.g. `{"Text":{"body":"hi",…}}`" — and `nimanyatta_protocol::MessageContent::Text { text }` for the WebSocket protocol in postcard. `nigig-common`'s manifest states the postcard format "must match `nimanyatta/crates/nimanyatta-protocol` (the client library)". (2) `TimelineEvent` has **no** serde tag attribute, so it is externally tagged: `{"Message":{…,"sender":…,"content":…}}`. **Correction to commit `ce9e12b`, which was wrong.** That commit changed the E2E test's outgoing posts from `{"Text":{"body":…}}` to `{"Text":{"text":…}}` on the reasoning that `MessageContent::Text` has a `text` field. That is true of the *WebSocket* type and false of the *HTTP* type these endpoints use; the original `body` was correct and the change would have made the requests fail to deserialize. Reverted. The original fixture was right about the field name; its only real defect was keying on `"type":"message"` instead of the externally-tagged `"Message"` envelope, and that fix stands. **Remaining blockers, all narrower than before.** (a) `nimanyatta` is still not a workspace member, but the cause is now **different and narrow**. Adding it fails `cargo metadata` with exit 101: "multiple workspace roots found in the same workspace: `nimanyatta`, `nigig-lite/crates/common`, `nigig-org`". Both sub-crates carry their own `[workspace]` table, so Cargo refuses to absorb them. (The earlier cause — `xitca-web/web/Cargo.toml` inheriting `rust-version`/`lints` from the wrong root — disappeared with the de-vendoring.) The fix is to delete those two `[workspace]` tables, or add both paths to the root `exclude` list; note `nigig-common`'s table is documented as deliberate ("keeps it independent from any parent workspace"), so this is a decision for the server team, not a mechanical change. (b) Building the server standalone now *resolves* and compiles — it reached `surrealdb`/`blake3`/`matrixmultiply` before a 1500 s wall — but a full `cargo check --features b_server` has not completed here, so **no build claim is made**. (c) The E2E fixture's test target still cannot compile on this host (`makepad-widgets` is SIGKILLed on 2 vCPU / 1.9 GiB), so the corrected fixture is verified only by extracting `event_text`/`sync_timelines` verbatim into a standalone harness and exercising four cases against `nigig_common`'s real shapes (Message with `body` → parsed; Membership → skipped; Redaction → skipped; legacy lowercase → tolerated), all passing. Still needed from the server team: a completed `cargo check`/`cargo test` for `nimanyatta --features b_server`, and a decision on whether to reconcile the two `MessageContent` types or keep them separate by design. |
|
||
| SITE-13 private AI refinement | 🟩 | `ai_policy.rs` consent/provider/labeling policy; 3 tests pass. **Gap: provider integration and the consent boundary in the UI.** |
|
||
| SITE-14 scanner-owned OCR | 🟩 | `ocr_policy.rs` returns geometry only and its `ui_label()` refuses to claim recognition; 3 tests pass. **Blocker: no on-device OCR engine. Ships as capture-only with manual entry.** |
|
||
| SITE-15 streaming media budgets | 🟩 | `media_bounds.rs` checked preflight, generation disabled; 4 tests pass. **Blocker: a maintained one-frame-at-a-time encoder.** |
|
||
| SITE-16 panic-free exports | 🟩 | `export_policy.rs` char-boundary-safe truncation/wrapping, page/cancel gates; 3 tests pass. **Gap: the contained PDF/DOCX writers are still test-only fixtures and still print paths.** |
|
||
| SITE-17 durable reminder state machine | 🟩 | `reminder_state.rs` + `scheduler.rs`; 4 tests pass. **Gap: no site-timezone source and no OS scheduling (deliberately, under SITE-01).** |
|
||
| SITE-18 screen isolation / observability | 🟩 | **New:** `notification_rules.rs` — per-subject delivery modes, midnight-crossing quiet hours in the site's zone, digest vs immediate, safety-critical subjects that cannot be muted and bypass both quiet hours and a global mute, notification bodies generated from a closed subject vocabulary so no personal data can enter one (checked by `assert_no_personal_data`), and per-subject recipient allow-lists so a new role never inherits safety notifications by default; 8 tests pass. **Gaps: the Makepad screen work and observability instrumentation named in §SITE-18; push transport (needs SITE-31).** |
|
||
| SITE-19 migration + release evidence | 🟩 | **New:** `multi_site.rs` — per-site membership with a different role at each site, a switcher that refuses a site the user is not a member of and refuses a stale registry selection rather than falling back, sessions that authorize only their own site (`authorize` returns `WrongSite` for any other), revocation that also drops that user's queued offline work, and an offline queue that drains **per site** so Site A work can never be replayed against Site B; 8 tests pass, including an FR-1.7 cross-site roll-up that flags the missing site. **Gaps: `tests/media_limits.rs` still does not exist and there is no real-device timing evidence — the migration/release-evidence half of this tranche is untouched.** |
|
||
| SITE-20 org / RBAC / registry / settings | 🟩 | **New:** `organisation.rs` — invites, per-site role assignment, the §4.2 matrix as testable data, site registry with geofence, settings; 6 tests pass. **Gaps: Makepad screens; server-side enforcement (scope §6 requires server-side authority, which needs SITE-31).** |
|
||
| SITE-21 report richness | 🟩 | **New:** `report_pack.rs` — `DR-SITE01-2026-09-25` numbering, entry status with mandatory return reason, signatures binding actor/device/timestamp/document-hash, `Draft→In Review→Approved→Locked`, versioned resubmission, multi-site compilation with missing-site flags, monthly packs; 9 tests pass. **New 2026-09-27 (R1 screens):** legacy `DailyReport` carries persisted `approval_signature` + `change_log` (old payloads decode without migration); `push_report` refuses Approved overwrites; the Reports screen has a Review tab (queue, detail with text hash, typed-signature submit/decide/resubmit with separation enforced atomically), archive search with DR numbers, monthly approved-counts, and missing-site flags with local chase reminders; CI gate pins the reviewed markers and bans raw transitions. **Gap: branded PDF/DOCX/PPTX rendering (needs SITE-16); drawn signatures and role sessions (need SITE-20 storage); pack-model persistence/versions.** Tabs are core `RadioButton` + `PageFlip` per the mpesa pattern — no plain tab buttons, and no site-local tab widget (View ref accessors resolve only for registered core patterns, so the custom-widget attempt was removed). |
|
||
| SITE-22 site-diary data | 🟩 | **New:** `site_diary.rs` — weather with provenance (override requires a reason), plant hours, deliveries, structured delay log where a weather delay must be supported by the recorded rainfall, visitor log, manpower by trade; 6 tests pass. **Gap: weather API source; screens.** |
|
||
| SITE-23 workforce depth | 🟩 | **New:** `workforce.rs` — consent mandatory before storage, blocklist stored as tags not identity, clock-in/out with geofence flag and overtime, QR badge issue, daily table with headcount by department, RFC 4180 payroll CSV that never emits identity, offboarding that deletes identity and keeps a tombstone; 8 tests pass. **Auto-purge deliberately refuses to run** until the scope §18 retention question is answered. **Gaps: QR rendering, geofence source, screens.** |
|
||
| SITE-24 chat completeness | 🟩 | **New:** `chat.rs` — channels (per-site/management/direct) with member/admin scoping, capability-gated post (`ChatPost`) and moderation (`ChatModerate`, admins only), @mentions restricted to members, cross-channel reply refusal, per-site idempotent offline outbox with dedup, read/delivered receipts, pin cap, member-scoped capped search, tombstoning moderation preserving audit metadata, retention purge with counts, closed-vocabulary bot notices (no free text, no PII), scoped broadcast that stops on first failure; 9 tests pass. **Gaps: delivery stays `TransportDisabled` (needs SITE-12 transport); screens; document read-receipts hook into SITE-30.** |
|
||
| SITE-25 programme depth | 🟩 | **New:** `programme.rs` — finish-to-start dependencies with cycle detection and insert rollback, deterministic topological order, critical path by longest chain, mid-project adoption with a frozen baseline, slippage alerts, inspection checklists that gate approval (a `Fail` needs evidence), snags requiring closure photo **and** sign-off, RFI overdue list, variations needing two distinct approvers; 9 tests pass. **Gaps: Gantt/calendar/board rendering — the scope's FR-4.6 asks for `nigig-build/.../project_management` to be extracted into its own crate; not done.** |
|
||
| SITE-26 procurement depth | 🟩 | **New:** `procurement_flow.rs` — material requirements → purchase orders with a two-approver gate (three above `FINANCE_APPROVAL_THRESHOLD_CENTS` = KES 500,000.00), budget commitment recorded at approval and released to paid on payment, LPO states where `Delivered` is reachable only through `verify_delivery` with non-empty evidence asset ids, deliveries, supplier profiles whose on-time and quality figures are `None` until a delivery or rating exists (never an invented 100%), and stock levels; money is integer cents with `i128` saturating multiplication, never `f64`; 10 tests pass. Pre-existing `workflows::procurement_transition` still passes. **Gaps: screens; no purchase-order PDF; supplier portal needs SITE-31.** |
|
||
| SITE-27 meetings depth | 🟩 | **New:** `meeting_flow.rs` — participants with RSVP, per-participant recording consent where `recording_permitted()` requires a `Granted` from every participant who has not declined, a refusal that cannot later be flipped to a grant, transcript and speaker labels refused until a recording has actually started, agenda items, and an action-item tracker with owner, due date and closure; 10 tests pass. Pre-existing `workflows::meeting_transition` still passes. **Gaps: audio capture and storage; STT (deliberately absent — no on-device engine); screens.** |
|
||
| SITE-28 HSE | 🟩 | **New:** `hse.rs` — append-only incidents (a near-miss cannot claim an injury; lost-time requires lost days), closure requires the corrective action taken, annotations instead of edits, toolbox talks with worker-id attendance, safety inspections where a stop-work order must carry findings, monthly statistics for the report pack, statutory-reportable list; 8 tests pass. **Gap: screens; photo capture.** |
|
||
| SITE-29 dashboards / analytics | 🟩 | **New:** `dashboards.rs` — integer chart series (no float round-trip in a published figure), programme progress as an integer mean where an empty programme reports 0 and **not** 100%, per-site snapshots drawn from the programme, attendance register, HSE ledger and procurement budget, worst-progress-first cross-site comparison for the Overall Supervisor, and a client digest built from an explicit field allow-list that fails closed on any smuggled key (`validate()` returns `Unauthorized`); 8 tests pass. **Gaps: chart rendering and PowerPoint export need SITE-16 plus a chart surface.** |
|
||
| SITE-30 document / drawing control | 🟩 | **New:** `documents.rs` — revision states where a revision cannot arrive already approved, approval that supersedes its predecessor, withdrawal that clears `current` in the same operation so there is no window in which a withdrawn drawing is in force, `revision_for_use` that refuses a superseded or withdrawn revision outright rather than silently substituting the current one, `drawing_for_task` that errors when nothing is approved, and issue/acknowledgement records with an unacknowledged list; 9 tests pass. **Gaps: file storage and preview; screens.** |
|
||
| SITE-31 integrations, API, server decision | 🟩 | **New:** `SITE_31_SERVER_ADR.md` (extend the pinned `nimanyatta` tree; per-data-class authority table resolving the E2EE divergence; hosting/residency, AI provider, portal timing and workspace membership recorded as non-decisions owned elsewhere) and `integrations.rs` — validated share payloads whose dispatch names the missing OS binding, calendar intents with no provider keys, idempotent webhook outbox with retry exhaustion, API scopes mapped 1:1 to SITE-20 capabilities (deny by default), verifiable backup manifests, consent-gated remote lookup that answers absent without consent; 6 tests pass. **Gaps: the server itself, provider credentials (never in this repo), screens.** |
|
||
| SITE-32 NFR / scale evidence | 🟩 | **New:** `nfr.rs` — §7 product budgets as constants, photo-rendition ceiling predicate, WCAG contrast arithmetic, a timing recorder that records (never asserts) durations into a markdown evidence table, deterministic 100-site/1,000-worker fixtures with bounded paging; `locales.rs` — 18 core EN/SW strings with completeness and no-PII tests; 9 tests pass. **Gaps: device-lab wall-clock timings, full screens i18n/a11y traversal, soak on reference hardware — so no NFR claim appears in release notes yet.** |
|
||
|
||
### Honest completion statement
|
||
|
||
Twenty-eight of the thirty-three tranches have implemented, unit-tested
|
||
contracts (`nigig-site-core`: 228 tests passing, clippy clean under
|
||
`-D warnings`); one (`SITE-06`) is fully done; two (`SITE-00`, `SITE-01`) are
|
||
published; four (`SITE-02`, `SITE-12`, `SITE-24`, `SITE-31`) are blocked on
|
||
external dependencies that cannot be resolved in this repository; one
|
||
(`SITE-32`) is not started. Those twenty-eight are **domain-layer** completions:
|
||
they are tested contracts with no Makepad screens and no server-side
|
||
enforcement, so none of them makes a user-visible feature work end to end.
|
||
**No release gate in §11 or §13 is met by this branch**, and the release
|
||
posture at the top of this plan is unchanged: do not enable current
|
||
multi-device sync, do not call current storage confidential, do not claim real
|
||
OCR, safe media processing, or lossless photo reports.
|
||
|
||
---
|
||
|
||
## 2. Evidence and reproducible baseline
|
||
|
||
### 2.1 Observed validation
|
||
|
||
| Check | Result at baseline | Honest interpretation |
|
||
|---|---:|---|
|
||
| `cargo check --locked -p nigig-site` | **Passed** | Compilation only. |
|
||
| `cargo test --locked -p nigig-site` | **Inconclusive; rustc was killed by signal 9 while compiling `makepad-widgets`** | No suite result may be claimed. Retry in low-memory CI with one job and stripped dev/test debug data. |
|
||
| Site-specific Forgejo workflow | **Absent** | Security and lifecycle regressions have no crate-owned CI. |
|
||
| `tests/sync_e2e.rs` without `NIMANYATTA_E2E_URL` | Returns success without testing | A silent early return is not an executed skip or pass. |
|
||
| In-repository `nimanyatta` server | **Present at repo-root `nimanyatta/` (31,458 lines, 78 files) but not buildable from this repository** — not a workspace member, and its `../nigig-lite` and `../xitca-web` path dependencies are absent. Only `crates/nimanyatta/` is a 2-file stub. | App/server protocol still cannot be reproduced from this repository, but for a different reason than an earlier revision of this plan claimed. See SITE-12. |
|
||
|
||
### 2.2 Primary evidence locations
|
||
|
||
- `src/store.rs`, `src/crypto.rs`: destructive load fallback, plaintext downgrade, swallowed write errors, unbounded snapshot queue, global whole-store lock/cache.
|
||
- `src/sync.rs`, `src/nimanyatta_client.rs`, `tests/sync_e2e.rs`, `crates/nimanyatta/README.md`: cross-site plaintext snapshots, unsafe chunking/merge/replay, singleton pending slots, protocol drift, absent server.
|
||
- `src/domain/{site,daily_report,workers,procurement,approvals,meetings}.rs`: stringly IDs, unrestricted mutable states, missing role/audit/reference/finite/date invariants, PII duplication.
|
||
- `src/site_frame/screens/*.rs`: implicit first-site fallback, stale/local caches, direct global mutations, broad disclosure, dead or weakly correlated actions.
|
||
- `src/{gif,video,ocr,report_pdf,doc_export,ai_refine,scheduler}.rs`: unbounded decode/encode, fake OCR, lossy exports, Unicode panic, provider/cancellation defects, and reminder ordering/time-zone defects.
|
||
|
||
---
|
||
|
||
## 3. Immediate threat statement
|
||
|
||
### Protected data
|
||
|
||
Worker identity numbers and images; names, phone/email; exact site/location data; attendance; reports and photographs; approvals; procurement/supplier data; meetings/transcripts; chat; credentials; device/site encryption keys; audit history.
|
||
|
||
### Adversaries and failures in scope
|
||
|
||
- Curious or compromised sync/chat server and its operators/backups/logs.
|
||
- Unauthorized or removed room/site member.
|
||
- Malicious member/device sending forged, replayed, oversized, future-dated, conflicting, or malformed events.
|
||
- Network attacker where TLS validation/configuration is weakened.
|
||
- Lost/stolen device and copied local files.
|
||
- Corrupt filesystem, disk full, power loss, process crash, missing/locked keystore, and downgrade/future-version binaries.
|
||
- Crafted images, text, PDFs/DOCX inputs/options, and sync payloads intended to exhaust resources or trigger panics.
|
||
- Normal concurrent use, out-of-order responses, project/site switching, and stale background jobs.
|
||
|
||
### Explicit limitation
|
||
|
||
End-to-end encryption cannot prevent an authorized endpoint from copying data, and a malicious server can withhold/reorder traffic. Availability and endpoint malware are not solved cryptographically. These limitations must appear in product documentation.
|
||
|
||
---
|
||
|
||
## 4. Non-negotiable invariants
|
||
|
||
1. **No plaintext downgrade.** If confidential persistence cannot encrypt, mutation is blocked or retained only in an explicitly volatile safe mode. It is never silently/plainly persisted.
|
||
2. **No destructive recovery.** Missing key, authentication failure, corrupt payload, unsupported version, I/O error, and valid empty are distinct states. None seeds demo data over user state.
|
||
3. **Durable success only.** Save success is a `Result` after serialization, encryption, write, flush, sync, atomic replace, and manifest/directory durability as supported.
|
||
4. **Explicit site context.** Every domain command, query, export, asset, reminder, and sync event takes typed `SiteId`; no “selected or first” fallback authorizes data access.
|
||
5. **Central commands and authorization.** Screens cannot mutate workflow fields directly. Identity, site role, transition, reference, finite value, revision, and audit checks occur in one service boundary.
|
||
6. **Data minimization.** Sync/export/AI/OCR receives only named required records/fields for one site and operation. The process-wide store is never serialized into a room.
|
||
7. **Authenticated site-scoped E2EE.** Server sees ciphertext and necessary routing metadata only. Envelopes bind protocol/version/site/key epoch/sender/sequence/content and reject forgery/replay/cross-site substitution.
|
||
8. **One shared protocol implementation.** App, server, and tests import the same typed protocol crate and endpoint semantics.
|
||
9. **Convergent explicit replication.** Edits and deletes replicate via operation IDs/revisions/tombstones and declared conflict rules. Attacker-controlled wall-clock strings do not decide authority.
|
||
10. **Correlated async work.** Every request/result has operation ID, site ID, base revision, and cancellation status. Stale results cannot mutate current state.
|
||
11. **Owned assets, not arbitrary paths.** Domain records refer to `AssetId`. Ingestion validates/copies bytes into a site-scoped managed store before decode/use.
|
||
12. **Bound before allocation.** Encoded bytes, dimensions, pixels, frames, decompressed bytes, text, pages, output, recursion, and queue depths are checked with overflow-safe arithmetic.
|
||
13. **No partial success.** AI timeout, media truncation, export failure, sync subset, and notification registration failure are explicit states.
|
||
14. **PII lifecycle is enforceable.** Access, purpose, retention, deletion/anonymization, backup, export, and audit rules exist for identity data and images.
|
||
15. **Demo data is opt-in.** Fixtures are visibly marked and created only by an explicit development/demo action.
|
||
|
||
---
|
||
|
||
## 5. Severity-ranked findings
|
||
|
||
### P0 — emergency release blockers
|
||
|
||
| ID | Finding | Concrete impact |
|
||
|---|---|---|
|
||
| SITE-P0-01 | `crypto::seal` falls back to a plaintext envelope when keyring/encryption is unavailable. | Worker/site PII is stored unencrypted while the app continues operating. |
|
||
| SITE-P0-02 | `load_or_create_dek` may create a new key for an existing encrypted store; any decrypt/parse failure then enters backup-and-reseed. | Key loss/tampering can make user data disappear from the app and be replaced by a demo site. |
|
||
| SITE-P0-03 | `save`/`save_to` discard directory, serialization, encryption fallback, write, sync, permission, rename, and directory-sync errors. | UI/state can report success although no durable update exists. |
|
||
| SITE-P0-04 | Async persistence uses an unbounded channel of full `SiteStore` clones and is never drained on production exit. | Memory amplification and lost final writes. |
|
||
| SITE-P0-05 | Sync puts the complete multi-site plaintext store into one site's ordinary chat room. | Cross-site disclosure of identity, contacts, reports, meetings, procurement, reminders, and chat to server/room members. |
|
||
| SITE-P0-06 | Sync envelopes have no cryptographic authenticity, site binding, trusted device identity, sequence/replay control, or decompression/total size limit. | Injection, replay, cross-site substitution, memory denial of service, and state poisoning. |
|
||
| SITE-P0-07 | Raw JSON is split at arbitrary bytes then lossy-converted to UTF-8; escaped envelope size can exceed transport limits; serialization failure becomes `{}`. | Corrupt/incomplete snapshots can look valid; Unicode and transport limits are violated. |
|
||
| SITE-P0-08 | Lexically greatest attacker-chosen snapshot ID wins; LWW accepts arbitrary future `updated_at`; add-only merges cannot propagate most edits/deletes. | Permanent eclipsing/poisoning and non-convergent devices. |
|
||
| SITE-P0-09 | Static Makepad request IDs plus singleton `PENDING_*` slots correlate concurrent room/send/fetch operations. | A response can be attributed to the wrong site/room; concurrent messages overwrite pending context. |
|
||
| SITE-P0-10 | AI timeout returns nonempty partial output as success and result events lack request/site/revision identity. | Partial or stale sensitive text overwrites a newer field/site. |
|
||
| SITE-P0-11 | Image/GIF/video paths decode arbitrary supplied files before pixel/byte limits and buffer every frame/copy. | Path abuse, decompression bombs, process OOM, and UI/job starvation. |
|
||
| SITE-P0-12 | AVI size/rate/count casts and multiplication are unchecked; all output is accumulated in memory. | Truncated headers, overflow, corrupt video, or memory exhaustion. |
|
||
|
||
### P1 — major security, correctness, and privacy defects
|
||
|
||
| ID | Finding | Consequence |
|
||
|---|---|---|
|
||
| SITE-P1-01 | Domain IDs are aliases to `String`; parent/site/supplier/meeting/worker references are not centrally validated. | Cross-site/dangling references and collisions survive into exports/sync. |
|
||
| SITE-P1-02 | Report/task/inspection/meeting/site status fields are publicly mutable; roles and legal transition order are not enforced. | Anyone/path can approve, inspect, complete, reopen, or rewrite history. |
|
||
| SITE-P1-03 | No immutable audit event records actor, reason, revision, device, and prior/new state. | Approval and PII actions are not accountable. |
|
||
| SITE-P1-04 | Worker scans duplicate complete ID numbers/names/phones into daily tables and expose them broadly. Captured ID images have no retention/delete lifecycle. | Excessive PII replication and disclosure. |
|
||
| SITE-P1-05 | Quantities, latitude/longitude, dates, durations, IDs, and uniqueness accept invalid/non-finite/inconsistent values. | NaN output, impossible schedules/locations, duplicate reports/scans, and invalid references. |
|
||
| SITE-P1-06 | Implicit first-site fallback substitutes another site when selection is absent/stale. | Wrong-site mutation/export/disclosure. |
|
||
| SITE-P1-07 | OCR performs corner detection/enhancement only and always returns no recognized text. | Product wording/integration overstates ID scanning capability. |
|
||
| SITE-P1-08 | PDF and DOCX print local media paths instead of embedding photos. | Reports are not portable and leak local filesystem structure. |
|
||
| SITE-P1-09 | PDF preview and meeting summary truncate UTF-8 strings with byte slicing. | Valid Unicode text can panic the app/export. |
|
||
| SITE-P1-10 | DOCX ZIP assembly uses production `unwrap()` and returns bytes instead of `Result`. | Recoverable file/codec failures crash and can lose unsaved work. |
|
||
| SITE-P1-11 | AI constructs `ClaudeApiChatProvider` with `ProviderKind::OpenAi`, uses environment credentials, and sends report text without a clear consent boundary. | Wrong provider behavior and undisclosed remote processing of site data. |
|
||
| SITE-P1-12 | Reminder fire time assumes fixed EAT regardless of site; invalid hour can panic; OS scheduling happens before durable state. | Wrong/duplicate/missing reminders and unrecoverable OS/app divergence. |
|
||
| SITE-P1-13 | Reminder is marked fired immediately after asynchronous post is spawned, even if delivery fails. | Missed reminders are recorded as delivered. |
|
||
| SITE-P1-14 | Sync E2E test silently returns success without a server and uses endpoint/method shapes that differ from the app. | CI claims nothing about real interoperability. |
|
||
|
||
### P2 — maintainability and performance debt
|
||
|
||
- One global clone-heavy store makes every mutation/write proportional to all sites and PII.
|
||
- Screens repeatedly clone whole state and cache derived site values without typed revision invalidation.
|
||
- Network code hand-parses loosely typed JSON and tolerates malformed success bodies as empty IDs/events.
|
||
- PDF/DOCX/media encoders accumulate full output and lack cancellation/metrics.
|
||
- Static/global environment manipulation makes tests serial, fragile, and unlike production configuration.
|
||
- No Site-owned workflow means dependency, security, test, and resource-limit regressions can merge unnoticed.
|
||
|
||
---
|
||
|
||
## 6. Target architecture
|
||
|
||
```text
|
||
SiteApp / Makepad screens
|
||
│ intents + explicit SiteContext
|
||
▼
|
||
SiteSessionController
|
||
├─ AuthSession { UserId, DeviceId, credentials/capabilities }
|
||
├─ SiteContext { SiteId, role/capabilities, revision }
|
||
├─ DomainCommandService (validation + authorization + audit)
|
||
├─ SiteRepository (encrypted, versioned, atomic, recoverable)
|
||
├─ AssetRepository (site-scoped, content-addressed, quota/retention)
|
||
├─ JobCoordinator (bounded, correlated, cancellable)
|
||
└─ SyncEngine
|
||
├─ local operation journal / tombstones
|
||
├─ shared typed protocol
|
||
├─ established/reviewed group key protocol
|
||
└─ authenticated encrypted transport envelopes
|
||
|
||
Server
|
||
├─ authentication and site membership/capabilities
|
||
├─ opaque encrypted event log + cursors + bounded snapshot blobs
|
||
└─ no application plaintext or client-side key material
|
||
```
|
||
|
||
### Persistence layout
|
||
|
||
```text
|
||
nigig-site/
|
||
profile/manifest # store ID, schema, key IDs; no PII plaintext
|
||
sites/<site-id>/manifest # encrypted aggregate pointers/revisions
|
||
sites/<site-id>/aggregates/... # encrypted envelopes
|
||
sites/<site-id>/journal/... # encrypted local operations
|
||
sites/<site-id>/assets/<id> # encrypted or platform-protected asset blobs
|
||
recovery/... # encrypted/preserved originals; never demo state
|
||
```
|
||
|
||
Selection and UI preferences are device-local metadata and are not replicated as domain state.
|
||
|
||
---
|
||
|
||
## 7. Resource and performance budgets
|
||
|
||
These are initial hard safety ceilings. Lower platform-specific quotas are allowed; unbounded operation is not.
|
||
|
||
| Resource | Initial limit | Enforcement point |
|
||
|---|---:|---|
|
||
| Legacy whole-store input | 64 MiB | Before read-to-end/decrypt/decode. |
|
||
| One aggregate plaintext | 16 MiB | Repository envelope decoder. |
|
||
| Sites per profile | 1,000 | Repository/query pagination. |
|
||
| Records per aggregate kind/site | 100,000 | Decode/command validation. |
|
||
| One sync operation plaintext | 256 KiB | Journal command codec. |
|
||
| Sync wire event | <= 48 KiB after encoding | Shared protocol before send. |
|
||
| Snapshot plaintext | 32 MiB | Streaming snapshot builder/reader. |
|
||
| Snapshot chunks | 1,024; unique indexes only | Reassembly before allocation. |
|
||
| Compression expansion | <= 20:1 and <=32 MiB | Streaming decompressor. |
|
||
| Concurrent network requests | 32 process-wide; 1 sync stream/site | Request manager. |
|
||
| Pending persistence | 1 coalesced revision/site + bounded journal | Repository writer. |
|
||
| Image encoded bytes | 20 MiB | Ingestion stream before decode. |
|
||
| Image dimensions/pixels | <= 8,192 each; <= 40 MP | Decoder header/limits before allocation. |
|
||
| Animation frames | <= 120 and <= 30 seconds | Manifest/request preflight. |
|
||
| Total decoded animation pixels | <= 30M pixels (~90 MiB RGB) | Shared media budget. |
|
||
| GIF output | 50 MiB | Counting writer. |
|
||
| Video output | 200 MiB and below format's checked size ceiling | Counting writer/container preflight. |
|
||
| Report text | 1 MiB/report; 64 KiB/field | Domain constructors. |
|
||
| Meeting transcript | 5 MiB | Ingest/editor/AI boundary. |
|
||
| PDF/DOCX | 500 pages and 100 MiB output | Paginated counting writer. |
|
||
| AI request/response | 64 KiB / 16 KiB; 30 s; 1 active/site | AI coordinator. |
|
||
| OCR input (grayscale) | 20 MiB / <= 8,192 edge / <= 40 MP; <= 100,000 components | Scanner engine preflight before allocation. |
|
||
| OCR suggestion | confidence >= 0.80 and exact field-charset, else `None` | Scanner engine + site confirmation gate. |
|
||
| Photo sync rendition | <= 1 MB per photo; original retained until synced | Client media pipeline (SITE-32). |
|
||
| Capture → saved | < 1 s on mid-range Android | No decode/export in handler (SITE-32). |
|
||
| Cold start | < 3 s on mid-range Android | Deferred content load (SITE-32). |
|
||
| Monthly pack generation | < 60 s | Streaming export (SITE-21/32). |
|
||
| Undo/offline journal | 10,000 ops or 64 MiB/site before compaction | Sync/repository. |
|
||
| UI event | p95 < 8 ms desktop / 12 ms mobile | No disk/network/decode/export in handler. |
|
||
| Site switch | p95 < 100 ms for metadata; content loads incrementally | Runtime benchmark. |
|
||
|
||
All count/size/rate arithmetic is checked. Limit errors preserve the prior durable revision and delete incomplete temporary output.
|
||
|
||
---
|
||
|
||
## 8. Dependency-ordered implementation tranches
|
||
|
||
Each tranche is a separately tested commit and push. Security containment must not wait for broad refactoring.
|
||
|
||
### SITE-00 — Site-owned truthful CI
|
||
|
||
**Priority:** P0
|
||
**Effort:** 2–3 person-days
|
||
**Depends on:** none; may proceed in parallel with SITE-01
|
||
|
||
**Change**
|
||
|
||
- Add `.forgejo/workflows/nigig-site.yml` covering Site-owned files, shared protocol/server, Cargo manifests/lock, and relevant dependencies.
|
||
- Use one build job and disabled test/dev debug info on memory-limited runners.
|
||
- Split check, unit, integration, runtime UI, sync interoperability, media limits, migration, clippy, and security/supply-chain jobs.
|
||
- Make missing server/config an explicit skipped job status in development and a hard failure in release CI.
|
||
- Preserve full logs and peak RSS/timeout artifacts.
|
||
|
||
**Tests / exit**
|
||
|
||
- Obtain a definitive test result; signal 9 is reported as infrastructure/resource failure.
|
||
- A test that returns before assertions cannot count as pass.
|
||
- Every path filter and source scan proves it matched owned files.
|
||
- Cargo/clippy failures are never hidden by shell filtering or `|| true`.
|
||
|
||
**Rollback:** runner tuning may change; mandatory ownership and honest status remain.
|
||
|
||
### SITE-01 — Emergency containment without destroying data
|
||
|
||
**Priority:** P0 immediate
|
||
**Effort:** 1–3 person-days
|
||
**Depends on:** none
|
||
|
||
**Change**
|
||
|
||
- Remove/disable Push/Pull and all automatic current sync code in production builds.
|
||
- On any non-successful store open, enter `RecoveryRequired` read-only UI; do not call `SiteStore::default`, seed Muthaiga Villas, save, or mutate the original.
|
||
- Disable confidential writes when encryption/keyring is unavailable; show explicit safe-mode state.
|
||
- Disable GIF/video generation, remote AI, and path-based export/OCR entry points until their limit/privacy tranches land; manual text/photo capture may continue only through safe asset ingestion.
|
||
- Preserve original files and display recovery/export-support actions without decrypting/logging content.
|
||
|
||
**Tests / exit**
|
||
|
||
- Missing key, wrong key, tamper, malformed JSON, future version, permission error, and empty file cannot create/overwrite a store.
|
||
- Default production build has no reachable sync send/pull path.
|
||
- A feature flag/config cannot bypass containment accidentally.
|
||
- Existing valid encrypted data remains readable and byte-identical until explicit mutation.
|
||
|
||
**Rollback:** unsafe capabilities stay disabled. There is no acceptable rollback to plaintext sync or destructive seeding.
|
||
|
||
### SITE-02 — Fail-closed recoverable encrypted repository
|
||
|
||
**Priority:** P0
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-01
|
||
|
||
**Change**
|
||
|
||
- Replace `load_or_create_dek` with distinct setup and existing-store open flows. An existing envelope names `store_id` and `key_id`; missing key is `KeyUnavailable`, never “create another key.”
|
||
- Make encryption/decryption/sealing return typed `Result`; eliminate plaintext fallback.
|
||
- Bind envelope header/version/store/aggregate/key ID to AEAD associated data and validate nonce/key-length/version exactly.
|
||
- Add user-controlled recovery/key escrow design, rotation, and secure deletion after security review; do not invent password crypto ad hoc.
|
||
- Replace `save` with an injected `SiteRepository` returning durable revision results. Use unique temp names, restrictive permissions at creation, checked write/flush/sync/rename/directory sync, and cleanup.
|
||
- Replace unbounded full-store channel with bounded coalescing per-site writers and explicit `flush_and_shutdown`.
|
||
- Expose persistence health and last durable revision to UI.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fault injection at key lookup, random generation, serialization, encryption, create, chmod, write, flush, sync, rename, and shutdown.
|
||
- Wrong/missing/rotated key and modified header/ciphertext are distinct recoverable errors.
|
||
- No plaintext bytes/known PII appear in data, temp, backup, or journal files.
|
||
- Queue depth is bounded; final accepted mutation either flushes on graceful shutdown or remains explicitly unsaved.
|
||
- Independent crypto/security review approves primitive use, nonce policy, associated data, key lifecycle, and recovery design.
|
||
|
||
**Migration:** legacy plaintext is read only after explicit user consent and key setup, then encrypted to a new location and verified before original disposition. Existing encrypted `NIGIG1` input is preserved and migrated without invoking key creation on lookup failure.
|
||
|
||
**Rollback:** use the prior encrypted reader against preserved originals; never emit plaintext or reseed.
|
||
|
||
### SITE-03 — Site-scoped versioned aggregates and explicit context
|
||
|
||
**Priority:** P0
|
||
**Effort:** 7–11 person-days
|
||
**Depends on:** SITE-02
|
||
|
||
**Change**
|
||
|
||
- Split monolithic `SiteStore` into typed versioned aggregates per site plus device-local preferences.
|
||
- Add opaque `SiteId`, `ReportId`, `TaskId`, `WorkerId`, `ScanId`, `SupplierId`, `MeetingId`, `AssetId`, `ReminderId`, `UserId`, and `DeviceId` newtypes.
|
||
- Introduce `SiteContext`; remove `selected_or_first` from command/query authorization.
|
||
- Add repository query pagination/indexes and aggregate revision compare-and-swap.
|
||
- Move selected site, transient screen state, and chat presentation cache out of replicated domain payloads.
|
||
|
||
**Tests / exit**
|
||
|
||
- APIs cannot compile a site mutation/query/export without `SiteContext`.
|
||
- A/B sentinel tests cover every aggregate, cache, asset, export, reminder, and job.
|
||
- Missing/stale selection is `NoSiteSelected`, never substitution with first site.
|
||
- A one-site mutation does not clone/serialize unrelated sites or PII.
|
||
- Duplicate/wrong-type/cross-site IDs are rejected.
|
||
|
||
**Migration:** split legacy whole store by `site_id`; global suppliers/ambiguous records require explicit assignment or quarantined shared-directory policy. Keep mapping and original encrypted bytes.
|
||
|
||
**Rollback:** compatibility reader may expose old data read-only; new writes never return to a global blob.
|
||
|
||
### SITE-04 — Domain command service, validation, and immutable audit
|
||
|
||
**Priority:** P0/P1
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-03
|
||
|
||
**Change**
|
||
|
||
- Make domain fields private where mutation needs invariants.
|
||
- Add `DomainCommandService` with expected revision, actor/device, site context, reason, timestamp source, validation, atomic aggregate update, and audit event.
|
||
- Validate uniqueness, parent/reference ownership, finite/range values, latitude/longitude, date order, duration, one-report-per-site/day, scan deduplication, and record/text limits.
|
||
- Use fixed-decimal quantity plus typed unit where commercial/measured quantities matter.
|
||
- Persist append-only audit events with hash/integrity linkage appropriate to the repository threat model; audit is not user-editable history.
|
||
|
||
**Tests / exit**
|
||
|
||
- Every public mutation has success, unauthorized, invalid transition/reference/value, stale revision, and rollback tests.
|
||
- NaN/inf, negative quantities, impossible coordinates/dates, duplicate IDs/scans/reports, cross-site parents/suppliers/assets fail.
|
||
- Failed command leaves aggregate, revision, audit, and sync journal unchanged.
|
||
- Audit identifies actor/device/command/target/prior-new revision/reason without logging secret payloads.
|
||
|
||
**Rollback:** aggregates remain read-only if command migration fails; no public-field bypass.
|
||
|
||
### SITE-05 — Authentication, site roles, and PII policy
|
||
|
||
**Priority:** P0 before sync
|
||
**Effort:** 8–14 person-days plus privacy/security review
|
||
**Depends on:** SITE-03, SITE-04
|
||
|
||
**Change**
|
||
|
||
- Define authenticated user/device session and site membership/roles/capabilities. Server and client enforce the same policy at different trust boundaries.
|
||
- Map commands to capabilities: report author/submit/approve, worker PII read/write/export, task inspect, procurement edit, meeting manage, site admin, key/device management.
|
||
- Separate stable worker/person record from attendance scan; daily tables reference `WorkerId` rather than duplicating full ID/contact data.
|
||
- Define purpose, masking, search, export, retention, correction, deletion/anonymization, backup expiration, and ID-image handling.
|
||
- Redact PII/secrets from logs, errors, crash reports, analytics, filenames, and notification bodies.
|
||
|
||
**Tests / exit**
|
||
|
||
- Deny-by-default role matrix at command, query, export, sync, and server endpoints.
|
||
- Removed member/device loses future key access and server authorization according to documented revocation limits.
|
||
- Worker ID is masked by default and omitted from broad report/export roles.
|
||
- Retention/deletion/anonymization propagates to indexes, assets, sync operations, and backups under stated policy.
|
||
- Privacy/security review and Kenya/target-market legal review approve processing flows.
|
||
|
||
**Rollback:** disable remote/PII workflows or make them local read-only; never rely on screen hiding as authorization.
|
||
|
||
### SITE-06 — Enforced report/task/inspection/procurement/meeting workflows
|
||
|
||
**Priority:** P1
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-04, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Define legal state machines and commands for site, report, construction task, inspection, procurement line, meeting, and minutes.
|
||
- Require submitter/approver separation if policy requires; approval/rejection reason and immutable approved revision.
|
||
- Validate task hierarchy, dates, quantities/units, inspections/assignees/outcomes, supplier references, meeting attendees/duration/status, and minutes approval.
|
||
- Replace byte-truncated “auto-summary” with no summary until real summarization is accepted.
|
||
- Define reopen/amendment behavior instead of mutating approved history.
|
||
|
||
**Tests / exit**
|
||
|
||
- Transition tables test every allowed/forbidden edge and role.
|
||
- Approved reports/minutes are immutable; amendment produces linked revision and audit.
|
||
- Parent/site, supplier/site, inspection/task, minutes/meeting, and attendee/user references validate.
|
||
- Unicode transcript of any valid text never panics.
|
||
- Templates are explicit user actions and visibly identified, not recovery seeds.
|
||
|
||
**Rollback:** block transitions and preserve drafts; do not permit arbitrary status mutation.
|
||
|
||
### SITE-07 — Managed asset ingestion and lifecycle
|
||
|
||
**Priority:** P0 media prerequisite
|
||
**Effort:** 7–11 person-days
|
||
**Depends on:** SITE-02, SITE-03, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Replace stored filesystem strings with site-scoped `AssetId` plus media metadata, content digest, ownership, source, creation, retention, and encryption key epoch.
|
||
- Ingest picker/camera handles by streaming into a quarantine temp file under quota; sniff/decode declared format, check encoded bytes and header dimensions before full allocation, then atomically publish.
|
||
- Use decoder allocation limits and checked pixel/channel arithmetic; reject decompression bombs and unsupported metadata.
|
||
- Generate bounded thumbnails separately. Strip EXIF/location by default; attach GPS only after explicit product/user policy.
|
||
- Prevent symlink/path races by never reopening an arbitrary caller path as authority.
|
||
- Implement reference counting or explicit ownership for deletion/retention.
|
||
|
||
**Tests / exit**
|
||
|
||
- Truncated, polyglot, extension-spoofed, huge-dimension, high-compression, symlink-swap, duplicate-content, quota, disk-full, and cancellation corpus.
|
||
- Domain/export/OCR/media APIs accept `AssetId`, not raw path strings.
|
||
- Delete/retention removes all derived thumbnails/transcodes and respects approved-record retention rules.
|
||
- Assets from Site A are inaccessible under Site B context even with guessed IDs.
|
||
|
||
**Rollback:** retain original external path only in migration quarantine/read-only UI; new captures use managed assets.
|
||
|
||
### SITE-08 — Shared sync protocol and threat-model ADR
|
||
|
||
**Priority:** P0
|
||
**Effort:** 6–10 person-days
|
||
**Depends on:** SITE-01, SITE-03, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Write an ADR/protocol specification with threat model, metadata leakage, identity/membership, key epochs, event fields, size limits, replay/order, snapshot/compaction, conflict, deletion, revocation, recovery, and version negotiation.
|
||
- Create one workspace protocol crate used by app, server, and tests; no hand-written parallel JSON shims.
|
||
- Define dedicated opaque sync endpoints/event types; do not piggyback application snapshots as ordinary chat text.
|
||
- Define canonical byte encoding and test vectors across implementations/versions.
|
||
- Select an established audited group encryption/key-management protocol or maintained implementation (for example MLS where platform support is viable). Any simpler design requires independent cryptographic review before code.
|
||
|
||
**Tests / exit**
|
||
|
||
- Protocol crate rejects unknown critical versions/fields, wrong site, oversized lengths/counts, duplicate indexes, invalid canonical encoding, and unsupported algorithm suites before large allocation.
|
||
- App/server compile against the same request/response types and HTTP methods.
|
||
- Threat model and crypto choice receive independent security review.
|
||
- Version negotiation cannot silently downgrade confidentiality/integrity.
|
||
|
||
**Rollback:** sync remains disabled; no “temporary” plaintext protocol ships.
|
||
|
||
### SITE-09 — Authenticated site-scoped E2EE and device/key lifecycle
|
||
|
||
**Priority:** P0
|
||
**Effort:** 12–20 person-days plus external review
|
||
**Depends on:** SITE-05, SITE-08
|
||
|
||
**Change**
|
||
|
||
- Provision per-site group context/key epochs through the selected reviewed protocol; bind authenticated user devices to site membership.
|
||
- Encrypt application payload client-side; include only minimum routing/version metadata outside ciphertext.
|
||
- Authenticate sender/device and bind site ID, protocol version, key epoch, operation ID, sequence, and payload digest as protected content/associated data.
|
||
- Implement device add/verify/remove, member add/remove, epoch rotation, lost-device recovery, backup/recovery, and key zeroization according to chosen library guarantees.
|
||
- Pin production TLS/security policy in addition to E2EE; allow loopback HTTP only in explicit development builds.
|
||
|
||
**Tests / exit**
|
||
|
||
- Known-answer vectors plus wrong site/key/epoch/device, modified header/body, nonce reuse guard, replay, reordering, duplicate, removed device, downgrade, and lost-key recovery tests.
|
||
- Server/database/log capture contains no worker/report/application plaintext or client group keys.
|
||
- A member without Site A membership cannot decrypt/inject A even if sharing another chat room.
|
||
- External security assessment has no unresolved critical/high findings.
|
||
|
||
**Rollback:** disable sync and retain encrypted local journal; never downgrade to TLS-only/plain room messages.
|
||
|
||
### SITE-10 — Operation journal, tombstones, and deterministic convergence
|
||
|
||
**Priority:** P0
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-04, SITE-08, SITE-09
|
||
|
||
**Change**
|
||
|
||
- Replicate validated domain commands/operations for one site, not whole mutable stores.
|
||
- Give operations globally unique ID, device sequence, base/entity revision, actor, schema, and cryptographically authenticated ordering metadata.
|
||
- Add tombstones with retention/compaction rules; edits/deletes must propagate.
|
||
- Define per-aggregate conflict rules. Use server cursor/log order, revision preconditions, CRDTs where justified, or explicit user conflict; do not let arbitrary future wall clocks win.
|
||
- Exclude chat cache, selection, secrets, keys, temporary files, and sync envelopes from snapshots.
|
||
- Build streaming encrypted snapshots for bootstrap/compaction with hash tree/digest, byte/record limits, and acknowledgement before journal truncation.
|
||
|
||
**Tests / exit**
|
||
|
||
- Multi-replica property tests randomly reorder/duplicate/drop/retry operations and prove convergence after eventual delivery.
|
||
- Edit/edit, edit/delete, delete/recreate, stale base, offline long fork, future/old clock, device reset, and compaction boundary cases.
|
||
- Syncing once does not make the next snapshot grow recursively.
|
||
- A malicious operation cannot bypass domain authorization/invariants merely because it decrypts.
|
||
- Journal/snapshot stays within §7 and interruption never discards unacknowledged ops.
|
||
|
||
**Rollback:** retain local encrypted journal and require manual export; do not apply ambiguous remote state.
|
||
|
||
### SITE-11 — Correlated bounded network client
|
||
|
||
**Priority:** P0
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-08
|
||
|
||
**Change**
|
||
|
||
- Replace static request IDs/singleton pending slots with `RequestManager` keyed by unique operation ID and carrying endpoint, site, room if applicable, base revision, deadline, retry/idempotency, and cancellation.
|
||
- Bound concurrent/pending requests and response body bytes; validate status/content type/typed body and required nonempty IDs.
|
||
- Use cursor-based pagination; do not fetch an entire room history to discover snapshots.
|
||
- Redact tokens and bodies from errors/logs; credentials use secure storage and explicit session lifecycle.
|
||
- Deliver response only to matching live controller/context.
|
||
|
||
**Tests / exit**
|
||
|
||
- 100 concurrent out-of-order create/send/fetch/sync operations correlate exactly.
|
||
- Timeout/cancel/site switch/retry/duplicate response/oversized body/invalid JSON/empty success ID tests.
|
||
- Idempotent retry cannot create duplicate room/events.
|
||
- Queue saturation is an explicit error/backpressure state.
|
||
|
||
**Rollback:** network features remain disabled; no global pending-slot fallback.
|
||
|
||
### SITE-12 — Reproducible real server interoperability
|
||
|
||
**Priority:** P0 release prerequisite for sync
|
||
**Effort:** 8–15 person-days depending on server import
|
||
**Depends on:** SITE-08 through SITE-11
|
||
|
||
**Change**
|
||
|
||
- Vendor/import the actual supported `nimanyatta` server/protocol history or depend on a reproducibly pinned source available to CI.
|
||
- Add migrations/test server launcher and typed endpoints for auth, membership, opaque events, cursors, snapshots, limits, rate limiting, and idempotency.
|
||
- Replace `sync_e2e` early-return behavior with explicit ignored developer test plus mandatory CI integration job that launches the pinned server.
|
||
- Test two users, three devices, two sites, removal/revocation, offline edits, and server restart/database migration.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fresh checkout can build and launch app protocol tests with no sibling repository.
|
||
- App and server use identical methods/routes/types; protocol drift is a compile/test failure.
|
||
- Database inspection verifies ciphertext-only application payload.
|
||
- Authz, rate/size limits, cursor semantics, idempotency, revocation, and schema migration pass.
|
||
- A required server job cannot be classified as pass when not executed.
|
||
|
||
**Rollback:** deploy no sync client UI; local encrypted operation journal remains intact.
|
||
|
||
### SITE-13 — Private, correct, correlated AI refinement
|
||
|
||
**Priority:** P0/P1
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-04, SITE-05, SITE-11
|
||
|
||
**Change**
|
||
|
||
- Instantiate the chosen provider with matching `ProviderKind`, model, endpoint, and credential source.
|
||
- Add operation/site/report/field/base revision IDs to request and result.
|
||
- Enforce request/response/time budgets; timeout/cancel/error/function call/partial stream is failure.
|
||
- Require explicit consent and show provider/data fields/region-retention link before remote processing. Minimize/redact input.
|
||
- Stream into preview only; apply as a domain command after complete response validation and explicit acceptance.
|
||
- Store credentials in platform secret storage; never mutate process environment as product settings.
|
||
|
||
**Tests / exit**
|
||
|
||
- Fake providers prove selection, data minimization, complete success, partial timeout, stale result, cancellation, oversized output, malformed/function response, and site switch.
|
||
- Original report is unchanged on all failures and before acceptance.
|
||
- Role/privacy policy gates who may submit data and persist refined text.
|
||
- Logs/artifacts contain no prompt, key, ID number, or report body.
|
||
|
||
**Rollback:** AI stays disabled; manual text remains fully functional.
|
||
|
||
### SITE-14 — Scanner-owned on-device OCR (no whole-PDF dependency)
|
||
|
||
**Priority:** P1 privacy
|
||
**Effort:** 6–12 person-days plus platform/license review
|
||
**Depends on:** SITE-05, SITE-07
|
||
**Owns:** `crates/apps/nigig_doc_scanner` (`ocr` cargo feature). **Will consume (next chunk):** `nigig-site` via `nigig_doc_scanner/ocr` only (buffer API, `default-features = false`; site bridge + UI enablement land separately — until then the site production graph contains no OCR path). **Explicitly not shipped:** `nigig-pdf-document` or any other `pdf-*` crate — the PDF OCR seam (`pdf-document/src/ocr.rs`, an engine trait only) is gated behind its own `ocr` cargo feature so no consumer pulls `rsa`/`x509`/`cms` and the document model just to recognize text.
|
||
**Related parallel effort:** `crates/apps/nigig-ocr` (desktop OCR-tool port; OS-native Vision/WinRT providers, stub fallback on Linux). It must not become the site recognition path: its Linux fallback should delegate to the scanner engine here, and its `nigig-pdf-document` dependency must stay inside that app, never inside `nigig-site` production.
|
||
|
||
**Change**
|
||
|
||
- Rename current behavior to crop/enhance if no OCR engine is shipped.
|
||
- Ship the engine in `nigig_doc_scanner` behind an explicit `ocr` cargo feature (`default = ["app", "ocr"]`; downstream device crates enable `nigig_doc_scanner/ocr` with `default-features = false` so Makepad UI, `nigig-core`/`nigig-uikit`, and `robius-location` never enter their production graph for text recognition).
|
||
- The engine is on-device, pure-Rust, no model download and no network: grayscale → Otsu binarization → connected-component segmentation → line grouping → embedded-glyph template match with per-word confidence. Language coverage is exactly the embedded glyph set (ASCII digits plus uppercase Latin for ID numbers/names); anything outside it must lower confidence, never invent text.
|
||
- The engine core takes raw 8-bit grayscale buffers (`&[u8], width, height`) so `nigig-site` production needs no `image` dependency; `DynamicImage` glue stays in the scanner crate beside `scanner_core`.
|
||
- Process managed `AssetId` bytes under media budgets in a bounded worker; correlate result to operation/site/worker/base revision; suggestions below the confidence floor return `None` and force manual entry.
|
||
- Present suggestions with confidence for user confirmation; never auto-approve identity.
|
||
- Define ID-image deletion immediately after verified extraction unless retention has explicit lawful purpose/consent.
|
||
- The scanner app itself becomes functional through the same `ocr` feature (same engine, same vectors); `nigig-site` transfers the functionality by calling the scanner crate, never by copying the engine or by depending on PDF crates.
|
||
|
||
**Tests / exit**
|
||
|
||
- Representative permitted ID fixtures measure field precision/recall and false-positive behavior; synthetic fixtures alone are insufficient.
|
||
- Synthetic determinism fixtures (text rendered from the embedded glyph set at multiple scales, plus blank/rotated/blurred/glare/adversarial/no-text inputs) prove the pipeline end to end in CI without camera hardware.
|
||
- Rotated, blurred, glare, Unicode names, no text, adversarial image, cancel, budget-overflow, and stale/site-switch tests.
|
||
- UI never says OCR found data when suggestions are `None`.
|
||
- `cargo tree` (or lockfile inspection) proves `nigig-site` production enables no `pdf-*` crate for OCR and `nigig_doc_scanner` without `app` enables no UI/location crates.
|
||
- Privacy/license/security review passes and no image leaves device absent explicit remote-OCR consent. License impact is nil (no new dependency; engine is first-party code).
|
||
|
||
**Rollback:** capture/manual entry only, with no OCR claim.
|
||
|
||
### SITE-15 — Streaming bounded GIF/video processing
|
||
|
||
**Priority:** P0 media
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-07
|
||
|
||
**Change**
|
||
|
||
- Accept managed assets and a `MediaBudget`, preflight encoded bytes/dimensions/frame count/duration/fps/output/container limits before decoding.
|
||
- Decode/resize/encode one bounded frame at a time; do not retain source images, resized images, RGB copies, JPEGs, `movi`, and final output simultaneously.
|
||
- Use checked arithmetic and fallible reservations/conversions for every pixel/RIFF/GIF/JPEG count.
|
||
- Prefer a maintained encoder/container library. If AVI remains, cap below 32-bit RIFF limits, include/validate needed indexing, and independently test playback.
|
||
- Use collision-resistant asset IDs, unique temp output, atomic publish, cancellation cleanup, and quotas.
|
||
|
||
**Tests / exit**
|
||
|
||
- Huge headers, decompression bombs, 0/too-high fps, >120 frames, mismatched dimensions, maximum counts, JPEG/output expansion, integer boundaries, disk full, cancel, and duplicate simultaneous jobs.
|
||
- Peak RSS stays under the declared decoded/output budget; test records metrics.
|
||
- Independent GIF/video decoders parse and play every golden output with expected frame count/duration/dimensions.
|
||
- No arbitrary path open and no second-resolution filename remains.
|
||
|
||
**Rollback:** disable animation/video; preserve original managed photos.
|
||
|
||
### SITE-16 — Portable panic-free PDF/DOCX exports
|
||
|
||
**Priority:** P1
|
||
**Effort:** 8–13 person-days
|
||
**Depends on:** SITE-04 through SITE-07, SITE-11 for job correlation
|
||
|
||
**Change**
|
||
|
||
- Return `Result<ExportArtifact, ExportError>` throughout; remove production `unwrap` and partial success.
|
||
- Iterate/truncate/wrap by Unicode grapheme/glyph boundaries, not byte offsets; embed a reviewed Unicode font with fallback.
|
||
- Resolve authorized `AssetId`s and embed compressed photos/thumbnails with captions/location policy. Never print local paths.
|
||
- Build valid DOCX image parts, relationships, content types, dimensions, and alt text using a maintained OOXML layer where feasible.
|
||
- Stream/paginate under page/byte/image budgets in a bounded export coordinator and deliver only after final picker/write completion.
|
||
- Apply role/PII minimization and watermark/status policy to compiled multi-site reports.
|
||
|
||
**Tests / exit**
|
||
|
||
- Emoji, combining marks, CJK/Arabic/long unbroken text, 500-page boundary, missing/deleted/unauthorized/corrupt photos, write failure, and cancellation never panic.
|
||
- Independent PDF parser/render and LibreOffice/Word-compatible DOCX open validate pages, text, images, relationships, and no repair warnings.
|
||
- Extracted package/PDF contains image bytes and no source filesystem paths.
|
||
- Export from Site A cannot include Site B without an explicit authorized multi-site scope.
|
||
|
||
**Rollback:** export text-only with explicit omitted-image warnings or disable the format; never claim photo inclusion.
|
||
|
||
### SITE-17 — Durable site-time-zone reminder state machine
|
||
|
||
**Priority:** P1
|
||
**Effort:** 5–8 person-days
|
||
**Depends on:** SITE-03 through SITE-05
|
||
|
||
**Change**
|
||
|
||
- Store IANA site time zone and validate local working-hour/calendar inputs; use device zone only by explicit policy.
|
||
- Make fire-time calculation return `Result`, handle DST ambiguous/nonexistent times according to documented policy, and remove panic/fallback-to-UTC guessing.
|
||
- Persist `PendingRegistration` with idempotency key before OS call; then persist `Registered`/`RegistrationFailed` and platform handle.
|
||
- Implement update/cancel/reschedule/reconcile on startup and permissions/time-zone/clock changes.
|
||
- Mark `Delivered` only on confirmed/successful post semantics available from platform; otherwise record attempted/unknown/failed honestly.
|
||
- Deduplicate app/OS paths by stable tag and state.
|
||
|
||
**Tests / exit**
|
||
|
||
- Nairobi and DST zones, midnight/year/month boundaries, invalid hour, past/future, clock rollback/forward, restart between each state, denied permission, OS failure, duplicate tick, cancel/reschedule.
|
||
- Site A zone never drives Site B.
|
||
- Repository and fake OS scheduler fault injection prove recoverable reconciliation.
|
||
- Notification body follows PII minimization policy.
|
||
|
||
**Rollback:** in-app due list only; no unreliable OS scheduling claims.
|
||
|
||
### SITE-18 — Screen/controller isolation, responsive UX, and observability
|
||
|
||
**Priority:** P1/P2
|
||
**Effort:** 10–16 person-days
|
||
**Depends on:** SITE-03 through enabled feature tranches
|
||
|
||
**Change**
|
||
|
||
- Replace direct `SiteStore` reads/mutations in screens with controllers and immutable revisioned view models.
|
||
- Route every screen with explicit `SiteContext`; invalidate/cancel old cache/jobs on switch.
|
||
- Remove static singleton action payloads and dead/inert actions.
|
||
- Virtualize large reports/workers/tasks/procurement/meeting lists; do not clone whole profile in draw.
|
||
- Add accessible labels/focus/touch targets/error/recovery/offline/conflict states.
|
||
- Instrument persistence revisions/latency/queue, sync encrypted bytes/cursor/retries/conflicts, media bytes/pixels/frames/RSS, AI duration, export pages/bytes, and frame/event latency. Never record content/PII.
|
||
|
||
**Tests / exit**
|
||
|
||
- Automated A/B route/switch suite checks every screen, dialog, async completion, cache, and export.
|
||
- Draw/event instrumentation detects disk/network/decode/export work and fails.
|
||
- 100k-record virtualized fixtures and site switch meet §7 budgets.
|
||
- Metrics cardinality/content review proves no IDs, names, paths, prompt/report text, tokens, or coordinates leak.
|
||
- Desktop/mobile lifecycle and accessibility tests exercise actual actions after app startup.
|
||
|
||
**Rollback:** presentation pieces may roll back; explicit site/controller/domain boundaries may not.
|
||
|
||
### SITE-19 — Migration, adversarial, interoperability, and release evidence
|
||
|
||
**Priority:** P0 release gate
|
||
**Effort:** 8–14 person-days plus external/device testing
|
||
**Depends on:** every enabled-feature tranche
|
||
|
||
**Change**
|
||
|
||
- Build sanitized legacy corpus: plaintext/raw JSON, plaintext envelope, valid encrypted, missing key, wrong key, tampered/truncated/future, multi-site/PII/media paths, recursive sync messages.
|
||
- Add storage fault, domain property, authorization, protocol fuzz, multi-replica convergence, media bomb, Unicode export, AI stale/cancel, reminder, and real runtime journeys.
|
||
- Run multi-device/server restart/migration/revocation tests and long offline/online soak.
|
||
- Perform independent security/privacy/crypto review and remediate critical/high findings.
|
||
- Publish capability matrix and release evidence with commit, toolchain, server schema, platform/device, limits, and known limitations.
|
||
|
||
**Tests / exit**
|
||
|
||
- Migration operates on copies, verifies encrypted outputs, and demonstrates rollback without data invention/loss.
|
||
- Zero unexplained/expired ignored tests; required server/device jobs actually execute.
|
||
- No PII plaintext appears in local encrypted artifacts, wire/server storage, logs, or CI artifacts.
|
||
- A/B isolation, role matrix, convergence, replay/forgery, resource limits, and forced failures all pass.
|
||
- Peak memory/latency/output meet §7 on supported devices.
|
||
|
||
**Rollback:** release is blocked or the failing capability remains disabled through SITE-01.
|
||
|
||
### SITE-20 — Organisation, full RBAC, site registry, settings and signatures
|
||
|
||
**Priority:** P0 product foundation (scope Phase 0: FR-0.1, FR-0.2, FR-0.3, FR-0.8)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-03, SITE-04, SITE-05
|
||
|
||
**Change**
|
||
|
||
- Add `Organization` aggregate: company creation, invite onboarding (email/phone/share link), per-site role assignment, consultant/client scoping strictly to shared scope.
|
||
- Extend the SITE-05 role set to the §4.2 product matrix: Org Admin, Overall Supervisor, Report Master, Site Supervisor, Engineer/Foreman, Procurement Officer, HSE Officer, External Consultant (assigned-tasks-only), Client viewer (digest-only). Capabilities remain deny-by-default; matrix defaults are per-organisation configurable.
|
||
- Extend site registry: per-site working hours, team assignment, geofence polygon, project type (already `SiteNature`), emergency info pointer (owned by SITE-28).
|
||
- Settings: user profile, drawn/typed signature capture (stored as a managed `AssetId`, never raw path), language English/Swahili, dark mode, notification preferences, storage management. Signatures bind to approvals per SITE-21 (identity + device + timestamp + document hash).
|
||
- No biometric attendance/verification: explicit non-goal (open question Q7 resolved as photo/QR per scope; biometrics would add DPA-2019 biometric-data obligations).
|
||
|
||
**Tests / exit**
|
||
|
||
- Invite/onboarding, role matrix (9 roles × capabilities, deny-by-default), consultant/client scope isolation, geofence in/out, working-hours validation, signature round-trip and tamper tests.
|
||
- Swahili string coverage for new surfaces; no hardcoded English in product flows.
|
||
|
||
**Rollback:** organisation features stay local single-user; never weaken SITE-05 denial.
|
||
|
||
### SITE-21 — Report richness, signatures, versioning and monthly packs
|
||
|
||
**Priority:** P1 (scope Phase 1: FR-1.2–1.5, FR-1.7, FR-1.14; escalation FR-1.6)
|
||
**Effort:** 10–15 person-days
|
||
**Depends on:** SITE-06, SITE-16, SITE-20
|
||
|
||
**Change**
|
||
|
||
- Per-entry category/tags (e.g. concrete works, plumbing) for aggregation; predefined quick-tags; voice-dictation hook (STT arrives in SITE-27; the entry editor reserves the affordance without claiming it).
|
||
- Report numbering (`DR-<SITE>-<YYYY>-<MM>-<DD>` with uniqueness guard), branded cover page, table of contents, customisable templates; monthly accumulation auto-builds from approved dailies plus HSE/procurement/progress stats (stats engines arrive with SITE-25/26/28; the accumulator consumes their query interfaces).
|
||
- Collaboration: per-entry status (pending/accepted/returned with comment), @mentions/comments, rejection reason + resubmission loop; approval captures drawn/typed signature bound to user/device/timestamp/document hash; approved reports lock and version (resubmission creates a new version with change log — SITE-06 immutability preserved).
|
||
- Multi-site compilation flags missing sites and sends chase notifications; escalation nudge 30 minutes after shift end when no report started (new SITE-17 triggers).
|
||
- Archive: status workflow `Draft → In Review → Approved → Locked`, full-text search, filter by site/date/author/status, batch export. PowerPoint and charts belong to SITE-29 (presentation layer), not here.
|
||
|
||
**Tests / exit**
|
||
|
||
- Numbering uniqueness under concurrency, template rendering golden files, signature binding/tamper, version-chain integrity, missing-site/chase/escalation journeys, archive search precision/recall on fixtures, batch-export failure atomicity.
|
||
|
||
**Rollback:** text-only export with explicit omissions (SITE-16) remains; never print paths.
|
||
|
||
### SITE-22 — Site-diary data: weather, plant, deliveries, delays, visitors
|
||
|
||
**Priority:** P1 (scope Phase 1: FR-1.9, FR-1.10, FR-1.11, FR-1.12, FR-1.13)
|
||
**Effort:** 6–10 person-days
|
||
**Depends on:** SITE-04
|
||
|
||
**Change**
|
||
|
||
- New aggregates (all site-scoped, validated, audited): `WeatherSnapshot` (auto-fetch + manual override; feeds delay justification), `PlantItem` (equipment/machinery with hours operated), `DelayRecord` (structured reason: weather/labour/materials/design/access + lost-time estimate), `VisitorRecord` (who/purpose/time in-out).
|
||
- Manpower auto-fills from Phase 2 attendance (SITE-23 query interface); materials-received links deliveries against orders (SITE-26 interface; unresolved link = explicit pending state, never silent).
|
||
- Weather fetch is the only network call in this tranche: explicit consent, cached offline, provider timeout treated as absent-data (manual override), never blocking report save.
|
||
|
||
**Tests / exit**
|
||
|
||
- Override-wins-fetch, offline-no-weather journeys, delay roll-up into monthly fixtures, plant-hour arithmetic (checked, finite), visitor overlap validation.
|
||
|
||
**Rollback:** diary sections degrade to manual-entry-only; never fabricate weather.
|
||
|
||
### SITE-23 — Workforce depth: consent, attendance, QR, register, payroll data
|
||
|
||
**Priority:** P1 privacy + payroll correctness (scope Phase 2: FR-2.1–2.6)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-05, SITE-07, SITE-14, SITE-20
|
||
|
||
**Change**
|
||
|
||
- Consent flag required on every ID capture before storage (DPA 2019 lawful basis); no-consent scans stay in volatile memory and are discarded with an explicit notice.
|
||
- `AttendanceRecord`: clock-in/clock-out per worker per day with optional geofence check; hours and overtime computed with checked finite arithmetic; payroll-ready CSV export (per department/day/week/month). Full payroll and statutory deductions stay out of scope (export-only, §17 of scope).
|
||
- QR badges: printable per-worker QR for repeat check-in (scan, not full ID capture); badge IDs are opaque, revocable, and distinct from national ID numbers.
|
||
- `WorkerRegister`: per-site status (active/inactive), skill/trade tags, organisation-level blocklist with appeal/audit trail.
|
||
- Registration photo at enrolment (anti buddy-punching) as managed asset with the same retention policy as ID images; PPE compliance checklist at sign-in (helmet/boots/vest) feeding HSE stats (SITE-28).
|
||
- Labour-cost summary (headcount × day rate by trade) for budget tracking; Excel/PDF export of workers table (CSV now, Excel via SITE-16 export work).
|
||
- Auto-purge job for expired ID data under the retention policy; full deletion on offboarding propagating to indexes, assets, sync ops and backups per stated policy.
|
||
|
||
**Tests / exit**
|
||
|
||
- No-consent-never-stored, double clock-in/out rejection, overnight-shift arithmetic, geofence edge, QR revocation, blocklist enforcement + audit, purge verification (no residual bytes), payroll CSV golden files, PPE roll-up.
|
||
|
||
**Rollback:** attendance stays single-scan daily table; never invent hours.
|
||
|
||
### SITE-24 — Chat completeness on the decided transport
|
||
|
||
**Priority:** P1 (scope Phase 3: FR-3.1–3.6)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-08 through SITE-12 transport decision, SITE-20, SITE-31 server
|
||
|
||
**Change**
|
||
|
||
- Channel model: per-site channels (e.g. Announcements, General), cross-site management channel, direct messages; membership tied to site teams and SITE-20 roles; clients excluded from channels entirely.
|
||
- Essentials: read receipts, @mentions with push, pinned messages, message search, offline send-queue with delivery on reconnect, file/document library with inline preview and pinned approvals/drawings, read receipts on critical documents.
|
||
- Bot notices for key events (report approved, meeting scheduled, PO issued, inspection due); Overall Supervisor broadcast across sites; channel admin roles, mute, archive, retention policy.
|
||
- Until the transport/server decision lands, chat remains the current local 50-line cache with no composer, no send path, and no sync claims (SITE-01 containment holds).
|
||
|
||
**Tests / exit**
|
||
|
||
- Membership isolation (cross-site DM refusal), receipt/mention/broadcast journeys, offline queue ordering and dedup, retention-purge verification, moderation actions audited.
|
||
|
||
**Rollback:** local cache only; never transmit chat as sync snapshots (SITE-P0-05 stays closed).
|
||
|
||
### SITE-25 — Programme depth: dependencies, Gantt, checklists, snags, RFIs, variations
|
||
|
||
**Priority:** P1 (scope Phase 4: FR-4.1, FR-4.3–4.9)
|
||
**Effort:** 12–18 person-days
|
||
**Depends on:** SITE-06, SITE-20
|
||
|
||
**Change**
|
||
|
||
- Scheduling core: task dependencies (predecessor/successor), milestones, baseline vs actual tracking with slippage alerts, percentage complete, daily-entry ↔ task linking (entries reference `TaskId`; dangling links rejected at command time).
|
||
- Views by reuse, not reimplementation: extract `crates/apps/nigig-build/src/construction_frame/pages/workspace/project_management/` (`GanttTask`/`TaskType`, `GanttRenderer`, `GanttHistory`, `persistence`, scheduling `logic`) into its own crate (proposed `nigig-gantt`) with the pure scheduling core separated from Makepad views; extend with dependency/critical-path computation and baseline capture. `nigig-site` depends on that crate and implements only site-specific wiring. Gantt, calendar and board views with status/owner/department/consultant filters.
|
||
- Inspection checklists (pass/fail/NA with notes + evidence photos), NCRs, inspection request forms and approval certificates as document hooks (SITE-30); AI tailored task breakdown from project type + scope description (SITE-13 pipeline, advisory only).
|
||
- Registers (all site-scoped, validated, revisioned): snag/defect list (location, photo, assignee, due date; closure requires closure photo + sign-off; stats feed monthly), RFI log (response tracking, due dates, delay-risk highlighting), variation register (justification, cost/time impact, approval trail, affected-task links).
|
||
|
||
**Tests / exit**
|
||
|
||
- Dependency-cycle rejection, critical-path golden schedules, baseline/slippage fixtures, checklist evidence requirements, snag-closure photo enforcement, RFI overdue escalation, variation impact arithmetic (fixed-decimal), extracted-crate independence (site builds without `nigig-build`).
|
||
|
||
**Rollback:** list/board views only; never show uncomputed dates as a schedule.
|
||
|
||
### SITE-26 — Procurement depth: LPO workflow, deliveries, budgets, payments
|
||
|
||
**Priority:** P1 (scope Phase 5: FR-5.1–5.6)
|
||
**Effort:** 10–14 person-days
|
||
**Depends on:** SITE-06, SITE-14, SITE-25
|
||
|
||
**Change**
|
||
|
||
- Required-vs-delivered auto-computation from daily-report deliveries against the material schedule; remaining-to-procure surfaced per task.
|
||
- Supplier rating (price, timeliness, quality) from history with price comparison per material; ratings are computed, never manually inflated without an audit event.
|
||
- Requisition → numbered LPO (PDF) workflow with approval chain (Supervisor → Procurement → Admin), drawn/typed signatures, status `Requested → Approved → Ordered → Delivered → Closed`.
|
||
- Delivery capture: photo + OCR of delivery notes (SITE-14 engine), quantity verification against the LPO, shortfall/damage flags; verified receipts update schedule and budget automatically.
|
||
- Budget vs actual per material and per task with variance highlights for the monthly report; BOQ linkage tying material requirements to SITE-25 tasks so procurement timing follows the schedule; on-site inventory levels with shortage alerts.
|
||
- Optional payment records with receipt images and mobile-money reference capture (e.g. M-Pesa). Full accounting stays out of scope (API export instead).
|
||
|
||
**Tests / exit**
|
||
|
||
- LPO numbering uniqueness, chain-of-approval enforcement, delivery shortfall arithmetic, budget variance fixtures, BOQ explosion against schedule changes, payment-reference format validation, OCR-misread quarantine (unverified quantities never update the schedule).
|
||
|
||
**Rollback:** schedule + directory only; never mark ordered/delivered without evidence.
|
||
|
||
### SITE-27 — Meetings depth: RSVP, consent-gated audio, actions, cross-links
|
||
|
||
**Priority:** P1 privacy (scope Phase 6: FR-6.1, FR-6.3–6.9)
|
||
**Effort:** 10–16 person-days plus STT evaluation
|
||
**Depends on:** SITE-05, SITE-13, SITE-20
|
||
|
||
**Change**
|
||
|
||
- Scheduling: RSVP per invitee, agenda templates, pre-read attachments (monthly report, programme update), attendance register (auto or manual) attached to minutes.
|
||
- Recording-consent gate: audio capture activates only after explicit recorded consent from attendees (jurisdictional notice); without it the meeting is minutes-manual-only. Audio never leaves the device without a second explicit processing consent.
|
||
- Speech-to-text with speaker identification (English and Swahili) via an evaluated explicit-consent cloud STT (scope2 build order keeps meetings last for this reason — on-device STT is not viable in the Makepad ecosystem today); transcript export alongside minutes; AI minutes draft (discussions → decisions → actions with owners and deadlines) through the SITE-13 pipeline, always advisory.
|
||
- Action-item tracker: tracked items with due-date reminders, status, automatic carry-forward of unresolved items; cross-links from minutes to reports, tasks, RFIs, snags and variations discussed.
|
||
|
||
**Tests / exit**
|
||
|
||
- No-consent-no-capture (microphone never opens), consent audit trail, RSVP/quorum rules, action carry-forward chains, cross-link dangling rejection, transcript redaction of non-consented segments, STT provider data-retention contract on file.
|
||
|
||
**Rollback:** manual minutes only with no recording claim; never transcribe without consent.
|
||
|
||
### SITE-28 — Safety, Health & Environment (HSE)
|
||
|
||
**Priority:** P1 (scope Phase 7: FR-7.1–7.5; scope2 safety/incident addendum)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-04, SITE-05, SITE-23
|
||
|
||
**Change**
|
||
|
||
- New aggregates (site-scoped, validated, audited): `IncidentRecord` (incidents and near misses with photos, severity, persons involved, immediate action; escalation to supervisor + HSE officer), `ToolboxTalk` (topic, date, attendees pulled from the worker register), `SafetyInspection` (scheduled PPE/scaffolding/housekeeping/electrical checklists with corrective actions, assignees, closure evidence).
|
||
- Per-site emergency information (contacts, assembly points, procedures) pinned for offline availability; synced as read-mostly reference data with version stamps.
|
||
- HSE statistics engine: days since last incident, toolbox-talk counts, open corrective actions, PPE compliance roll-up (from SITE-23 sign-in checklists) — consumed by monthly reports (SITE-21) and dashboards (SITE-29) through query interfaces, never by copying.
|
||
|
||
**Tests / exit**
|
||
|
||
- Severity-escalation matrix, corrective-action closure-evidence enforcement, offline emergency-info availability, statistics golden fixtures (incident-free streaks, reopen handling).
|
||
|
||
**Rollback:** incident log manual-only; never auto-close corrective actions.
|
||
|
||
### SITE-29 — Dashboards, analytics, charts and client digest
|
||
|
||
**Priority:** P1/P2 (scope Phase 8: FR-8.1–8.5; FR-1.7 charts and PowerPoint)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-21, SITE-25, SITE-26, SITE-28
|
||
|
||
**Change**
|
||
|
||
- Site dashboard: programme progress %, workers on site today, report status (submitted/missing), open snags/RFIs, delays this month, spend vs budget — all from the query interfaces of their owning tranches.
|
||
- Portfolio view for the Overall Supervisor: all sites side by side with drill-down and traffic-light health; trends (manpower over time, task burn-down, materials consumed, report timeliness).
|
||
- Monthly-pack charts (progress trend, manpower trend, incident count) embedded in the PDF; PowerPoint export for the meeting presentation; any dashboard/chart exportable as PDF/image.
|
||
- Client digest: automated weekly/monthly progress digest (summary + photo highlights) shared read-only; optional lightweight web-portal access (portal timing is open question Q5 — digest-by-share ships first, portal follows the server decision in SITE-31).
|
||
|
||
**Tests / exit**
|
||
|
||
- Dashboard golden fixtures (missing-data states render honestly, never zero-filled as real), chart data-point audits against source aggregates, digest scope test (client sees shared-only), export fidelity for charts.
|
||
|
||
**Rollback:** no dashboard; monthly pack without charts rather than with wrong charts.
|
||
|
||
### SITE-30 — Document and drawing control
|
||
|
||
**Priority:** P1 (scope Phase 9: FR-9.1–9.4; scope2 drawing-revision addendum)
|
||
**Effort:** 8–12 person-days
|
||
**Depends on:** SITE-05, SITE-07
|
||
|
||
**Change**
|
||
|
||
- Permissioned central `DocumentLibrary`: drawings, contracts, approvals, permits, insurance certificates, survey reports; upload from camera or files; in-app PDF/image viewer; search by name, type, discipline.
|
||
- Drawing revision control: revision register per drawing (Rev A/B/C…), supersede/withdraw with automatic team notification; offline access to current revisions on device; task-level latest-approved-revision tracking (SITE-25 hook) so crews never build from superseded drawings.
|
||
- Transmittals: who received which revision and when, with read receipts (SITE-24 hook where chat exists).
|
||
|
||
**Tests / exit**
|
||
|
||
- Supersede-notification journeys, offline-current-revision guarantee, withdrawn-revision refusal at task level, transmittal completeness audit, viewer fuzz over malformed PDFs/images (panic-free, budgeted).
|
||
|
||
**Rollback:** flat file list with no revision claims; never serve a withdrawn revision as current.
|
||
|
||
### SITE-31 — Integrations, API, portability and the server decision
|
||
|
||
**Priority:** P1/P2 (scope Phase 10: FR-10.1–10.5; scope §5 backend; scope §6 sync)
|
||
**Effort:** 10–16 person-days plus backend build
|
||
**Depends on:** SITE-12 protocol work, SITE-17, SITE-20
|
||
|
||
**Change**
|
||
|
||
- Server decision (recorded in an ADR): evaluate extending the existing server at `/Users/aok/Projects/rustdev/CratesCode/nimanyatta/src` (auth, db, broadcast, client exist) into the scope §5 backend — Rust API service + PostgreSQL + S3-compatible storage — covering delta sync with resumable media upload, server arbitration for shared-document conflicts (flagged for the Report Master), server-side approval/signature/permission enforcement, data-residency option per organisation, and organisation-level backup/export archives.
|
||
- Architectural divergence resolved explicitly: scope §6 requires server-side authority for approvals, signatures, locking and roles; the SITE-08/09 E2EE group protocol remains the only permitted path for payloads the server must not read, and only after its independent review. The ADR states per-data-class authority (server-arbitrated vs end-to-end) so the two designs compose instead of contradicting.
|
||
- Integrations: two-way calendar sync (meetings + reminders), email/WhatsApp share targets for reports/minutes/POs, map view of site registry, weather-service wiring (SITE-22 provider behind the same consent/timeout contract).
|
||
- REST API + webhooks for accounting/ERP integration (timesheet and PO push) with the SITE-20 capability matrix enforced on every call.
|
||
|
||
**Tests / exit**
|
||
|
||
- Arbitration golden conflicts (both orders converge, loser flagged), resumable-upload interruption suite, webhook delivery/retry/idempotency, API authorization matrix (server-side, independent of client checks), residency configuration test, backup-restore drill.
|
||
|
||
**Rollback:** local encrypted operation journal remains intact (SITE-10); no sync claims without the server.
|
||
|
||
### SITE-32 — Product NFRs, scale proof and feature-release evidence
|
||
|
||
**Priority:** P1 release gate for the feature-rich app
|
||
**Effort:** 6–10 person-days plus device lab
|
||
**Depends on:** every enabled-feature tranche (SITE-20–31)
|
||
|
||
**Change**
|
||
|
||
- Latency budgets (scope §10, added to §7): cold start < 3 s on mid-range Android; photo capture → entry saved < 1 s; monthly pack generation < 60 s; draw/event and site-switch budgets from §7 unchanged.
|
||
- Media pipeline target: client-side compression to ≤ 1 MB per photo for sync; originals retained on device until synced with storage display and cleanup tools; evidence watermarks (timestamp + GPS + site name) applied at capture and verifiable thereafter.
|
||
- Localisation: Swahili + English UI with complete string coverage gates (no hardcoded product strings); accessibility: dynamic text sizing, contrast modes, large touch targets (glove operation), sunlight-glare contrast checks.
|
||
- Scale fixtures: 100+ sites and 1,000+ workers per organisation without redesign (virtualized lists per SITE-18, paginated queries per SITE-03, repository budgets per §7).
|
||
- Compatibility matrix: Android 9+, iOS 15+, Windows 10+, macOS 12+, modern browsers; battery discipline for GPS/camera/batched sync.
|
||
- Feature-release evidence per R1–R5 (§13 roadmap): capability matrix, FR traceability run, performance table, known limitations — published with commit, toolchain, server schema and device list.
|
||
|
||
**Tests / exit**
|
||
|
||
- Startup/capture/generation timing on reference devices (recorded, not asserted on CI runners), photo-size distribution audit, i18n completeness lint, accessibility traversal, 100-site/1,000-worker soak within §7 memory budgets.
|
||
|
||
**Rollback:** features missing their NFR evidence stay out of the release notes; never claim performance without device data.
|
||
|
||
---
|
||
|
||
## 9. Detailed legacy migration and recovery policy
|
||
|
||
1. **Never open destructively.** Read bounded original bytes once, hash them, and work on a copy.
|
||
2. **Classify before action:** absent, zero length, raw legacy JSON, explicit plaintext envelope, encrypted known version/key ID, encrypted missing key, authentication failure, corrupt payload, or unsupported future version.
|
||
3. **Do not create a key while opening encrypted data.** Setup creates keys only for a confirmed new profile/store ID.
|
||
4. **Require encryption before migration writes.** If key setup fails, retain read-only recovery state; do not write plaintext.
|
||
5. **Split by site.** Reports/workers/tasks/procurement/meetings/directories/assets map by validated site ID. Dangling and global records are quarantined for explicit resolution.
|
||
6. **Do not replicate device state.** Selected site and cached chat presentation are excluded.
|
||
7. **Import assets.** Canonicalize only for one-time migration, enforce byte/pixel limits, copy into managed asset storage, and replace path with `AssetId`. Missing/unsafe files become warnings, not arbitrary reads.
|
||
8. **Preserve PII policy.** ID images are not imported automatically without a retention decision.
|
||
9. **Write new aggregates atomically, reopen/decrypt/validate, compare record counts/digests, then activate new manifest.**
|
||
10. **Keep originals encrypted/restricted for a stated recovery window.** Deletion requires explicit confirmation and backup policy.
|
||
11. **Never seed demo data as migration/recovery.** A new empty profile is a valid explicit user choice.
|
||
12. **No automatic downgrade.** Newer-schema data stays read-only for an old binary.
|
||
|
||
---
|
||
|
||
## 10. Required CI command matrix
|
||
|
||
Memory-constrained baseline commands:
|
||
|
||
```bash
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_DEV_DEBUG=0 cargo check --locked -p nigig-site --all-targets
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_TEST_DEBUG=0 cargo test --locked -p nigig-site --lib -- --test-threads=1
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_TEST_DEBUG=0 cargo test --locked -p nigig-site --tests -- --test-threads=1
|
||
cargo fmt -p nigig-site -- --check
|
||
cargo run -p nigig-site -- --check
|
||
CARGO_BUILD_JOBS=1 CARGO_PROFILE_DEV_DEBUG=0 cargo clippy --locked -p nigig-site --all-targets -- -D warnings
|
||
git diff --check
|
||
```
|
||
|
||
Required named jobs as tranches land:
|
||
|
||
```bash
|
||
cargo test --locked -p nigig-site --test storage_recovery -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test project_site_isolation -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test authorization_matrix -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test protocol_adversarial -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test sync_convergence -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test media_limits -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test export_interop -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test runtime_ui -- --test-threads=1
|
||
cargo test --locked -p nigig-site --test sync_e2e_required -- --test-threads=1
|
||
```
|
||
|
||
Release CI launches the pinned server itself; `NIMANYATTA_E2E_URL` cannot be an optional condition for the required job. Fuzzers and independent parsers run with fixed wall-clock/memory ceilings and retain crashing inputs.
|
||
|
||
---
|
||
|
||
## 11. Release gates
|
||
|
||
### Local/offline release
|
||
|
||
- [ ] Current sync is absent from production build.
|
||
- [ ] Missing/wrong key, tamper, corruption, future version, empty, and I/O failures are distinct non-destructive states.
|
||
- [ ] No plaintext fallback exists; durable save errors are surfaced.
|
||
- [ ] Repository queues/writes are bounded, atomic, and flushed/reconciled on lifecycle exit.
|
||
- [ ] Every command/query/export/job uses explicit typed site context.
|
||
- [ ] Domain transitions, references, finite/range rules, roles, revisions, and audit are centrally enforced.
|
||
- [ ] Worker PII and ID-image lifecycle policy is approved and tested.
|
||
- [ ] All enabled media uses managed assets and passes encoded/pixel/frame/output/peak-memory limits.
|
||
- [ ] OCR is the scanner-owned on-device engine (measured, confidence-gated, confirmation-required, no PDF dependency) or the flow is labelled capture-only.
|
||
- [ ] PDF/DOCX are Unicode-safe, embed authorized photos, return errors, and pass independent consumers.
|
||
- [ ] AI is disabled or passes provider/privacy/correlation/acceptance gates.
|
||
- [ ] Reminders pass time-zone/durability/idempotency/reconciliation gates or remain in-app only.
|
||
- [ ] Site-owned CI has a definitive green result with real runtime assertions.
|
||
|
||
### Feature-rich product release gates (R1–R5, §13)
|
||
|
||
- [ ] **R1 — Verified reporting core:** SITE-20 (org/RBAC/registry/settings) + SITE-21 (report richness, signatures, versioning, monthly packs) on top of the local/offline gates above. Approval signatures bind identity/device/timestamp/document hash; approved reports lock and version.
|
||
- [ ] **R2 — Field operations complete:** SITE-22 (site-diary data) + SITE-23 (consent, attendance, QR, register, payroll CSV, auto-purge) + SITE-24 chat only if its transport gates pass, otherwise chat stays local-cache-only with no send claims.
|
||
- [ ] **R3 — Planning and materials:** SITE-25 (dependencies, Gantt via the extracted crate, checklists, snags, RFIs, variations) + SITE-26 (LPO workflow, delivery verification, budgets). No ordered/delivered state without evidence.
|
||
- [ ] **R4 — Governance and safety:** SITE-27 (RSVP, consent-gated audio, action tracker) + SITE-28 (HSE records and stats). No recording or transcription without recorded consent.
|
||
- [ ] **R5 — Oversight and ecosystem:** SITE-29 (dashboards, charts, digest) + SITE-30 (document/drawing control, no withdrawn revision served as current) + SITE-31 (server ADR, arbitration, API auth, backup drill).
|
||
- [ ] **NFR evidence (SITE-32):** startup/capture/generation timings on reference devices, photo-size audit, i18n completeness (EN/SW), accessibility traversal, 100-site/1,000-worker soak. Features missing NFR evidence stay out of release notes.
|
||
- [ ] **No biometric verification ships** (photo/QR sufficiency per scope Q7); full payroll, accounting, BIM/CAD editing, telematics and offline client-side AI stay out of scope with export/API paths instead.
|
||
|
||
### Additional sync release gates
|
||
|
||
- [ ] Shared protocol and threat-model ADR are approved.
|
||
- [ ] App/server/tests use one protocol crate and reproducible in-repo/pinned server.
|
||
- [ ] Authenticated site-scoped E2EE passes external review with no open critical/high findings.
|
||
- [ ] Device/member lifecycle, revocation, recovery, replay, downgrade, cross-site, and ciphertext-at-server tests pass.
|
||
- [ ] Operation journal/tombstones/conflicts converge under randomized multi-replica tests.
|
||
- [ ] Network client correlates bounded concurrent requests and validates all responses.
|
||
- [ ] Required E2E server job actually executes; no silent return/skip counts as pass.
|
||
- [ ] Product copy accurately states metadata/endpoint/availability limitations.
|
||
|
||
---
|
||
|
||
## 12. Delivery, push safety, and rollback protocol
|
||
|
||
For each `SITE-NN` tranche:
|
||
|
||
1. Record starting SHA and keep a clean worktree.
|
||
2. Add regression/adversarial tests that fail against the parent commit.
|
||
3. Run the tranche matrix under memory/resource limits plus `git diff --check`.
|
||
4. Commit only that independently reviewable change using its tranche ID.
|
||
5. Immediately before push, run `git fetch origin main` and compare remote/merge base.
|
||
6. If remote moved, inspect all incoming changes and rebase/merge without discarding them; rerun tests after resolution.
|
||
7. Push without force and verify the exact commit exists remotely before starting the next pushed tranche.
|
||
8. If authentication is unavailable, report the exact blocker and preserve the tested local commit. Never claim success, expose credentials, or overwrite remote work later.
|
||
|
||
Rollback defaults to **feature off + preserved encrypted data**. Never roll back to plaintext storage/sync, first-site fallback, destructive demo seeding, unrestricted path decode, public workflow mutation, or stale async acceptance.
|
||
|
||
---
|
||
|
||
## 13. Critical path and cross-plan dependencies
|
||
|
||
```text
|
||
SITE-01 containment → SITE-02 encrypted repository → SITE-03 site aggregates/context
|
||
├→ SITE-04 commands/audit → SITE-05 auth/privacy → SITE-06 workflows
|
||
└→ SITE-07 managed assets → SITE-14/15/16
|
||
SITE-03 + SITE-05 → SITE-08 protocol → SITE-09 E2EE → SITE-10 convergence
|
||
└────────→ SITE-11 client → SITE-12 real server
|
||
SITE-04/05/11 → SITE-13 AI
|
||
SITE-03/04/05 → SITE-17 reminders
|
||
All enabled paths + SITE-00 → SITE-18 → SITE-19
|
||
```
|
||
|
||
- Build/Traffic/CAD work does not justify delaying Site's emergency containment.
|
||
- OCR is owned by `nigig_doc_scanner` (`ocr` feature); `nigig-site` consumes `nigig_doc_scanner/ocr` with `default-features = false` and never depends on `pdf-*` for text recognition. The PDF `ocr` seam stays engine-free and feature-gated.
|
||
- Future integration with `nigig-build` requires an authenticated mapping between Build project ID and Site ID; never sync one app's global store into the other.
|
||
- Shared chat rooms may coexist with sync, but sync payloads must use dedicated opaque protocol events and keys, not visible text bodies.
|
||
- A safe release may omit sync, AI, OCR, video, DOCX, or OS reminders. It may not ship unsafe substitutes for them.
|
||
|
||
```text
|
||
SITE-20 org/RBAC/registry ─┬→ SITE-21 reports ─┬→ SITE-29 dashboards ─┐
|
||
│ └→ SITE-30 documents ─┤
|
||
├→ SITE-22 site-diary ────────────────────┤
|
||
├→ SITE-23 workforce ─┬→ SITE-28 HSE ──────┤→ SITE-32 NFRs
|
||
├→ SITE-24 chat (needs SITE-08..12+31) ───┤ + release
|
||
├→ SITE-25 programme ─┬→ SITE-26 procure ──┤ evidence
|
||
└→ SITE-27 meetings ─┘ ┘
|
||
SITE-31 server/ADR underpins SITE-24 + multi-device sync + API + backup.
|
||
```
|
||
|
||
### Feature release roadmap (scope §16: R1–R5)
|
||
|
||
| Release | Tranches | Value |
|
||
|---|---|---|
|
||
| **R1 — MVP** | SITE-00–07 + SITE-20 + SITE-21 (FR-1.1–1.8, 1.14 core) | Verified daily reports, multi-site compilation, monthly packs |
|
||
| **R2** | SITE-22 + SITE-23 + SITE-24 (transport permitting) | Complete daily field operations |
|
||
| **R3** | SITE-25 + SITE-26 | Planning and materials control |
|
||
| **R4** | SITE-27 + SITE-28 | Governance and safety |
|
||
| **R5** | SITE-29 + SITE-30 + SITE-31 | Oversight, control and ecosystem |
|
||
|
||
R1 is the critical path; R2–R5 proceed in parallel streams once SITE-20 exists. SITE-32 gates every release's NFR claims.
|
||
|
||
### FR → tranche traceability (scope `construction-site-app-scope.md` + `construction-site-app-scope2.md`)
|
||
|
||
| Scope requirement | Tranche |
|
||
|---|---|
|
||
| FR-0.1 organisation & invites, FR-0.2 RBAC matrix, FR-0.3 site registry, FR-0.8 settings/signatures/language | SITE-20 |
|
||
| FR-1.1 media (+annotation, voice note, watermark), FR-1.2 categories/tags/dictation/captioning, FR-1.3 numbering/cover/TOC/templates | SITE-21 (+ SITE-07 assets, SITE-15 media, SITE-27 STT) |
|
||
| FR-1.4 entry status/comments/signatures/lock+version, FR-1.5 chase, FR-1.6 escalation, FR-1.14 lifecycle/search/archive/batch | SITE-21 (+ SITE-17 triggers) |
|
||
| FR-1.7 monthly accumulation (+AI summary), charts, PowerPoint | SITE-21 (accumulation) + SITE-29 (charts/PPT) |
|
||
| FR-1.9 weather, FR-1.10 manpower/plant, FR-1.11 deliveries, FR-1.12 delays, FR-1.13 visitors | SITE-22 |
|
||
| FR-2.1 consent, FR-2.2 Excel export, FR-2.3 attendance/payroll CSV, FR-2.4 QR, FR-2.5 register/blocklist, FR-2.6 auto-purge, registration photo, PPE checklist, labour cost | SITE-23 |
|
||
| FR-3.1–3.6 chat, channels/DMs, receipts, bots, broadcast, moderation | SITE-24 (needs transport + server) |
|
||
| FR-4.1 AI breakdown/library, FR-4.3 deps/milestones/baseline, FR-4.4 checklists/NCRs, FR-4.5 statuses/%/links | SITE-25 (+ SITE-13 AI) |
|
||
| FR-4.6 Gantt/calendar/board via `nigig-build/.../project_management` extraction | SITE-25 (new `nigig-gantt`-style crate) |
|
||
| FR-4.7 snags, FR-4.8 RFIs, FR-4.9 variations, drawing-register hooks | SITE-25 (+ SITE-30) |
|
||
| FR-5.1 req-vs-delivered, FR-5.2 ratings/compare, FR-5.3 LPO workflow, FR-5.4 delivery verify, FR-5.5 budgets, FR-5.6 payments/M-Pesa, BOQ, inventory | SITE-26 |
|
||
| FR-6.1 RSVP, FR-6.3 speaker ID, FR-6.4 action owners, FR-6.6 agendas/pre-reads, FR-6.7 tracker, FR-6.8 cross-links, FR-6.9 STT/transcript, attendance register, recording consent | SITE-27 |
|
||
| FR-7.1–7.5 HSE + scope2 incident addendum | SITE-28 |
|
||
| FR-8.1–8.5 dashboards/digest/portal | SITE-29 |
|
||
| FR-9.1–9.4 documents/drawings + scope2 revision control | SITE-30 |
|
||
| FR-10.1 calendar, FR-10.2 share, FR-10.3 maps/weather, FR-10.4 API/webhooks, FR-10.5 backup | SITE-31 |
|
||
| Scope2 §0 foundations (offline, RBAC, audit, switcher, signatures, CSV/Excel, i18n, a11y) | SITE-00–05, SITE-16, SITE-18, SITE-20, SITE-32 |
|
||
| AI summary table (refine, STT, captioning, templates, minutes, anomaly nudges) | SITE-13 + SITE-21 + SITE-25 + SITE-27 |
|
||
| Notifications table (escalation, chase, inspection-due, approvals, snag/RFI, meeting, incident, LPO) | SITE-17 extended per tranche |
|
||
|
||
### Out of scope v1 (scope §17, binding on this plan)
|
||
|
||
Full payroll processing and statutory deductions (payroll-ready export only); accounting/bookkeeping (API integration); BIM/CAD authoring or editing (documents viewed, not edited); equipment telematics/IoT; client-side internet-free AI (cloud AI with graceful offline degradation); biometric worker verification.
|
||
|
||
### Open decisions to record (scope §18)
|
||
|
||
Retention period for worker ID data (needs legal/HR guidance — blocks SITE-23 purge defaults); Word parity vs PDF-first on day one; client portal in R1 vs R5 (default: R5, digest-by-share first); AI provider and budget; hosting model and data-residency offering (blocks SITE-31 ADR).
|