255 lines
12 KiB
Markdown
255 lines
12 KiB
Markdown
# govoplan-cases Concept
|
|
|
|
## Purpose
|
|
|
|
`govoplan-cases` is the formal administrative case module. It creates a durable
|
|
container for a procedure such as a permit application, inspection, complaint,
|
|
grant request, or internal administrative matter.
|
|
|
|
The case is not the workflow engine and not the file store. It is the stable
|
|
record that ties together participants, status, assignments, deadlines,
|
|
evidence, decisions, communications, audit events, and retention references.
|
|
|
|
## Ownership
|
|
|
|
The module owns:
|
|
|
|
- case identifiers, references, titles, types, and status
|
|
- case-local party references and role labels such as applicant, respondent,
|
|
beneficiary, and representative, using a shared Parties contract when present
|
|
- case metadata and tags
|
|
- due dates, service-level targets, and milestone dates
|
|
- links to evidence provided by other modules
|
|
- case comments and internal notes once the collaboration boundary is decided
|
|
- case-level access checks and resource ACL contributions
|
|
- case event emission for lifecycle changes
|
|
- case summary APIs and WebUI route contributions
|
|
|
|
The module does not own:
|
|
|
|
- form submissions and validation, owned by forms/forms-runtime
|
|
- binary file storage, owned by files or DMS
|
|
- workflow state machines and transition execution, owned by workflow
|
|
- work queues and task assignment semantics, owned by tasks
|
|
- generated documents, owned by templates/DMS
|
|
- appointments, mail, notifications, postbox, payments, or ledger postings
|
|
- identity/contact/organization masters, representation powers, formal
|
|
institutional decision semantics, mandates, or jurisdiction
|
|
|
|
## Core Contracts
|
|
|
|
The module should integrate through:
|
|
|
|
- module manifest metadata, route factories, permissions, and migrations
|
|
- a `cases.access` or similar case access capability for resource checks
|
|
- a `cases.summary` capability for dashboards and cross-module previews
|
|
- shared party, mandate, service, and decision reference DTOs when those
|
|
providers are available
|
|
- events such as `case.created`, `case.updated`, `case.status_changed`,
|
|
`case.assigned`, and `case.closed`
|
|
- commands such as `cases.open`, `cases.update_status`, `cases.link_evidence`,
|
|
and `cases.assign`
|
|
- DTOs with stable IDs and labels, not ORM objects
|
|
|
|
Other modules should link to a case by stable references:
|
|
|
|
```json
|
|
{
|
|
"case_id": "case-uuid",
|
|
"tenant_id": "tenant-uuid",
|
|
"relation": "submission"
|
|
}
|
|
```
|
|
|
|
## Reference Journey
|
|
|
|
Permit-to-payment MVP:
|
|
|
|
1. Portal/forms runtime receives a public application.
|
|
2. Workflow asks cases to create a case record.
|
|
3. Files/DMS links uploaded evidence to the case.
|
|
4. Tasks assigns an internal review task.
|
|
5. Workflow moves the case from intake to review to appointment to decision.
|
|
6. Templates generates the permit or decision document and links it to the case.
|
|
7. Payments links payment evidence.
|
|
8. Audit and records retain the case history.
|
|
|
|
## Implemented MVP Backend
|
|
|
|
The persistent backend provides:
|
|
|
|
- tenant-local case type and status catalogs with guarded revisions
|
|
- create/list/read/update/history/timeline APIs
|
|
- stable case identities and immutable OCC-guarded record revisions
|
|
- parties stored as stable procedure-party references with a compatibility
|
|
path for direct identity/organization references; assignments remain
|
|
responsibility references
|
|
- evidence links as module/resource references
|
|
- replay-safe case timeline events carrying the institutional context and a
|
|
stable event/audit reference
|
|
- tenant-level resource ACL and tenant summary providers
|
|
- tenant-wide or restricted object access with explicit, revisioned grants
|
|
- deterministic, replay-safe case launch from an exact Service binding
|
|
- deterministic, replay-safe Case escalation from an authorized Ticket handoff
|
|
|
|
The `/cases` list and `/cases/:caseId` detail workspace provide server-side
|
|
search/status filtering, case facts, typed references, history, timeline, and
|
|
OCC-guarded title/status/access revisions with an explicit change reason.
|
|
Restricted cases are visible to their creator, case administrators, explicit
|
|
user/group grants, and matching function, function-assignment, or
|
|
organization-unit assignments. The same decision filters list, detail,
|
|
history, timeline, and update operations so an inaccessible identifier does
|
|
not disclose case existence. API reads and writes are tenant-bound, and
|
|
create/update/assign/close/share/catalog operations have separate permissions.
|
|
|
|
## Permissions
|
|
|
|
Implemented scopes:
|
|
|
|
- `cases:case:read`
|
|
- `cases:case:create`
|
|
- `cases:case:update`
|
|
- `cases:case:assign`
|
|
- `cases:case:close`
|
|
- `cases:case:share`
|
|
- `cases:case:admin`
|
|
|
|
Access decisions combine tenant permissions, creator/administrative authority,
|
|
case assignments, and explicit case shares. Restricted access is versioned
|
|
with the case record so its history remains reconstructable.
|
|
|
|
## Data Model Sketch
|
|
|
|
Candidate tables:
|
|
|
|
- `cases`
|
|
- `case_parties`
|
|
- `case_assignments`
|
|
- `case_evidence_links`
|
|
- `case_timeline_entries`
|
|
- `case_type_definitions`
|
|
- `case_status_definitions`
|
|
- `case_access_grants`
|
|
|
|
`case_parties` is a compatibility seed for a Cases-only composition. It must
|
|
not grow a second identity/contact master or duplicate effective-dated powers
|
|
of representation. Once a shared Parties provider exists, Cases should retain
|
|
the provider reference plus the minimal immutable role/evidence snapshot needed
|
|
to reconstruct the case.
|
|
|
|
The implemented headless compatibility path therefore accepts only a stable
|
|
subject reference, case-local role, effective interval, permitted/preferred
|
|
channels, delivery flag, evidence, and immutable contact snapshot references.
|
|
It cannot model representation powers. When `parties.resolver` is available,
|
|
Cases uses its effective, tenant-scoped party and representation records and
|
|
rejects mismatched procedures or conflicting active role assignments.
|
|
Shared Core revision helpers keep corrections, expiry, supersession, and
|
|
revocation immutable and OCC-guarded. Representation revocation has its own
|
|
effective timestamp so a historical delivery decision can be reconstructed.
|
|
|
|
`cases.service_intake` consumes a published, effective shared Service
|
|
definition. It requires one case binding and preserves the exact Service
|
|
version plus its responsible unit/function, Mandate, jurisdictions, legal
|
|
bases, required evidence, deadlines, forms, workflows, and result bindings in
|
|
a governed intake plan. It does not copy or persist the Service definition.
|
|
|
|
`cases.service_launcher` is the effect boundary used by Portal. It derives a
|
|
stable case ID from tenant, exact Service revision, and idempotency key, creates
|
|
the case through the normal registry, and returns that exact case and Service
|
|
reference. Retrying the same launch returns the existing case; a changed request
|
|
cannot reuse the key. Portal therefore cannot create duplicate cases after an
|
|
ambiguous network response and does not gain access to Cases tables.
|
|
|
|
`tickets.case_escalation` is the optional effect boundary used by Tickets. It
|
|
rechecks `cases:case:create`, tenant identity, and the requested active Case
|
|
type, then derives a stable Case identifier from the Ticket and idempotency key.
|
|
The Case stores a governed `work_item` reference plus bounded source and handoff
|
|
metadata. It never imports Tickets or copies Ticket messages, files,
|
|
participants, assignment, or lifecycle. Replaying the same handoff returns the
|
|
existing Case, while an unavailable Cases capability leaves the operational
|
|
Ticket intact.
|
|
|
|
Cases links formal Decision records and may retain a current outcome/status
|
|
projection. It does not own decision authority, rule versions, reasoning,
|
|
correction, revocation, or remedy semantics.
|
|
|
|
The Case detail decision path is a guarded composition boundary. It resolves an
|
|
exact Case revision, rechecks Case update access, verifies the current acting
|
|
assignment through Access, and requires exactly one effective Mandate for
|
|
`cases.formal_decision` and the selected Decision type. Only then does it call
|
|
the optional Decisions registry and append the exact Decision reference to a
|
|
new Case revision in the same transaction. Deterministic identifiers make an
|
|
unchanged retry safe, while changed replay payloads and stale Case revisions
|
|
fail closed. Cases still does not store the protected result or reasoning.
|
|
|
|
Evidence links should store only stable module/resource references and display
|
|
metadata snapshots. The owning module remains responsible for the real object.
|
|
|
|
When Records is enabled, `records.source.cases` resolves one exact immutable
|
|
case revision for eAkte filing. Cases rechecks current tenant, scope, and
|
|
object-level access, then returns the case number, lifecycle state, represented
|
|
valid interval, recorded time, canonical snapshot digest, and launch link.
|
|
Records owns the filing decision and chronology; Cases remains authoritative
|
|
for the case and its revision history.
|
|
|
|
When Search is enabled, `cases.cases` indexes current Case titles, numbers,
|
|
types, states, and the existing bounded search projection. It includes no
|
|
provider-owned evidence content or protected Decision reasoning. Restricted
|
|
Case candidates carry bounded ACL tokens and every result is checked again
|
|
against the current Case object-access decision before disclosure.
|
|
|
|
## WebUI
|
|
|
|
Initial route contributions:
|
|
|
|
- `/cases`
|
|
- `/cases/:caseId`
|
|
|
|
The detail view renders the module-owned record, stable references, history,
|
|
timeline, and provider-resolved formal Decisions when currently authorized. It
|
|
can record a direct officer Decision when Access, Mandates, and Decisions are
|
|
enabled and can launch exact Case or Decision filing when Records is enabled.
|
|
Future linked forms, files, tasks, workflow state,
|
|
appointments, documents, communication, payment evidence, and richer audit
|
|
panels must arrive through declarative extension points, without direct UI
|
|
imports from sibling modules.
|
|
|
|
## Tests
|
|
|
|
The focused suite covers:
|
|
|
|
- provider-neutral Service intake and Party resolution
|
|
- committed-only lifecycle events with institutional context
|
|
- replay conflict detection, stale revision rejection, and immutable history
|
|
- tenant isolation, terminal status handling, and server-side filtering
|
|
- stable typed assignment/evidence/Decision/record references
|
|
- migration, uninstall-guard, ACL, summary, and capability registration
|
|
- explicit and assignment-derived case access, including non-disclosure
|
|
- exact Service launch, deterministic replay, and conflict behavior
|
|
|
|
## Data-subject requests
|
|
|
|
Cases publishes `privacy.dsar.cases`. Canonical account, identity, and
|
|
membership selectors cover Cases-owned operator attribution; account and
|
|
identity selectors also cover explicit case-access grants. Exact
|
|
`cases.case`, `cases.revision`, `cases.access_grant`, and `cases.timeline`
|
|
references select lifecycle data. When a canonical and direct selector are
|
|
combined, a Cases-owned relationship must corroborate them and all supplied
|
|
direct references must identify the same case.
|
|
|
|
Direct case results are typed projections rather than stored snapshots. They
|
|
exclude opaque metadata, search text, free-text change reasons, timeline
|
|
payloads and summaries, evidence identifiers, request hashes, idempotency
|
|
keys, audit identifiers, and unrelated access subjects. Applicant-to-identity
|
|
correlation is owned by Parties; Cases does not infer it from opaque party
|
|
identifiers. Immutable case identities, revisions, timelines, and operator
|
|
attribution receive retention actions. Current open case facts and active
|
|
access grants receive non-executable manual-review actions and can only be
|
|
changed through the authorized case/access lifecycle.
|
|
|
|
## Open Decisions
|
|
|
|
- Whether comments belong in cases, tasks, or a collaboration module.
|
|
- Whether case type/status catalogs are fully configurable in MVP or seeded.
|
|
- How records/legal-hold integration should own retention of closed cases.
|