From eb334739323250f7937bb5d7beddab7f45d04ddd Mon Sep 17 00:00:00 2001 From: Michael Walters Date: Fri, 17 Jul 2026 13:00:55 +0100 Subject: [PATCH 1/6] Small helper files --- Launch-Dev.bat | 3 + Sync-Upstream.bat | 3 + Sync-Upstream.ps1 | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 Launch-Dev.bat create mode 100644 Sync-Upstream.bat create mode 100644 Sync-Upstream.ps1 diff --git a/Launch-Dev.bat b/Launch-Dev.bat new file mode 100644 index 0000000000..0db7f20a38 --- /dev/null +++ b/Launch-Dev.bat @@ -0,0 +1,3 @@ +@echo off +cd /d "%~dp0" +start "" "%~dp0runtime\Path{space}of{space}Building.exe" diff --git a/Sync-Upstream.bat b/Sync-Upstream.bat new file mode 100644 index 0000000000..1ed05c9468 --- /dev/null +++ b/Sync-Upstream.bat @@ -0,0 +1,3 @@ +@echo off +cd /d "%~dp0" +powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0Sync-Upstream.ps1" %* diff --git a/Sync-Upstream.ps1 b/Sync-Upstream.ps1 new file mode 100644 index 0000000000..5ccd243c2c --- /dev/null +++ b/Sync-Upstream.ps1 @@ -0,0 +1,142 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Syncs this fork's local `dev` branch with upstream PathOfBuildingCommunity/PathOfBuilding. + +.DESCRIPTION + Follows CONTRIBUTING.md "Keeping your fork up to date": + 1. Ensure the `upstream` remote exists + 2. Fetch upstream + 3. Check out `dev` + 4. Rebase onto upstream/dev + 5. Optionally push to origin (use -Push) + +.PARAMETER Push + After a successful rebase, force-push the updated `dev` branch to origin. + Required when the rebase rewrites history relative to origin/dev. + +.PARAMETER Branch + Local branch to update. Defaults to `dev`. + +.EXAMPLE + .\Sync-Upstream.ps1 + +.EXAMPLE + .\Sync-Upstream.ps1 -Push +#> +[CmdletBinding()] +param( + [switch]$Push, + [string]$Branch = "dev" +) + +$ErrorActionPreference = "Stop" +Set-Location -LiteralPath $PSScriptRoot + +$UpstreamUrl = "https://github.com/PathOfBuildingCommunity/PathOfBuilding.git" + +function Write-Step([string]$Message) { + Write-Host "`n==> $Message" -ForegroundColor Cyan +} + +function Assert-GitRepo { + git rev-parse --is-inside-work-tree 2>$null | Out-Null + if ($LASTEXITCODE -ne 0) { + throw "Not inside a git repository: $PSScriptRoot" + } +} + +function Get-Remotes { + git remote 2>$null +} + +Assert-GitRepo + +# Block on modified/staged tracked files only; untracked files are fine. +$dirty = git status --porcelain --untracked-files=no +if ($dirty) { + throw @" +Working tree has uncommitted changes to tracked files. Commit, stash, or discard them first: + +$dirty +"@ +} + +Write-Step "Ensuring upstream remote" +$remotes = @(Get-Remotes) +if ($remotes -notcontains "upstream") { + Write-Host "Adding upstream -> $UpstreamUrl" + git remote add upstream $UpstreamUrl + if ($LASTEXITCODE -ne 0) { throw "Failed to add upstream remote." } +} else { + $currentUrl = (git remote get-url upstream).Trim() + Write-Host "upstream already configured: $currentUrl" +} + +Write-Step "Fetching from upstream" +git fetch upstream +if ($LASTEXITCODE -ne 0) { throw "git fetch upstream failed." } + +$upstreamRef = "upstream/$Branch" +git rev-parse --verify "$upstreamRef^{commit}" 2>$null | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Upstream branch '$upstreamRef' not found after fetch." +} + +Write-Step "Checking out $Branch" +git checkout $Branch +if ($LASTEXITCODE -ne 0) { throw "Failed to check out '$Branch'." } + +$before = (git rev-parse HEAD).Trim() +$upstreamTip = (git rev-parse $upstreamRef).Trim() + +if ($before -eq $upstreamTip) { + Write-Host "`nAlready up to date with $upstreamRef." -ForegroundColor Green +} else { + $behind = (git rev-list --count "HEAD..$upstreamRef").Trim() + $ahead = (git rev-list --count "$upstreamRef..HEAD").Trim() + Write-Host "Local $Branch is $behind commit(s) behind and $ahead commit(s) ahead of $upstreamRef." + + Write-Step "Rebasing $Branch onto $upstreamRef" + git rebase $upstreamRef + if ($LASTEXITCODE -ne 0) { + Write-Host @" + +Rebase failed (likely conflicts). Resolve them, then run: + git add + git rebase --continue + +Or abort with: + git rebase --abort +"@ -ForegroundColor Yellow + exit 1 + } + + $after = (git rev-parse HEAD).Trim() + Write-Host "`nRebase complete: $before -> $after" -ForegroundColor Green +} + +if ($Push) { + Write-Step "Force-pushing $Branch to origin" + git push -f origin $Branch + if ($LASTEXITCODE -ne 0) { throw "git push -f origin $Branch failed." } + Write-Host "Pushed origin/$Branch." -ForegroundColor Green +} else { + $localTip = (git rev-parse HEAD).Trim() + $originTip = $null + git rev-parse --verify "origin/$Branch^{commit}" 2>$null | Out-Null + if ($LASTEXITCODE -eq 0) { + $originTip = (git rev-parse "origin/$Branch").Trim() + } + + if ($originTip -and $localTip -ne $originTip) { + Write-Host @" + +Local $Branch differs from origin/$Branch. +To update your fork on GitHub, re-run with -Push: + .\Sync-Upstream.ps1 -Push +"@ -ForegroundColor Yellow + } else { + Write-Host "`nDone. Use -Push if you also want to update origin/$Branch." -ForegroundColor Green + } +} From 547c805289c44b9aae004a95e8cd4b2da616679f Mon Sep 17 00:00:00 2001 From: Michael Walters Date: Fri, 17 Jul 2026 13:43:23 +0100 Subject: [PATCH 2/6] Document PoE1 knowledge sources and patch-notes feeds. Capture allowlist/denylist seeds and sync feed options for issue #5 without implementing scrapers. Co-authored-by: Cursor --- .../poe1-knowledge-sources-patch-notes.md | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 docs/research/poe1-knowledge-sources-patch-notes.md diff --git a/docs/research/poe1-knowledge-sources-patch-notes.md b/docs/research/poe1-knowledge-sources-patch-notes.md new file mode 100644 index 0000000000..bbe60027e5 --- /dev/null +++ b/docs/research/poe1-knowledge-sources-patch-notes.md @@ -0,0 +1,289 @@ +# Research: PoE1 knowledge sources and patch-notes feeds + +**Ticket:** https://github.com/waltersmike/PathOfBuilding/issues/5 +**Branch:** `research/poe1-knowledge-sources` +**Date:** 2026-07-17 +**Scope:** Facts for Knowledge Pack allowlist/denylist and Patch-Notes Sync design. No scraper or agent implementation. + +**Critical constraint:** Path of Exile 1 only. Treating PoE2 data (including same-named items/skills) as PoE1 truth is a catastrophic failure mode for the planned agent. + +--- + +## Question + +What high-trust PoE1 sources should seed the Knowledge Pack allowlist/denylist, and what machine-readable or scrapable PoE1 patch-notes feeds exist for Patch-Notes Sync? + +--- + +## Short answers + +1. **Allowlist seeds (PoE1):** official `pathofexile.com` (especially the Patch Notes forum + news RSS as a secondary signal), `poewiki.net`, `poe.ninja/poe1/*`, Reddit `r/pathofexile` + `r/PathOfExileBuilds`, and PoB-adjacent community tools that already namespace PoE1 (`poedb.tw`, `maxroll.gg/poe`, `pobb.in`). +2. **Denylist / PoE2 lookalike traps:** `pathofexile2.com`, GGG forum id `2212` (“Early Access Patch Notes”, `0.x` versions), `poe2wiki.net`, `poe2db.tw`, `poe.ninja/poe2/*`, `maxroll.gg/poe2`, `r/PathOfExile2`, and `grindinggear/poe2-skilltree-export`. Same skill/item **names** exist on both wikis with different mechanics. +3. **Patch-Notes Sync:** there is **no** official dedicated patch-notes API or RSS. The durable primary surface is HTML forum listing + thread pages under `https://www.pathofexile.com/forum/view-forum/patch-notes`. Official news RSS exists but is incomplete for hotfixes and can mention PoE2. Discriminate games by **forum section** and **version scheme** (`3.x` PoE1 vs `0.x` PoE2 EA). + +--- + +## 1. Official GGG / Path of Exile 1 + +### 1.1 Site and forums (primary for patch notes) + +| Surface | URL | Role | +| --- | --- | --- | +| PoE1 site | https://www.pathofexile.com/ | Official home; news, forums, trade, shop | +| **PoE1 Patch Notes forum** | https://www.pathofexile.com/forum/view-forum/patch-notes | Canonical chronological list of PoE1 patch/hotfix threads | +| Example PoE1 thread | https://www.pathofexile.com/forum/view-thread/3985151 | `3.28.0k Patch Notes` (verified 2026-07-17) | +| Example league patch | https://www.pathofexile.com/forum/view-thread/3985332 | `Content Update 3.29.0 - Path of Exile: Curse of the Allflame` | +| Developer docs | https://www.pathofexile.com/developer/docs | OAuth API policies; **no patch-notes resource** | +| Data exports | https://www.pathofexile.com/developer/docs/data | Official passive/atlas tree exports only | +| PoE1 passive tree export | https://github.com/grindinggear/skilltree-export | First-party tree JSON | +| PoE1 atlas tree export | https://github.com/grindinggear/atlastree-export | First-party atlas tree (linked from data docs) | +| Trade leagues JSON | https://www.pathofexile.com/api/trade/data/leagues | Official league list (PoE1 trade stack) | + +**Observed (2026-07-17):** the Patch Notes forum listing currently shows PoE1 `3.28.x` / `3.29.0` threads (e.g. `3.28.0k`, Curse of the Allflame `3.29.0`). Thread bodies are ordinary HTML forum posts authored by GGG staff accounts — scrapable, not machine-schema’d. + +### 1.2 Official news RSS (partial, not patch-notes-complete) + +| Feed | URL | Notes | +| --- | --- | --- | +| PoE1 site news RSS | https://www.pathofexile.com/news/rss | Valid RSS 2.0; channel title `Path of Exile News`; category typically `news` | +| PoE2 site news RSS | https://pathofexile2.com/news/rss | Separate host; **denylist for PoE1 sync** | + +**Facts:** + +- Feed is live and returns XML (HTTP 200 probed 2026-07-17). +- Items link to forum news threads (e.g. expansion announcements), not every Patch Notes hotfix. +- Community asked for a dedicated patch-notes RSS category; thread archived without an official feed: https://www.pathofexile.com/forum/view-thread/3554274 +- Cloudflare has historically blocked some RSS clients (`403` challenges reported): https://www.pathofexile.com/forum/view-thread/3648166 +- Sample titles on 2026-07-17 were mostly PoE1 league/marketing, but the same feed can carry PoE2-adjacent headlines (e.g. “Path of Exile 2: Return of the Ancients FAQ”). **Do not treat news RSS as a pure PoE1 patch-notes stream.** + +### 1.3 What GGG does *not* provide for patch notes + +From https://www.pathofexile.com/developer/docs : + +- Supported resources are only those in the API reference and data exports. +- Reverse-engineering undocumented internal endpoints is against Terms of Use §7i. +- Data exports explicitly cover **passive/atlas trees**, not patch notes, skill gems, or item text: https://www.pathofexile.com/developer/docs/data + +**Implication for Patch-Notes Sync:** design around **forum HTML polling** (list → thread), optionally using news RSS as a noisy secondary hint — not as the sole source of truth. + +--- + +## 2. poewiki (PoE1) — high-trust community encyclopedia + +| Surface | URL | Role | +| --- | --- | --- | +| PoE1 wiki | https://www.poewiki.net/ | Canonical community wiki for Path of Exile 1 | +| MediaWiki API | https://www.poewiki.net/w/api.php | Standard MW API (query, parse, etc.) | +| Cargo / data query docs | https://www.poewiki.net/wiki/Path_of_Exile_Wiki:Data_query_API | Structured item/skill/mod queries (Cargo) | +| PoB deep-link precedent | `src/Modules/ItemTools.lua` opens `https://www.poewiki.net/wiki/...` on F1 | First-party PoB integration signal | + +**Bot protection:** HTML and API fetches from this environment received Anubis proof-of-work interstitial pages (“Making sure you're not a bot!”). Design implication: allowlist the domain, but plan for polite human-browser or authenticated access patterns, caching, and rate limits — not naive bulk scrape. + +**Legacy Fandom host:** `pathofexile.fandom.com` is a historical Gamepedia/Fandom surface; not the current community home. Prefer `poewiki.net` only for PoE1 wiki facts. + +--- + +## 3. poe.ninja — economy / builds (PoE1 namespace) + +poe.ninja hosts **both** games with explicit path prefixes: + +| PoE1 (allow) | PoE2 (deny) | +| --- | --- | +| https://poe.ninja/poe1/ | https://poe.ninja/poe2/ | +| https://poe.ninja/poe1/builds | https://poe.ninja/poe2/builds | +| https://poe.ninja/poe1/data | (PoE2 equivalents under `/poe2/`) | + +### Machine-readable PoE1 economy endpoints (verified 2026-07-17) + +Example (HTTP 200, JSON): + +```text +https://poe.ninja/poe1/api/economy/stash/current/currency/overview?league=Standard&type=Currency +``` + +League index-ish JSON: + +```text +https://poe.ninja/poe1/api/data/index-state +``` + +**Breaking change vs older community docs:** legacy URLs such as + +```text +https://poe.ninja/api/data/currencyoverview?league=Standard&type=Currency +``` + +returned **HTTP 404** on 2026-07-17. Community trackers report the migration to `/poe1/api/...` paths (e.g. https://github.com/exilence-ce/exilence-ce/issues/49). Knowledge Pack tooling must prefer the **`/poe1/`** prefix. + +**PoB already namespaces PoE1 PoB share URLs** under `poe.ninja/poe1/pob/...` (`src/Modules/BuildSiteTools.lua`). + +--- + +## 4. Reddit community surfaces + +| Subreddit | PoE game | Seed | +| --- | --- | --- | +| https://www.reddit.com/r/pathofexile/ | PoE1 primary discussion | **Allow** | +| https://www.reddit.com/r/PathOfExileBuilds/ | PoE1 builds | **Allow** | +| https://www.reddit.com/r/pathofexiledev/ | Tooling / API discussion | **Allow** (lower weight for build truth) | +| https://www.reddit.com/r/pathofexile/wiki/tools | Community tool index | **Allow** as discovery, not ground truth | +| https://www.reddit.com/r/PathOfExile2/ | PoE2 | **Deny** | + +**Notes:** + +- Reddit JSON endpoints often return `403` to non-browser clients; design sync/research around official Reddit API or browser-grade access, not anonymous scraping assumptions. +- Community Discord (`discord.gg/pathofexile`) is mixed PoE1+PoE2; treat as low-trust / optional, not allowlist-default for factual claims. + +--- + +## 5. Other PoE1-friendly community tools (secondary allow) + +These are not “official,” but are high-signal and already used by PoB or the PoE1 ecosystem. Prefer paths that **encode PoE1 in the URL**: + +| Tool | PoE1 URL pattern | PoE2 lookalike | +| --- | --- | --- | +| PoEDB | https://poedb.tw/ | https://poe2db.tw/ | +| Maxroll | https://maxroll.gg/poe/ | https://maxroll.gg/poe2/ | +| pobb.in | https://pobb.in/ | (PoB share; confirm content is PoE1 before trusting) | +| FilterBlade / Awakened PoE Trade / etc. | listed on r/pathofexile wiki/tools | N/A — verify game | + +PoB’s own import/export allowlist (`src/Modules/BuildSiteTools.lua`) already distinguishes `maxroll.gg/poe/...` and `poe.ninja/poe1/...`. + +--- + +## 6. PoE2 lookalike traps (denylist seeds) + +### 6.1 Domain / path traps + +| Trap | Why catastrophic | +| --- | --- | +| https://pathofexile2.com/ (+ `/news/rss`) | Separate official PoE2 site and news feed | +| https://www.pathofexile.com/forum/view-forum/2212 | Title: **“Early Access Patch Notes”**; threads are `0.5.x` PoE2 patches (e.g. `0.5.4c`) — same GGG forum chrome as PoE1 | +| https://www.poe2wiki.net/ | Parallel wiki; pages like `/wiki/Spark` exist with **PoE2** gem data | +| https://poe2db.tw/ | Parallel DB site (e.g. `/us/Lightning_Strike`) | +| https://poe.ninja/poe2/ | Explicit PoE2 builds/economy UI | +| https://maxroll.gg/poe2/ | Explicit PoE2 Maxroll namespace | +| https://github.com/grindinggear/poe2-skilltree-export | Official PoE2 tree export (pair of PoE1 `skilltree-export`) | +| `r/PathOfExile2` | PoE2 subreddit | +| Fandom / mirror pages that do not resolve to `poewiki.net` | Easy to confuse with PoE1 wiki | + +### 6.2 Same-name, wrong-game content (examples) + +These names exist as distinct entities in both games; wiki/DB pages are **not interchangeable**: + +| Name | PoE1 | PoE2 | +| --- | --- | --- | +| Spark | https://www.poewiki.net/wiki/Spark | https://www.poe2wiki.net/wiki/Spark | +| Lightning Strike | (poewiki) | https://poe2db.tw/us/Lightning_Strike | +| Ball Lightning / Flame Dash / many legacy skill names | PoE1 gems | Reworked / weapon-gated PoE2 skills | + +**Exclusion rule for implementers:** host + game namespace beats item/skill string equality. Never resolve a bare skill name to the first search hit across games. + +### 6.3 Version-scheme discriminator (patch notes) + +| Game | Typical version strings on GGG forums | Forum | +| --- | --- | --- | +| PoE1 | `3.28.0k`, `3.29.0`, `Content Update 3.x` | `/forum/view-forum/patch-notes` | +| PoE2 EA | `0.5.4c`, `0.5.3 Hotfix N` | `/forum/view-forum/2212` | + +Sync should **hard-fail** (or quarantine) any candidate patch thread whose version matches `^0\.` or whose forum id is `2212`. + +--- + +## 7. Patch-Notes Sync — feed / scrape options ranked + +| Rank | Source | Machine-readable? | PoE1 purity | Completeness for balance changes | Recommendation | +| --- | --- | --- | --- | --- | --- | +| 1 | Forum list + threads: `/forum/view-forum/patch-notes` | HTML only (scrape) | High if scoped to this forum | High (includes hotfixes) | **Primary sync input** | +| 2 | News RSS: `/news/rss` | RSS/XML | Medium (PoE2 mentions possible) | Low–medium (misses many hotfixes) | Secondary “new post” hint only | +| 3 | GGG OAuth / developer API | JSON (other resources) | N/A | **None for patch notes** | Do not use for notes | +| 4 | Unofficial third-party “patch notes APIs” | Varies | Untrusted / often PoE2-focused | Unknown | Not for Knowledge Pack truth | +| 5 | PoE2 forum `2212` or `pathofexile2.com/news/rss` | HTML/RSS | **Wrong game** | N/A | **Deny** | + +### Practical sync shape (design only) + +1. Poll `https://www.pathofexile.com/forum/view-forum/patch-notes` (and paginate if needed). +2. Extract thread ids/titles/dates; filter by title/version heuristics (`3.` / `Content Update`). +3. Fetch each new thread HTML; extract the first staff post body as the note text. +4. Optionally cross-check news RSS for large league announcements — never as the only source. +5. Reject anything from forum `2212`, `pathofexile2.com`, or `0.x` version strings. + +Cloudflare/WAF and forum markup changes are operational risks; there is no first-party schema guarantee. + +--- + +## 8. Suggested Knowledge Pack seed lists + +### Allowlist (domains / path prefixes) + +```text +pathofexile.com # prefer PoE1 paths; still filter patch forum vs 2212 +pathofexile.com/forum/view-forum/patch-notes +pathofexile.com/news/rss # secondary; content-filter required +pathofexile.com/developer/docs +pathofexile.com/api/trade # PoE1 trade API family +github.com/grindinggear/skilltree-export +github.com/grindinggear/atlastree-export +poewiki.net +poe.ninja/poe1/ +reddit.com/r/pathofexile +reddit.com/r/PathOfExileBuilds +reddit.com/r/pathofexiledev +poedb.tw # not poe2db.tw +maxroll.gg/poe/ # not /poe2/ +pobb.in +``` + +### Denylist (domains / path prefixes) + +```text +pathofexile2.com +pathofexile.com/forum/view-forum/2212 +poe2wiki.net +poe2db.tw +poe.ninja/poe2/ +maxroll.gg/poe2/ +github.com/grindinggear/poe2-skilltree-export +reddit.com/r/PathOfExile2 +# Plus any URL whose path or title clearly encodes PoE2 / Early Access 0.x patch notes +``` + +### Content-level exclusion (beyond hosts) + +- Same-named skills/items: require `poewiki.net` / `poe.ninja/poe1` / PoE1 forum evidence before accepting claims. +- Mixed Discord / YouTube / random guides: deny-by-default unless host is allowlisted. + +--- + +## 9. Open risks for later tickets + +- **Anubis on poewiki.net** may block Cargo/API automation; need an access strategy before relying on live wiki sync. +- **News RSS ≠ patch notes**; players already requested a dedicated feed and did not get one. +- **poe.ninja API paths changed**; older community documentation is stale — pin `/poe1/api/...`. +- **Shared GGG forum chrome** makes PoE1 vs PoE2 threads easy to confuse if filtering by “site:pathofexile.com” alone. +- Whether Patch-Notes Sync auto-applies Knowledge Pack edits vs flags for human review remains a product decision (map issue #1). + +--- + +## Sources consulted (primary) + +1. https://www.pathofexile.com/forum/view-forum/patch-notes +2. https://www.pathofexile.com/forum/view-forum/2212 +3. https://www.pathofexile.com/news/rss +4. https://pathofexile2.com/news/rss +5. https://www.pathofexile.com/developer/docs +6. https://www.pathofexile.com/developer/docs/data +7. https://www.pathofexile.com/developer/docs/reference +8. https://www.pathofexile.com/api/trade/data/leagues +9. https://www.pathofexile.com/forum/view-thread/3554274 +10. https://www.pathofexile.com/forum/view-thread/3648166 +11. https://www.poewiki.net/ (incl. Anubis interstitial; Data query API page) +12. https://www.poe2wiki.net/wiki/Spark +13. https://poe.ninja/poe1/ , `/poe1/builds`, `/poe1/api/economy/...`, `/poe1/api/data/index-state` +14. https://poe.ninja/poe2/builds +15. https://github.com/grindinggear/skilltree-export +16. https://github.com/grindinggear/poe2-skilltree-export +17. https://poedb.tw/ , https://poe2db.tw/ +18. https://maxroll.gg/poe , https://maxroll.gg/poe2 +19. https://www.reddit.com/r/pathofexile/wiki/tools +20. In-repo PoB references: `src/Modules/BuildSiteTools.lua`, `src/Modules/ItemTools.lua` From cb21851c1288a5f416a279f5e8907cfd7f536a48 Mon Sep 17 00:00:00 2001 From: Michael Walters Date: Fri, 17 Jul 2026 14:28:52 +0100 Subject: [PATCH 3/6] Add throwaway Agent Window UX prototype (3 variants). EOF Co-authored-by: Cursor --- .../prototype/agent-window/index.html | 645 ++++++++++++++++++ 1 file changed, 645 insertions(+) create mode 100644 docs/pob-cursor-agent/prototype/agent-window/index.html diff --git a/docs/pob-cursor-agent/prototype/agent-window/index.html b/docs/pob-cursor-agent/prototype/agent-window/index.html new file mode 100644 index 0000000000..3627f520ed --- /dev/null +++ b/docs/pob-cursor-agent/prototype/agent-window/index.html @@ -0,0 +1,645 @@ + + + + + + PROTOTYPE — PoB Agent Window UX + + + + + + +
+
+
Fake Path of Building (backdrop only)
+
Tree / Items / Skills — agent window floats above
+ + +
+
+

