Rental Lifecycle Agent Guidance
Read-only preflight, legal command sequences, version and idempotency examples, and installed rental conflict recovery.
Rental lifecycle preflight
Run this read-only report inside the intended installation or selected Cloud tenant:
php artisan signals:rentals:lifecycle-preflight --json --no-interaction
The JSON lists the built-in composite codes, every stored rental-status definition (including inactive custom derivatives), and counts for every stored state/status pair. Composite codes are state * 100 + status; an invalid state or a status outside 0–99 is reported without borrowing a built-in meaning. Unknown codes, invalid derivatives and duplicate definitions make preflight_clear false.
project_tree_conflicts lists rental/project identifiers for connected trees with distinct direct projects, cycles, self-links or missing parents. Matching repeated assignments and a single child-only assignment are compatible with the current invariant. The audit includes archived rentals and all warehouses in the selected database. It does not repair rows, normalize assignments, fire events or change status definitions.
Cloud requires a bound database tenant; central and config-only contexts are refused before inspection. The output includes the tenant slug and application environment. Names, custom labels, account details, notes and connection credentials are omitted. Keep the resulting identifiers in the operator's internal incident or release record.
Exit status 0 means the selected data was available and no findings were detected. Exit status 1 means findings, unavailable reads or a refused tenant context. An unavailable read has null lists and a generic error, so an empty or partial report cannot be mistaken for a clean result.
Production findings
Production output has not been collected yet. The automated examples exercise fixtures in an isolated test database. They do not establish any production status vocabulary or conflict count. Production execution is a separate release-checklist item. Before release, a production operator must run the report in the intended tenant context and publish its actual code/derivative and conflict lists.
The command uses read-only queries and never applies a repair. Review findings with the rental source owner; resolve conflicting assignments through the admitted association/project commands after an operator decision.
Agent workflow: inspect, preflight, then command
Use whoami to establish the current identity, abilities and tenant. Read the rental with get-rental, then get-rental-available-actions (REST GET /api/v1/rentals/{rental}/available_actions). Retain aggregate_version; it is the command's expected_version, not the quote version number. Discover the exact operation with describe-endpoint before using execute-endpoint. Do not invent a curated tool for a command that is only exposed through the generic endpoint surface.
A positive preflight is not authorization. The command rechecks current permissions, state, warehouse access and evidence. For supported lifecycle transitions, first send dry_run: true to the same endpoint. Inspect allowed, reason and code; a dry run is not a reservation and does not commit the command. Submit once only after the operator approves the consequential change. After success, read the rental again before the next step.
For complete/cancel with descendants, use get-rental-cascade-preview first. Review the exact selected descendants and submit the root and selected-child version preconditions required by the described endpoint. Never interpret an empty selection as permission to cascade to every child.
Legal task sequences
| Task | Sequence and stop point |
|---|---|
| Create to quote to order | Discover create-rental fields, create an enquiry, add permitted lines, read/preflight, then convert_to_quote; read/preflight again before convert_to_order. Resolve its real shortage/approval result rather than forcing state/status fields. |
| Revision acceptance | Read the versions tree, create a revision or alternative with its intended source, review/diff the resulting version, send and accept only the customer's chosen version. Conversion uses the accepted version (active fallback if none); viewing a version does not activate it. |
| Safe line edit | Read the current rental and line; submit only the intended quantity or field with the current aggregate version where admitted. For move, delete or restructure, also supply the exact required tree revision and active-version preconditions from the endpoint contract. Read committed totals afterward. |
| Dispatch and return | An eligible order must use asset or loose-quantity fulfilment commands, with the actual assignment/quantity and evidence. Dispatch before return; never manufacture Dispatched, On Hire, Returned or Checked with change-rental-status. |
| Checkout | Read/preflight the current source and review the selected invoice type and quantities. Generate the draft through the rental invoices endpoint, inspect its captured terms, then use the separate invoice issue workflow. An accepted-manifest refusal is not permission to replace captured terms with live prices. |
| Conflict recovery | Read code, stop the old request, reconcile any uncertain effect, then re-read and ask before a destructive reapply. Never silently substitute current_version into the old payload. |
Version and idempotency examples
The following are independent request shapes, not a script. IDs and versions are illustrative: replace them with the values just read in the selected tenant, and refresh after every successful command. Dry-run and commit requests are different intents. Use a fresh semantic key for each deliberate new operation and retain it with the exact payload for its retry.
The body idempotency_key is command metadata; replay exists only where that command installs a receipt. In particular, admitting this field on a lifecycle command does not promise deduplication. The HTTP Idempotency-Key header is a separate route-level contract: check x-signals-idempotency in the operation description. An absent extension promises no HTTP replay. Rental invoice generation has the route middleware; loose fulfilment also maps a supplied header to its body key and refuses mismatching keys. Do not send different header/body keys.
{
"method": "POST",
"path": "/api/v1/rentals/123/convert_to_quote",
"body": {
"dry_run": true,
"expected_version": 7
}
}
{
"method": "POST",
"path": "/api/v1/rentals/123/convert_to_quote",
"body": {
"expected_version": 7,
"idempotency_key": "job-123-quote-decision-1"
}
}
{
"method": "POST",
"path": "/api/v1/rentals/123/versions",
"body": {
"version_type": 0,
"source_version_id": 456,
"label": "Customer revision",
"expected_version": 8,
"idempotency_key": "job-123-revision-1"
}
}
{
"method": "POST",
"path": "/api/v1/rentals/123/versions/457/accept",
"body": {
"expected_version": 9,
"idempotency_key": "job-123-accept-457"
}
}
{
"method": "PATCH",
"path": "/api/v1/rentals/123/items/789",
"body": {
"quantity": "2.00",
"expected_version": 10,
"idempotency_key": "job-123-line-789-quantity-1"
}
}
{
"method": "PATCH",
"path": "/api/v1/rentals/123/items/789/fulfilment",
"body": {
"action": "dispatch",
"quantity": "1.00",
"expected_version": 11,
"idempotency_key": "job-123-line-789-dispatch-1"
}
}
{
"method": "PATCH",
"path": "/api/v1/rentals/123/items/789/fulfilment",
"body": {
"action": "return",
"quantity": "1.00",
"expected_version": 12,
"idempotency_key": "job-123-line-789-return-1"
}
}
{
"method": "POST",
"path": "/api/v1/rentals/123/invoices",
"headers": {
"Idempotency-Key": "job-123-standard-invoice-1"
},
"body": {
"type": "standard",
"expected_version": 13,
"idempotency_key": "job-123-standard-invoice-1"
}
}
For a known receipt-backed command, retry the same key and unchanged payload only after checking its recorded outcome. A timeout alone does not prove failure. Reconcile uncertain or expired results against recorded fulfilment or invoices before any new key. A new key is not a way to bypass an unresolved earlier command. If the operation has no installed replay contract, re-read its state/history instead of automatically resending it.
Conflict and refusal recovery
These are installed core codes, not a replacement for the current endpoint schema. Preserve the code and permitted diagnostic fields in the operator's incident record; do not disclose hidden tenants, accounts, costs or credentials. Custom/plugin refusals and an unrecognized code require source-owner review, not guessed recovery.
| Code | Recovery |
|---|---|
version_conflict |
Re-read the rental and its current customer, lines and version; review the intended change, obtain approval for a destructive reapply, and send a fresh command. The change summary may be incomplete. |
rental_repair_version_conflict |
Stop the stale repair. Reload the rental and aggregate version, rerun the supported repair inspection in report-only mode, and review the intended correction with the operator. A deliberate repair retry must supply the freshly reviewed expected version and reason, with all safety gates and current authorization still satisfied; never silently replace the version or bypass a hold. |
rental_tree_version_conflict |
Reload the complete tree, its revision and active version. Rebuild the intended move/delete/restructure against that tree; do not reuse stale line IDs or a stale placement. |
rental_version_constraint_conflict |
Reload the version tree and active version; check whether the intended revision/activation already happened before proposing another operation. |
idempotency_conflict |
The key is bound to different intent (HTTP payload or editor command). Recover the original request/result; a genuinely different, approved operation needs its own key. |
rental_item_delete_idempotency_conflict |
The delete key belongs to a different line. Reconcile that original deletion and use a distinct key only for a separately approved deletion. |
rental_invoice_idempotency_conflict |
The checkout key's request or receipt contract differs. Inspect the original invoice/receipt; do not create another invoice to escape the conflict. |
fulfilment_key_conflict |
Intent or authority differs from the recorded movement. Reconcile the original assignment/quantity and current authority before any new movement. |
fulfilment_conflict |
A fulfilled rental cannot be reset to Active by ordinary status change. Use the supported fulfilment correction workflow and its actual evidence. |
rental_owner_changed |
Reload the rental and component ownership/customer/state; re-establish access and review intent before resubmitting. |
rental_lock_timeout, credit_lock_timeout |
Allow contention to clear, re-read the current resource and preflight before retrying. Do not assume a response timeout or surrounding workflow proves no earlier effect. |
mutation_retry_exhausted |
The root mutation exhausted bounded database retries. Reload and review before a deliberate retry; do not spin an unbounded retry loop. |
fulfilment_receipt_expired, fulfilment_outcome_unknown |
Reconcile recorded movements and source history; do not repeat the physical effect or rotate the key automatically. |
idempotency_outcome_uncertain |
Stop replay and ask the operator to reconcile the recorded claim and effect before any new request. |
idempotency_replay_authorization_changed |
Re-establish current access and reconcile the original outcome. Stored response access is refused; a new key requires a separate approved operation, not a blind retry. |
rental_tree_revision_required |
Read the current tree token and supply the endpoint's explicit revision field/header. Do not invent a token or remove the precondition. |
fulfilment_key_invalid, fulfilment_batch_selection_invalid |
Correct the blank/invalid key or actual bounded selection after reviewing the request; never change intent under a previously committed key. |
fulfilment_boundary_required |
Integration code must start the keyed command at its own outer mutation boundary, with earlier queued intent committed or rolled back. Escalate to the integration owner. |
invalid_state |
Read the current lifecycle and available actions; use a legal sequence rather than forcing a stored state/status. |
shortage_block, fx_tax_locked, accepted_terms_locked |
Follow the specific shortage or pricing-evidence workflow with its permissions and operator decision. Do not weaken a lock, invent accepted evidence or substitute today's prices. |
approval_required |
Inspect the existing approval handoff; do not keep calling conversion to force it through. |
HTTP 425 means the same key is still in flight: wait briefly and inspect/retry the unchanged request under its advertised contract. HTTP 401/403/404 and ordinary field-validation failures are not concurrency retries; correct access or input through the supported workflow. Do not expose a hidden resource by switching tenants or dropping warehouse scope.
The installed command registrations, DTO rules, receipt handlers and refusal codes remain authoritative. This guidance does not claim generated-manifest coverage, mandatory versions for every legacy caller, universal semantic replay, production preflight collection, or adoption of pending status-repair/read-model changes.