Skip to content

Implement guided-interview filing handoffs - #225

Open
nonprofittechy wants to merge 3 commits into
mainfrom
feature/106-docassemble-handoff
Open

Implement guided-interview filing handoffs#225
nonprofittechy wants to merge 3 commits into
mainfrom
feature/106-docassemble-handoff

Conversation

@nonprofittechy

Copy link
Copy Markdown
Member

A completed guided interview can now create a durable, editable LITEFile filing draft containing its known answers and private PDFs. LITEFile resolves semantic suggestions against live court metadata, lets the filer complete or change every filing detail, and supports idempotent retries, account claiming, clerk-return corrections, and PDF replacement without losing the prior submission record.

Closes #106.

The Vermont sender is implemented in LSVermont/docassemble-RFApackage#426.

Receiver configuration

Apply the migration, configure private document storage as usual, and register each sending application with its own secret, jurisdiction allowlist, and allowed return origins:

cd efile_app
uv run python manage.py migrate
LITEFILE_HANDOFF_SOURCES={"court-interviews":{"token":"replace-with-a-long-random-secret","jurisdictions":["vermont"],"return_origins":["https://interviews.example.org"]}}

The sender uses the same source name and secret in the Docassemble server configuration. This is one configuration per Docassemble server:

litefile:
  enabled: true
  base_url: https://litefile.example.org
  source: court-interviews
  token: replace-with-the-same-long-random-secret

An interview may select another top-level server configuration, while defaulting to litefile:

code: |
  litefile_config_name = "alternate_filing"

Interview configuration

Include the adapter YAML after AssemblyLine and display its template on the download screen:

include:
  - docassemble.AssemblyLine:assembly_line.yml
  - litefile.yml
---
# Inside the interview's download-screen subquestion:
# ${ litefile_continue_button }

The adaptation declares what leaves the interview in ordinary Docassemble data blocks. The reusable Python transport contains no Vermont, RFA, variable-path, or document-classification branches:

variable name: litefile_data
data from code:
  schema_version: 1
  jurisdiction: '"vermont"'
  filing_intent: '"relief_from_abuse"'
  case_category_name_hints: '["Family"]'
  case_type_name_hints: '["Relief from Abuse"]'
  filing_type_name_hints: '["Complaint"]'
  case:
    existing_case: 'False'
    court_name: showifdef("trial_court.name")
    county: showifdef("user_selected_county")
  filer: litefile_person("users[0]")
  parties:
    - source: '"users[0]"'
      person: litefile_person("users[0]")
      semantic_role: '"plaintiff"'
      case_side_hint: '"plaintiff"'
      is_self: 'True'
      is_filing_party: 'True'
---
variable name: litefile_document_map
data:
  complaint:
    role: lead
    form_name: Complaint
    filing_type_name_hints:
      - Complaint
    document_type_name_hints: []
    filing_component_name_hints:
      - Lead Document

litefile_person() defaults to normal AssemblyLine ALIndividual and Docassemble Individual name, address, email, and phone fields, read through showifdef. Different object shapes can pass fields={...} and tests can pass known=... as keyword arguments.

County and court-specific overrides handle jurisdictions where the same semantic hint maps differently by location. A court entry replaces a county entry for the same field; a county entry replaces the general hint. Omitted fields retain the less-specific value:

variable name: litefile_filing_hint_overrides
data:
  counties:
    Cook:
      case_type_name_hints:
        - Cook County case type
      documents:
        complaint:
          filing_type_name_hints:
            - Cook County complaint
  courts:
    First Municipal District:
      case_category_name_hints:
        - Court-specific category
      documents:
        complaint:
          filing_type_name_hints:
            - Court-specific complaint
          document_type_name_hints:
            - Court-specific document type
          filing_component_name_hints:
            - Court-specific lead document

County names may include or omit County. Court names match either the source court name or the official name resolved from live metadata. Scoped document keys must match declared document IDs. All values remain semantic names; only one unique live metadata match preselects a code.