PoB Agent

+ Composer 2.5 + +
+
+ + + +
+
+
+
You · build thread
+ Can we squeeze more DPS without losing spell suppression? +
+
+
Agent · streaming…
+ I’d trade a bit of life for cluster jewels on the right side — suppression stays ≥100% if we keep the Watcher’s Eye. + poewiki · Spell Suppression + poe.ninja/poe1 · similar trees +
+
+
+
+ Proposal · multi-surface · all-or-nothing + ops JSON +
+
+
+ tree: allocate Midpoint (12 nodes) +− tree: refund Heart of Oak path ++ item: Large Cluster — Spell Blast ++ config: bossing = true
+
+
+ + + +
+
+
+ + +
+
+ Usage 62% of daily cap +
+
+
+ + + + + +
+ + Agent + Build + + +
+ +
+
+ + + + + + From 88fead5afdfe002845c65c69bf2913c65c0553b6 Mon Sep 17 00:00:00 2001 From: Michael Walters Date: Fri, 17 Jul 2026 14:39:55 +0100 Subject: [PATCH 4/6] Prototype: lock winner D (A chat stack + C compact pill). Co-authored-by: Cursor --- .../prototype/agent-window/index.html | 89 +++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/docs/pob-cursor-agent/prototype/agent-window/index.html b/docs/pob-cursor-agent/prototype/agent-window/index.html index 3627f520ed..6c0f81fb36 100644 --- a/docs/pob-cursor-agent/prototype/agent-window/index.html +++ b/docs/pob-cursor-agent/prototype/agent-window/index.html @@ -393,8 +393,8 @@ -->
@@ -403,6 +403,65 @@
Tree / Items / Skills — agent window floats above
+ + + +

