# RedmineUP Local Fork Changelog

The installed RedmineUP `redmine_contacts` and `redmine_contacts_helpdesk`
plugins are treated as locally maintained legacy code for this Redmine 3.4.4
environment. Before risky edits, archive the current plugin directories in
`dist/` and record the purpose, touched behavior, and LAN test result here.

## Current Checkpoint

- Baseline:
  - Redmine `3.4.4`
  - `redmine_contacts` `4.1.2 PRO`
  - `redmine_contacts_helpdesk` `3.0.9 PRO`
- Strategic direction:
  - Treat helpdesk/customer data as first-class.
  - Prefer local-fork plugin edits when they unlock safer search/indexing.
  - Keep Redmine request paths independent from external worker/index failures.
- Implemented locally:
  - `redmine_event_outbox` plugin with issue/journal/contact events.
  - Optional helpdesk outbox hooks for `HelpdeskTicket` and `JournalMessage`.
  - Read-only `helpdesk_search/*` JSON endpoints in the local helpdesk fork.
  - Standalone contact CLI and read-only helpdesk export/search CLI.
- LAN deployment status:
  - Helpdesk search routes were deployed and route-loaded successfully on the
    LAN Redmine copy.
  - Short alias/usage routes were added to avoid noisy routing errors during
    manual browser testing.
  - Helpdesk mail import, Helpdesk metadata lookup, default non-email updates,
    and explicit outbound Helpdesk replies are live-smoke-tested through redMCP.
  - Helpdesk outbox/worker validation now has a repeatable live script.
- Next meaningful milestone:
  - Run `validate_helpdesk_outbox_worker.py` after outbox or worker changes,
    then choose the external index target.

## 2026-04-25 - redMCP Native Search, Filtering, And MCP Operations

- Touched areas:
  - `redMCP`
- Purpose:
  - Make Redmine's existing issue filtering and built-in text search explicit
    before adding external search infrastructure.
  - Make redMCP runnable as an MCP server for live client testing.
  - Make the network MCP server easier to debug and restart during local tests.
- Behavior changed:
  - Added `filterIssues()` as a named alias for Redmine's `/issues.json`
    filtering.
  - Added `search()` for Redmine's built-in `/search.json` endpoint.
  - Added `searchIssues()` for issue-only Redmine text search.
  - Added `projects()`, `listProjects()`, and `project()` for Redmine's
    `/projects.json` APIs.
  - Added `users()`, `listUsers()`, `user()`, and `projectMemberships()` for
    Redmine's user and membership APIs.
  - Added `ListQueryNormalizer` so MCP list tools accept friendly paging,
    sorting, status, and date options while preserving raw Redmine
    `filters`/`params` overrides.
  - Added `redMCP/bin/test-query-normalizer.php` for no-network checks of
    Redmine query parameter normalization.
  - Added a shared MCP dispatcher and transport-specific server wrappers.
  - Added `redMCP/bin/redmcp-server.php` for stdio MCP clients.
  - Added `redMCP/bin/redmcp-http-server.php` for bearer-token-protected
    Streamable HTTP network clients on `/mcp`.
  - Added PID/status/stop handling to the HTTP server.
  - Added optional full-argument JSONL debug logging via `--debug-log` or
    `MCP_DEBUG_LOG`.
  - Added recursive credential redaction for MCP tool output and debug logs.
  - Added `redMCP/bin/generate-bearer-token.php`.
  - Both transports expose Redmine project reads, users, project memberships,
    filtering/search, issue CRUD, Helpdesk-aware reads, and explicit Helpdesk
    response tools.
  - Registered all MCP helper commands as Composer `bin` entries.
