Commit Graph

74 Commits

Author SHA1 Message Date
Paul Huliganga 854d112372 Add bulk send prototype — script, sample CSV, and demo guide 2026-04-20 01:49:19 -04:00
Paul Huliganga 39982008d3 Add latest migration files and validation outputs 2026-04-20 01:12:46 -04:00
Paul Huliganga c63d49e208 fix: Adobe Sign connects via .env refresh token; restore DocuSign OAuth
- GET /api/auth/adobe/connect: reads ADOBE_REFRESH_TOKEN from .env,
  refreshes the access token, stores in session — no login required
- Falls back to OAuth dialog only if no .env credentials exist
- Restores DocuSign OAuth start/callback endpoints alongside JWT connect
- 33/33 tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:27:42 -04:00
Paul Huliganga aa88ba363d fix: DocuSign connects via JWT grant from .env, no browser sign-in
Replaces the DocuSign OAuth redirect flow with a direct JWT grant call
using credentials already in .env (same as the CLI). Clicking
"Connect DocuSign" now calls GET /api/auth/docusign/connect which calls
get_access_token() and stores the result in the session cookie.

No email sign-in required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:24:50 -04:00
Paul Huliganga 1383586d91 fix: use existing Adobe Sign redirect URI (localhost:8080) in web UI
The Adobe Sign OAuth app has https://localhost:8080/callback registered
(same as CLI). The web UI now uses the same manual paste flow:
- GET /api/auth/adobe/url returns the auth URL for the frontend to open
- POST /api/auth/adobe/exchange accepts the full redirect URL the user
  copies after authorizing, extracts the code, exchanges for tokens
- Dialog UI guides user through the 3-step process

DocuSign keeps its standard redirect callback flow unchanged.
31/31 tests passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:20:10 -04:00
Paul Huliganga f78a50282a docs: update README with web UI + upsert docs; add CLAUDE.md and AGENT-INSTRUCTIONS
- README.md: web UI setup/usage, --force-create flag, running tests, full project structure
- CLAUDE.md: definition of done (code + tests + README + board + push)
- docs/agent-harness/AGENT-INSTRUCTIONS.md: expanded with DoD, test strategy, doc conventions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 15:06:50 -04:00
Paul Huliganga 51f532f452 feat: idempotent upload + FastAPI web UI with full test coverage
Phase 1 — Idempotent upload:
- upload_docusign_template.py now upserts: PUT if template with same name
  exists (most recently modified), POST otherwise
- --force-create flag to bypass upsert

Phase 2-6 — FastAPI web UI:
- web/app.py: FastAPI app with /health, static file serving
- web/routers/auth.py: Adobe Sign + DocuSign OAuth start/callback/disconnect
- web/routers/templates.py: template listing + migration status badges
  (not_migrated / migrated / needs_update)
- web/routers/migrate.py: POST /api/migrate pipeline + GET /api/migrate/history
- web/static/: vanilla HTML/CSS/JS side-by-side template browser UI

Phase 7 — Tests (29/29 passing):
- test_upload_upsert.py: 4 upsert unit tests
- test_api_health/auth/templates/migrate.py: full API coverage
- test_e2e.py: 7-step full pipeline end-to-end test
- test_regression.py: compose output vs snapshots for 3 real templates
- conftest.py: --update-snapshots CLI option

Docs: IMPLEMENTATION-PLAN.md, updated EXECUTION-BOARD.md + architecture.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 14:47:27 -04:00
Paul Huliganga 2d167421b2 chore: gitignore PDFs, private.key, and .env-orig
PDFs are generated artifacts. private.key and .env-orig are secrets
that should never be tracked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 12:35:20 -04:00
Paul Huliganga 7aac78f3a4 docs: document Adobe Sign non-standard refresh token endpoint
Adobe Sign uses /oauth/v2/refresh (not /oauth/v2/token) for token
refresh — a deviation from the OAuth2 spec that caused all refresh
attempts to fail with a misleading "Invalid grant_type" error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 12:35:17 -04:00
Paul Huliganga cabf753326 docs: update README — reference .env-sample and mention conditional logic
- Setup step 2 now directs users to copy .env-sample instead of
  repeating all variables inline