PoB Agent

@@ -575,15 +634,16 @@

Agent

+ + + + +
+
...
+
...
+
...
+
+ + +``` + +## Header + +Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph — straight into the candidates. + +## Candidate card + +The diagrams carry the weight. Prose is sparse, plain, and uses the glossary terms (from the `/codebase-design` skill) without ceremony. + +Each candidate is one `
`: + +- **Title** — short, names the deepening (e.g. "Collapse the Order intake pipeline"). +- **Badge row** — recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`). +- **Files** — monospaced list, `font-mono text-sm`. +- **Before / After diagram** — the centrepiece. Two columns, side by side. See patterns below. +- **Problem** — one sentence. What hurts. +- **Solution** — one sentence. What changes. +- **Wins** — bullets, ≤6 words each. e.g. "Tests hit one interface", "Pricing logic stops leaking", "Delete 4 shallow wrappers". +- **ADR callout** (if applicable) — one line in an amber-tinted box. + +No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram. + +## Diagram patterns + +Pick the pattern that fits the candidate. Mix them. Don't make every diagram look the same — variety is part of the point. + +### Mermaid graph (the workhorse for dependencies / call flow) + +Use a Mermaid `flowchart` or `graph` when the point is "X calls Y calls Z, and look at the mess." Wrap it in a Tailwind-styled card so it doesn't feel parachuted in. Style with classDef to colour leakage edges red and the deep module dark. Sequence diagrams work well for "before: 6 round-trips; after: 1." + +```html +
+
+    flowchart LR
+      A[OrderHandler] --> B[OrderValidator]
+      B --> C[OrderRepo]
+      C -.leak.-> D[PricingClient]
+      classDef leak stroke:#dc2626,stroke-width:2px;
+      class C,D leak
+  
+
+``` + +### Hand-built boxes-and-arrows (when Mermaid's layout fights you) + +Modules as `
`s with borders and labels. Arrows as inline SVG `` or `` elements positioned absolutely over a relative container. Reach for this when you want the "after" diagram to feel like one thick-bordered deep module with greyed-out internals — Mermaid won't render that with the right weight. + +### Cross-section (good for layered shallowness) + +Stack horizontal bands (`h-12 border-l-4`) to show layers a call passes through. Before: 6 thin layers each doing nothing. After: 1 thick band labelled with the consolidated responsibility. + +### Mass diagram (good for "interface as wide as implementation") + +Two rectangles per module — one for interface surface area, one for implementation. Before: interface rectangle is nearly as tall as the implementation rectangle (shallow). After: interface rectangle is short, implementation rectangle is tall (deep). + +### Call-graph collapse + +Before: a tree of function calls rendered as nested boxes. After: the same tree collapsed into one box, with the now-internal calls shown faded inside it. + +## Style guidance + +- Lean editorial, not corporate-dashboard. Generous whitespace. Serif optional for headings (`font-serif` works well with stone/slate). +- Colour sparingly: one accent (emerald or indigo) plus red for leakage and amber for warnings. +- Keep diagrams ~320px tall so before/after sits comfortably side by side without scrolling. +- Use `text-xs uppercase tracking-wider` for module labels inside diagrams — they should read as schematic, not as UI. +- The only scripts are the Tailwind CDN and the Mermaid ESM import. The report is otherwise static — no app code, no interactivity beyond Mermaid's own rendering. + +## Top recommendation section + +One larger card. Candidate name, one sentence on why, anchor link to its card. That's it. + +## Tone + +Plain English, concise — but the architectural nouns and verbs come straight from the `/codebase-design` skill. Concision is not an excuse to drift. + +**Use exactly:** module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality. + +**Never substitute:** component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module, when you mean module). + +**Phrasings that fit the style:** + +- "Order intake module is shallow — interface nearly matches the implementation." +- "Pricing leaks across the seam." +- "Deepen: one interface, one place to test." +- "Two adapters justify the seam: HTTP in prod, in-memory in tests." + +**Wins bullets** name the gain in glossary terms: *"locality: bugs concentrate in one module"*, *"leverage: one interface, N call sites"*, *"interface shrinks; implementation absorbs the wrappers"*. Don't write *"easier to maintain"* or *"cleaner code"* — those terms aren't in the glossary and don't earn their place. + +No hedging, no throat-clearing, no "it's worth noting that…". If a sentence could be a bullet, make it a bullet. If a bullet could be cut, cut it. If a term isn't in the `/codebase-design` glossary, reach for one that is before inventing a new one. diff --git a/.agents/skills/improve-codebase-architecture/SKILL.md b/.agents/skills/improve-codebase-architecture/SKILL.md new file mode 100644 index 0000000000..b56969e92f --- /dev/null +++ b/.agents/skills/improve-codebase-architecture/SKILL.md @@ -0,0 +1,71 @@ +--- +name: improve-codebase-architecture +description: Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick. +disable-model-invocation: true +--- + +# Improve Codebase Architecture + +Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability. + +This command is _informed_ by the project's domain model and built on a shared design vocabulary: + +- Run the `/codebase-design` skill for the architecture vocabulary (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**) and its principles (the deletion test, "the interface is the test surface", "one adapter = hypothetical seam, two = real"). Use these terms exactly in every suggestion — don't drift into "component," "service," "API," or "boundary." +- The domain language in `CONTEXT.md` gives names to good seams; ADRs in `docs/adr/` record decisions this command should not re-litigate. + +## Process + +### 1. Explore + +**Scope before you scan — YAGNI.** Deepening a module pays off by making future changes to it easier, so put extra weight on the parts of the codebase that have recently changed. Decide *where* to look before you look: + +- If the user named a direction — a module, a subsystem, a pain point — take it, and skip the inference below. +- Otherwise, walk back a good stretch of the commit history (`git log --oneline`) to find the codebase's hot spots — the files and areas that keep coming up — and let those paths pull your attention first. If the changes are scattered with no clear hot spot, widen the net. + +Read the project's domain glossary (`CONTEXT.md`) and any ADRs in the area you're touching first. + +Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction: + +- Where does understanding one concept require bouncing between many small modules? +- Where are modules **shallow** — interface nearly as complex as the implementation? +- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)? +- Where do tightly-coupled modules leak across their seams? +- Which parts of the codebase are untested, or hard to test through their current interface? + +Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want. + +### 2. Present candidates as an HTML report + +Write a self-contained HTML file to the OS temp directory so nothing lands in the repo. Resolve the temp dir from `$TMPDIR`, falling back to `/tmp` (or `%TEMP%` on Windows), and write to `/architecture-review-.html` so each run gets a fresh file. Open it for the user — `xdg-open ` on Linux, `open ` on macOS, `start ` on Windows — and tell them the absolute path. + +The report uses **Tailwind via CDN** for layout and styling, and **Mermaid via CDN** for diagrams where a graph/flow/sequence reliably communicates the structure. Mix Mermaid with hand-crafted CSS/SVG visuals — use Mermaid when relationships are graph-shaped (call graphs, dependencies, sequences), and hand-built divs/SVG when you want something more editorial (mass diagrams, cross-sections, collapse animations). Each candidate gets a **before/after visualisation**. Be visual. + +For each candidate, render a card with: + +- **Files** — which files/modules are involved +- **Problem** — why the current architecture is causing friction +- **Solution** — plain English description of what would change +- **Benefits** — explained in terms of locality and leverage, and how tests would improve +- **Before / After diagram** — side-by-side, custom-drawn, illustrating the shallowness and the deepening +- **Recommendation strength** — one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge + +End the report with a **Top recommendation** section: which candidate you'd tackle first and why. + +**Use CONTEXT.md vocabulary for the domain, and the `/codebase-design` vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service." + +**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card (e.g. a warning callout: _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids. + +See [HTML-REPORT.md](HTML-REPORT.md) for the full HTML scaffold, diagram patterns, and styling guidance. + +Do NOT propose interfaces yet. After the file is written, ask the user: "Which of these would you like to explore?" + +### 3. Grilling loop + +Once the user picks a candidate, run the `/grilling` skill to walk the decision tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive. + +Side effects happen inline as decisions crystallize — run the `/domain-modeling` skill to keep the domain model current as you go: + +- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md`. Create the file lazily if it doesn't exist. +- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there. +- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. +- **Want to explore alternative interfaces for the deepened module?** Run the `/codebase-design` skill and use its design-it-twice parallel sub-agent pattern. diff --git a/.agents/skills/improve-codebase-architecture/agents/openai.yaml b/.agents/skills/improve-codebase-architecture/agents/openai.yaml new file mode 100644 index 0000000000..706fdca096 --- /dev/null +++ b/.agents/skills/improve-codebase-architecture/agents/openai.yaml @@ -0,0 +1,5 @@ +interface: + display_name: "Improve Codebase Architecture" + short_description: "Find and grill architecture improvements" +policy: + allow_implicit_invocation: false diff --git a/.agents/skills/prototype/LOGIC.md b/.agents/skills/prototype/LOGIC.md new file mode 100644 index 0000000000..fe9a2c29f7 --- /dev/null +++ b/.agents/skills/prototype/LOGIC.md @@ -0,0 +1,79 @@ +# Logic Prototype + +A tiny interactive terminal app that lets the user drive a state model by hand. Use this when the question is about **business logic, state transitions, or data shape** — the kind of thing that looks reasonable on paper but only feels wrong once you push it through real cases. + +## When this is the right shape + +- "I'm not sure if this state machine handles the edge case where X then Y." +- "Does this data model actually let me represent the case where..." +- "I want to feel out what the API should look like before writing it." +- Anything where the user wants to **press buttons and watch state change**. + +If the question is "what should this look like" — wrong branch. Use [UI.md](UI.md). + +## Process + +### 1. State the question + +Before writing code, write down what state model and what question you're prototyping. One paragraph, in the prototype's README or a comment at the top of the file. A logic prototype that answers the wrong question is pure waste — make the question explicit so it can be checked later, whether the user is watching now or returning to it AFK. + +### 2. Pick the language + +Use whatever the host project uses. If the project has no obvious runtime (e.g. a docs repo), ask. + +Match the project's existing conventions for tooling — don't add a new package manager or runtime just for the prototype. + +### 3. Isolate the logic in a portable module + +Put the actual logic — the bit that's answering the question — behind a small, pure interface that could be lifted out and dropped into the real codebase later. The TUI around it is throwaway; the logic module shouldn't be. + +The right shape depends on the question: + +- **A pure reducer** — `(state, action) => state`. Good when actions are discrete events and state is a single value. +- **A state machine** — explicit states and transitions. Good when "which actions are even legal right now" is part of the question. +- **A small set of pure functions** over a plain data type. Good when there's no implicit current state — just transformations. +- **A class or module with a clear method surface** when the logic genuinely owns ongoing internal state. + +Pick whichever shape best fits the question being asked, *not* whichever is easiest to wire to a TUI. Keep it pure: no I/O, no terminal code, no `console.log` for control flow. The TUI imports it and calls into it; nothing flows the other direction. + +This is what makes the prototype useful past its own lifetime: when the question's been answered, the validated reducer / machine / function set can be lifted into the real module on its own. + +### 4. Build the smallest TUI that exposes the state + +Build it as a **lightweight TUI** — on every tick, clear the screen (`console.clear()` / `print("\033[2J\033[H")` / equivalent) and re-render the whole frame. The user should always see one stable view, not an ever-growing scrollback. + +Each frame has two parts, in this order: + +1. **Current state**, pretty-printed and diff-friendly (one field per line, or formatted JSON). Use **bold** for field names or section headers and **dim** for less important context (timestamps, IDs, derived values). Native ANSI escape codes are fine — `\x1b[1m` bold, `\x1b[2m` dim, `\x1b[0m` reset. No need to pull in a styling library unless one is already in the project. +2. **Keyboard shortcuts**, listed at the bottom: `[a] add user [d] delete user [t] tick clock [q] quit`. Bold the key, dim the description, or vice-versa — whatever reads cleanly. + +Behaviour: + +1. **Initialise state** — a single in-memory object/struct. Render the first frame on start. +2. **Read one keystroke (or one line)** at a time, dispatch to a handler that mutates state. +3. **Re-render** the full frame after every action — don't append, replace. +4. **Loop until quit.** + +The whole frame should fit on one screen. + +### 5. Make it runnable in one command + +Add a script to the project's existing task runner (`package.json` scripts, `Makefile`, `justfile`, `pyproject.toml`). The user should run `pnpm run ` or equivalent — never need to remember a path. + +If the host project has no task runner, just put the command at the top of the prototype's README. + +### 6. Hand it over + +Give the user the run command. They'll drive it themselves; the interesting moments are when they say "wait, that shouldn't be possible" or "huh, I assumed X would be different" — those are the bugs in the _idea_, which is the whole point. If they want new actions added, add them. Prototypes evolve. + +### 7. Capture the answer and the prototype + +Once the prototype has answered its question, capture the answer, then capture the prototype the way the [SKILL](SKILL.md) describes. The logic-specific mapping: the validated reducer / machine / function set lifts into the real module (the decision, absorbed); the TUI shell rides along to the throwaway branch that keeps the prototype as a primary source. + +## Anti-patterns + +- **Don't add tests.** A prototype that needs tests is no longer a prototype. +- **Don't wire it to the real database.** Use an in-memory store unless the question is specifically about persistence. +- **Don't generalise.** No "what if we wanted to support X later." The prototype answers one question. +- **Don't blur the logic and the TUI together.** If the reducer / state machine references `console.log`, prompts, or terminal escape codes, it's no longer portable. Keep the TUI as a thin shell over a pure module. +- **Don't ship the TUI shell into production.** The shell is optimised for being driven by hand from a terminal. The logic module behind it is the bit worth keeping. diff --git a/.agents/skills/prototype/SKILL.md b/.agents/skills/prototype/SKILL.md new file mode 100644 index 0000000000..e75d5331ce --- /dev/null +++ b/.agents/skills/prototype/SKILL.md @@ -0,0 +1,26 @@ +--- +name: prototype +description: Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like. +--- + +# Prototype + +A prototype is **throwaway code that answers a question**. The question decides the shape. + +## Pick a branch + +Identify which question is being answered — from the user's prompt, the surrounding code, or by asking if the user is around: + +- **"Does this logic / state model feel right?"** → [LOGIC.md](LOGIC.md). Build a tiny interactive terminal app that pushes the state machine through cases that are hard to reason about on paper. +- **"What should this look like?"** → [UI.md](UI.md). Generate several radically different UI variations on a single route, switchable via a URL search param and a floating bottom bar. + +The two branches produce very different artifacts — getting this wrong wastes the whole prototype. If the question is genuinely ambiguous and the user isn't reachable, default to whichever branch better matches the surrounding code (a backend module → logic; a page or component → UI) and state the assumption at the top of the prototype. + +## Rules that apply to both + +1. **Throwaway from day one, and clearly marked as such.** Locate the prototype code close to where it will actually be used (next to the module or page it's prototyping for) so context is obvious — but name it so a casual reader can see it's a prototype, not production. For throwaway UI routes, obey whatever routing convention the project already uses; don't invent a new top-level structure. +2. **One command to run.** Whatever the project's existing task runner supports — `pnpm `, `python `, `bun `, etc. The user must be able to start it without thinking. +3. **No persistence by default.** State lives in memory. Persistence is the thing the prototype is _checking_, not something it should depend on. If the question explicitly involves a database, hit a scratch DB or a local file with a clear "PROTOTYPE — wipe me" name. +4. **Skip the polish.** No tests, no error handling beyond what makes the prototype _runnable_, no abstractions. The point is to learn something fast. +5. **Surface the state.** After every action (logic) or on every variant switch (UI), print or render the full relevant state so the user can see what changed. +6. **Capture it when done.** Fold any validated decision into the real code, then capture the prototype itself as a **primary source**: commit it to a throwaway branch, out of main, and leave a context pointer to that branch on the implementation issue. Capture the answer too — the verdict and the question it settled — in the issue or a commit. The main branch keeps only the validated decision. diff --git a/.agents/skills/prototype/UI.md b/.agents/skills/prototype/UI.md new file mode 100644 index 0000000000..76c0f6012b --- /dev/null +++ b/.agents/skills/prototype/UI.md @@ -0,0 +1,112 @@ +# UI Prototype + +Generate **several radically different UI variations** on a single route, switchable from a floating bottom bar. The user flips between variants in the browser, picks one (or steals bits from each), then throws the rest away. + +If the question is about logic/state rather than what something looks like — wrong branch. Use [LOGIC.md](LOGIC.md). + +## When this is the right shape + +- "What should this page look like?" +- "I want to see a few options for this dashboard before committing." +- "Try a different layout for the settings screen." +- Any time the user would otherwise spend a day picking between three vague mockups in their head. + +## Two sub-shapes — strongly prefer sub-shape A + +A UI prototype is much easier to judge when it's **butting up against the rest of the app** — real header, real sidebar, real data, real density. A throwaway route on its own is a vacuum: every variant looks fine in isolation. Default to sub-shape A whenever there's a plausible existing page to host the variants. Only reach for sub-shape B if the prototype genuinely has no nearby home. + +### Sub-shape A — adjustment to an existing page (preferred) + +The route already exists. Variants are rendered **on the same route**, gated by a `?variant=` URL search param. The existing data fetching, params, and auth all stay — only the rendering swaps. This is the default; pick it unless there's a specific reason not to. + +If the prototype is for something that doesn't yet have a page but *would naturally live inside one* (a new section of the dashboard, a new card on the settings screen, a new step in an existing flow) — that's still sub-shape A. Mount the variants inside the host page. + +### Sub-shape B — a new page (last resort) + +Only use this when the thing being prototyped genuinely has no existing page to live inside — e.g. an entirely new top-level surface, or a flow that can't be embedded anywhere sensible. + +Create a **throwaway route** following whatever routing convention the project already uses — don't invent a new top-level structure. Name it so it's obviously a prototype (e.g. include the word `prototype` in the path or filename). Same `?variant=` pattern. + +Before committing to sub-shape B, sanity-check: is there really no existing page this could be embedded in? An empty route hides design problems that a populated one would expose. + +In both sub-shapes the floating bottom bar is identical. + +## Process + +### 1. State the question and pick N + +Default to **3 variants**. More than 5 stops being radically different and starts being noise — cap there. + +Write down the plan in one line, in the prototype's location or a top-of-file comment: + +> "Three variants of the settings page, switchable via `?variant=`, on the existing `/settings` route." + +This works whether the user is here to push back or not. + +### 2. Generate radically different variants + +Draft each variant. Hold each one to: + +- The page's purpose and the data it has access to. +- The project's component library / styling system (TailwindCSS, shadcn, MUI, plain CSS, whatever). +- A clear exported component name, e.g. `VariantA`, `VariantB`, `VariantC`. + +Variants must be **structurally different** — different layout, different information hierarchy, different primary affordance, not just different colours. Three slightly-tweaked card grids isn't a UI prototype, it's wallpaper. If two drafts come out too similar, redo one with explicit "do not use a card grid" guidance. + +### 3. Wire them together + +Create a single switcher component on the route: + +```tsx +// pseudo-code — adapt to the project's framework +const variant = searchParams.get('variant') ?? 'A'; +return ( + <> + {variant === 'A' && } + {variant === 'B' && } + {variant === 'C' && } + + +); +``` + +For sub-shape A (existing page): keep all the existing data fetching above the switcher; only the rendered subtree changes per variant. + +For sub-shape B (new page): the throwaway route under `/prototype/` mounts the same switcher. + +### 4. Build the floating switcher + +A small fixed-position bar at the bottom-centre of the screen with three pieces: + +- **Left arrow** — cycles to the previous variant (wraps around). +- **Variant label** — shows the current variant key and, if the variant exports a name, that name too. e.g. `B — Sidebar layout`. +- **Right arrow** — cycles forward (wraps around). + +Behaviour: + +- Clicking an arrow updates the URL search param (use the framework's router — `router.replace` on Next, `navigate` on React Router, etc) so the variant is shareable and reload-stable. +- Keyboard: `←` and `→` arrow keys also cycle. Don't intercept arrow keys when an ``, `