Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Eligibility made easy

Eligibility Made Easy (Emmy) using Consent-Based Verification (CBV) is a prototype that allows benefit applicants to verify their income directly using payroll providers. It is currently being piloted for testing and validation purposes.

## Project Vision
## Project Vision
Eligibility Made Easy (Emmy) is a project to allow applicants to verify their income and community engagement directly using payroll providers and educational records. Emmy was developed and is supported by CMS in order to offer states a drop-in component for their application process to allow applicants to apply for state benefits more easily. This is part of a more comprehensive process to [improve data services for benefits delivery](https://assets.performance.gov/cx/files/OMB-CX-LifeExperience-FFS-ImprovingData.pdf).

## Project Mission
Emmy uses consent-based verification (CBV) with multiple data sources, making the process much faster and more efficient than a simple document-upload service. CBV enables additional cost avoidance by optimizing manual document review processes. Rather than having to process incorrect or blurry documents, consent-based verification produces an easily consumed report with essential information. Verification information is returned in a standardized format easy for other systems to process (JSON), allowing integration with existing state systems.
## Project Mission
Emmy uses consent-based verification (CBV) with multiple data sources, making the process much faster and more efficient than a simple document-upload service. CBV enables additional cost avoidance by optimizing manual document review processes. Rather than having to process incorrect or blurry documents, consent-based verification produces an easily consumed report with essential information. Verification information is returned in a standardized format easy for other systems to process (JSON), allowing integration with existing state systems.

Emmy is under active development by CMS, with new updates released on a 2-week cadence.
# Core Team
Expand Down Expand Up @@ -110,15 +110,28 @@ To run database migrations on the test environment that is used by rpec tests, r

### JSON API Testing

To acceptance test the JSON API, you can run the independent **reference server implementation**.

1. **Create an API key for the agency you want to test:**
```bash
cd app
rails 'users:create_api_token[agency_name]'
bin/rails 'users:create_api_token[agency_id]'
```

2. **Run the standalone test receiver:**
```bash
JSON_API_KEY=$(rails runner "puts User.api_key_for_agency('agency_name')") ruby lib/json_api_receiver.rb
JSON_API_KEY=$(bin/rails runner "puts User.api_key_for_agency('agency_id')") ruby lib/json_api_receiver.rb
```

3. **Configure Emmy App to POST to the reference server.** Add this to your `.env.local`:
```bash
# For testing LA SFTP against sinatra reference implementation
LA_LDH_TRANSMISSION_METHOD=json_and_pdf
LA_LDH_INCOME_REPORT_URL=http://localhost:4567
LA_LDH_PDF_API_URL=http://localhost:4567/pdf
LA_LDH_INCOME_REPORT_APIKEY=foo
LA_LDH_INCLUDE_REPORT_PDF=false
LA_LDH_INCOME_REPORT_ACCOUNTCODE=foobar
```

This starts a standalone test server on port 4567 that logs incoming JSON data and verifies HMAC signatures. The receiver is completely independent and can be used as a reference implementation for agencies building their own JSON API endpoints.
Expand Down Expand Up @@ -453,9 +466,6 @@ See [GOVERNANCE.md](./GOVERNANCE.md)

If you have ideas for how we can improve or add to our capacity building efforts and methods for welcoming people into our community, please let us know by sending an email to: ffs at nava pbc dot com. If you would like to comment on the tool itself, please let us know by filing an **issue on our GitHub repository.**

## Glossary
Information about terminology and acronyms used in this documentation may be found in [GLOSSARY.md](GLOSSARY.md).

## Policies

### Open Source Policy
Expand Down Expand Up @@ -491,4 +501,4 @@ This project is in the public domain within the United States, and copyright and
All contributions to this project will be released under the CC0 dedication. By submitting a pull request or issue, you are agreeing to comply with this waiver of copyright interest.

## Core team
See [COMMUNITY.md](./COMMUNITY.md).
See [COMMUNITY.md](./COMMUNITY.md).
2 changes: 2 additions & 0 deletions app/.env
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# ##############################################################################
LA_LDH_PINWHEEL_ENVIRONMENT=sandbox
SANDBOX_PINWHEEL_ENVIRONMENT=sandbox
RESEARCH_PINWHEEL_ENVIRONMENT=sandbox
MAINTENANCE_MODE=false
LA_LDH_WEEKLY_REPORT_RECIPIENTS=test@email.com

Expand All @@ -22,6 +23,7 @@ SUPPORTED_PROVIDERS=pinwheel,argyle
DOMAIN_NAME=localhost
LA_LDH_DOMAIN_NAME=la.localhost
SANDBOX_DOMAIN_NAME=localhost
RESEARCH_DOMAIN_NAME=research.localhost

PINWHEEL_API_TOKEN_SANDBOX=API secret
ARGYLE_SANDBOX_WEBHOOK_SECRET=Webhook Secret
Expand Down
2 changes: 1 addition & 1 deletion app/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ When developing on the Rails app, ensure you are always in the `app` subdirector
- JS/TS: Prettier (`tabWidth: 2`, double quotes, no semicolons, `printWidth: 100`) via `npm run format` or `npm run format:precommit`.
- Tests follow `_spec.rb` / `.test.ts`; favor descriptive, imperative example names. Use snake_case for Ruby, camelCase for JS, kebab-case for Stimulus files. Prefer `let` for object setup, `before` blocks for shared session/context setup, and `Timecop` for time freezing in controller specs (using `around` blocks).
- ERB/HTML: Put each HTML tag on its own line (opening tag, contents, closing tag) for readability and avoid `usa-prose` classes unless required by design.
- Do not use margin or padding utility helpers (e.g., `margin-bottom-*`, `padding-*`) unless explicitly requested.
- Layout and spacing: Prefer USWDS / project utility classes in ERB for one-off layout and spacing (e.g., `display-flex`, `flex-justify-center`, `margin-top-*`, `padding-*`). Add SCSS when the same rules repeat across elements, when a named class carries semantic meaning (states, variants), or when styling is too complex or token-heavy to express cleanly as utilities.

## Testing Guidelines
- Add coverage for new endpoints, logic, and service objects; exercise eligibility and payroll edge cases.
Expand Down
30 changes: 15 additions & 15 deletions app/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ GEM
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
axe-core-api (4.11.1)
axe-core-api (4.11.2)
dumb_delegator
ostruct
virtus
axe-core-rspec (4.11.1)
axe-core-api (= 4.11.1)
axe-core-rspec (4.11.2)
axe-core-api (= 4.11.2)
dumb_delegator
ostruct
virtus
Expand Down Expand Up @@ -215,7 +215,7 @@ GEM
tzinfo
factory_bot (6.5.6)
activesupport (>= 6.1.0)
faker (3.6.1)
faker (3.8.0)
i18n (>= 1.8.11, < 2)
faraday (2.14.1)
faraday-net_http (>= 2.0, < 3.5)
Expand Down Expand Up @@ -267,7 +267,7 @@ GEM
jmespath (1.6.2)
jsbundling-rails (1.3.1)
railties (>= 6.0.0)
json (2.19.3)
json (2.19.4)
json-logic-rb (0.1.5)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
Expand Down Expand Up @@ -298,7 +298,7 @@ GEM
method_source (1.1.0)
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (6.0.3)
minitest (6.0.5)
drb (~> 2.0)
prism (~> 1.5)
mission_control-jobs (1.1.0)
Expand Down Expand Up @@ -333,7 +333,7 @@ GEM
net-smtp (0.5.1)
net-protocol
net-ssh (7.3.2)
newrelic_rpm (10.2.0)
newrelic_rpm (10.4.0)
logger
nio4r (2.7.5)
nokogiri (1.19.2-aarch64-linux-gnu)
Expand All @@ -346,8 +346,8 @@ GEM
racc (~> 1.4)
orm_adapter (0.5.0)
ostruct (0.6.3)
parallel (1.27.0)
parser (3.3.10.2)
parallel (2.0.1)
parser (3.3.11.1)
ast (~> 2.4.1)
racc
pdf-reader (2.15.1)
Expand Down Expand Up @@ -461,7 +461,7 @@ GEM
redis-client (>= 0.22.0)
redis-client (0.26.4)
connection_pool
regexp_parser (2.11.3)
regexp_parser (2.12.0)
reline (0.6.3)
io-console (~> 0.5)
responders (3.2.0)
Expand All @@ -486,11 +486,11 @@ GEM
rspec-mocks (>= 3.13.0, < 5.0.0)
rspec-support (>= 3.13.0, < 5.0.0)
rspec-support (3.13.7)
rubocop (1.86.0)
rubocop (1.86.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
parallel (~> 1.10)
parallel (>= 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
Expand Down Expand Up @@ -579,7 +579,7 @@ GEM
uri (1.1.1)
useragent (0.16.11)
vcr (6.4.0)
view_component (4.6.0)
view_component (4.7.0)
actionview (>= 7.1.0)
activesupport (>= 7.1.0)
concurrent-ruby (~> 1)
Expand Down Expand Up @@ -608,7 +608,7 @@ GEM
wkhtmltopdf-binary (0.12.6.10)
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.38)
yard (0.9.42)
zeitwerk (2.7.5)

PLATFORMS
Expand Down Expand Up @@ -646,7 +646,7 @@ DEPENDENCIES
ed25519
erb_lint
factory_bot (~> 6.5)
faker (~> 3.6)
faker (~> 3.8)
faraday (~> 2.14.1)
gpgme (~> 2.0)
i18n-tasks (~> 1.1)
Expand Down
1 change: 1 addition & 0 deletions app/app/assets/stylesheets/application.postcss.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@forward "activity_hub.scss";
@forward "cbv.scss";
@forward "demo-launcher.scss";
@forward "document_uploads.scss";

/* Import styling from ViewComponents */
@forward "../../components/activity_flow_progress_indicator/activity_flow_progress_indicator.scss";
Expand Down
21 changes: 0 additions & 21 deletions app/app/assets/stylesheets/cbv.scss
Original file line number Diff line number Diff line change
Expand Up @@ -532,24 +532,3 @@ input#invitation_link {
font-weight: bold;
color: #005EA2;
}

/*
* Document upload page
*/
.document-uploads__preview {
display: flex;
align-items: center;
}
.document-uploads__preview-icon {
box-sizing: content-box;
color: color("gray-cool-40");
flex-shrink: 0;
height: units(4);
padding: units(1);
width: units(4);
}
.document-uploads__preview-filename {
@include u-font("sans", 2);
overflow: hidden;
word-break: break-word;
}
41 changes: 40 additions & 1 deletion app/app/assets/stylesheets/demo-launcher.scss
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,42 @@
margin: 0;
}

