From 5addc9dae9f5945b84d068b0e4d444b286ff3bb8 Mon Sep 17 00:00:00 2001 From: Developer Date: Thu, 9 Jul 2026 23:29:19 +0000 Subject: [PATCH] chore(service-credential-tester): archive verified+synced change Move to openspec/changes/archive/2026-07-09-service-credential-tester/ (R100 renames preserved). 9 artifacts. Canonical openspec/specs/ service-credential-testing/ remains. Resolves qBit 'login failed' #3 pain at the UI layer (auth failure surfaced in result pill, no log-digging). Carry-overs in archive-report incl N-2 strengthened, N-6 presentational panel, edit-surface-is-Settings.tsx source-finding. --- .../apply-progress.md | 0 .../archive-report.md | 242 ++++++++++++++++++ .../design.md | 0 .../proposal.md | 0 .../spec.md | 0 .../specs/service-credential-testing/spec.md | 0 .../sync-report.md | 0 .../tasks.md | 0 .../verify-report.md | 0 9 files changed, 242 insertions(+) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/apply-progress.md (100%) create mode 100644 openspec/changes/archive/2026-07-09-service-credential-tester/archive-report.md rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/design.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/proposal.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/spec.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/specs/service-credential-testing/spec.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/sync-report.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/tasks.md (100%) rename openspec/changes/{service-credential-tester => archive/2026-07-09-service-credential-tester}/verify-report.md (100%) diff --git a/openspec/changes/service-credential-tester/apply-progress.md b/openspec/changes/archive/2026-07-09-service-credential-tester/apply-progress.md similarity index 100% rename from openspec/changes/service-credential-tester/apply-progress.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/apply-progress.md diff --git a/openspec/changes/archive/2026-07-09-service-credential-tester/archive-report.md b/openspec/changes/archive/2026-07-09-service-credential-tester/archive-report.md new file mode 100644 index 0000000..9117f51 --- /dev/null +++ b/openspec/changes/archive/2026-07-09-service-credential-tester/archive-report.md @@ -0,0 +1,242 @@ +# Archive Report — `service-credential-tester` + +> Phase: **archive** · Change: `service-credential-tester` · Repo: `/home/user/manage` +> Mode: file-backed (`artifactStore: openspec`). No source-code edits; only OpenSpec artifacts +> were touched. **Not committed** — the parent/orchestrator owns the archive commit. No push, no `gh`. + +**Status: ARCHIVED.** All eight lifecycle phases are complete (proposal → spec → design → tasks → +apply → verify → sync → **archive**). Every archive precondition is verified PASS (see §2). The +canonical `openspec/specs/service-credential-testing/spec.md` (a **new** domain created by +`sdd-sync` — pure `## ADDED Requirements`, 21 requirements CT-101 … CT-121) remains in place as the +durable end-state spec and is **not** moved (archive never moves canonical specs). The change folder +was moved to `openspec/changes/archive/2026-07-09-service-credential-tester/` via `git mv` to +preserve history. + +--- + +## 0. Archive disposition + +- **Disposition: `archived`.** The parent explicitly requested the `git mv` and owns the commit, so + the move is executed here and left staged for the parent's explicit-path commit. +- **Archive convention:** OpenSpec SDD archive contract for `openspec` mode — completed file-backed + sync → write the in-folder archive report → move the change folder to + `openspec/changes/archive/YYYY-MM-DD-{change}/`. No standalone manifest/index exists under + `openspec/` (only `config.yaml`, `changes/`, `specs/`), so the folder move **is** the archive + mechanism. No `rules.archive` override exists in `openspec/config.yaml` (verified: no `archive` + key under `rules`). +- **Target archived path:** `openspec/changes/archive/2026-07-09-service-credential-tester/` +- **Archive date:** `2026-07-09` (ISO). +- **Canonical spec left in place (not moved):** `openspec/specs/service-credential-testing/spec.md` — + 21 requirements (CT-101 … CT-121). Verified present and untouched after the move (sha256 + `edcdfecc54fafb3729a9c4f5bcbe48a2f6b6b430336e20823bb363b9a2c2e330`, unchanged pre/post move). +- **The other canonical domains are also left untouched:** `openspec/specs/web-ui/spec.md`, + `openspec/specs/prometheus-charting/spec.md`, `openspec/specs/service-storage/spec.md`. +- **Audit-trail integrity:** the change folder was moved as a whole, including the legacy flat + `spec.md`, the per-domain delta `specs/service-credential-testing/spec.md`, `apply-progress.md`, + `verify-report.md`, and `sync-report.md`, which travel with the record. Nothing was silently + deleted or rewritten. + +## 1. Native `sdd-status` read & discrepancy statement + +The native `gentle-pi.sdd-status` engine supplied by the parent reports **non-actionable state for +this archive** because it was resolved without a change context: `changeName: null`, +`artifacts: all missing`, `applyState: blocked`, `dependencies.archive: blocked`, +`blockedReasons: ["Change selection is ambiguous: per-instance-hook-scoping, +service-credential-tester."]`, `isNonAuthoritative: false`. This is a **parent-resolution artifact**: +the engine auto-detected two active changes and could not pick one. The ambiguity does **not** +reflect the state of `service-credential-tester`, which this archive task was **explicitly +assigned**. + +**Discrepancy with the parent's authoritative confirmed state — RESOLVED in favor of the parent.** +The parent physically verified (and this executor re-confirmed directly against the filesystem in +§2) that all 29 tasks are ticked, `apply-progress.md` is present and records the work, the verify +report confirms functional completion (21/21 PASS), and the sync report confirms a SYNCED canonical +domain. Per the archive contract's non-authoritative-store carve-out guidance and the parent's +explicit instruction ("native sdd-status may report stale blockers — DISREGARD; parent verified. +PROCEED"), the stale `archive: blocked` / "ambiguous" labels are **disregarded** and the archive +**proceeds**. + +Direct filesystem re-validation (§2) is the source of truth for this report. + +## 2. Archive preconditions (validated directly against the filesystem) + +| Precondition | Evidence | Result | +|---|---|---| +| Verify report present | `verify-report.md` | ✓ verdict **PASS (functionally)** — 21/21 requirements PASS | +| Verify clearly passing — no unresolved `FAIL`/`BLOCKED`/`CRITICAL` | the two verify CRITICALs were **reconcilable archive-only** items (missing `apply-progress.md` + 29 unchecked boxes), now **resolved**; no unresolved critical verification issue remains | ✓ | +| Sync report present & successful | `sync-report.md` → **Status: SYNCED** | ✓ | +| Canonical spec exists (sync target, created) | `openspec/specs/service-credential-testing/spec.md` (21 requirements, CT-101 … CT-121) | ✓ | +| Change-side domain delta exists | `specs/service-credential-testing/spec.md` (`## ADDED Requirements`) | ✓ | +| Delta op-class = pure additive (ADDED; new domain; no MODIFIED/REMOVED) | ADDED=21, MODIFIED=0, REMOVED=0, RENAMED=0 | ✓ | +| Requirement-ID parity (flat ↔ delta ↔ canonical) | 21 == 21 == 21; identical IDs CT-101 … CT-121 | ✓ | +| proposal / design / tasks artifacts present | all populated | ✓ | +| **Final Task Completion Gate — zero unchecked `- [ ]`** | `grep -cE '^\s*- \[ \]' tasks.md` → **0**; `grep -cE '^\s*- \[x\]'` → **29** | ✓ | +| `apply-progress.md` present & records the work | present; status "complete — all 29 tasks done, all gates green"; 2 slices documented | ✓ | +| No active same-domain (`service-credential-testing`) collision | new domain; the only other active change (`per-instance-hook-scoping`) carries no `service-credential-testing` delta; `sameDomainActiveChanges: []`, `collisions: []` (per sync-report §5) | ✓ | + +**Stale-checkbox reconciliation note.** At verify time, all 29 implementation/verification checkboxes +were unchecked and `apply-progress.md` did not exist (verify findings B-1/B-2). That condition was +reconciled **before** archive: `apply-progress.md` was authored documenting the two landed slices +(`3391fbc` slice-1 backend, `f6c67bd` slice-2 frontend) and the gate results, and all 29 boxes are +now ticked. `apply-progress.md` plus the verify report prove every previously-unchecked task +complete. No archive-time mechanical checkbox repair was needed — the gate now passes on the +persisted `tasks.md` as-is (0 unchecked). No partial-archive approval applies. + +## 3. Artifacts read (archive preflight) + +- `openspec/changes/service-credential-tester/proposal.md` +- `openspec/changes/service-credential-tester/spec.md` (flat, authoritative planning artifact — 21 requirements CT-101 … CT-121) +- `openspec/changes/service-credential-tester/specs/service-credential-testing/spec.md` (change-side domain delta — `## ADDED Requirements`) +- `openspec/changes/service-credential-tester/design.md` +- `openspec/changes/service-credential-tester/tasks.md` +- `openspec/changes/service-credential-tester/apply-progress.md` +- `openspec/changes/service-credential-tester/verify-report.md` +- `openspec/changes/service-credential-tester/sync-report.md` +- `openspec/specs/service-credential-testing/spec.md` (canonical, sync target — verified present and untouched) +- `openspec/config.yaml` (rules: proposal/tasks; no `rules.archive` override) +- House-style reference: `openspec/changes/archive/2026-07-09-grafana-metric-gateway/archive-report.md` + +> The legacy flat `spec.md` is **not** the only spec artifact: a per-domain delta +> (`specs/service-credential-testing/spec.md`) and a canonical spec both exist, so the "legacy flat +> spec as the *only* artifact" archive-block condition does not apply. The flat spec travels with the +> archived folder as part of the audit trail. + +## 4. Domains synced & requirement delta + +| Domain | Change-side delta | Canonical | Action | +|---|---|---|---| +| `service-credential-testing` | `specs/service-credential-testing/spec.md` | `openspec/specs/service-credential-testing/spec.md` | **NEW domain — ADDED** — 21 requirements copied into the canonical store as a brand-new spec | + +- **ADDED (21)** — all to the new `service-credential-testing` domain (canonical did not exist + pre-change). IDs and text preserved verbatim from the verified flat `spec.md`. Grouped logically: + - *Endpoint shape, validation, dispatch* — CT-101, CT-102, CT-103 + - *Per-type test routines* — CT-104, CT-105, CT-106, CT-107, CT-108, CT-109, CT-110, CT-111 + - *No-persistence + security* — CT-112, CT-113 + - *Frontend test UI* — CT-114, CT-115, CT-116, CT-117, CT-118 + - *Tests + gate greenness* — CT-119, CT-120, CT-121 +- **MODIFIED (0)** — none (new domain; no pre-existing canonical requirements to replace). +- **REMOVED (0)** · **RENAMED (0)** — nothing destructive. + +## 5. Final lifecycle status (all 8 phases done) + +| Phase | Status | Evidence | +|---|---|---| +| Proposal | ✅ done | `proposal.md` | +| Spec | ✅ done | flat `spec.md` (21) + domain delta `specs/service-credential-testing/spec.md` (21 ADDED) | +| Design | ✅ done | `design.md` | +| Tasks | ✅ done | `tasks.md` — **29/29** checked, zero `- [ ]` | +| Apply | ✅ done | 2 slices delivered (commits `3391fbc` slice-1 backend, `f6c67bd` slice-2 frontend) | +| Verify | ✅ PASS | `verify-report.md` — 21/21 PASS; gates green (verify's two CRITICALs were archive-only hygiene, now resolved) | +| Sync | ✅ done | `sync-report.md` — SYNCED; canonical `service-credential-testing` domain ADDED (now 21 requirements) | +| Archive | ✅ done | this report + folder move performed | + +## 6. Gate results (per verify-report / apply-progress) + +| Gate | Command | Result | +|---|---|---| +| Backend tests | `cd backend && PYTHONPATH=src python3 -m pytest -q` | **PASS** — 362 passed (2 pre-existing warnings) | +| Backend lint | `cd backend && PYTHONPATH=src python3 -m ruff check src tests` | **PASS** — All checks passed | +| Frontend build | `cd frontend && npm run build` | **PASS** — exit 0 (pre-existing chunk-size advisory) | +| Frontend lint | `cd frontend && npm run lint` | **PASS** — 0 errors (1 pre-existing unrelated warning) | +| Frontend tests | `cd frontend && npx vitest run` | **PASS** — 158 passed / 45 files | + +## 7. Carry-over follow-ups & non-blocking notes (recorded for the record) + +1. **[RESOLVED — verify N-2] No-secret-logs test was strengthened.** Verify flagged the original + `test_secrets_not_logged` as weak/misleading because it sent an empty `secrets: {}` payload (the + assertion was then trivially satisfied). The test was rewritten (`backend/tests/test_api.py`) + to drive the **full endpoint path** (validate → dispatch to the real `test_callable` → success + log) with **real-looking secret payloads** — `grafana_api_key: "glc_somethingverysecret"` and + `password: "SUPER-SECRET-PW-12345"` — mocking at the network boundary so both requests succeed, + then asserting that neither the full values nor meaningful fragments (`"verysecret"`, `"SUPER"`) + leak into `caplog.text`. This genuinely proves CT-113 (secrets never logged). The verify finding + is closed. +2. **[DESIGN — intentional improvement, verify N-6] `ServiceTestPanel` is presentational.** The + design proposed a stateful panel (`useEffect([input])`); the shipped component is deliberately + **presentational** (props `{ input, onResult, disabled }`), with the parent owning `testResult` + - `saveAnyway` state and the store-previous pattern that clears the result on input change. This + avoids the React `setState`-in-effect footgun and works identically in both surfaces. All of + CT-114 … CT-118 are still satisfied via parent wiring. Documented in the component docstring and + `apply-progress.md`. Informational only. +3. **[DESIGN — source-finding] Edit surface is `Settings.tsx::ServiceConfigEditor`, not + `ServicePage.tsx`.** The spec's literal CT-115 named `ServicePage.tsx`, but that file is + read-only (it renders tab content for a configured service). The actual service add/edit dialog + surfaced by the app is `ServiceConfigEditor` inside `Settings.tsx` (create surface: + `CreateServiceDialog` in `ServicesPage.tsx`). The design's §0 source-finding corrected this, and + tasks targeted the right file. `ServiceTestPanel` is wired into **both** `CreateServiceDialog` + and `ServiceConfigEditor`. `ServicePage.tsx` was correctly left untouched. +4. **[USER VALUE — pain resolved] qBittorrent "login failed" surfaced at the UI layer (CT-104).** + Previously a wrong qBittorrent username/password produced only a generic `RuntimeError` / + "login failed" buried in backend logs, leaving the operator to dig through logs to diagnose a + credential problem. The credential tester now logs into qBittorrent + probes `maindata()` during + the live Test, and specifically translates the `"Fails."` / `"login failed"` response into + *"Authentication failed — qBittorrent rejected the credentials."*, shown directly in the result + pill (✗ red detail). The credential-error pain is resolved at the UI layer — the operator gets + an actionable message without touching logs. +5. **[INFO — non-blocking, verify N-3] Defensive `logger.exception`.** `services.py` emits a full + traceback via `logger.exception` only on the rare path where a `test_callable` itself raises + (all routines catch internally). Low risk; a future change may switch to a truncated + `logger.warning`. Not blocking. +6. **[INFO — non-blocking, verify N-4] Edit flow requires re-typing secrets to test.** In + `ServiceConfigEditor`, stored secrets are encrypted at rest and are not sent as plaintext on the + test request, so testing an existing authed service without re-entering its secret yields a + "required" validation error. This is inherent to the no-persistence design (the test operates on + request-body plaintext) and is hinted by the "Leave blank to keep the current value" copy. + Non-blocking UX note; a future change may document it more prominently in the UI/user docs. +7. **[INFO — non-blocking, verify N-5] Page-level gating proven by source, not a page test.** + `ServiceTestPanel.test.tsx` covers the presentational panel (button states, success/failure + pills, checkbox toggle). Because the panel is stateless, there is no explicit automated test that + the Create/Save confirm button enables only after a passing test, or that editing a field clears + `testPassed`. The logic is correct by source inspection (store-previous pattern in both parents); + a future change may add a page-level integration test. +8. **[INFO]** The working tree carries unrelated dirty/untracked items **not owned by this archive**: + an uncommitted cosmetic (indentation/prettier reflow, no functional diff) modification to + `frontend/src/pages/ServicesPage.tsx` and untracked `.pi-tmp/*` scratch files (other-change + strays). Per discipline these were **left untouched**. + +## 8. Residual risks & destructive-merge statement + +- **Destructive sync / merge:** **none destructive.** This was a brand-new canonical domain + (`service-credential-testing`) — a pure `## ADDED Requirements` delta. Zero MODIFIED and zero + REMOVED requirements, so no destructive-removal guard was triggered and no destructive-sync parent + approval was required. +- **Backend / data-contract impact:** none. The `POST /api/services/test` endpoint is + **side-effect-free** (no persistence, no cache invalidation) and **sanitized** (logs only + `type` + `ok`, never secrets). It gates on the existing validation path (`_validate_input`) and + dispatches through the closed `test_callable` registry that mirrors `SERVICE_DEFINITIONS`. Archive + touched only OpenSpec docs + the folder move. +- **No critical verification issues** remain (CRITICAL issues are non-overridable; the two verify + CRITICALs were the reconcilable checkbox/apply-progress gap, now resolved; N-2's weak-test gap was + strengthened and closed). +- **No browser/visual smoke** was performed (out of scope); the panel is covered by Vitest component + tests only. +- **Memory observation IDs:** none — `artifactStore: openspec`; traceability lives in the filesystem + archive + canonical spec. + +## 9. Move performed + +``` +git mv openspec/changes/service-credential-tester openspec/changes/archive/2026-07-09-service-credential-tester +``` + +- **All 9 artifacts confirmed present at the archived path:** `proposal.md`, `spec.md`, + `specs/service-credential-testing/spec.md` (delta), `design.md`, `tasks.md`, `apply-progress.md`, + `verify-report.md`, `sync-report.md`, `archive-report.md` (this file). +- **Canonical `openspec/specs/service-credential-testing/spec.md` remains in place** (verified + untouched after the move — sha256 unchanged). `openspec/specs/web-ui/spec.md`, + `openspec/specs/prometheus-charting/spec.md`, and `openspec/specs/service-storage/spec.md` also + untouched. +- Renames were left **staged** (R100 detection preserved) for the parent's explicit-path commit. + `git restore --staged` was **not** run after the `git mv`. + +--- + +### Appendix — Files written/moved by this archive (OpenSpec only; no source code) + +- **Written:** `openspec/changes/service-credential-tester/archive-report.md` (this file) — at the + active path before the move; travels with the move into the archive. +- **Moved (via `git mv`):** the entire + `openspec/changes/service-credential-tester/` directory → + `openspec/changes/archive/2026-07-09-service-credential-tester/`. +- **Left in place (durable canonical):** `openspec/specs/service-credential-testing/spec.md`. +- **Not committed / not pushed** — the parent owns the commit with explicit paths. diff --git a/openspec/changes/service-credential-tester/design.md b/openspec/changes/archive/2026-07-09-service-credential-tester/design.md similarity index 100% rename from openspec/changes/service-credential-tester/design.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/design.md diff --git a/openspec/changes/service-credential-tester/proposal.md b/openspec/changes/archive/2026-07-09-service-credential-tester/proposal.md similarity index 100% rename from openspec/changes/service-credential-tester/proposal.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/proposal.md diff --git a/openspec/changes/service-credential-tester/spec.md b/openspec/changes/archive/2026-07-09-service-credential-tester/spec.md similarity index 100% rename from openspec/changes/service-credential-tester/spec.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/spec.md diff --git a/openspec/changes/service-credential-tester/specs/service-credential-testing/spec.md b/openspec/changes/archive/2026-07-09-service-credential-tester/specs/service-credential-testing/spec.md similarity index 100% rename from openspec/changes/service-credential-tester/specs/service-credential-testing/spec.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/specs/service-credential-testing/spec.md diff --git a/openspec/changes/service-credential-tester/sync-report.md b/openspec/changes/archive/2026-07-09-service-credential-tester/sync-report.md similarity index 100% rename from openspec/changes/service-credential-tester/sync-report.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/sync-report.md diff --git a/openspec/changes/service-credential-tester/tasks.md b/openspec/changes/archive/2026-07-09-service-credential-tester/tasks.md similarity index 100% rename from openspec/changes/service-credential-tester/tasks.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/tasks.md diff --git a/openspec/changes/service-credential-tester/verify-report.md b/openspec/changes/archive/2026-07-09-service-credential-tester/verify-report.md similarity index 100% rename from openspec/changes/service-credential-tester/verify-report.md rename to openspec/changes/archive/2026-07-09-service-credential-tester/verify-report.md