- LAN test result:
  - `php -l redMCP/app/RedmineClient.php` passed.
  - `php -l redMCP/bin/redmcp-server.php` passed.
  - `php -l redMCP/bin/redmcp-http-server.php` passed.
  - `php -l redMCP/bin/generate-bearer-token.php` passed.
  - `composer validate --working-dir=redMCP` passed; Composer emitted PHP 8.5
    deprecation notices from system Composer dependencies.
  - Live stdio MCP framing test passed for `initialize`, `tools/list`, and
    `tools/call` using `redmine_search_issues` against `fud-helpdesk`.
  - Live Streamable HTTP test passed for authenticated `initialize`,
    `tools/list`, and `tools/call` using `redmine_search_issues`.
  - `redmcp-http-server.php` refused to start without `MCP_SERVER_TOKEN`.
  - Unauthenticated `/mcp` returned `401`; wrong path returned `404`.
  - HTTP PID helpers reported stopped/running states, rejected a duplicate
    start, stopped the live process, detected a stale PID file, and started
    with `--force`.
  - Live Streamable HTTP tests passed for `redmine_list_users`,
    `redmine_get_user`, and `redmine_list_project_memberships`.
  - `redmine_get_user` redacted the returned Redmine `api_key` field.
  - `redmine_list_project_memberships` returned direct and inherited
    memberships for `customer-service`; `fud-helpdesk` returned a valid empty
    membership list.
  - `php redMCP/bin/test-query-normalizer.php` passed with coverage for paging,
    sort shortcuts, status aliases, date presets/ranges, free-text dates, and
    raw override precedence.
  - Live Streamable HTTP tests passed for friendly `redmine_list_issues`,
    `redmine_search_issues`, `redmine_list_users`, `redmine_list_projects`, and
    `redmine_list_project_memberships` arguments.
  - Debug logging wrote JSONL records with full project-tool arguments and did
    not include the bearer token, `Authorization`, or Redmine API key.
  - Token generation passed default, `--bytes 48`, and `--env-line` modes.
  - `redmine_list_projects` returned three projects from 117 total.
  - `redmine_get_project` returned `fud-helpdesk` by identifier and by id 117.
  - The live MCP tool calls returned issue search results from seven total for
    `redMCP-smoke`.

## 2026-04-25 - Test Helpdesk Credential Sanitization

- Touched areas:
  - Test instance post-import tooling
  - Post-import validation
- Purpose:
  - Ensure production database imports cannot leave real Helpdesk POP3/SMTP
    credentials on projects where Helpdesk is disabled but settings rows still
    exist.
- Behavior changed:
  - `reset_helpdesk_mail_settings.py` now rewrites Helpdesk mail settings for
    every active project, not only projects with `contacts_helpdesk` enabled.
  - `validate_test_instance.py` checks every active project for Mailpit
    Helpdesk settings.
- LAN test result:
  - `./reset_helpdesk_mail_settings.py --dry-run` matched 52 active projects.
  - `./reset_helpdesk_mail_settings.py` updated 1,092 setting rows across 52
    active projects.
  - `./validate_test_instance.py` passed and reported 52 active projects with
    matching Mailpit Helpdesk settings.

## 2026-04-25 - Outbox Worker Processing Policy

- Touched areas:
  - External outbox worker
  - Worker operations documentation
- Purpose:
  - Make the worker usable for bounded JSONL publishing before a message bus is
    chosen.
  - Add queue visibility and explicit processed-row cleanup for test-instance
    maintenance.
- Behavior changed:
  - Default worker output is `/tmp/redmine-outbox/derived_documents.jsonl`.
  - `--status` reports pending, ready, locked, failed, and processed counts.
  - `--purge-processed-days N` previews processed-row cleanup; `--apply-purge`
    is required to delete rows.
  - Normal processing still marks rows processed only after successful output.
- LAN test result:
  - `./redmine_outbox_worker.py --status` reported 100 total rows, 100 ready,
    0 locked, 0 failed, and 0 processed before the processing run.
  - `./redmine_outbox_worker.py --dry-run --batch-size 5` previewed the first
    five rows without marking them processed.
  - `./redmine_outbox_worker.py --batch-size 5` processed five rows and wrote
    six JSONL documents to `/tmp/redmine-outbox/derived_documents.jsonl`.
  - Follow-up status reported 95 pending, 95 ready, 0 locked, 0 failed, and
    5 processed.
  - `./redmine_outbox_worker.py --purge-processed-days 30` previewed zero rows.
  - Follow-up `./validate_helpdesk_outbox_worker.py` passed with controlled
    Helpdesk issue `#39875`; final status reported 118 total rows, 113 ready,
    0 locked, 0 failed, and 5 processed.

## 2026-04-25 - Helpdesk Outbox Worker Validation

- Touched areas:
  - Helpdesk test tooling
  - External outbox worker validation docs
- Purpose:
  - Make Helpdesk outbox coverage repeatable after database refreshes and
    worker changes.
  - Validate worker enrichment against a real Mailpit-imported Helpdesk ticket
    without claiming rows or marking them processed.
- Behavior checked:
  - Controlled Helpdesk import produces `helpdesk_ticket.created`,
    `helpdesk_ticket.updated`, `journal_message.created`, `journal.created`,
    and `issue.updated` rows.
  - Worker dry-run enrichment emits `event`, `ticket`, and `message` documents.
  - Derived message documents expose `has_bcc_address` but do not expose raw
    `bcc_address`.