Behavior and safety

  • Source authentication, jurisdiction restrictions, return-origin validation, PDF type/size/hash checks, and payload limits are enforced before creating a draft.
  • Stable source IDs and idempotency keys prevent duplicate drafts and replacement receipts.
  • Imported documents remain private and receive renewed signed URLs when resumed.
  • Unclaimed handoffs expire and can be removed with expire_unclaimed_handoffs.
  • Claim tokens bind the draft to the signed-in account.
  • Suggestions, live resolutions, user changes, clerk returns, and replacement hashes have append-only provenance.
  • Submitted snapshots remain immutable. Correction drafts stay grouped with the same matter.
  • Document correction links return to the original interview with a scoped, expiring token.
  • Existing filing choices survive PDF replacement; fee quotes are invalidated and recalculated.

Validation

  • uv run pytest -q — 784 passed.
  • uv run ruff check . — passed.
  • uv run ty check — passed.
  • uv run python manage.py makemigrations --check --dry-run — no model drift.
  • Docusaurus npm run build — passed.
  • Pre-push Python and JavaScript hooks — passed.
  • Vermont sender tests — 17 passed.
  • Installed the sender on the running Docassemble server and transferred four actual generated court PDFs through BackgroundAction. The one-click success flow completed, and retrying returned HTTP 200 while retaining one receipt, one draft, four documents, and the same source identity.
  • Verified the final sender payload containing the default county/court override maps created a four-document draft successfully.
  • With explicit authorization, a real Vermont dev account completed metadata selection, received a $14.40 quote, and made one dev submission. All four filing statuses were confirmed submitted. The configured dev EFSP stand-in PDF was active, so this validates the transaction and status flow rather than remote review of the actual RFA PDF contents.

The detailed test record is in docs/developer-notes/issue-106-vermont-handoff-validation.md.

@nonprofittechy

Copy link
Copy Markdown
Member Author

@mnewsted @VTskier what do you think about this as a configuration layer? Happy to explain more what I was thinking.

The idea is you can import docassemble.LITEFile:litefile.yml and just override the things you need to with a block like

variable name: litefile_data
data from code:
  schema_version: 1
  jurisdiction: '"vermont"'
  filing_intent: '"relief_from_abuse"'
  case_category_name_hints: '["Family"]'
  case_type_name_hints: '["Relief from Abuse"]'
  filing_type_name_hints: '["Complaint"]'
  case:
    existing_case: 'False'
    court_name: showifdef("trial_court.name")
    county: showifdef("user_selected_county")
  filer: litefile_person("users[0]")
  parties:
    - source: '"users[0]"'
      person: litefile_person("users[0]")
      semantic_role: '"plaintiff"'
      case_side_hint: '"plaintiff"'
      is_self: 'True'
      is_filing_party: 'True'
---
variable name: litefile_document_map
data:
  complaint:
    role: lead
    form_name: Complaint
    filing_type_name_hints:
      - Complaint
    document_type_name_hints: []
    filing_component_name_hints:
      - Lead Document

You can also have county/court specific code hints.

Inside LITEFile, there's another configuration layer that can override the hints without forcing you to update the docassemble interview. And you can always override the hints inside LITEFile manually, too.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Confirmed issues in correction draft field-reset consistency, metadata-edit signal performance overhead, and browser UX error handling for correct_filing should be addressed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR adds a guided-interview “handoff” receiver to LITEFile: an authenticated external API can create an unclaimed draft (with private PDFs + semantic hint suggestions), and the filer can later claim, review, correct clerk-returned filings, and replace PDFs while preserving immutable submission snapshots and provenance.

Changes:

  • Adds source-authenticated handoff endpoints + claim/review/correction UI to turn interview payloads into durable, editable FilingDrafts.
  • Introduces receipt/provenance/correction models and supporting services (metadata resolution, matter grouping, idempotent retries, PDF replacement).
  • Updates workflow navigation and documentation to support correction flows and interview integration.
