4.8 KiB
4.8 KiB
Implementation Plan: Inventory Core (040)
Branch: spec/040-inventory-core | Date: 2026-01-07 | Spec: specs/040-inventory-core/spec.md
Scope (this step): Produce a clean, implementable plan.md + consistent tasks.md for Spec 040 only.
Summary
Implement tenant-scoped Inventory + Sync Run tracking as the foundational substrate for later Inventory UI and higher-order features.
Key outcomes:
- Inventory is “last observed” (not backup), stored as metadata + whitelisted
meta_jsonb. - Sync runs are observable, selection-scoped via deterministic
selection_hash. - “Missing” is derived relative to latest completed run for the same
(tenant_id, selection_hash). - Automation is safe: locks, idempotency, throttling handling, global+per-tenant concurrency limits.
Technical Context
- Language/Version: PHP 8.4
- Framework: Laravel 12
- Admin UI: Filament v4 + Livewire v3
- Storage: PostgreSQL (JSONB available)
- Queue/Locks: Laravel queue + cache/Redis locks (as configured)
- Testing: Pest v4 (
php artisan test) - Target Platform: Sail-first local dev; container deploy (Dokploy)
Constitution Check
- Inventory-first: inventory stores last observed state only (no snapshot/backup side effects).
- Read/write separation: this feature introduces no Intune write paths.
- Single contract path to Graph: Graph reads (if needed) go via Graph abstraction and contracts.
- Tenant isolation: all reads/writes tenant-scoped; no cross-tenant shortcuts.
- Automation: locked + idempotent + observable; handle 429/503 with backoff+jitter.
- Data minimization: no payload-heavy storage; safe logs.
No constitution violations expected.
Project Structure (Impacted Areas)
specs/040-inventory-core/
├── spec.md
├── plan.md
└── tasks.md
app/
├── Models/
├── Jobs/
├── Services/
└── Support/
database/migrations/
tests/Feature/
tests/Unit/
Implementation Approach
Phase A — Data Model + Migrations
-
Add
inventory_itemstable- Identity: unique constraint to prevent duplicates, recommended:
(tenant_id, policy_type, external_id)
- Fields:
display_name,platform/category(if applicable),meta_jsonb,last_seen_at,last_seen_run_id. - Indexing: indexes supporting tenant/type listing; consider partials as needed.
- Identity: unique constraint to prevent duplicates, recommended:
-
Add
inventory_sync_runstable- Identity:
tenant_id,selection_hash - Status fields:
status,started_at,finished_at,had_errors - Counters:
items_observed_count,items_upserted_count,errors_count - Error reporting: stable error code(s) list or summary field.
- Identity:
Phase B — Selection Hash (Deterministic)
Implement canonicalization exactly as Spec Appendix:
- Only include scope-affecting keys in
selection_payload. - Sort object keys; sort
policy_types[]andcategories[]arrays. - Compute
selection_hash = sha256(canonical_json(selection_payload)).
Phase C — Sync Run Lifecycle + Upsert
- Create a service that:
- acquires a lock for
(tenant_id, selection_hash) - creates a run record
- enumerates selected policy types
- upserts inventory items by identity key
- updates
last_seen_atandlast_seen_run_idper observed item - finalizes run status + counters
- never creates/modifies snapshot/backup records (
policy_versions,backup_*)
- acquires a lock for
Phase D — Derived “Missing” Semantics
- Implement “missing” as a computed state relative to
latestRun(tenant_id, selection_hash). - Do not persist “missing” or “deleted”.
- Mark missing as low-confidence when
latestRun.status != successorlatestRun.had_errors = true.
Phase E — Meta Whitelist
- Define a whitelist of allowed
meta_jsonbkeys. - Enforce by dropping unknown keys (never fail sync).
Phase F — Concurrency Limits
- Enforce global concurrency (across tenants) and per-tenant concurrency.
- The implementation may be via queue worker limits, semaphore/lock strategy, or both; the behavior must be testable.
- When limits are hit, create an observable run record with
status=skipped,had_errors=true, and stable error code(s).
Test Plan (Pest)
Minimum required coverage aligned to Spec test cases:
- Upsert identity prevents duplicates;
last_seen_*updates. selection_hashdeterminism (array ordering invariant).- Missing derived per latest completed run for same
(tenant_id, selection_hash). - Low-confidence missing when latest run is partial/failed or had_errors.
- Meta whitelist drops unknown keys.
- Lock prevents overlapping runs per tenant+selection.
- No snapshot/backup rows are created/modified by inventory sync.
- Error reporting uses stable
error_codesand stores no secrets/tokens.
Out of Scope (Explicit)
- Any UI (covered by Spec 041)
- Any snapshot/backup creation
- Any restore/promotion/remediation write paths