Replicant Property Menu Operations
Date: 2026-07-08
Status: implemented in RIFF pack/compiler/runtime; Replicant receiver endpoints must be available at REPLICANT_API_BASE.
Related:
What changed
The property_management voice menu now opens on a front-desk menu instead of sending every caller straight into the maintenance category intake:
- Press 1: start maintenance request.
- Press 2: existing request.
- Press 3: leave a message.
The start-maintenance path opens the repair category submenu:
- Press 1: plumbing.
- Press 2: appliance.
- Press 3: heating or cooling.
- Press 4: electrical.
- Press 5: pests.
- Press 6: locks or keys.
- Press 7: common areas.
The existing-request path is generated from packs/property_management.pack.yaml with a first-class replicant_ticket_ops capability, so regenerated flows keep the behavior. The generated states support:
- List accessible open or recent tickets.
- Select a ticket from Replicant-returned choices.
- Read safe status/detail fields.
- Add a tenant update after confirmation.
- Request cancellation after confirmation.
- Tell the property manager the issue is resolved after confirmation.
- Return to the main menu.
After a caller selects an existing ticket, the action menu is scoped to the selected request title, for example "For leaky sink, what would you like to do?" The selected-ticket menu does not include "start a new request"; new issue creation remains top-level option 1 and still uses the generated maintenance category intake and create endpoint.
There is no /riff/tickets/create alias.
Replicant receiver contract
RIFF calls Replicant through signed POST requests under REPLICANT_API_BASE.
Aman's side needs to expose or implement the signed receiver for:
POST /riff/tickets/listPOST /riff/tickets/getPOST /riff/tickets/updatePOST /riff/tickets/cancel
These use the same HMAC headers as identify/create:
X-Riff-TimestampX-Riff-Signature
operation_id is included only on mutations:
- update
- cancel / tenant-resolved
Read calls do not send operation_id.
Source of truth
Replicant is the source of truth for ticket access, status, lease authorization, and ticket mutation semantics.
RIFF local state is only for:
- live call slots,
- intake/profile speed,
- generated menu state,
- durable retry spooling for failed mutations.
RIFF must not grant ticket access from a local DB or phone-to-unit match. The authorization set is enforced by Replicant: original submitter plus primary tenant and LeaseTenant co-signers on the active lease for the ticket unit. If the phone matches a unit but no active lease record authorizes the caller, Replicant returns pending_reviews instead of ticket access.
Safe read fields
The status path reads only tenant-safe fields returned by Replicant:
- title,
- category,
- status and status summary,
- priority,
- created/updated timestamps,
- schedule block with vendor company name only,
- permission to enter,
- entry instructions,
- tenant-visible timeline from visible maintenance notes and status audit events.
Money fields stay excluded: estimates, quotes, invoices, balances, and vendor phone numbers.
Mutation behavior
Updates never overwrite ticket title or description after creation. The voice path sends only an updates object containing tenant_update_text for the spoken tenant note.
Cancel/resolved behavior is Replicant-owned:
- Immediate cancel for
SUBMITTED,TRIAGED,AWAITING_APPROVAL,APPROVED. - Requested with PM notification for
VENDOR_ASSIGNED,SCHEDULED,IN_PROGRESS,EMERGENCY_DISPATCHED. - No-op for
COMPLETED,CLOSED,CANCELLED.
RIFF queues timeout, config, and 5xx update/cancel failures to the Replicant ticket spool. Durable retries use an idempotency key based on call_sid|operation_id.
M3 and M1 rollout
M3 is the candidate/latest-code node. M1 is the stable/fallback node.
Practical behavior:
- Deploy and test this menu on M3 first.
- M1 remains on the old stable behavior until the same commit is promoted there.
- Both nodes can call the same Replicant API base with the same signing secret.
- Retry spools are local per node, but Replicant idempotency dedupes mutation retries by
call_sid|operation_id. - The local profile/intake DB remains M3-authority-only; it is not a ticket authority.
- Real OTA calls still require explicit human
GO.
If a caller is routed to M3 after this ships, they can use the existing-request path. If a caller is routed to M1 before promotion, they will not see the new menu yet.
Verification
Focused verification for this implementation:
tests/business_pack/test_replicant_compile.pytests/test_replicant_ticket_tools.pytests/test_replicant_client.pypython -m riff.business_pack compile packs/property_management.pack.yaml --out-dir flows/generatedpython -m riff.business_pack compile packs/property_management.pack.yaml --out-dir flows
The property DID must be attached to a 10DLC campaign before keyed builds pass the SMS deliverability gate. On 2026-07-08, +15102160079 was attached to the same Telnyx campaign used by the other RIFF SMS-capable numbers and the Telnyx assignment reached ASSIGNED (att=ADDED, tmobile=ADDED at verification time).