Skip to content

Contracts 1.7.0 — Accept Evidence Production Readiness Audit

FieldValue
Date2026-09-24
StatusGo — migrate + SPA; signature/ID toggles hard-gated on Storage
ScopeTenant Contracts module contracts 1.6.0 → 1.7.0 (optional phone / signature image / ID document on public accept)
CompanionContracts production · 1.1.0 readiness · Storage readiness · Developer · User · API · Tenant settings · CHANGELOG

Additive MINOR on the existing contracts module. No new permissions, queues, scheduler entries, or env vars. Evidence toggles default off — workspaces that never change Settings keep the 1.5.0 name/email/IP accept path.

This audit is Contracts 1.7.0 accept evidence only. Prior Contracts readiness pages remain valid for their scopes.


Executive summary

Public contract accept (/#/accept/contracts/{token}) can require phone, a drawn or uploaded signature image, and/or a national ID / passport / other upload. Each requirement is an independent Settings → General boolean. Signatures and ID files store on the private uploads disk under tenant-scoped paths; staff download via authenticated contracts.view endpoints (timeline-audited). IP and user agent are always recorded on accept. Accepted contracts embed signer metadata and the signature image on the branded PDF (ID files remain download-only).

GateResult
Catalog 1.7.0 migrate-only (no db:seed)Pass
Evidence columns additive + nullablePass
Settings toggles default offPass
Public accept throttle contract-acceptancePass
Token hashed; cleared on accept / terminatePass
Uploads private disk; paths omitted from list/show JSONPass
Staff downloads gated module:contracts + can:contracts.viewPass
Workspace upload validation (size/MIME) via ValidatesWorkspaceUploadsPass
Storage entitlement required to enable signature/ID settings (API + SPA)Pass
Orphan files cleaned if accept txn fails after store()Pass
Force-delete purges signature/ID files; soft-delete retainsPass
Download audit on contract timelinePass
PDF embeds signature image + acceptance blockPass
Empty stub migration 2026_09_23_210611_…Pass — not shipped
Pest ContractAcceptanceTestPass
Playwright e2e/tests/contractsPass
Docs + upgrade + this pagePass

Go / No-Go: Go after companion CI and php artisan migrate --force before the SPA that posts evidence multipart / Settings toggles.


Findings

IDSeverityStatusFindingAction
F1HighFixedEmpty stub migration duplicated evidence filename intentDeleted; only 2026_09_24_020619_… ships
F2MediumFixedSettings allowed signature/ID without StorageAPI rejects enabling those keys without Storage; SPA disables switches + copy
F3LowFixedOrphan private files if accept txn failed after storeTrack paths and delete in finally when txn does not commit
F4MediumFixedID/signature PII had no purge or download auditForce-delete purges files; soft-delete retains; downloads record acceptance_evidence_downloaded
F5LowFixedPDF lacked signature imageAcceptance block + signature data URI on PDF; ID stays download-only
F6LowFixedOps docs lagged version / upgrade / readinessThis page + contracts ops + Upgrade

Accepted / intentional

ItemNotes
No new permissionsReuse contracts.view for downloads; public accept remains token-gated
Defaults offPhone / signature / ID requirements are opt-in per workspace
ID not embedded in PDFFull ID images stay on authenticated download only
Soft-delete keeps evidenceRestore must recover files; permanent delete is the purge boundary
Quotation accept parityOnly URL/workspace= tenancy fix; quotation does not gain evidence fields
Catalog bump ≠ auto-installWorkspaces that never entitled Contracts are unchanged

Test evidence

SuiteResultNotes
php artisan test --compact tests/Feature/Tenant/Contract/ContractAcceptanceTest.phpRequired greenLink issue, basic accept, expired token, email link, terminate clears token, PDF, evidence + downloads + purge + Storage settings gate
npm run test:e2e:contractsRequired greenWorkflow activate + contracts.accept-evidence

Deploy order

  1. Companion CI green (Backend / Frontend / Docs)
  2. Backendphp artisan migrate --force (2026_09_24_020619_add_contract_acceptance_evidence_fields_to_contracts_table + catalog bump 1.7.0)
  3. Confirm catalog contracts version 1.7.0 (no db:seed)
  4. Confirm only the 2026_09_24_020619_… evidence migration ships
  5. SPA — Settings soft-gate + guest accept evidence UI + record download actions
  6. Docs
  7. Staging smoke below before production traffic

Suggested merge: Backend → Frontend → Docs. Do not db:seed.


Pre-flight checklist

#CheckOwnerPass?
1Pest ContractAcceptanceTest green in CIEng
2Playwright test:e2e:contracts greenQA
3Migration 2026_09_24_020619_… applied; no empty stub in releaseOps
4Catalog contracts version 1.7.0Ops
5SPA deployed after migrateOps
6Staging smoke signed offQA / Ops

Staging smoke (human)

  1. Marketplace → Opportunities, Contracts (free). Optionally install Storage before testing file evidence.
  2. Settings → General → without Storage, signature/ID switches are disabled; enabling via API returns 422. Phone toggle still works.
  3. Install Storage → enable all three toggles → send contract → public accept incomplete → validation; complete with phone + draw/upload + ID → Active.
  4. Contract view: phone, IP, Download signature / ID → timeline shows download events.
  5. Download PDF shows acceptance block + signature image (not the ID file).
  6. Soft-delete → files still downloadable after restore; force-delete → signature/ID objects gone.
  7. Confirm JSON show does not expose storage paths.

Rollback

LayerAction
FrontendRedeploy previous SPA (evidence UI disappears; columns remain)
Backend codeRedeploy previous release; keep additive migrations
SettingsTurn all three contracts.acceptance_require_* toggles off
Module disableMarketplace uninstall Contracts (rows retained)
SchemaDo not drop evidence columns in prod without a data / PII plan

Sign-off

RoleNameDateDecision
EngGo / No-Go
QA
Ops

Official documentation for the EloSync SaaS Platform.