docs: define portable CLI runtime and cache safety contract
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
# Portable Redmine and NMI CLI runtime
|
||||||
|
|
||||||
|
## Approved direction
|
||||||
|
|
||||||
|
Improve the existing tools without migrating caches or weakening mutation gates. Preserve existing unrelated working-tree changes. No package installations, production mutations, cron changes, credential rewrites, or cache migration.
|
||||||
|
|
||||||
|
## Commands and configuration
|
||||||
|
|
||||||
|
- Keep `bin/rcontacts-live`; introduce `bin/rtickets-live`; make `bin/rtl` the short alias using the same resolver. Keep `rtd` explicitly development-oriented.
|
||||||
|
- Centralize Redmine wrapper setup in a Python standard-library module. Shell entry points locate their checkout reliably and invoke it. Resolve paths independently of the calling working directory.
|
||||||
|
- Precedence: explicit CLI options > exported environment > selected env file > documented defaults. Empty required values are missing. Preserve legacy JSON/YAML ticket config support with documented interactions; explicit CLI still wins.
|
||||||
|
- Select env file explicitly with `--env-file` or `REDMINE_ENV_FILE`; otherwise search the checkout `.env`, legacy `bin/.env`, then `~/.redmine.live` (development uses a distinct file, never silently loads live credentials). Report searched/selected paths. An explicitly selected missing/malformed file is an error, not permission to fall back. Parse key/value data, never execute arbitrary shell code.
|
||||||
|
- Expose `--show-config` (redacted resolved values and sources, no API calls) and `--doctor` (offline validation with nonzero status for problems). Help works without credentials; cache-only operations retain current credential requirements rather than globally demanding credentials at startup.
|
||||||
|
- Default live Redmine URL: `https://rm.ldrprep.com`, project `customer-service`; default NMI mode production. Explicit dev selection is visible on stderr. Normal machine-readable stdout is not polluted by banners.
|
||||||
|
- Preserve existing `--apply`, `--allow-prod-writes`, customer-email confirmations and NMI mutation gates.
|
||||||
|
|
||||||
|
## Cache contract
|
||||||
|
|
||||||
|
- `REDMINE_BASE_CACHE` selects a common root, default `<checkout>/cache` for live wrappers and `<checkout>/cache-dev` for development. The existing live ticket default remains `<checkout>/cache/redmine_tickets` for customer-service.
|
||||||
|
- Tickets: `<root>/redmine_tickets` for the default production customer-service project; other endpoints/projects use distinct deterministic paths unless explicitly overridden. Exact `--cache-dir` wins. Contacts CLI: `<root>/redmine_contacts/<host>_<project>.json`; exact `--cache` wins and must be a file path.
|
||||||
|
- Tickets' internal contacts mirror remains `<ticket-cache>/contacts`; it is a different schema from the contacts CLI snapshot. Diagnostics name both and explain this distinction. Printed handoffs retain resolved endpoint, project, contacts cache, and selected configuration context without printing credentials.
|
||||||
|
- Reuse the resolved API key consistently, including `--api-key-file` branches. No secret goes in a subprocess argument or diagnostic output.
|
||||||
|
|
||||||
|
## NMI contract
|
||||||
|
|
||||||
|
- Improve `mrscClient-php/bin/nmi` and shared client configuration only as necessary. Preserve existing uncommitted edits in `bin/nmi`, `AgentApi.php`, `MutationGuard.php`, and `.gitignore`.
|
||||||
|
- `nmi --show-config` / `nmi --doctor` are offline and use the same configuration resolver as actual commands. Also support natural command forms if convenient. Report mode, endpoint, selected config/env paths, key names/source/presence (not values).
|
||||||
|
- Explicit CLI wins over exported environment; exported variables win over env-file values. Canonical production keys include `NMI_ENDPOINT_PRODUCTION`, `NMI_MRSC_ACCESS_CODE_PRODUCTION`, `NMI_SECRETKEY_PRODUCTION`; existing generic/alternate names stay supported and are documented.
|
||||||
|
- Default production mode is consistent across direct CLI and Redmine bridge; dev must be explicit and visible. Discover default env file relative to checkout (`examples/modern/.env`, with documented user/repo candidates), not `/home/jason`.
|
||||||
|
- Redmine bridge discovers NMI via `NMI_CLI`, PATH, or sibling checkout. `NMI_ENV_FILE` is optional when valid exported credentials exist; explicit file errors remain fatal. `rtickets-live --doctor-nmi` delegates to the actual NMI diagnostic path without API requests, using the same command/env as real lookups. Bound subprocess timeouts and report nonzero failures clearly.
|
||||||
|
|
||||||
|
## Concurrent cache safety
|
||||||
|
|
||||||
|
- Use standard-library POSIX advisory locking on persistent lock files; do not infer liveness from the existence of a file and do not unlink a live lock inode.
|
||||||
|
- Protect read-modify-write transactions by taking the lock before loading current manifest state. Do not merely lock the final write of an old in-memory manifest.
|
||||||
|
- Use unique temporary files in the destination directory, flush/fsync as appropriate, then atomic replace for manifests and record JSON. Clean up failed temporary files. OS closes/releases locks on exit, including forced termination.
|
||||||
|
- A contending writer reports that it is waiting on stderr, then fails with an actionable error on a bounded configurable timeout (`REDMINE_LOCK_TIMEOUT`, default 60 seconds). Invalid timeouts fail explicitly. Avoid whole-session TUI locks.
|
||||||
|
- Full contacts snapshots require serialization across fetch/write where necessary to avoid an older fetch replacing a newer one. Preserve snapshot semantics (filtered caches are deliberate), not an accidental append-only merge.
|
||||||
|
- No manifest chunking or SQLite migration in this work. Regression tests cover concurrent distinct updates, reader-safe atomic replacement, timeout visibility, and process-death release.
|
||||||
|
|
||||||
|
## Implementation plan
|
||||||
|
|
||||||
|
1. Add failing clean-environment wrapper tests in `tests/test_cli_runtime.py`: help, missing/empty config, env precedence, paths with spaces, working-directory independence, cache override, redaction, explicit dev, and alias behavior. Implement `redmine_cli_runtime.py` and wrapper entries. Extend completion and operator docs.
|
||||||
|
2. Add NMI PHP subprocess tests (standalone PHP or Python unittest; no new packages). Exercise actual CLI with test-only config and no network. Implement diagnostic/config changes and update completion/help. Ensure pre-existing mutation logic is unchanged.
|
||||||
|
3. Add `tests/test_cache_concurrency.py` with subprocess writers and killed lock holders. Implement shared `redmine_cache_io.py` and integrate ticket state store, record writes, ticket contacts cache, and full CRM snapshot writes. Run existing focused cache tests.
|
||||||
|
4. Integrate Redmine-to-NMI profile resolution and diagnostics after NMI CLI contract exists. Test with a fake NMI executable recording argv/env; separately run real NMI offline doctor. Correct contacts handoff propagation and API-key-file consistency.
|
||||||
|
5. Run focused existing suites plus new tests, Python compile checks, PHP lint, shell syntax/completion smoke probes. Execute wrappers from an unrelated directory under a minimal environment using isolated temporary config/cache fixtures. Never touch real production data for tests.
|
||||||
|
6. Review diffs, preserve baseline edits, record exact verification and any residual limitations in `docs/cli_runtime.md`; update relevant procedural skill.
|
||||||
|
|
||||||
|
## Acceptance checklist
|
||||||
|
|
||||||
|
- [ ] Contacts and tickets wrappers run/help cleanly and consistently.
|
||||||
|
- [ ] Missing configuration explains required variables and lookup paths.
|
||||||
|
- [ ] Cache root and per-tool override semantics are verified.
|
||||||
|
- [ ] Tickets contact handoffs preserve runtime context and expose distinct formats.
|
||||||
|
- [ ] Direct NMI and bridge agree on mode, credentials and file precedence.
|
||||||
|
- [ ] Offline bridge preflight exercises actual NMI resolver.
|
||||||
|
- [ ] Concurrent cache updates and abandoned-lock recovery pass real process tests.
|
||||||
|
- [ ] Existing safety gates remain unchanged.
|
||||||
|
- [ ] Usage, completion and verification results are documented.
|
||||||
Reference in New Issue
Block a user