- "What it does" step 3 mentions conditional field logic
- Field type mapping section updated to mention conditional logic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 12:35:13 -04:00
Paul Huliganga 785107e8de feat: map Adobe Sign conditional logic to DocuSign conditionalParentLabel/Value
Adobe Sign conditionalAction (SHOW/EQUALS) is now translated to
DocuSign's conditionalParentLabel + conditionalParentValue on the
dependent tab, making conditional fields work in the migrated template.

For radio groups, conditionalParentLabel matches the radio group name.

Unsupported cases emit warnings rather than silently dropping conditions:
- HIDE action (no DocuSign equivalent — field left always visible)
- Non-EQUALS operators (skipped)
- Multi-predicate ANY/ALL (first EQUALS predicate used, rest ignored)

Also updates field-mapping.md: adds Conditional Logic Mapping table
and moves this item out of Known Gaps into documented behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 12:18:48 -04:00
Paul Huliganga ad0131c2d9 chore: add .env-sample with all required environment variables
Documents every variable used across the project, grouped by service
(Adobe Sign and DocuSign), with comments explaining where to find each
value. Auto-written cache variables (tokens, expiry) are included but
left blank.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 10:10:28 -04:00
Paul Huliganga e2e47f2662 feat: map Adobe STAMP field to DocuSign stampTabs
Adobe STAMP (hanko/seal) has a direct DocuSign equivalent via
stampTabs. Previously marked as skipped with no equivalent.

- compose_docusign_template.py: emit stampTabs for STAMP input type;
  PARTICIPATION_STAMP remains skipped (still no equivalent)
- field-mapping.md: update STAMP row, add stampTabs to multi-location
  non-merging list, add account feature prerequisite to Known Gaps

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 10:10:22 -04:00
Paul Huliganga 9c6c01d619 fix: correct Adobe Sign token refresh endpoint and add auth error handling
Adobe Sign uses a non-standard separate endpoint for refresh:
/oauth/v2/refresh (not /oauth/v2/token). Using the wrong endpoint
returned a misleading "Invalid grant_type refresh_token" error.

Also:
- Remove redirect_uri from refresh requests (not required)
- Add clear RuntimeError message directing user to re-authenticate
- Validate access_token is non-empty before saving in adobe_auth.py
- Log token lengths and exchange response keys on successful auth

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 10:10:12 -04:00
Paul Huliganga 766986a795 refactor: rename auth_adobe.py to adobe_auth.py for naming consistency
Matches the naming convention of docusign_auth.py. Update all
references in README.md and the error message in adobe_api.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 10:10:05 -04:00
Paul Huliganga 780099172f docs: update README and field-mapping to reflect current codebase
README:
- Remove Node.js prerequisite (upload now handled by Python script)
- Complete .env template with all DocuSign JWT auth keys
- Add DocuSign consent step to setup instructions
- Update all script examples: download_templates subcommands, Python upload,
  migrate_template.py replacing migrate_paul_template.py
- Update project structure to include all current src files

field-mapping.md:
- Replace incorrect coordinate translation note with confirmed behaviour:
  both platforms use top-left origin, direct pass-through with MIN_TEXT_WIDTH floor
- Add dedicated Multi-location (Cloned) Fields section documenting tab merging
  and which tab types support it
