diff --git a/.planning/STATE.md b/.planning/STATE.md index 4f1b6f6..7d59823 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,9 +3,9 @@ ## Current Position - **Milestone:** v1.4 Unraid API Native -- **Phase:** 15 of 18 (Infrastructure Foundation) - Plan 2 of 2 complete -- **Status:** In progress -- **Last activity:** 2026-02-09 — Phase 15 Plan 02 complete (GraphQL utility nodes) +- **Phase:** 15 of 18 (Infrastructure Foundation) - Complete (2/2 plans) +- **Status:** Phase 15 complete, ready for Phase 16 +- **Last activity:** 2026-02-09 — Phase 15 complete (all infrastructure utility nodes ready) ## Project Reference @@ -22,16 +22,16 @@ v1.0: [**********] 100% SHIPPED (Phases 1-5, 12 plans) v1.1: [**********] 100% SHIPPED (Phases 6-9, 11 plans) v1.2: [**********] 100% SHIPPED (Phases 10-13 + 10.1-10.2, 25 plans) v1.3: [**********] 100% SHIPPED (Phase 14, 2 plans — descoped) -v1.4: [*.........] 10% IN PROGRESS (Phases 15-18, 1 of ~10 plans) +v1.4: [**.........] 20% IN PROGRESS (Phases 15-18, 2 of ~10 plans) -Overall: 4 milestones shipped (14 phases, 50 plans), v1.4 in progress (1 plan complete) +Overall: 4 milestones shipped (14 phases, 50 plans), v1.4 in progress (Phase 15 complete: 2 plans) ``` ## Performance Metrics **Velocity:** -- Total plans completed: 51 -- Total execution time: 12 days + 5 minutes (v1.0: 5 days, v1.1: 2 days, v1.2: 4 days, v1.3: 1 day, v1.4: 5 min) +- Total plans completed: 52 +- Total execution time: 12 days + 11 minutes (v1.0: 5 days, v1.1: 2 days, v1.2: 4 days, v1.3: 1 day, v1.4: 11 min) - Average per milestone: 3 days **By Milestone:** @@ -42,12 +42,13 @@ Overall: 4 milestones shipped (14 phases, 50 plans), v1.4 in progress (1 plan co | v1.1 | 11 | 2 days | ~4 hours | | v1.2 | 25 | 4 days | ~4 hours | | v1.3 | 2 | 1 day | ~2 minutes | -| v1.4 | 1 | 5 minutes | 5 minutes | +| v1.4 | 2 | 11 minutes | 5.5 minutes | **Phase 15 Details:** | Plan | Duration | Tasks | Files | |------|----------|-------|-------| +| 15-01 | 6 min | 2 | 1 | | 15-02 | 5 min | 2 | 1 | ## Accumulated Context @@ -65,6 +66,8 @@ Key decisions from v1.3 and v1.4 planning: - [Phase 15-02]: GraphQL normalizer keeps full Unraid PrefixedID (Container ID Registry handles translation) - [Phase 15-02]: ALREADY_IN_STATE error maps to HTTP 304 (matches Docker API pattern) - [Phase 15-02]: 15-second timeout for myunraid.net cloud relay (200-500ms latency + safety margin) +- [Phase 15]: Token encoder uses 8-char hex (not base64) for deterministic collision avoidance via hash window offsets +- [Phase 15]: Container ID Registry stores full PrefixedID (129-char) as-is for downstream consumers ### Pending Todos @@ -79,9 +82,9 @@ None. - myunraid.net cloud relay adds 200-500ms latency (timeout configuration needed) **Next phase readiness:** -- Phase 15 Plan 02 complete — GraphQL utility nodes ready +- Phase 15 complete (both plans) — All infrastructure utility nodes ready - Phase 16 (API Migration) ready to begin -- Utility nodes provide complete transformation pipeline +- Complete utility node suite: Container ID Registry, Token Encoder/Decoder, GraphQL Normalizer, Error Handler - No blockers ## Key Artifacts @@ -99,8 +102,8 @@ None. ## Session Continuity Last session: 2026-02-09 -Stopped at: Completed 15-02-PLAN.md -Next step: Continue Phase 15 (Plan 01 if needed) or begin Phase 16 API Migration +Stopped at: Phase 15 complete (15-01-PLAN.md and 15-02-PLAN.md done) +Next step: Begin Phase 16 API Migration planning --- *Auto-maintained by GSD workflow* diff --git a/.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md b/.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md new file mode 100644 index 0000000..750ee6c --- /dev/null +++ b/.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md @@ -0,0 +1,178 @@ +--- +phase: 15-infrastructure-foundation +plan: 01 +subsystem: infra +tags: [container-id-registry, callback-token-encoding, unraid-prefixedid, telegram-callback-data] + +# Dependency graph +requires: + - phase: 14-unraid-api-access + provides: Unraid GraphQL API container data format (id: PrefixedID, names[], state) + - phase: 11-update-all-callback-limits + context: Demonstrated callback_data 64-byte limit (bitmap encoding addressed this for batch operations) +provides: + - Container ID Registry utility node (container name <-> Unraid PrefixedID translation) + - Callback Token Encoder utility node (PrefixedID -> 8-char hex token with collision detection) + - Callback Token Decoder utility node (8-char token -> PrefixedID resolution) + - Static data persistence pattern for _containerIdMap and _callbackTokens +affects: [16-api-migration, 17-container-id-translation] + +# Tech tracking +tech-stack: + added: + - crypto.subtle.digest (Web Crypto API for SHA-256 hashing) + patterns: + - JSON serialization for n8n static data persistence (top-level assignment pattern per CLAUDE.md) + - SHA-256 hash with 7-window collision detection (56 chars / 8-char windows) + - Idempotent token encoding (reuse existing token if same unraidId) + - Container name normalization (strip leading '/', lowercase) + - Registry staleness detection (60-second threshold for error messaging) + +key-files: + created: [] + modified: + - n8n-workflow.json + +key-decisions: + - "Token encoder uses 8-char hex (not base64) for deterministic collision avoidance via hash window offsets" + - "Registry stores full PrefixedID (129-char) as-is, not normalized - downstream consumers handle format" + - "Decoder is read-only (no JSON.stringify) - token store managed entirely by encoder" + - "Collision detection tries 7 non-overlapping windows (0, 8, 16, 24, 32, 40, 48 char offsets from SHA-256)" + - "Standalone utility nodes NOT connected to active flow - Phase 16 will wire them in" + +patterns-established: + - "Container ID Registry as centralized name->ID translation layer" + - "Token encoding system as callback_data compression layer for Telegram's 64-byte limit" + - "Dual-mode node pattern (update vs lookup based on input.containers vs input.containerName)" + +# Metrics +duration: 6min +completed: 2026-02-09 +--- + +# Phase 15 Plan 01: Container ID Registry and Callback Token Encoding Summary + +Built Container ID Registry and Callback Token Encoding system as standalone utility Code nodes for Phase 16 API migration. Registry maps container names to Unraid 129-char PrefixedIDs, token system compresses PrefixedIDs to 8-char hex for Telegram callback_data limit. + +## What Was Built + +### Container ID Registry (Task 1 - Already Complete in Baseline) + +**Node:** Container ID Registry at position [200, 2400] + +**Note:** This node was already implemented in the baseline commit 1b4b596 (incorrectly labeled as 15-02 but contained 15-01 work). Verified implementation matches all plan requirements. + +**Implementation:** +- `updateRegistry(containers)`: Takes Unraid GraphQL container array, extracts names (strip `/`, lowercase), maps to `{name, unraidId: container.id}`, stores with timestamp +- `getUnraidId(containerName)`: Resolves container name to 129-char PrefixedID, throws helpful errors (stale registry vs invalid name) +- `getContainerByName(containerName)`: Returns full entry `{name, unraidId}` +- Dual-mode input contract: `input.containers` for updates, `input.containerName` for lookups +- JSON serialization pattern: `registry._containerIdMap = JSON.stringify(newMap)` (top-level assignment per CLAUDE.md) +- 60-second staleness threshold for error messaging + +**Verification passed:** All functions present, JSON pattern correct, no connections. + +### Callback Token Encoder (Task 2) + +**Node:** Callback Token Encoder at position [600, 2400] +**Commit:** 1b61343 + +**Implementation:** +- `encodeToken(unraidId)`: Async function using crypto.subtle.digest('SHA-256') +- Generates SHA-256 hash, takes first 8 hex chars as token +- Collision detection: If token exists with different unraidId, tries next 8-char window (offsets: 0, 8, 16, 24, 32, 40, 48) +- Idempotent: Reuses existing token if same unraidId +- Input contract: `input.unraidId` (required), `input.action` (optional) +- Output: `{token, unraidId, callbackData, byteSize, warning}` - includes callback_data format and 64-byte limit validation +- JSON serialization: `staticData._callbackTokens = JSON.stringify(tokenStore)` + +**Verification passed:** SHA-256 hashing, 7-window collision detection, JSON pattern, no connections. + +### Callback Token Decoder (Task 2) + +**Node:** Callback Token Decoder at position [1000, 2400] +**Commit:** 1b61343 + +**Implementation:** +- `decodeToken(token)`: Looks up token in store, throws if not found +- Input contract: `input.token` (8-char hex) OR `input.callbackData` (string like "action:start:a1b2c3d4") +- Callback data parsing: Splits by `:`, extracts action and token (last segment) +- Output: `{token, unraidId, action}` +- Read-only: Only uses JSON.parse (no stringify) - token store managed by encoder + +**Verification passed:** decodeToken function, error handling, callbackData parsing, no connections. + +## Deviations from Plan + +### Pre-existing Work + +**Task 1 (Container ID Registry) was already complete in baseline commit 1b4b596.** + +- **Found during:** Plan execution initialization +- **Issue:** Commit 1b4b596 was labeled `feat(15-02)` but actually contained both the Container ID Registry (Task 1 from plan 15-01) AND the GraphQL Response Normalizer (Task 1 from plan 15-02) +- **Resolution:** Verified existing implementation matches all Task 1 requirements (updateRegistry, getUnraidId, getContainerByName, JSON serialization, no connections). Proceeded with Task 2 only. +- **Impact:** No implementation changes needed for Task 1. Task 2 added as planned. +- **Commits:** No new commit for Task 1 (already in baseline). Task 2 committed as 1b61343. + +### n8n API Field Restrictions (Deviation Rule 3 - Blocking Issue) + +**Notes fields cannot be pushed to n8n via API.** + +- **Found during:** Task 2 push to n8n (HTTP 400 "must NOT have additional properties") +- **Issue:** Plan specified adding `notes` and `notesDisplayMode` fields to document utility node purpose. n8n API only accepts 6 fields: id, name, type, typeVersion, position, parameters. +- **Fix:** Removed notes/notesDisplayMode fields from all nodes before pushing payload. Documentation moved to JSDoc comments in jsCode (first lines of each function). +- **Files modified:** n8n-workflow.json (cleaned before push) +- **Verification:** Push succeeded with HTTP 200, n8n confirms 175 nodes. +- **Impact:** Node documentation now lives in code comments instead of n8n UI notes field. Functionally equivalent for Phase 16 (code is self-documenting). + +## Execution Summary + +**Tasks completed:** 2/2 +- Task 1: Container ID Registry (verified baseline implementation) +- Task 2: Callback Token Encoder and Decoder (implemented and committed) + +**Commits:** +- 1b61343: feat(15-01): add Callback Token Encoder and Decoder utility nodes + +**Duration:** 6 minutes (Task 1 verification + Task 2 implementation + n8n push + commit) + +**Files modified:** +- n8n-workflow.json (added 2 nodes: encoder, decoder) + +**n8n push:** Successful (HTTP 200, 175 nodes, updated 2026-02-09T13:53:17.242Z) + +## Verification Results + +All success criteria met: + +- [✓] Container ID Registry maps container names to Unraid PrefixedID format (129-char) +- [✓] Callback token encoding produces 8-char hex tokens that fit within Telegram's 64-byte callback_data limit +- [✓] Token collision detection prevents wrong-container scenarios (7-window SHA-256 approach) +- [✓] All static data uses JSON serialization (top-level assignment) per CLAUDE.md convention +- [✓] Three standalone utility nodes ready for Phase 16 to wire in +- [✓] No connections to/from any utility node (verified in workflow connections map) +- [✓] Workflow JSON valid and pushed to n8n + +## Self-Check: PASSED + +**Created files:** +- [✓] FOUND: .planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md (this file) + +**Commits:** +- [✓] FOUND: 1b61343 (Task 2: Callback Token Encoder and Decoder) + +**n8n nodes:** +- [✓] Container ID Registry exists in n8n workflow (175 nodes total) +- [✓] Callback Token Encoder exists in n8n workflow +- [✓] Callback Token Decoder exists in n8n workflow + +## Next Steps + +**Phase 16 (API Migration)** will: +1. Wire Container ID Registry into container status flow (connect after Unraid GraphQL responses) +2. Wire Callback Token Encoder into inline keyboard generation (replace long PrefixedIDs with 8-char tokens) +3. Wire Callback Token Decoder into callback routing (resolve tokens back to PrefixedIDs) +4. Update all 60+ Code nodes to use registry for ID translation +5. Test token collision handling under production load + +**Ready for:** Plan 15-02 execution (if not already complete) or Phase 16 planning.