.demo-launcher__launch-section {
padding-top: units(2);
border-top: 1px solid color("base-lighter");
}

.demo-launcher__share-widget {
margin-top: units(2);
padding: units(2);
border: 1px solid color("base-lighter");
border-radius: radius("md");
background-color: color("base-lightest");
}

.demo-launcher__share-row {
display: grid;
gap: units(1);
align-items: end;

@include at-media("tablet") {
grid-template-columns: minmax(0, 1fr) auto auto;
}

.usa-input,
.usa-button {
margin: 0;
}
}

.demo-launcher__share-status {
margin-top: units(1);
margin-bottom: 0;
}

// -- Left column: test scenarios --
.demo-launcher__test-scenarios {
margin-top: units(8);
margin-top: 0;
}

.demo-launcher__scenario-radios {
Expand Down Expand Up @@ -186,4 +219,10 @@
flex-shrink: 0;
}
}

.demo-launcher__renewal-required {
&.demo-launcher__renewal-required--hidden {
display: none;
}
}
}
57 changes: 57 additions & 0 deletions app/app/assets/stylesheets/document_uploads.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@forward "uswds";
@use "uswds" as *;

/*
* Document uploads
*/
.document-uploads {
margin-bottom: units(4);
margin-top: units(4);
}
.document-uploads__heading {
margin-bottom: units(2);
margin-top: 0;
}
.document-uploads__list {
list-style: none;
margin: 0;
padding: 0;
}
.document-uploads__item {
align-items: center;
border-bottom: 1px solid color("gray-cool-20");
display: flex;
gap: units(2);
justify-content: space-between;
padding-bottom: units(1);
padding-top: units(1);
}
.document-uploads__file {
align-items: center;
display: flex;
gap: units(2);
min-width: 0;
}
.document-uploads__icon {
background-color: color("blue-warm-5");
border: 1px solid color("blue-warm-20v");
border-radius: radius("md");
box-sizing: content-box;
color: color("primary");
flex-shrink: 0;
height: 1.5rem;
padding: units(1);
width: 1.5rem;
}
.document-uploads__filename {
@include u-font("sans", 4);
line-height: line-height("sans", 4);
overflow: hidden;
word-break: break-word;
}
.document-uploads__remove-link {
color: color("secondary-dark");
flex-shrink: 0;
@include u-font("sans", 4);
line-height: line-height("sans", 4);
}
4 changes: 4 additions & 0 deletions app/app/components/activity_flow_header_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ def initialize(title:, exit_url:, back_url: nil)
@exit_url = exit_url
@back_url = back_url
end