- LAN test result:
  - `./validate_helpdesk_outbox_worker.py` passed against `fud-helpdesk` and
    `fud-nohelpdesk`.
  - Latest documented passing run created and closed controlled Helpdesk issue
    `#39873`.
  - Observed outbox rows: 1 `helpdesk_ticket.created`, 3
    `helpdesk_ticket.updated`, 1 `journal_message.created`, 3
    `journal.created`, and 2 `issue.updated`.
  - Worker dry-run enrichment produced 10 `event`, 6 `ticket`, and 2
    `message` documents without claiming or marking rows processed.

## 2026-04-24 - Helpdesk/redMCP Smoke Validation

- Touched areas:
  - `redMCP`
  - Helpdesk test tooling
- Purpose:
  - Prove the LAN test instance can import a Helpdesk email, expose its
    Helpdesk metadata through redMCP, update a non-Helpdesk control issue, send
    an explicit Helpdesk reply, and verify outbound delivery through Mailpit.
  - Make customer-visible Helpdesk email opt-in in redMCP.
- Behavior clarified:
  - `RedMCP\RedmineClient::updateIssue()` uses the normal Redmine REST API and
    does not send a Helpdesk email by default.
  - `updateIssue(..., ['send_helpdesk_email' => true])` and
    `sendHelpdeskIssueResponse()` deliberately use the Helpdesk email path.
- LAN test result:
  - `./helpdesk_smoke_test.py` passed against `fud-helpdesk` and
    `fud-nohelpdesk`.
  - The smoke test verifies that a default issue update does not send Mailpit
    mail, while an explicit Helpdesk response does.
  - Latest documented passing run created and closed controlled Helpdesk issue
    `#39871`.

## 2026-04-24 - POP3 Get Mail Compatibility Fix

- Touched plugin:
  - `redmine_contacts`
  - `redmine_contacts_helpdesk`
- Purpose:
  - Fix Helpdesk POP3 retrieval on the LAN test host when Ruby 2.5 raises
    `FrozenError: can't modify frozen String` inside `Net::POP3`.
  - Allow Helpdesk outbound mail to use Mailpit's unauthenticated SMTP listener.
- Behavior changed:
  - Changed POP3 message retrieval from `msg.pop` to `msg.pop(String.new)` so
    Ruby's POP3 code appends chunks into an explicit mutable destination string.
  - This does not change message handling semantics; it only avoids relying on
    Ruby's default empty string argument being mutable.
  - Changed Helpdesk SMTP delivery option construction to omit
    `authentication`, `user_name`, and `password` when the project SMTP
    authentication setting is blank.
- LAN test result:
  - Deployed to `/usr/share/redmine/plugins/redmine_contacts`.
  - `HelpdeskMailer.check_project(Project.find("fud-helpdesk").id)` completed
    successfully and processed 1 message.
  - Deployed to `/usr/share/redmine/plugins/redmine_contacts_helpdesk`.
  - Mailpit rejected `AUTH PLAIN` with `502 5.5.1 Command not implemented`.
    After blanking SMTP auth settings and omitting auth options, a Helpdesk test
    mail for issue `#39863` was delivered to Mailpit.

## 2026-04-21 - Helpdesk Search Foundation

- Archives created before plugin edits:
  - `dist/redmine_contacts-4.1.2-local-before-helpdesk-search-20260421T215548Z.tar.gz`
  - `dist/redmine_contacts_helpdesk-3.0.9-local-before-helpdesk-search-20260421T215548Z.tar.gz`
- Touched plugins:
  - `redmine_contacts_helpdesk`
  - `redmine_event_outbox`
- Purpose:
  - Make helpdesk ticket and message identity available to external search and
    indexing workers.
  - Avoid relying on Redmine issue author when helpdesk-created tickets use
    `Anonymous`.
- Behavior changed:
  - Added read-only `helpdesk_search/*` JSON endpoints guarded by the existing
    `view_helpdesk_tickets` permission.
  - Added optional outbox hooks for `HelpdeskTicket` and `JournalMessage`.
- Payload/content policy:
  - Include ids, source, direction, message id, and non-body address metadata.
  - Do not copy email bodies, private note text, attachments, or BCC addresses
    into event rows or the read API.
- LAN test result:
  - Pending. Validate on the LAN Redmine copy by creating/updating a controlled
    helpdesk ticket and journal message, checking `event_outbox_events`, and
    calling the new `helpdesk_search/*` endpoints with a user/API key that has
    `view_helpdesk_tickets`.
