Remove duplicated code and unused wrappers, rename against the naming rules, use Drupal idioms, and correct stale docblocks
## Problem/Motivation
Found in the second release audit of 1.x at `8af6d2c`. These are code-shape findings with no behaviour change.
**Duplication**
- The lifecycle HMAC is computed with all 15 named arguments at eight places (`SegmentIndex::sealEntry()`, `ChainArchiver::createBareSegment()`, `archiveSegment()` twice, `purgeSegmentTransientContext()` twice, `writeCompactedSegment()`, `SegmentRestorer::importFromFile()`). `SegmentSignature::computeLifecycleHmacFromRow()` exists for this.
- Four hand-written NDJSON envelope readers: `ArchiveEnvelope::readArchiveRecordFooter()`, `replayRows()`, `walkRowChain()` and `SegmentRestorer::replayArchivedRows()`.
- `ChainArchiver`'s coverage lock repeats `ChainWriteLock::acquireWithDeadline()` and `renewIfStale()` line for line.
- Retention, redaction and granularity defaults are restated as literals beside `config/install`: in `RetentionThresholds`, both settings forms, `ConfigRedaction`, `ForensicStamp`, `AuditTrailEntityHooks::getRedactionPatterns()` and `AuditTrailChainAutoArchiveForm::resolveCompactAfter()`.
- "Split a textarea into trimmed lines" is written five times.
- `SegmentIndex::findAnchorBreaks()` repeats `ChainRepository::getRowCountInRange()`. `AuditTrailChainOperationsController::reportResult()` repeats `AutoVerify::readBrokenRange()`. `AuditTrailEntriesController::getNeighborMessage()` repeats `renderListMessage()`.
- The drush restore report exists twice. The two JavaScript copy routines disagree about the plain-HTTP fallback. `'tsa_timestamp'` is spelled at six places. The TSA controller repeats its tally messages nine times.
**Unused code**
- `AuditTrail::record()` only forwards to `doRecord()`. `ChainArchiver::purgeSegmentLiveRows()` only forwards to `applyLivePurge()`.
- `SegmentReader::getVerificationMessage()` has a default argument that is never used. The chain list builder has one-line private lookups. `SegmentRestorer` computes a row hash twice. The verifier adds a message prefix and strips it again.
- `audit_trail_tsa.services.yml` registers the TSA drush class as an autowired Drupal service tagged `drush.command`. Drush reads that tag only from `drush.services.yml`, so the definition does nothing, and it asks the container to autowire a class whose parent comes from Drush.
**Naming and idioms**
- `SegmentSignatureState` is a set of string constants. As a result the verifier carries an unreachable branch whose operator-facing message is a developer note ("…or the state is a typo; investigate both"). Make it a backed enum.
- Lazily initialized values are named `*Cache`, although they are not caches (`ForensicStamp`, `ChainRegistry`, `KeyBackedSecretRepository`).
- Some method names break the naming rule: `findIdsInside`, `hasLivePurgedRangeInside`, `matchAgainst`, and eight noun-named test helpers.
- Severity labels are listed by hand instead of using `RfcLogLevel::getLevels()`. Admin paths are hard-coded in messages, which breaks under a base path or language prefix. HTML is built with `htmlspecialchars()` into `#markup`.
- The file bridge passes downloads from its hook to its response listener through request attributes.
**Documentation** (filed as #3620467, folded in here to keep one merge request)
- `SnapshotDelta` says "`CanonicalJson::encode()` sorts keys at every depth, so two snapshots differing only in it hash identically". That is not true for context buckets. `AuditTrailChainWriter::encodeBucket()` stores them with plain `json_encode()`, in insertion order, and the row hash covers that string. The `encodeBucket()` docblock calls it "the same canonical encoder".
- The help overview says "Each chain claims one or more PSR-3 channels". The shipped `default` chain claims none, and any chain may list none.
- A docblock in `AuditTrailChainListBuilder` tells the history of a past defect ("Four of the seven were read…").
## Proposed resolution
Apply each item above, without changing behaviour; correct the three texts and the release-history wording found beside them. The existing tests cover the code involved, and renamed methods are renamed in their tests.
Looked at and left as is:
- The file bridge's request attribute carries request-scoped state on purpose (the listener service is shared, the request is not); it is not a cache, and a per-download closure would have to unregister itself to stay correct.
- `ConfigRedaction::DEFAULT_PATTERNS` and the `ForensicStamp` default lists stay: they are what a caller outside the bridges gets when it passes nothing.
- `AuditTrailChainAutoArchiveForm::resolveCompactAfter()` stays: it shows the configured duration string, which `RetentionThresholds` does not return.
## Remaining tasks
- [x] Changes
- [ ] Review
## User interface changes
The help overview's definition of a chain. Severity filter options read "Debug (severity 7): no filtering" and so on.
## API changes
Renamed methods, the `SegmentSignatureState` enum, `ArchiveEnvelope::readEnvelopes()` and `Form\TextareaLines::split()` added.
## Data model changes
None.
## AI disclosure
AI-Generated: Yes (Claude Code was used to help draft this issue summary and to write the changes during a release audit. I reviewed both.)
issue
GitLab AI Context
Project: project/audit_trail
Instance: https://git.drupalcode.org
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://git.drupalcode.org/project/audit_trail/-/raw/1.x/README.md — project overview and setup
Repository: https://git.drupalcode.org/project/audit_trail
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD