Reply Pilot Refactoring Tracker
This is the active status tracker for the app/backend boundary refactor. Durable
target context, guardrails, and validation gates live in
docs/refactoring-context.md.
Legacy note: the old active tracker lived at docs/refactoring.md. That file is
now only a compatibility pointer.
Open Items
To Solve
No unfinished implementation item is currently queued.
Regular Maintenance
- [ ] M1. Keep the architecture report honest after boundary changes. Run
python3 script/architecture-report.py --output reports/architecture-report.mdonly when implemented code changes alter the architecture baseline, then runscript/check-architecture.sh. - [ ] M2. Keep this tracker current after each refactor item. Move completed
non-maintenance items to
## Done (Archive), keep evidence, and update## Unfinished Audit. - [ ] M3. Keep documentation validation green after docs changes:
mkdocs build --strict.
Unfinished Audit
- Jira task sync, automatic incoming-email Jira operations, and Jira reports
intentionally remain on technical credentials because they have no
authenticated human actor.
JIRA_EMAIL/JIRA_API_TOKENmust remain configured while this allowlist exists. - No unfinished item 28 work remains after the follow-up Gmail mailbox cache
boundary move. The earlier weekly-report-only move was partial because
reply-pilot-bestill owned the durable mailbox cache. - No separate sent-only Gmail cache or worker remains planned. Sent-only threads are handled as a second pass into the same Gmail-owned account cache.
Done (Archive)
- [x] 30.8 Close the delegated Jira migration with an exhaustive call-site audit, documentation, and live Jira acceptance.
- Evidence: the production call-site audit classifies every browser-triggered Jira read/write as delegated with the backend-authenticated app user. The only technical-account allowlist is Jira task sync, automatic incoming-email Jira operations, and Jira reports.
- Evidence: the OAuth authorization request includes
read:jira-user,read:jira-work,write:jira-work, andoffline_access. Create preserves an already returned Jira key if its follow-up enrichment read requires a new consent, preventing false428responses and duplicate retries. - Evidence: after re-consent, live status and
/myselfsucceeded.RP-3509was created through Reply Pilot and Jira recorded the same human user as creator, reporter, comment author, attachment author, unassign and reassignment author, summary/description update author, and transition author. The task finished inDone. Earlier invalid-format live testing returned428 jira_oauth_required; a no-header background task-sync smoke succeeded through the technical no-actor path. - Evidence: Jira OAuth, backend, web-app, integration, module README, profile microcopy, refactoring context, and the PlantUML/PNG flow all describe the final boundary.
-
Validation: backend
mvn -q verify, all 289 app tests, all 21 Jira reports tests, regenerated architecture report plusscript/check-architecture.sh, strict MkDocs build, regenerated PlantUML PNG, andgit diff --checkpassed. -
[x] 30.7 Delegate all remaining user-facing Jira reads while preserving the background allowlist.
- Evidence: Jira proxy myself, user lookup, issue/search/assigned/stale/
detail/transition/comment read routes now require the backend-authenticated
app user and pass that actor through
JiraProxyServiceto the delegated Jira client. Missing, malformed, or unusable OAuth credentials use the shared HTTP428 jira_oauth_requiredcontract and never retry with the technical account. - Evidence: explicit task refresh passes the actor returned by the existing task authorization check into the Jira issue read. Direct and delegated task access therefore use the authenticated caller's OAuth token, not a task owner or request-body identity.
- Evidence: task overview/detail, related-task, comment, transition, and
Supplier Onboarding read workflows preserve available local data and offer
the existing Jira connection action when Jira returns
428. One request emits at most one connection prompt, and a missing token suppresses the global remote task badge instead of turning unrelated pages into errors. - Evidence: Jira task synchronization, automatic incoming-email Jira operations, and Jira reports remain the explicit no-human-actor technical allowlist. Regression coverage asserts that task sync and incoming-email automation do not acquire an arbitrary app-user actor.
-
Validation: focused backend Jira proxy/task refresh/service tests and focused app OAuth/read UI tests passed; the full Flask suite passed with 289 tests, the Jira reports suite passed with 21 tests, full backend
mvn -q verify,mkdocs build --strict, andgit diff --checkpassed.script/check-architecture.shstill reports the pre-existing stalereports/architecture-report.mdbaseline documented in the unfinished audit; this slice did not regenerate the broader dirty-tree report. -
[x] 30.6 Secure and delegate lead-import and generic Jira mutation APIs.
- Evidence: the lead-import action controller requires authenticated
UserAuthorization, ignores request-bodyoperator_user_idas identity, and passes the authenticatedappUserIdas operator and Jira actor. Lead-import Jira user lookup, issue creation, optional transition, and final description update use that same actor. A delegated OAuth failure is rethrown to the shared428 jira_oauth_requiredhandler after preserving the existing item-error recording and external-side-effect ordering. - Evidence: generic Jira create, update, transition, comment, and unassign
routes now require the existing trusted app-user context and use its actor
for every Jira write. A repository-wide production string-literal audit
found no consumers outside
reply-pilot-app; consumers outside this repository cannot be proven absent and must now send the trusted authentication context. The existing app HTTP clients already attach that header. - Evidence: the lead-import app workflow handles the shared OAuth exception,
renders HTTP
428, keeps the submitted operator note and email fields, and offers the existing Jira connection flow. No new client, credential store, or technical-account fallback was added. -
Validation: focused
LeadImportServiceTest,LeadImportControllerTest,JiraProxyControllerTestpassed with 13 tests; the controller test wires the realJiraProxyServicebecause no standaloneJiraProxyServiceTestexists. Focused app OAuth/client/lead-import coverage passed with 11 tests; fullmvn -q verify, the full Flask suite with 286 tests,mkdocs build --strict, andgit diff --checkpassed.script/check-architecture.shstill reports the pre-existing stalereports/architecture-report.mdbaseline documented in the unfinished audit; this slice did not regenerate the broader dirty-tree report. -
[x] 30.5 Delegate Jira attachment operations.
- Evidence: each existing attachment controller action retains its task
authorization check and passes the resulting
UserAuthorization.appUserId()to the attachment service. Settings, listing, streamed upload, same-filename replacement deletion, rollback deletion, download metadata verification, and binary content download use that actor; no delegated failure retries with technical credentials. - Evidence: replacement failures keep the previous filename behavior and
attempt to remove the newly uploaded attachment with the same actor.
OAuth failure during replacement preserves the shared HTTP
428 jira_oauth_requiredcontract after the delegated rollback attempt. Existing size/type validation, task authorization, response headers, and multipart streaming remain unchanged. - Evidence: the Flask attachment client sends the authenticated actor only in the trusted backend header, including its streamed multipart request. Attachment list, upload, and download workflows handle the shared OAuth exception and offer the existing Jira connection flow instead of returning a 500.
-
Validation: focused backend
TaskAttachmentServiceTest,TaskAttachmentControllerTest,HttpJiraIssueClientTestpassed with 17 tests; focused app attachment/client/OAuth coverage passed with 11 tests; fullmvn -q verify, full Flask suite with 284 tests,mkdocs build --strict, andgit diff --checkpassed.script/check-architecture.shstill reports the pre-existing stalereports/architecture-report.mdbaseline documented in the unfinished audit; this slice did not regenerate the broader dirty-tree report. -
[x] 30.4 Propagate the authenticated actor through the remaining user-triggered task/company mutations.
- Evidence: the six existing task mutation controller actions retain their
authorization checks and pass the resulting
UserAuthorization.appUserId()separately from requester and assignee business data. Company reassignment, Supplier Onboarding creation/edit, Reply Pilot Task creation, task reassignment, and manual Email Thread Reply task creation use that actor for Jira user lookup, issue reads, creation, updates, and assignment. - Evidence: the Flask task mutation and email clients expose the shared
jira_oauth_requirederror, and affected company, reassignment, task, and email workflows offer the existing Jira connection flow without changing their browser or backend routes. Supplier Onboarding and Reply Pilot Task post-create link failures preserve the existing partial-success warning instead of inviting duplicate issue creation. -
Validation: focused backend
TaskMutationServiceTest,TaskMutationControllerTestpassed with 40 tests; focused app client/workflow coverage passed with 11 tests; fullmvn -q verify, full Flask suite with 283 tests,mkdocs build --strict, andgit diff --checkpassed.script/check-architecture.shstill reports the pre-existing stalereports/architecture-report.mdbaseline documented in the unfinished audit; this slice did not regenerate the broader dirty-tree report. -
[x] 30.3 Delegate task workflows that already carry an authenticated actor.
- Evidence: Supplier Onboarding reply uses one authenticated app user for issue/status/comment reads, comment creation, assignment, and transition; Reply Pilot resolve uses that actor for assignee lookup, comment, and issue update; Email Thread Reply update uses it for lookup, update, and every requested transition, not only report-attributed statuses.
- Evidence: Reply Pilot close, move-to-waiting, Email Thread Reply close, and the post-forward move-to-waiting path retain their actor-aware transition metadata. Assignee/requester ids remain business data and never select the OAuth credential. Supplier reply comment identity is loaded from the authenticated app user rather than trusted request payload fields.
- Evidence: an OAuth reconnect requirement after a Supplier comment or after an email send returns the existing partial-success warning instead of inviting a retry of an already completed external side effect. Existing authorization, status checks, and local cache synchronization remain unchanged.
-
Validation: focused backend
TaskMutationServiceTest,TaskMutationControllerTest,EmailSendControllerTestpassed with 51 tests; seven corresponding Flask workflow tests passed; fullmvn -q verify, full Flask suite with 281 tests,mkdocs build --strict, andgit diff --checkpassed.script/check-architecture.shstill reports the pre-existing stalereports/architecture-report.mdbaseline documented in the unfinished audit; this slice did not regenerate the broader dirty-tree report. -
[x] 30.2 Make OAuth-required handling one consistent backend/app contract.
- Evidence:
JiraOAuthRequiredExceptionHandleris the single backend mapping to HTTP428withstatus=error, codejira_oauth_required, and the original reason; controller-local copies were removed. - Evidence: missing, malformed, missing-refresh, rejected-refresh, and
delegated Jira
401paths converge onJiraOAuthRequiredException; no delegated request falls back to the technical account. - Evidence: task mutation, Jira backend, attachment, and lead-import HTTP
clients require both HTTP
428and the contract code before exposing the sharedJiraOAuthRequiredError. - Evidence: Reply Pilot resolve, Supplier Onboarding reply, and Email Thread Reply edit preserve submitted values and render the shared Jira connection prompt; existing actor-aware transition actions offer the same connection flow without changing their success routes or payloads.
-
Validation:
mvn -q -Dtest=JiraOAuthServiceTest,JiraProxyControllerTest,TaskMutationControllerTest testpassed; focused app client/workflow/template selection passed with 11 tests; fullmvn -q verify, full app suite with 281 tests, andmkdocs build --strictpassed. -
[x] 30.1 Add an explicit delegated Jira request path for every supported API operation.
- Evidence:
JiraIssueClientnow exposes required actor-aware variants for issue reads/searches, user lookup, create/update, assign/unassign, transition, comments, and attachment settings/list/upload/download/delete; the existing no-actor variants remain the explicit technical-account path. - Evidence: actor-aware methods are not allowed to default to technical methods, so an implementation cannot compile while silently omitting the delegated path.
- Evidence:
HttpJiraIssueClientresolves oneJiraDelegatedAccessthrough the existingJiraOAuthAccessProvider, reuses it across every request in the operation, and sends Bearer authentication toapi.atlassian.com/ex/jira/{cloudId}/rest/api/3; the same request access object also covers multipart upload and binary download. - Evidence: delegated HTTP
401responses becomeJiraOAuthRequiredException; no delegated method retries with Basic credentials. Existing technical methods still use the configured Basic authentication and Jira base URL for background callers. - Validation:
mvn -q -Dtest=HttpJiraIssueClientTest,JiraOAuthServiceTest testpassed, covering Bearer/cloud URL selection, token refresh, delegated transition metadata, delegated attachment operations, delegated401, and the technical Basic-auth path. - Validation: full
mvn -q testpassed inreply-pilot-be. -
Validation:
mkdocs build --strictandgit diff --checkpassed. -
[x] 29. Add sent-only Gmail threads to the single Gmail-owned cache.
- Evidence:
POST /api/mailbox/snapshotaccepts optionalskip_cached_threads; when true,reply-pilot-gmailreads cached thread IDs from its index, lists Gmail thread IDs for the requested query, skips cached IDs beforethreads.get, and merges new sent-only threads into the same account-scoped cache. - Evidence:
reply-pilot-besupportssync_mode=sent_backfillandPOST /api/mailbox/sync/sent; both route through the existing mailbox import state machine and worker step endpoint. - Evidence: full mailbox sync is now sequential: inbox snapshot with reset,
then
in:sentwith page size 20 andskip_cached_threads=true, then the activity import runs against the unified cache. - Evidence: Gmail watch defaults and encrypted local/prod Gmail env now use
GMAIL_WATCH_LABEL_IDS=INBOX,SENT;ensureWatchre-registers when saved labels, filter behavior, or topic differ from current configuration. - Evidence: incremental history keeps cache-relevant threads when any message
in the thread has
INBOXorSENT. - Validation:
mvn -f reply-pilot-gmail/pom.xml clean verify,mvn -f reply-pilot-be/pom.xml clean verify, andpython3 -m pytest reply-pilot-worker/testspassed during implementation. -
Validation:
script/check-architecture.sh,mkdocs build --strict, andgit diff --checkpassed;GMAIL_WATCH_LABEL_IDSwas verified through SOPS for local and prod Gmail env files. -
[x] 28. Move Gmail mailbox cache ownership back to the Gmail module.
- Evidence: the old weekly-report-only state was partial; the completed
boundary is now
Google Gmail API/mailbox -> reply-pilot-gmail -> reply-pilot-be. - Evidence:
reply-pilot-gmailowns the account-scoped physical cache underdata/accounts/<GMAIL_CACHE_ACCOUNT_ID>/emails/, including index, thread snapshots, attachment metadata, attachment bytes, remote page token, and history cursor. - Evidence: Gmail attachment
relative_pathvalues remain logical paths such asemails/attachments/<thread>/<file>, so DB/debug-context behavior does not need a migration. - Evidence:
reply-pilot-gmailexposes internal cache read endpoints for BE inbox/detail/attachment reads and the Java Spring BootGET /api/reports/email-weekly-countsendpoint computes from Gmail-owned cache/runtime data. - Evidence:
reply-pilot-jira-reportsdefaultsJIRA_REPORTS_EMAIL_BASE_URLtohttp://reply-pilot-gmail:5000in config, Compose, scripts, docs, and env template. - Evidence:
reply-pilot-beuses an HTTP-backed cache adapter inEMAIL_SYNC_BACKEND=gmail_servicemode, does not create a Gmail cache file repository bean in that mode, and proxies attachment bytes from the Gmail module instead of reading local BE files. -
Validation: focused Gmail/BE cache tests passed during implementation; full validation is tracked by the implementation task outcome.
-
[x] 27.7 Align search, docs, and architecture checks after delegated task workflow changes.
- Evidence:
reply-pilot-searchindexesreply_pilot_taskJira issues, includes delegatedtask_type_valuein task search text/subtitles, and storesdelegated_task_assignee_app_user_idonly for active delegated tasks. - Evidence: assigned-scope task search now allows active delegated tasks for the cached assignee in addition to unscoped tasks and tasks owned through normal company assignment.
- Evidence:
Donedelegated tasks remain indexed for explicit search through normal company scope orcompany.view_all, but they do not get the active delegated-assignee search exception. - Evidence:
docs/backend.md,docs/permissions.md,docs/web-app.md,reply-pilot-search/README.md, anddocs/refactoring-context.mddocument the final search/visibility behavior. - Evidence:
reports/architecture-report.mdwas regenerated after the search implementation change, andscript/check-architecture.shpassed against the updated baseline. -
Validation: focused search validation
python3 -m pytest reply-pilot-search/tests/test_runtime.py -k 'task_scope or task_documents'passed with 2 tests; full search validationpython3 -m pytest reply-pilot-search/testspassed with 19 tests;python3 script/architecture-report.py --output reports/architecture-report.md,script/check-architecture.sh,mkdocs build --strict, andgit diff --checkpassed. -
[x] 27.6 Add delegated task UI workflow.
- Evidence:
reply-pilot-appkeepsreply_pilot_taskas its own Jira work type, reads backend-providedtask_type_valueinto task list/detail models, and shows delegated task context in the existing task overview. - Evidence: delegated task detail renders the linked company, Jira
description, and read-only Jira comments through the extracted
_jira_comments.htmlcomponent shared with the current supplier task UI. - Evidence: assignees can open a one-textarea resolve page that calls backend
POST /api/tasks/{taskId}/reply-pilot-task/resolve; requesters can close returned tasks through a POST action that callsPOST /api/tasks/{taskId}/reply-pilot-task/close. - Evidence: app routes remain thin
views.pyroute-map entries delegated toreply_pilot_app.workflows.tasks, and mutation controls follow the UI rule that data-changing actions are submitted with buttons. - Evidence:
docs/web-app.mdanddocs/refactoring-context.mddescribe the app delegated task UI and backend-owned mutation boundary. -
Validation: focused app validation
python3 -m pytest reply-pilot-app/tests/test_task_mutation_client.py reply-pilot-app/tests/test_app.py -k 'reply_pilot_task or task_mutation_client_maps_task_mutation_payloads or supplier_onboarding_reply_page_renders_summary_and_comments_newest_first'passed with 6 tests; full app validationpython3 -m pytest reply-pilot-app/testspassed with 249 tests;mkdocs build --strictandgit diff --checkpassed. -
[x] 27.2 Add delegated task cache schema.
- Evidence: migration
reply-pilot-db/migrations/0041_add_reply_pilot_task_subtype.sqladdsreply_pilot_tasktotask_jira_work_typeand createstask_jira_reply_pilot_taskwithtask_type_value,requester_app_user_id, timestamps, FK rollback, and no Jira dropdown check constraint. - Evidence:
reply-pilot-db/migrations/db.changelog.xmlincludes the new changeset, anddocs/database.md,docs/backend.md,docs/permissions.md, anddocs/refactoring-context.mddescribe the delegated task cache, requester ownership, and narrow authorization rules. - Evidence: Docker was running with healthy
reply-pilot-db, and the local DB ended in the migrated state after update, rollback, and re-update. -
Validation:
./scripts/db-liquibase.sh validate,./scripts/db-liquibase.sh update,./scripts/db-liquibase.sh rollback-count 1, and./scripts/db-liquibase.sh updateall passed fromreply-pilot-db/;mvn -f reply-pilot-be/pom.xml verifypassed with 212 tests;mkdocs build --strictandgit diff --checkpassed. -
[x] 27.5 Implement delegated task mutations.
- Evidence:
TaskMutationControllerexposesPOST /api/tasks/{taskId}/reply-pilot-task/resolveandPOST /api/tasks/{taskId}/reply-pilot-task/close; both require an authenticated app user and delegate ownership checks to the backend service instead of adding a new RBAC permission. - Evidence:
TaskMutationServiceresolves only for the cached Jira assignee of an active delegated task, appends the submitted text to Jira/plain-text description, reassigns Jira/local cache to the stored requester app user, and preservesIn Progress. - Evidence: requester close transitions Jira/local cache to
Donewithout changing assignee, and wrong-assignee/wrong-requester paths return 403. - Evidence:
JdbcTaskMutationRepositoryloads delegated mutation targets and checks active assignee ownership throughtask_jira.user_idor the app user's Jira account profile; tests cover service, controller, and JDBC behavior. - Evidence:
docs/backend.md,docs/permissions.md,docs/database.md, anddocs/refactoring-context.mddocument the delegated task mutation contract. DB migration validation for the subtype table remains tracked separately by blocked item 27.2. -
Validation:
mvn -f reply-pilot-be/pom.xml -Dtest=TaskMutationServiceTest,TaskMutationControllerTest,JdbcTaskMutationRepositoryTest testpassed with 24 tests;mvn -f reply-pilot-be/pom.xml verifypassed with 212 tests;mkdocs build --strictandgit diff --checkpassed. -
[x] 27.4 Implement task-derived company visibility.
- Evidence:
JdbcReadModelRepositorynow grants a read-only delegated task exception inside company/task read-model scope checks when the current app user is the cached Jira assignee of an activereply_pilot_tasklinked to that company. - Evidence: the delegated exception does not change mutation authorization;
it excludes unrelated companies, unassigned delegated tasks, and
Donedelegated tasks. - Evidence: task read paths use task-level delegated scope, so company access from one active assigned delegated task does not expose other delegated tasks on the same company.
- Evidence:
docs/permissions.md,docs/backend.md,docs/database.md, anddocs/refactoring-context.mdrecord the read-only delegated visibility rule and the remaining search alignment boundary. -
Validation:
mvn -f reply-pilot-be/pom.xml -Dtest=JdbcReadModelRepositoryTest testpassed with 6 tests;mvn -f reply-pilot-be/pom.xml verifypassed with 208 tests;mkdocs build --strictandgit diff --checkpassed. -
[x] 27.3 Extend backend Jira sync and read models for delegated tasks.
- Evidence:
JiraTaskSyncServicenow includes configuredReply Pilot Taskissues in task-sync JQL, maps them to local work typereply_pilot_task, copies assignee/status/summary/description as before, and normalizes blank delegated task type values toGeneral. - Evidence:
HttpJiraIssueClientrequests configuredJIRA_REPLY_PILOT_TASK_TYPE_CUSTOM_FIELD_ID(defaultcustomfield_10269) in search/detail reads and parses Jira dropdown objects/lists intoJiraIssue.replyPilotTaskTypeValue. - Evidence:
JdbcJiraTaskSyncRepositoryupsertstask_jira_reply_pilot_task.task_type_valueforreply_pilot_taskrows and removes stale subtype rows if an issue is no longer a delegated task. - Evidence: backend task list/detail/company-task read models expose
task_type_value; the default task list hidesDonedelegated tasks unless the request explicitly filters by status or Jira key, while explicit lookup and company task lists remain discoverable. - Evidence:
/api/meta//api/jira/meta,.env.example,reply-pilot-be/README.md, anddocs/backend.mddocument the delegated task issue type and custom-field configuration. Existing read-only Jira comments remain available through the existing Jira comments API for the UI component slice. -
Validation:
mvn -f reply-pilot-be/pom.xml -Dtest='JiraTaskSyncServiceTest,JdbcJiraTaskSyncRepositoryTest,JdbcReadModelRepositoryTest,JiraProxyControllerTest,HttpJiraIssueClientTest,HealthControllerTest' testpassed with 23 tests;mvn -f reply-pilot-be/pom.xml verifypassed with 207 tests;mkdocs build --strictandgit diff --checkpassed. -
[x] 27.1 Finalize delegated Jira task contract.
- Evidence: user confirmed Jira issue type
Reply Pilot Taskwith admin edit URLhttps://internet-handel.atlassian.net/secure/admin/EditIssueType!default.jspa?id=10206. - Evidence: user confirmed Jira taskType field
customfield_10269and optionsGeneral,Meeting organization, andRegistration (B2B). - Evidence: user confirmed company link appears in Jira description while
Reply Pilot DB company id is workflow/authorization source of truth;
requester is the Reply Pilot app user who created the task; resolve text is
stored in Jira description; Jira comments remain read-only through the
existing comment component;
Doneis hidden only from default task view. -
Validation:
mkdocs build --strictpassed. -
[x] 26.1 Finish Java backend runtime cutover validation.
- Evidence: Docker Desktop was started locally and
docker build -t reply-pilot-be:runtime-cutover-check reply-pilot-besuccessfully built the Java 21 Spring Boot runtime image fromreply-pilot-be/Dockerfile. - Evidence: the previously running
reply-pilot-becontainer was still the old Gunicorn/Python runtime, so Compose was rebuilt and force-recreated from the current Java Dockerfile withHOST_UID=$(id -u) HOST_GID=$(id -g) docker compose up --build -d --force-recreateinreply-pilot-be. - Evidence: the recreated container runs
java -jar /app/reply-pilot-be.jaras non-root host user501:20, Docker reports it healthy, Spring Boot logs show Java 21.0.11 on Tomcat port 5000, andcurl -fsS http://127.0.0.1:9091/healthzreturned{"status":"ok"}. -
Validation:
mvn -f reply-pilot-be/pom.xml clean verifypassed with 169 Java tests;python3 -m pytest reply-pilot-app/testspassed with 213 tests;script/check-architecture.sh,mkdocs build --strict, andgit diff --checkpassed. -
[x] 26.2 Remove legacy Python backend implementation and test harness.
- Evidence: removed the legacy backend package
reply-pilot-be/reply_pilot_be, deletedreply-pilot-be/tests, and removed Python-only backend runtime leftovers:gunicorn.conf.py,requirements.txt,requirements-dev.txt,scripts/be-generate-gmail-token.py, andscripts/repair-contaminated-lead-import-party.py. - Evidence: backend documentation now records Java 21 Spring Boot/Maven/JUnit
as the maintained backend implementation and validation surface. The app
README points Gmail OAuth token generation to
reply-pilot-gmail/scripts/gmail-generate-token.sh. - Evidence:
reports/architecture-report.mdnow listsreply-pilot-bewith no Python package, no Python production files, no Python test files, no Python script files, and no Python runtime/dev dependencies. -
Validation:
mvn -f reply-pilot-be/pom.xml clean verifypassed with 169 Java tests;python3 -m pytest reply-pilot-app/testspassed with 213 tests;script/check-architecture.sh,mkdocs build --strict, andgit diff --checkpassed. A working-tree scan for.pyfiles underreply-pilot-beoutside build/data/log paths returned no files. -
[x] 25.10 Resolve Java lead-import outbound email transaction boundary.
- Evidence:
LeadImportServicenow sends the optional outbound Gmail message and passes the resultingCachedEmailItemtoLeadImportRepository.markImportedinstead of importing activity in a separate repository transaction. - Evidence:
JdbcLeadImportRepository.markImportedlinks the sent recipient email contact to the imported party, callsActivityEmailImportRepository.importEmails(Connection, ...), creates the lead-import activity note, marks the lead item imported, and refreshes batch progress in one caller-owned DB transaction. - Evidence:
JdbcActivityEmailImportRepositorynow supports transactional email import on a supplied JDBC connection and rejects automatic reply-task side effects in that mode, because those task side effects are post-commit behavior for incoming supplier replies. - Evidence: direct JDBC tests cover the successful transaction, recipient contact linking, no non-transactional importer call, and rollback of the imported sent email marker/contact link/item/batch updates when the transactional importer fails.
- Evidence: the touched outreach/contact upsert paths are JDBC-portable and savepoint-guarded so duplicate-key races do not leave a PostgreSQL transaction aborted before fallback update.
-
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest=JdbcLeadImportRepositoryTest,LeadImportServiceTest,JdbcActivityEmailImportRepositoryTest testpassed with 10 tests. Full Java validationmvn -f reply-pilot-be/pom.xml clean verifypassed with 169 tests. Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests; app validationpython3 -m pytest reply-pilot-app/testspassed with 213 tests. Architecture and documentation validationscript/check-architecture.shandmkdocs build --strictpassed. Whitespace validationgit diff --checkand a direct trailing-whitespace scan of Java/doc files passed. -
[x] 25.9 Port concrete historical requirement backfill worker bean to Java.
- Evidence: Java now provides
JdbcRequirementBackfillWorkeras the concreteRequirementBackfillWorkerbean behindPOST /api/requirements/backfill/run. It mirrors the Python party selection modes forparty_id,after_party_id, and service-levelparty_idsfan-out, selects incoming email threads for active organization parties, and preserves the existing backfill result payload keys. - Evidence: the worker first synchronizes cached email thread artifacts through
ActivityEmailImportRepository.syncEmailArtifacts; when cache lookup misses or cached artifact sync fails, it falls back to DB-derived thread messages and DB-derived attachments without inserting duplicate base email activity rows. - Evidence: Java AI classification and requirement aggregation workers now expose party-scoped processing methods for backfill. The scoped aggregation path claims only selected-party queue rows and intentionally skips the global ruleset-version full rebuild.
- Evidence: direct JDBC tests cover paged historical backfill with one cached thread and one DB fallback thread, empty target selection, explicit DB fallback attachments in the activity importer, party-scoped AI candidate processing, and party-scoped aggregation queue claiming.
-
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest=JdbcActivityEmailImportRepositoryTest,JdbcRequirementBackfillWorkerTest,JdbcRequirementAiClassificationWorkerTest,JdbcRequirementAggregationWorkerTest testpassed with 18 tests. Full Java validationmvn -f reply-pilot-be/pom.xml clean verifypassed with 167 tests; Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests; app-side validationpython3 -m pytest reply-pilot-app/testspassed with 213 tests. Architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strict,git diff --check, and direct trailing-whitespace scan of touched Java/doc files passed. -
[x] 25.8 Port concrete requirement monitoring/report worker bean to Java.
- Evidence: Java now provides
JdbcRequirementMonitoringWorkeras the concreteRequirementMonitoringWorkerbean behindGET /api/requirements/report. It loads requirement state counts, manual-review quality metrics, AI evidence metrics, and aggregation queue backlog directly from the backendDataSource. - Evidence: the worker mirrors the Python report payload shape, including state-count totals/rows, precision/recall/accuracy/value-match metrics, OpenAI configured/model fields, AI evidence counts, worker status payload, worker error totals, last AI status/detail/update time, and unavailable worker-status reasons.
- Evidence:
RequirementMonitoringSettingsmirrorsWORKER_STATUS_BASE_URLandWORKER_STATUS_TIMEOUT_SECONDS, whileOpenAIClientSettingssupplies the AI configured/model fields. - Evidence: direct JDBC tests cover DB-derived report metrics with a local
/statuszHTTP server and the not-configured worker-status fallback. -
Validation: full Java validation
mvn -f reply-pilot-be/pom.xml clean verifypassed with 163 tests; Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests. Architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.7 Port concrete requirement aggregation worker bean to Java.
- Evidence: Java now provides
JdbcRequirementAggregationWorkeras the concreteRequirementAggregationWorkerbean behindPOST /api/requirements/evaluate/run. It claims dueparty_requirement_eval_queuerows, uses PostgreSQLFOR UPDATE SKIP LOCKEDwhere available, increments attempts, and keeps H2-compatible SQL for focused JDBC tests. - Evidence: the worker ports the Python aggregation rules for
ENLISTMENT_TABLE,FEED, andSUPPLIER_IDENTIFIER, including source bonuses, confidence thresholds, competing candidate review states, final feed URL/delivery-mode projection, supplier identifier projection, fulfilled timestamp handling, andparty_requirement_stateupsert without overwriting manual overrides. - Evidence: per-item failures roll back to a savepoint, defer only the failed
queue row, and continue the batch.
RequirementAggregationSettingsmirrors the existingAPP_DATA_DIR,REQUIREMENT_EVAL_LOCK_TTL_SECONDS,REQUIREMENT_AGGREGATION_RULESET_VERSION, andREQUIREMENT_RULESET_STATE_FILEbehavior, including one-time full-company requeue when the aggregation ruleset version changes. - Evidence: direct JDBC tests cover successful
FEEDaggregation, manual override preservation, unsupported requirement deferral with continuation, and ruleset-version rebuild queueing/state-file persistence. -
Validation: full Java validation
mvn -f reply-pilot-be/pom.xml clean verifypassed with 161 tests; Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests. Architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.6 Port concrete AI requirement classification worker bean to Java.
- Evidence: Java now provides
JdbcRequirementAiClassificationWorkeras the concreteRequirementAiClassificationWorkerbean. It scans deterministic candidates forENLISTMENT_TABLE,FEED, andSUPPLIER_IDENTIFIER, skips candidates already materialized for the currentREQUIREMENT_AI_PROMPT_VERSION, loads email/source context, calls the structured OpenAI JSON client with the committed schema payload, validates the fixed AI payload shape, replaces AI-created evidence/fact rows, and enqueuesparty_requirement_eval_queuework after successful writes. - Evidence:
RequirementAiClassificationSettingsmirrors the existingAPP_DATA_DIR,REQUIREMENT_AI_SCHEMA_DIR, andREQUIREMENT_AI_PROMPT_VERSIONconfiguration.HttpOpenAIJsonClientnow supports input files for JSON-schema calls so attachment-backed classifications can upload cached files like the Python reference path. - Evidence: direct JDBC tests cover successful
FEEDclassification with an attachment input file,SUPPLIER_IDENTIFIERfact materialization without writing requirement evidence rows, and invalid AI payload handling without partial DB writes. -
Validation: full Java validation
mvn -f reply-pilot-be/pom.xml clean verifypassed with 157 tests; Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests. Architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.5 Replace the historical CME matching worker with Java full sync.
- Evidence:
JdbcCmeCompanySyncWorkerimports onlydodavatelandosloveni_dodavateleintoparty_cme_source; the old queue, match read model, endpoint, worker job, and configuration were removed. -
Validation: full Java validation
mvn -f reply-pilot-be/pom.xml clean verifypassed with 153 tests; Python backend compatibility validationpython3 -m pytest reply-pilot-be/testspassed with 227 tests. Architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.4 Port automatic incoming supplier reply task creation to Java.
- Evidence:
TaskMutationServicenow implements the domainEmailThreadReplyTaskAutomationport. For incoming messages without an existing thread task, it createsEmail Thread ReplyJira issues, uses the Python source priority for default assignee resolution (party_organization,app_configuration, configured Jira account, configured assignee email, otherwise unassigned), transitions new issues toDrafting Reply, storestask_jira_email_thread_reply, and backfills the app task link into Jira. - Evidence: existing thread tasks found by
task_jira_email_thread_reply.external_thread_idare transitioned toDrafting Replythrough the same automation port instead of creating a duplicate task. - Evidence:
JdbcActivityEmailImportRepositorynow collects automatic reply task requests only when a newly imported incoming external message did not update any linked reply task and resolved to exactly one supplier company. It runs the automation after the email-import transaction commits, so the Java task service can read company rows created by the import while failed Jira automation still does not roll back the email import. - Evidence:
docs/backend.mdnow records the actual Python/Java behavior and removes the stale claim that automatic reply-task creation depends on a Supplier Onboarding task. -
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest='JdbcActivityEmailImportRepositoryTest,TaskMutationServiceTest,ArchitectureRulesTest' testpassed; clean full Java validationmvn -f reply-pilot-be/pom.xml clean verifypassed; full Python backend validationpython3 -m pytest reply-pilot-be/testspassed; architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.3 Port linked incoming-reply Jira task transition to Java activity importer.
- Evidence:
JdbcActivityEmailImportRepositorynow receivesJiraIssueClientand, when a newly imported incoming external email belongs to an existingtask_jira_email_thread_replylink, transitions each linkedemail_thread_replyJira issue toDrafting Replyand syncstask_jira.status. - Evidence: the Java importer preserves the Python fallback where a failed
transition still updates local status if a subsequent Jira issue load shows
the issue is already in
Drafting Reply. - Evidence: this item intentionally covers linked-task status automation only. Automatic creation of a new Email Thread Reply task for an unlinked incoming supplier reply remains in the unfinished audit.
-
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest='JdbcActivityEmailImportRepositoryTest,TaskMutationServiceTest,ArchitectureRulesTest' testpassed; clean full Java validationmvn -f reply-pilot-be/pom.xml clean verifypassed; full Python backend validationpython3 -m pytest reply-pilot-be/testspassed; architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.2 Port deterministic requirement/fact extraction to Java activity importer.
- Evidence:
JdbcActivityEmailImportRepositorynow mirrors the deterministic incoming-email extraction layer for non-internal senders. It replaces its owndeterministic-email-parserrows inparty_feed_factandparty_supplier_identifier_fact, replaces its owndeterministic-enlistment-detectorrows inparty_requirement_evidence, and queuesENLISTMENT_TABLE,FEED, andSUPPLIER_IDENTIFIERaggregation work inparty_requirement_eval_queue. - Evidence: the Java detector writes explicit feed URL facts, labeled
IČO/DIČ/GLN/vendor-code identifier facts, and deterministic enlistment-table
evidence from matching spreadsheet attachment filenames or body markers.
While porting the DIČ detector, Java intentionally avoids the Python regex
bug where
\scan consume the next email line into the VAT value. - Evidence: H2 repository coverage verifies deterministic fact/evidence insertion, aggregation queueing, and idempotent replacement on reimport of the same Gmail message.
-
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest='JdbcActivityEmailImportRepositoryTest,ArchitectureRulesTest' testpassed; full Java validationmvn -f reply-pilot-be/pom.xml verifypassed; full Python backend validationpython3 -m pytest reply-pilot-be/testspassed; architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 25.1 Add production Java JDBC activity-email import repository.
- Evidence: Java now provides
JdbcActivityEmailImportRepositoryas the production SpringActivityEmailImportRepositoryimplementation. It transactionally imports cached email thread messages intoactivity,activity_email,activity_participant, contact mechanism and party identity rows, and synchronizesactivity_email_attachmentandactivity_email_linkrows from the backend email cache. - Evidence: the adapter supports idempotent message updates by
provider = 'gmail'andexternal_message_id, replaces participants and artifacts on re-import, and implementssyncEmailArtifactsfor the mailbox attachment phase. - Evidence: this item intentionally covered base activity-email/artifact
persistence only. Deterministic requirement/fact extraction is covered by
later item 25.2; incoming-reply Jira automation from Python
ActivityImportStoreremains in the unfinished audit. -
Validation: focused Java validation
mvn -f reply-pilot-be/pom.xml -Dtest='JdbcActivityEmailImportRepositoryTest,ArchitectureRulesTest' testpassed; full Java validationmvn -f reply-pilot-be/pom.xml verifypassed; full Python backend validationpython3 -m pytest reply-pilot-be/testspassed; architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 24.21 Final Java endpoint parity audit.
- Evidence: Java now includes the previously missing
POST /api/emails/{emailId}/debug/generateroute throughDebugReplyController; it preserves instruction validation, missing email handling, selected context normalization, attachment file inclusion, OpenAI text generation, response envelope, andreply_subjectbehavior. - Evidence:
reply-pilot-be/tests/test_java_route_parity.pyparses Flaskapi_bpdecorators and Java Spring controller mappings and verifies all 112 current Flask backend JSON/health routes are represented by Java controllers. The test expands Java's email-thread catch-all dispatch route and records the requirement-review path-shape equivalence for current single-segment requirement codes. - Evidence:
docs/backend.mdnow records the Java endpoint parity audit, family-level route inventory, route-shape notes, and remaining non-endpoint migration caveats. No endpoint-level deferrals remain in the audit. -
Validation: focused Java tests
mvn -f reply-pilot-be/pom.xml -Dtest='DebugReplyControllerTest,AiDraftControllerTest,HttpOpenAIJsonClientTest' testpassed; focused route/compatibility validationpython3 -m pytest reply-pilot-be/tests/test_java_route_parity.py reply-pilot-be/tests/test_api_compatibility.py -qpassed; full Java validationmvn -f reply-pilot-be/pom.xml verifypassed; full Python backend validationpython3 -m pytest reply-pilot-be/testspassed; architecture validationpython3 script/architecture-report.py --output reports/architecture-report.mdandscript/check-architecture.shpassed; documentation and whitespace validationmkdocs build --strictandgit diff --checkpassed. -
[x] 24.20 Port worker job endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/cme/company-sync/run,POST /api/requirements/ai-classify/run,POST /api/requirements/evaluate/run,POST /api/requirements/backfill/run, andGET /api/requirements/reportthroughWorkerJobController,WorkerJobService, and typed domain worker ports. - Evidence: the Java endpoint behavior preserves compatible limit parsing,
party_id/after_party_id/party_idsparsing, multi-party backfill result merging, success envelopes, not-configured503responses, expected worker400responses, and unexpected worker500error prefixes. - Evidence: CME sync, requirement AI, requirement aggregation, or historical backfill worker bean is claimed by this item. Without a configured port bean, the new endpoints intentionally return the same not-configured errors as the Flask reference.
-
Validation: focused Java tests
mvn -f reply-pilot-be/pom.xml -Dtest='WorkerJobControllerTest' testpassed; full Java validationmvn -f reply-pilot-be/pom.xml verifypassed; Flask compatibility validationpython3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -qpassed; docs validationmkdocs build --strictandgit diff --checkpassed. -
[x] 24.19 Port lead import endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/lead-imports/files,POST /api/lead-imports/batches,GET /api/lead-imports/batches/{batchId},GET /api/lead-imports/batches/{batchId}/items/{itemId},POST /api/lead-imports/batches/{batchId}/items/{itemId}/actions, andPOST /api/lead-imports/batches/{batchId}/items/{itemId}/draft. - Evidence: the Java lead import service handles server-side JSONL inbox to
processing/done/failed file movement, batch/item persistence, pending-item
draft generation through
OPENAI_MODEL_LEAD_IMPORT, prompt id/snapshot storage, item errors, skip/do-not-contact/import actions, party/contact upsert, outreach policy, activity notes, Jira Supplier Onboarding task creation with lead-import labels, optional outbound Gmail send, and compatible validation/error envelopes. - Evidence: outbound email activity recording uses the existing Java
ActivityEmailImportRepositoryhook when configured; it is not a single DB transaction with lead item update, and the production JDBC activity importer remains tracked in the unfinished audit. -
Validation: focused Java tests
mvn -f reply-pilot-be/pom.xml test -Dtest=LeadImportControllerTest,LeadImportServiceTestpassed; full Java validationmvn -f reply-pilot-be/pom.xml verifypassed; Flask compatibility validationpython3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -qpassed. -
[x] 24.18 Port email draft/send endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/emails/drafts/outbound,POST /api/emails/{emailId}/drafts/reply,POST /api/emails/send/outbound, andPOST /api/emails/{emailId}/send/replywith compatible JSON payloads, multipart reply payloads, validation messages, missing-email handling, response envelopes, and attachment count/size limits. - Evidence: the Gmail HTTP adapter now forwards outbound/reply draft and
send calls to
reply-pilot-gmail, resolvesreply_to_message_idfrom thread history, and emits multipart form-data for reply attachments. - Evidence: successful reply sends update the local email cache immediately;
sent replies and outbound sends call the pluggable
ActivityEmailImportRepositorywhen present. This slice does not add the production JDBC activity-email importer, so DB contact-history persistence remains tracked in the unfinished audit. - Evidence: compatibility harness now covers email draft/send endpoint contracts, including multipart attachment limit behavior, against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='EmailSendControllerTest,HttpGmailServiceEmailSyncClientTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 24.17 Port mailbox watch/import endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/mailbox/watch,POST /api/mailbox/watch/ensure,POST /api/mailbox/watch/stop,POST /api/mailbox/watch/notify,POST /api/mailbox/watch/pull,POST /api/mailbox/sync/full,POST /api/mailbox/import,GET /api/mailbox/import/status, andPOST /api/mailbox/import/stepwith compatible envelopes, status codes, notification history comparison, full-sync alias behavior, and rate-limit JSON fields. - Evidence: the Gmail HTTP adapter now forwards watch status/ensure/stop, notification record/pull/ack calls, maps 429 retry metadata, and preserves the existing email-cache sync bridge.
- Evidence: mailbox import status/state is persisted through
mailbox-import-status.json,mailbox-import-state.json, and a process lock; import orchestration is pluggable throughActivityEmailImportRepository. This slice does not add the production JDBC activity-email importer, so database artifact import remains an explicit unfinished audit item instead of a hidden success. - Evidence: compatibility harness now covers mailbox watch/import endpoint contracts against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='MailboxControllerTest,HttpGmailServiceEmailSyncClientTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 24.16 Port inbox, email, and attachment read endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/inbox,GET /api/emails,GET /api/emails/{emailId},GET /api/emails/{emailId}/attachments, andGET /api/emails/{emailId}/attachments/{filename}with compatible inbox pagination, email list filtering, detail payloads, attachment metadata, binary attachment serving, JSON error envelopes, and file-cache path safety. - Evidence: Java now reads the existing email cache layout under
data/emails, preserves legacyemails.json, computes attachment content hashes, preserves remote page cursors during single-thread refreshes, and adds a Gmail-service HTTP sync adapter for read-side detail fetch, snapshot page fetch, and attachment refresh behavior. - Evidence: compatibility harness now covers inbox/list/detail/attachment metadata/binary download behavior plus missing email and missing attachment errors against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='EmailReadControllerTest,HttpGmailServiceEmailSyncClientTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 24.15 Port AI draft generation endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/supplier-onboarding/draftandPOST /api/email-thread-task/draftwith compatible success envelopes, OpenAI JSON schema requests, supplier lead-import model selection, validation messages, empty-AI-response handling, and OpenAI client error mapping. - Evidence: the Java OpenAI JSON client now supports
/responsesJSON schema calls with stricttext.format, parsed-output extraction, output-text JSON fallback, model/response-id propagation, and missing-key/upstream error handling. - Evidence: compatibility harness now covers supplier onboarding and email-thread task draft success payloads plus missing company/thread context validation against the current Flask reference backend.
-
Validation: full Java validation passed:
mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 24.14 Port Jira proxy and task-sync endpoints to Java.
- Evidence: Java Spring Boot now implements Jira proxy endpoints for
GET /api/jira/myself,GET /api/jira/users/by-email,GET /api/jira/issues/{issueKey},PUT /api/jira/issues/{issueKey},GET /api/jira/issues/{issueKey}/transitions,POST /api/jira/issues/{issueKey}/transition,GET /api/jira/issues/{issueKey}/comments,POST /api/jira/issues/{issueKey}/comments,POST /api/jira/issues/{issueKey}/unassign,GET /api/jira/issues/search,GET /api/jira/issues/assigned,GET /api/jira/issues/stale,POST /api/jira/issues, andPOST /api/jira/task-sync/runwith compatible request parsing, response envelopes, Jira upstream error mapping, validation messages, pagination token handling, and environment-label behavior. - Evidence: Java task-sync service now builds full/incremental Jira JQL, honors safety overlap, maps Jira assignees to app users, writes Jira task cache fields and sync state through JDBC, and records skipped/failed runs.
- Evidence: compatibility harness now covers Jira user lookup, issue detail, transitions, comments, search/assigned/stale lists, create/update, transition/unassign, task-sync run, and validation behavior against the current Flask reference backend.
-
Validation: full Java validation passed:
mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 24.13 Port task and email-thread reply mutation endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/companies/{partyId}/supplier-onboarding-tasks,PATCH /api/tasks/{taskId}/company,POST /api/tasks/{taskId}/email-threads,PATCH /api/tasks/{taskId}/supplier-onboarding,PATCH /api/tasks/{taskId}/email-thread-reply,POST /api/tasks/{taskId}/refresh-from-jira,POST /api/tasks/{taskId}/supplier-onboarding/reply,POST /api/tasks/{taskId}/move-reply-to-waiting, andPOST /api/email-threads/{emailId}/reply-taskswith compatible response envelopes, duplicate reply-task conflicts, validation/error mapping, Jira issue/user/transition/comment calls, app task links, assignee profile sync, and JDBC writes totask,task_jira,task_jira_supplier_onboarding, andtask_jira_email_thread_reply. - Evidence: compatibility harness now covers supplier-onboarding task creation, task company/thread links, supplier-onboarding updates/replies, email-thread reply updates, Jira refresh, move-to-waiting, manual email-thread reply task creation, and duplicate-thread conflict behavior against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='TaskMutationServiceTest,TaskMutationControllerTest,JdbcTaskMutationRepositoryTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q. -
[x] 01. Lock intended boundary.
-
Evidence:
docs/module-boundaries.mddocuments target boundary, allowed transitional calls, and app DB/Gmail/OpenAI debt. -
[x] 02. Inventory AI prompt usage.
-
Evidence: current prompt routes, workflows, templates, prompt types, payload fields, tests, and backend endpoint needs were captured before implementation.
-
[x] 03. Add backend AI prompt API.
-
Evidence: backend prompt service/workflow/routes and API tests were added.
-
[x] 04. Switch app AI prompt UI to backend.
-
Evidence: app prompt CRUD uses backend-backed prompt store when
BACKEND_API_BASE_URLis configured. -
[x] 05. Migrate reply draft storage.
-
Evidence: backend reply-draft API exists and app uses
HttpReplyDraftStorein standard runtime. -
[x] 06. Migrate user profile storage.
-
Evidence: user profile reads/writes go through backend app-state endpoints via
HttpUserProfileStore. -
[x] 07. Migrate app configuration and Jira assignee mapping.
-
Evidence: app configuration and Jira assignable-user/profile mapping go through backend app-state endpoints via HTTP adapters.
-
[x] 08. Inventory read models before moving them.
-
Evidence: route/workflow reads, template fields, backend endpoint gaps, and safe migration order were captured before implementation.
-
[x] 09. Move company and person read pages.
-
Evidence: company/person list and detail read paths are backend-backed in standard runtime.
-
[x] 10. Move activity and email detail read pages.
-
Evidence: activity/email detail and related company-by-thread reads are backend-backed in standard runtime.
-
[x] 11. Move task read pages.
-
Evidence: task list/detail and related task lookup reads are backend-backed in standard runtime.
-
[x] 12. Inventory mutations.
-
Evidence: mutation inventory classified existing backend-backed flows, remaining task-store debt, backend endpoint gaps, and safe migration order.
-
[x] 13. Move company/person/contact mutations.
-
Evidence: company update, company merge, visibility, and requirement review use
record_mutation_client.pyand backend/api/companies/...mutation endpoints. Person/contact/identifier/link mutations use backend clients. -
[x] 14. Move task and Jira mutations.
-
Evidence: supplier onboarding task creation/editing, task company selection, email-thread reply task creation/editing, email-thread linking, supplier onboarding replies, Jira refresh, and reply-task status movement delegate to backend email/task mutation APIs.
-
[x] 15. Remove app email/Gmail/OpenAI fallback production code.
- Evidence:
reply-pilot-appno longer containsgmail_store.pyoropenai_client.py; app factory requiresBACKEND_API_BASE_URLoutside tests, builds backend-backed draft/debug clients in standard runtime, and no longer wires anopenai_clientextension. Reply debug generation delegates to backend email service APIs. Test-only local email cache behavior remains isolated to generic cache-service fixtures. -
Validation:
python3 -m pytest reply-pilot-app/testspassed;python3 -m pytest reply-pilot-be/testspassed;script/check-architecture.shpassed after regeneratingreports/architecture-report.md;mkdocs build --strictpassed. -
[x] 16. Move simple-auth nonce persistence.
-
Evidence: app owns browser auth/session flow, but used nonce persistence is backend-backed through
POST /api/simple-auth/nonces/checkandPOST /api/simple-auth/nonces/mark-used. -
[x] 17. Move search behind backend.
- Evidence: backend exposes app-facing
GET /api/searchand delegates toreply-pilot-searchthrough a backend search client. App search client now usesBACKEND_API_BASE_URL; app runtime config, Compose, and scripts no longer exposeSEARCH_API_BASE_URLorSEARCH_TIMEOUT_SECONDS. Boundary docs and PlantUML diagrams now showreply-pilot-app->reply-pilot-be->reply-pilot-search. -
Validation: focused backend proxy tests
python3 -m pytest reply-pilot-be/tests/test_api.py -q -k "api_search"passed; focused app search/create-app testspython3 -m pytest reply-pilot-app/tests/test_app.py -q -k "search_page or create_app_uses_backend_email_thread_task_draft_client_when_backend_configured"passed. Full validation also passed:python3 -m pytest reply-pilot-app/tests;python3 -m pytest reply-pilot-be/tests;script/check-architecture.sh;mkdocs build --strict. -
[x] 18. Remove remaining app DB/runtime dependency debt.
- Evidence: app
directory_store,record_store, andtask_storeno longer contain PostgreSQL-backed production store classes orpsycopgimports. Standard app runtime requires backend HTTP read/task APIs;task_storewiring is test-only.reply-pilot-appno longer declarespsycopg, exposesREPLY_PILOT_DB_*runtime config, or documents app-to-DB runtime access. -
Validation:
python3 -m pytest reply-pilot-app/tests -qpassed;python3 -m pytest reply-pilot-be/tests -qpassed;python3 script/architecture-report.py --output reports/architecture-report.mdregenerated the baseline;script/check-architecture.shpassed;mkdocs build --strictpassed. -
[x] 19.1-19.7 Close Python backend route-map cleanup as obsolete.
- Evidence: on 2026-06-12, project direction changed: further Python Flask backend route-map refactoring is no longer needed because Java backend endpoint parity is the active migration path and Python backend code will be removed later. This closes slices 19.1-19.7 for lead import, inbox/email/attachment, mailbox, CME/requirement worker, email draft/send, debug reply generation, and final Flask route-map audit.
- Evidence: this is not an implementation-complete claim for the Flask route
cleanup. Remaining Python
views.pyimplementation logic is intentionally left in place until later Python backend removal. -
Validation: tracker/context-only change;
mkdocs build --strictandgit diff --checkpassed. -
[x] 20. Align refactoring ledger with the
refactoring-planskill. - Evidence: active ledger moved to
docs/refactoring.md; rootrefactoring.mdis only a compatibility pointer; active tasks use checkbox status, stable IDs, concise prompt-free entries, unfinished audit, maintenance items, and done archive. -
Validation:
mkdocs build --strict,script/check-architecture.sh, andgit diff --check -- refactoring.md docs/refactoring.md mkdocs.ymlpassed. -
[x] 21. Apply updated prompt-free
refactoring-planledger rules and re-audit item statuses. - Evidence: open items 15, 17, 18, and 19 were re-audited against current code and documentation; full prompt blocks were removed from active ledger entries; current statuses remain open as documented.
-
Validation:
mkdocs build --strict,script/check-architecture.sh, andgit diff --check -- docs/refactoring.md refactoring.md mkdocs.ymlpassed. -
[x] 22. Split refactoring plan into context and tracker documents.
- Evidence:
docs/refactoring-context.mdnow owns target boundary, source-of-truth docs, guardrails, and validation gates;docs/refactoring-tracker.mdnow owns open items, maintenance items, unfinished audit, and done archive. Legacy refactoring files are pointers. -
Validation:
mkdocs build --strictpassed;script/check-architecture.shpassed; diff whitespace check for the refactoring docs andmkdocs.ymlpassed. -
[x] 24.1 Port AI prompt endpoints to Java.
- Evidence: Java Spring Boot implements
GET /api/ai-prompt-types,GET /api/ai-prompts,GET /api/ai-prompts/{promptId},POST /api/ai-prompts,PATCH /api/ai-prompts/{promptId}, andDELETE /api/ai-prompts/{promptId}with DTOs, service validation, not-found handling, and JDBC persistence against the existingai_prompt_typeandai_prompttables. - Evidence: Python compatibility harness now covers AI prompt JSON payloads,
status codes, validation messages, legacy
prompt_textinput, filtering, update, delete, and missing-resource behavior against the Flask reference backend. -
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='AiPromptControllerTest,AiPromptServiceTest,JdbcAiPromptRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests; architecture report was regenerated andscript/check-architecture.shpassed. -
[x] 24.2 Restore Java migration tracker before the next endpoint slice.
- Evidence: active tracker now lists remaining Java Spring Boot endpoint
migration slices 24.3-24.21 based on
docs/backend.md, Flask route decorators inreply-pilot-be/reply_pilot_be/views.py, current compatibility harness coverage, and existing Java code underreply-pilot-be/src. - Evidence: the unfinished audit now states the actual migration state:
/healthzand AI prompt endpoints are implemented in Java; all other endpoint families require focused ports with compatibility contracts. -
Validation:
mkdocs build --strictpassed; diff whitespace check fordocs/refactoring-context.mdanddocs/refactoring-tracker.mdpassed. -
[x] 24.3 Port backend status and metadata endpoints to Java.
- Evidence: Java Spring Boot now implements
/api/metaand/api/jira/metafromBackendMetadataSettingswith compatible status, email, OpenAI, Jira, CME, lead-import, andemail_import_status: nullfields./healthzremains compatible through the same controller. - Evidence: compatibility harness now checks
/api/jira/metaagainst the current Flask reference, and Java tests cover default metadata plus configured Jira/OpenAI/email-import metadata. -
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='HealthControllerTest,BackendMetadataSettingsTest,BackendRuntimeSettingsTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.4 Port reply draft storage endpoints to Java.
- Evidence: Java Spring Boot now implements
GET/POST /api/reply-draftsandGET/PATCH/DELETE /api/reply-drafts/{draftId}with service validation, not-found handling, HTTP status mapping, and JDBC persistence againstpublic.email_replay_draft. - Evidence: compatibility harness now covers reply draft create, list, detail, update, validation errors, missing update/delete, delete, and missing detail against the current Flask reference.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='ReplyDraftControllerTest,ReplyDraftServiceTest,JdbcReplyDraftRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.5 Port app-state and Jira assignee storage endpoints to Java.
- Evidence: Java Spring Boot now implements
GET/POST /api/user-profiles,GET/PATCH /api/user-profiles/{loginKey},GET/PATCH /api/app-configuration,GET /api/jira/assignees,GET /api/jira/assignees/{appUserId}/account-id, andPUT /api/jira/assignees/{appUserId}with compatible JSON payloads, validation messages, not-found handling, DB-connectivity error mapping, and JDBC persistence againstpublic.app_user,public.app_configuration, andpublic.app_user_jira_profile. - Evidence: compatibility harness now covers user profile create/list/detail and Gmal box link update, app configuration load/update, Jira assignable list, Jira account-id lookup, Jira profile upsert, missing profile, and app configuration validation against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='AppStateControllerTest,AppStateServiceTest,JdbcAppStateRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.6 Port simple-auth nonce endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/simple-auth/nonces/checkandPOST /api/simple-auth/nonces/mark-usedwith compatibleused/markedJSON payloads, validation messages, DB-connectivity error mapping, duplicate-mark handling, and expiry purge semantics againstpublic.simple_auth_nonce. - Evidence: compatibility harness now covers initial check, mark-used,
duplicate mark, expiry boundary behavior, missing nonce, invalid
now_ts, missingexpires_at, and non-positiveexpires_atagainst the current Flask reference backend. -
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='SimpleAuthNonceControllerTest,SimpleAuthNonceServiceTest,JdbcSimpleAuthNonceRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.7 Port backend search proxy endpoint to Java.
- Evidence: Java Spring Boot now implements
GET /api/search, normalizesq,type, andlimit, caps limit at 50, forwards requests toreply-pilot-searchthroughHttpSearchClientusingSEARCH_API_BASE_URLandSEARCH_TIMEOUT_SECONDS, preserves successful search JSON payloads, and maps search-client failures to HTTP 503 error payloads. - Evidence: compatibility harness now covers search success and search-client failure against the current Flask reference backend with a fake search client.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='SearchControllerTest,SearchServiceTest,HttpSearchClientTest,SearchClientSettingsTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.8 Port company and person read-model endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/companies,GET /api/companies/{partyId},GET /api/companies/{partyId}/tasks,GET /api/people,GET /api/people/{partyId}, andGET /api/parties/{partyId}/activitieswith compatible pagination, filtering, nested company/person detail payloads, not-found messages, company tasks, party activity lists, and JDBC persistence against the existing party/activity/task tables. - Evidence: compatibility harness now covers company/person list and detail, company tasks, party activities, and missing company/person responses against the current Flask reference backend with in-memory read-model stores.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='ReadModelControllerTest,ReadModelServiceTest,JdbcReadModelRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed. -
[x] 24.9 Port activity, email-thread, and contact read-model endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/activity-emails,GET /api/activity-emails/{activityId},GET /api/contact-methods/{contactMechId},GET /api/activities/{activityId},GET /api/email-threads/{externalThreadId}/companies,GET /api/email-threads/{externalThreadId}/requirements,GET /api/contact-emails/companies, andPOST /api/company-mentionswith compatible JSON payloads, not-found messages, contact-email parsing, slash-capable email-thread id routing, company mention matching, and JDBC persistence against the existing contact/activity/requirement tables. - Evidence: compatibility harness now covers activity email list/detail, contact detail, activity detail, email-thread company and requirement lookups, contact-email company lookup, company mentions, and missing contact/email/activity responses against the current Flask reference backend with in-memory read-model stores.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='ReadModelControllerTest,ReadModelServiceTest,JdbcReadModelRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed;mkdocs build --strictpassed. -
[x] 24.10 Port task read-model endpoints to Java.
- Evidence: Java Spring Boot now implements
GET /api/tasks,GET /api/tasks/{taskId},GET /api/email-threads/{externalThreadId}/task, andGET /api/email-threads/taskswith compatible pagination, status-code filter normalization including the legacyDrafting Replaytypo, assigned-user filtering through Jira account profile, task detailemail_threads, optional company filter for email-thread task lookup, sorted thread-id-with-task responses, and missing task errors. - Evidence: compatibility harness now covers task list, task detail, email-thread task lookup with and without company filters, unmatched thread task lookup, thread-id batch lookup, and missing task responses against the current Flask reference backend with in-memory read-model stores.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='ReadModelControllerTest,ReadModelServiceTest,JdbcReadModelRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed;mkdocs build --strictpassed. -
[x] 24.11 Port company mutation endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/companies,PATCH /api/companies/{partyId},POST /api/companies/{targetPartyId}/merge,POST /api/companies/{partyId}/visibility, andPOST /api/companies/{partyId}/requirements/{requirementCode}/reviewwith compatible response envelopes, validation messages, duplicate conflict payloads, not-found handling, DB-connectivity error mapping, and JDBC transaction behavior for company create/update/merge/visibility plus manual requirement review facts/state updates. - Evidence: compatibility harness now covers company create success, validation errors, duplicate conflicts, update, missing update, visibility, requirement review, invalid review, merge validation, and merge success against the current Flask reference backend.
-
Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='CompanyMutationControllerTest,CompanyMutationServiceTest,JdbcCompanyMutationRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed;mkdocs build --strictpassed. -
[x] 24.12 Port person/contact/identifier mutation endpoints to Java.
- Evidence: Java Spring Boot now implements
POST /api/people,PATCH /api/people/{partyId},POST /api/parties/{partyId}/contacts,PATCH /api/parties/{partyId}/contacts/{contactMechId},DELETE /api/parties/{partyId}/contacts/{contactMechId},POST /api/companies/{partyId}/identifiers,DELETE /api/companies/{partyId}/identifiers/{identifierId}, andDELETE /api/companies/{companyId}/people/{personId}with compatible response envelopes, validation messages, duplicate/conflict candidate payloads, not-found handling, DB-connectivity error mapping, JDBC transactions, contact soft unlink, identifier delete, and company-personCONTACT_FORunlink behavior. - Evidence: compatibility harness now covers person create/update, validation errors, duplicate person email conflicts, party contact add/update/delete and duplicate-contact conflicts, company identifier add/delete and duplicate conflicts, and company-person unlink success and missing-link errors against the current Flask reference backend.
- Validation: focused Java tests passed:
mvn -f reply-pilot-be/pom.xml -Dtest='PartyMutationServiceTest,PartyMutationControllerTest,JdbcPartyMutationRepositoryTest,ArchitectureRulesTest' test; full Java validation passed:mvn -f reply-pilot-be/pom.xml verify; focused compatibility passed:python3 -m pytest reply-pilot-be/tests/test_api_compatibility.py -q; full backend Python validation passed:python3 -m pytest reply-pilot-be/tests;script/check-architecture.shpassed;mkdocs build --strictpassed.
Latest broad validation recorded before this tracker split:
python3 -m pytest reply-pilot-app/testspassed.python3 -m pytest reply-pilot-be/testspassed.script/check-architecture.shpassed.