def confirm_on_exit?
helpers.params[:from_edit].blank?
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<div class="activity-flow-header"
data-controller="activity-flow-header"
data-activity-flow-header-exit-url-value="<%= exit_url %>">
data-activity-flow-header-exit-url-value="<%= exit_url %>"
data-activity-flow-header-confirm-on-exit-value="<%= confirm_on_exit? %>">
<% close_icon_href = helpers.uswds_sprite_icon_href("close") %>
<% back_icon_href = helpers.uswds_sprite_icon_href("navigate_before") %>

<div class="activity-header-title">
<div class="activity-header-title__inner">
Expand All @@ -11,7 +14,7 @@
class="activity-header-title__exit-link">
<%= t("activities.activity_header_component.exit") %>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="<%= asset_path("@uswds/uswds/dist/img/sprite.svg#close") %>"></use>
<use xlink:href="<%= close_icon_href %>"></use>
</svg>
</a>
</div>
Expand Down Expand Up @@ -49,7 +52,7 @@
aria-label="Close this window"
data-close-modal>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="<%= asset_path("@uswds/uswds/dist/img/sprite.svg#close") %>"></use>
<use xlink:href="<%= close_icon_href %>"></use>
</svg>
</button>
</div>
Expand All @@ -69,7 +72,7 @@
<div class="back-nav">
<%= link_to back_url, class: "back-nav__link" do %>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
<use xlink:href="<%= asset_path("@uswds/uswds/dist/img/sprite.svg#navigate_before") %>"></use>
<use xlink:href="<%= back_icon_href %>"></use>
</svg>
<%= t("activities.activity_header_component.back") %>
<% end %>
Expand Down
Loading
Loading