- Replace vague To Do items with accurate known gaps (conditional logic,
  formula fields, advanced validation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 01:17:26 -04:00
Paul Huliganga e30e9d4f14 docs: README, platform quirks, validation notes, and sample reference data
README.md — rewritten to reflect actual usage: setup, auth flows, CLI
commands for all three scripts, and links to field-mapping and quirks docs.

tests/PLATFORM-QUIRKS.md — documents confirmed bugs and API quirks found
during development: numberTabs rendering as text+validation (DocuSign API
bug), multi-location field fix, zero-width tab fix, Company/Title contentType
SIGNER_ prefix variant from Adobe Sign API.

tests/ — SCENARIOS, EDGE-CASES, FIELD-TYPE-REGRESSION test planning docs.

validation/ — research notes: field eval, mapping ambiguity log, decision
log, conditional logic analysis, round-trip eval, DocuSign ingest eval.

docs/architecture.md — system architecture overview.
api-samples.md — annotated Adobe Sign API response examples.
PRODUCT-SPEC.md — product requirements and migration scope definition.
sample-templates/ — JSON fixtures (NDA, onboarding, sales contract) for
offline testing; PDFs excluded from version control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:45:46 -04:00
Paul Huliganga 9c0910f30f feat: end-to-end migration runner and test template utilities
migrate_template.py — generic end-to-end CLI replacing the earlier
migrate_paul_template.py:
  --list                   list available Adobe Sign templates
  --template "Name"        download → convert → upload a named template
  --template "Name" --skip-upload  convert only, write JSON to migration-output/
  Picks most recently modified when multiple templates share a name.

create_adobe_template.py — utility for creating a test template in Adobe Sign
that exercises all 15+ field types. Uses the David Tag Demo Form PDF as the
base document and positions extra fields (Number, Email, Company, Title) in
the gaps of the original layout.

generate_pdfs.py — generates realistic sample PDFs with labelled form areas
matching the *-formfields.json fixtures in sample-templates/, for use in
offline testing without a live Adobe Sign account.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:45:31 -04:00
Paul Huliganga 93b6ad248a feat: DocuSign JWT auth and pure-Python template upload client
docusign_auth.py — authentication helper supporting two flows:
- JWT Grant: service-to-service token generation using an RSA private key;
  caches token + expiry in .env to avoid redundant round-trips
- Auth Code Grant (--consent): one-time browser flow to grant the app the
  'impersonation' scope required for JWT; must be run once per user/app before
  JWT will work

upload_docusign_template.py — posts a docusign-template.json to the DocuSign
Templates REST API (v2.1). No Node.js dependency. Retries once on 401.

requirements.txt — adds PyJWT>=2.0 and cryptography for RSA key handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:45:23 -04:00
Paul Huliganga 76568672d7 feat: core migration — Adobe Sign to DocuSign field mapping and composition
compose_docusign_template.py — converts a downloaded template folder into a
DocuSign envelopeTemplate JSON ready for the Templates API. Key behaviours:

- Full field type mapping: TEXT_FIELD, SIGNATURE, CHECKBOX, RADIO, DROP_DOWN,
  BLOCK, FILE_CHOOSER (with warning), INLINE_IMAGE (skipped with warning)
- contentType dispatch: SIGNER_NAME → fullNameTabs, SIGNER_EMAIL →
  emailAddressTabs, SIGNATURE_DATE → dateSignedTabs, COMPANY/SIGNER_COMPANY →
  companyTabs, TITLE/SIGNER_TITLE → titleTabs, DATA+NUMBER → numberTabs,
  DATA+DATE → dateTabs, SIGNER_INITIALS → initialHereTabs
- Multi-location (cloned) fields: emits one tab per location with the same
  tabLabel so DocuSign tab merging replicates Adobe Sign's sync behaviour
- Width/height passed through from Adobe Sign locations; MIN_TEXT_WIDTH=120pt
  ensures text fields render as visible boxes rather than vertical lines
- Coordinate system: both platforms use top-left origin — no inversion needed

test_mapping.py — unit test harness validating tab grouping and field mapping.

field-mapping.md — full Adobe Sign → DocuSign tab type reference table with
edge cases, known gaps, and decision log.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:45:13 -04:00
Paul Huliganga e655d8b4f5 feat: Adobe Sign template download pipeline
download_templates.py — subcommand CLI for listing and downloading library
templates from Adobe Sign.

  list                   — print all templates with name, modified date, ID
  download               — download all templates (default)
  download --all         — explicit download all
  download "Name"        — download a single named template; picks the most
                           recently modified if duplicates exist

Each template is saved to downloads/<name>__<id8>/ containing metadata.json,
form_fields.json, documents.json, and the source PDF.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:44:52 -04:00
Paul Huliganga 343955241d feat: Adobe Sign OAuth client and API wrapper
auth_adobe.py — one-time browser Auth Code Grant flow; saves access and
refresh tokens to .env. Targets the EU2 shard.

adobe_api.py — thin API client with auto token refresh on 401. Supports
GET, POST (JSON and multipart), PUT, and binary download.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:44:43 -04:00
Paul Huliganga a1601009dc chore: exclude generated outputs and binary assets from git
Add downloads/, migration-output/ (account-specific / generated files)
and *.b64 (redundant base64 copies of PDFs) to .gitignore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 19:44:26 -04:00
Paul Huliganga b9a4e75da9 Initial project scaffold (Cleo) 2026-04-14 19:21:17 -04:00