File summaries
File Description
efile_app/efile/views/my_drafts.py Redirects resumed handoff/correction drafts to the handoff review hub.
efile_app/efile/views/my_cases.py Exposes a local draft link for rejected filings to start correction flow.
efile_app/efile/views/login.py Preserves a post-login continuation path for handoff claim links.
efile_app/efile/views/handoff.py Adds the handoff API (create + replace documents) and claim/review/correction browser views.
efile_app/efile/views/confirmation.py Expands confirmation-number extraction to include envelope identifiers.
efile_app/efile/urls.py Registers new API and browser routes for handoff, claim, review, corrections, and returns.
efile_app/efile/tests/test_handoff.py Adds comprehensive test coverage for handoff, claim, resolution, corrections, and replacements.
efile_app/efile/templates/efile/workflow_base.html Adds a navigation link back to the handoff review hub during workflow steps.
efile_app/efile/templates/efile/handoff_review.html New “saved interview answers & corrections” hub page for handoff drafts.
efile_app/efile/templates/efile/handoff_claim.html New claim screen to explicitly attach an unclaimed handoff to an account.
efile_app/efile/templates/efile/filing_detail.html Adds “Correct and resubmit” affordance when a locally-linked filing is rejected.
efile_app/efile/templates/efile/correct_filing.html New correction-field selection UI for clerk-returned filings.
efile_app/efile/templates/efile/confirmation.html Adds a link from confirmation to the correction checker flow.
efile_app/efile/static/config/states/vermont.yaml Adds Vermont-specific handoff hint aliases.
efile_app/efile/signals.py Records provenance for user edits to metadata fields on handoff/correction drafts.
efile_app/efile/settings_base.py Adds env-configured handoff source registry and token TTL settings.
efile_app/efile/services/handoff.py Core handoff domain logic: validation, populate, resolution, issues, corrections, replacements, grouping.
efile_app/efile/services/filings.py Groups filings into “matters” using local correction chains before remote case IDs exist.
efile_app/efile/services/drafts.py Renews signed URLs for imported handoff PDFs when resuming drafts.
efile_app/efile/services/draft_urls.py Surfaces handoff draft context for workflow templates/navigation.
efile_app/efile/models.py Adds correction/snapshot fields and new receipt/provenance/replacement models.
efile_app/efile/migrations/0023_interview_handoff.py Database migration for new handoff/correction/provenance schema.
efile_app/efile/middleware.py Ensures targeted edits from handoff flows return to the handoff review hub.
efile_app/efile/management/commands/expire_unclaimed_handoffs.py Adds retention cleanup for unclaimed handoffs + private PDFs.
docs/docs/user-guide/case-management.md Updates user guidance for clerk returns to use “Correct and resubmit”.
docs/docs/partners-courts/interview-integration.md Adds full partner-facing integration guide for guided interview handoffs.
docs/developer-notes/issue-106-vermont-handoff-validation.md Adds internal validation record for the Vermont handoff implementation.
Review details
  • Files reviewed: 27/27 changed files
  • Comments generated: 5
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread efile_app/efile/services/handoff.py
Comment on lines +33 to +37
def remember_metadata_before_edit(sender, instance, **kwargs):
if not instance.pk:
instance._metadata_before = None
return
instance._metadata_before = sender.objects.filter(pk=instance.pk).values(*_METADATA_FIELDS[sender]).first()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 992e8dd. The pre_save metadata tracker now short-circuits when update_fields does not include any tracked metadata field, avoiding the extra lookup query.

Comment on lines +4 to +14
<h1>Correct and resubmit</h1>
<p>The court returned this filing. Select the details the clerk asked you to correct. Your other answers and documents will stay in the same matter.</p>
{% for comment in detail.comments %}<p>{{ comment.text }}</p>{% endfor %}
<form method="post">
{% csrf_token %}
<fieldset>
<legend>What needs a correction?</legend>
{% for value, label in choices %}<div><label><input type="checkbox" name="fields" value="{{ value }}"> {{ label }}</label></div>{% endfor %}
</fieldset>
<button type="submit" class="btn btn-primary">Create correction draft</button>
</form>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 992e8dd. The template now shows an error message when provided and hides the correction checklist form in that state.

Comment thread efile_app/efile/views/handoff.py Outdated
Comment on lines +301 to +302
except HandoffError as exc:
return JsonResponse({"error": str(exc)}, status=exc.status)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 992e8dd. correct_filing now returns the browser template with an error message (and proper status) for expected error conditions instead of JSON responses.

<link rel="stylesheet" href="{% static 'css/confirmation.css' %}" />
{% endblock extra_css %}
{% block workflow_content %}
<p><a href="{% url 'correct_filing' draft.pk %}">Check for a clerk return and correct this filing</a></p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 992e8dd. The new confirmation-page link text is now wrapped in {% translate %}.

Co-authored-by: nonprofittechy <7645641+nonprofittechy@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bridge to a Docassemble interview

3 participants