tftsr-devops_investigation/AGENTS.md
Shaun Arman 6ebe3612cd
All checks were successful
Test / frontend-tests (pull_request) Successful in 1m9s
Test / frontend-typecheck (pull_request) Successful in 1m15s
Test / rust-fmt-check (pull_request) Successful in 2m44s
Test / rust-clippy (pull_request) Successful in 24m22s
Test / rust-tests (pull_request) Successful in 25m43s
fix: lint fixes and formatting cleanup
- Fix TypeScript lint errors in setup.ts and LogUpload
- Remove unused imports and variables
- Fix duplicate Separator exports in ui/index.tsx
- Apply cargo fmt formatting to Rust code
- Update ESLint configuration
2026-04-09 20:42:40 -05:00

158 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGENTS.md — Quick Start for OpenCode
## Commands
| Task | Command |
|------|---------|
| Run full dev (Tauri + Vite) | `cargo tauri dev` |
| Frontend only (port 1420) | `npm run dev` |
| Frontend production build | `npm run build` |
| Rust fmt check | `cargo fmt --manifest-path src-tauri/Cargo.toml --check` |
| Rust fmt fix | `cargo fmt --manifest-path src-tauri/Cargo.toml` |
| Rust clippy | `cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings` |
| Rust tests | `cargo test --manifest-path src-tauri/Cargo.toml -- --test-threads=1` |
| Rust single test module | `cargo test --manifest-path src-tauri/Cargo.toml -- --test-threads=1 pii::detector` |
| Rust single test | `cargo test --manifest-path src-tauri/Cargo.toml -- --test-threads=1 test_detect_ipv4` |
| Frontend test (single run) | `npm run test:run` |
| Frontend test (watch) | `npm run test` |
| Frontend coverage | `npm run test:coverage` |
| TypeScript type check | `npx tsc --noEmit` |
| Frontend lint | `npx eslint . --quiet` |
**Lint Policy**: **ALWAYS run `cargo fmt` and `cargo clippy` after any Rust code change**. Fix all issues before proceeding.
**Note**: The build runs `npm run build` before Rust build (via `beforeBuildCommand` in `tauri.conf.json`). This ensures TS is type-checked before packaging.
**Requirement**: Rust toolchain must be in PATH: `source ~/.cargo/env`
---
## Project Structure
| Path | Responsibility |
|------|----------------|
| `src-tauri/src/lib.rs` | Entry point: app builder, plugin registration, IPC handler registration |
| `src-tauri/src/state.rs` | `AppState` (DB, settings, integration_webviews) |
| `src-tauri/src/commands/` | Tauri IPC handlers (db, ai, analysis, docs, integrations, system) |
| `src-tauri/src/ai/provider.rs` | `Provider` trait + `create_provider()` factory |
| `src-tauri/src/pii/` | Detection engine (12 patterns) + redaction |
| `src-tauri/src/db/models.rs` | DB types: `Issue`, `IssueDetail` (nested), `LogFile`, `ResolutionStep`, `AiConversation` |
| `src-tauri/src/audit/log.rs` | `write_audit_event()` before every external send |
| `src/lib/tauriCommands.ts` | **Source of truth** for all Tauri IPC calls |
| `src/lib/domainPrompts.ts` | 15 domain system prompts (Linux, Windows, Network, K8s, DBs, etc.) |
| `src/stores/` | Zustand: `sessionStore` (ephemeral), `settingsStore` (persisted), `historyStore` |
---
## Key Patterns
### Rust Mutex Usage
Lock `Mutex` inside a block and release **before** `.await`. Holding `MutexGuard` across await points fails to compile (not `Send`):
```rust
let state: State<'_, AppState> = app.state();
let db = state.db.clone();
// Lock and release before await
{ let conn = state.db.lock().unwrap(); /* use conn */ }
// Now safe to .await
db.query(...).await?;
```
### IssueDetail Nesting
`get_issue()` returns a **nested** struct — use `detail.issue.title`, not `detail.title`:
```rust
pub struct IssueDetail {
pub issue: Issue,
pub log_files: Vec<LogFile>,
pub resolution_steps: Vec<ResolutionStep>,
pub conversations: Vec<AiConversation>,
}
```
TypeScript mirrors this shape exactly in `tauriCommands.ts`.
### PII Before AI Send
`apply_redactions` **must** be called before sending logs to AI. Record the SHA-256 hash via `audit::log::write_audit_event()`. PII spans are non-overlapping (longest span wins on overlap); redactor iterates in reverse order to preserve offsets.
### State Persistence
- `sessionStore`: ephemeral triage session (issue, messages, PII spans, why-level 05, loading) — **not persisted**
- `settingsStore`: persisted to `localStorage` as `"tftsr-settings"`
---
## CI/CD (Gitea Actions)
| Workflow | Trigger | Jobs |
|----------|---------|------|
| `.gitea/workflows/test.yml` | Every push/PR | `rustfmt``clippy``cargo test` (64 tests) → `tsc --noEmit``vitest run` (13 tests) |
| `.gitea/workflows/auto-tag.yml` | Push to master | Auto-tag, build linux/amd64 + windows/amd64 + linux/arm64 + macOS, upload assets to Gitea release |
**Artifacts**: `src-tauri/target/{target}/release/bundle/`
**Environments**:
- Test CI images at `172.0.0.29:3000` (pull `trcaa-*:rust1.88-node22`)
- Gitea instance: `http://172.0.0.29:3000`
- Wiki: sync from `docs/wiki/*.md``https://gogs.tftsr.com/sarman/tftsr-devops_investigation/wiki`
---
## Environment Variables
| Variable | Default | Purpose |
|----------|---------|---------|
| `TFTSR_DATA_DIR` | Platform data dir | Override database location |
| `TFTSR_DB_KEY` | Auto-generated | SQLCipher encryption key override |
| `TFTSR_ENCRYPTION_KEY` | Auto-generated | Credential encryption key override |
| `RUST_LOG` | `info` | Tracing level (`debug`, `info`, `warn`, `error`) |
**Database path**:
- Linux: `~/.local/share/trcaa/trcaa.db`
- macOS: `~/Library/Application Support/trcaa/trcaa.db`
- Windows: `%APPDATA%\trcaa\trcaa.db`
---
## Architecture Highlights
### Rust Backend
- **Entry point**: `src-tauri/src/lib.rs::run()` → init tracing → init DB → register plugins → `generate_handler![]`
- **Database**: `rusqlite` + `bundled-sqlcipher-vendored-openssl` (AES-256). `cfg!(debug_assertions)` → plain SQLite; release → SQLCipher
- **AI providers**: `Provider` trait with factory dispatch on `config.name`. Adding a provider: implement `Provider` trait + add match arm
- **Integration clients**: Confluence, ServiceNow, Azure DevOps stubs (v0.2). OAuth2 via WebView + callback server (warp, port 8765)
### Frontend (React + Vite)
- **Dev server**: port **1420** (hardcoded)
- **IPC**: All `invoke()` calls in `src/lib/tauriCommands.ts` — typed wrappers for every backend command
- **Domain prompts**: 15 expert prompts injected as first message in every triage conversation (Linux, Windows, Network, K8s, DBs, Virtualization, Hardware, Observability, Telephony, Security, Public Safety, Application, Automation, HPE, Dell, Identity)
### Security
- **Database encryption**: AES-256 (SQLCipher in release builds)
- **Credential encryption**: AES-256-GCM, keys stored in `TFTSR_ENCRYPTION_KEY` or auto-generated `.enckey` (mode 0600)
- **Audit trail**: Hash-chained entries (`prev_hash` + `entry_hash`) for tamper evidence
- **PII protection**: 12-pattern detector → user approval gate → hash-chained audit entry
---
## Testing
| Layer | Command | Notes |
|-------|---------|-------|
| Rust | `cargo test --manifest-path src-tauri/Cargo.toml` | 64 tests, runs in `rust:1.88-slim` container |
| TypeScript | `npm run test:run` | Vitest, 13 tests |
| Type check | `npx tsc --noEmit` | `skipLibCheck: true` |
| E2E | `TAURI_BINARY_PATH=./src-tauri/target/release/tftsr npm run test:e2e` | WebdriverIO, requires compiled binary |
**Frontend coverage**: `npm run test:coverage``tests/unit/` coverage report
---
## Critical Gotchas
1. **Mutex across await**: Never `lock().unwrap()` and `.await` without releasing the guard
2. **IssueDetail nesting**: `detail.issue.title`, never `detail.title`
3. **PII before AI**: Always redact and record hash before external send
4. **Port 1420**: Vite dev server is hard-coded to 1420, not 3000
5. **Build order**: Rust fmt → clippy → test → TS check → JS test
6. **CI images**: Use `172.0.0.29:3000` registry for pre-baked builder images