chore: move firmware skills in-repo + AGENTS.md

- Migrates tactility-firmware and tactility-bluetooth from
  ~/.hermes/skills/ into .claude/skills/ for repo co-location
- Adds AGENTS.md with local workstation context, board table,
  board-direct build rule, Hermes PYTHONPATH pitfall, common
  Tactility pitfalls, and in-repo skill index

Refs: personal Gitea mirror
This commit is contained in:
Adolfo Reyna
2026-07-17 09:51:08 -04:00
parent e7fb5fb38f
commit 12035f2f39
22 changed files with 2362 additions and 0 deletions
@@ -0,0 +1,60 @@
# MCP Settings Persistence Bug — SD vs Internal (2026-07-13)
## Symptom
User enables MCP Screen in Settings → go back → shows disabled again. `/api/mcp` returns `404: MCP server is disabled`. Board: ES3C28P (color 2.8" ILI9341V, 16MB + PSRAM, `storage.userDataLocation=SD`).
## Root Cause
`Tactility/Source/settings/McpSettings.cpp` hardcoded:
```cpp
constexpr SETTINGS_FILE = "/data/service/mcp/settings.properties";
file::findOrCreateDirectory("/data/service/mcp", 0755);
```
- On SD boards, `getUserDataPath()` = `/sdcard/tactility` (see `Paths.cpp:getUserDataRootPath()` → finds SDCARD_TYPE FS)
- Internal `/data` partition may not be mounted, is read-only, or gets wiped on SD mode
- `save()` wrote to internal, `load()` on next boot looked at internal but file missing → return false → default `mcpEnabled=false`
Other settings (`WebServerSettings`, `DisplaySettings`, `DevelopmentSettings`) correctly used `getUserDataPath() + "/settings/..."`.
## Fix Applied 2026-07-13
```cpp
#include <Tactility/Paths.h>
static std::string getSettingsFilePath() {
return getUserDataPath() + "/settings/mcp.properties";
}
bool load(...) {
auto p = getSettingsFilePath();
if (!file::isFile(p)) return false;
return file::loadPropertiesFile(p, map);
}
bool save(...) {
auto p = getSettingsFilePath();
if (!file::findOrCreateParentDirectory(p, 0755)) return false;
return file::savePropertiesFile(p, map);
}
McpSettings loadOrGetDefault() {
McpSettings s;
if (!load(s)) { s=getDefault(); save(s); }
return s;
}
```
## Pitfall Checklist for New Settings
- [ ] Never hardcode `/data/...`
- [ ] Use `getUserDataPath()` + "/settings/<name>.properties"
- [ ] Include `Tactility/Paths.h`
- [ ] Guard load with `isFile(path)` before parse (matches WebServerSettings)
- [ ] Use `findOrCreateParentDirectory` not `findOrCreateDirectory` on file path
- [ ] `loadOrGetDefault()` saves default if missing
## Verification
- Ad-hoc script `hermes-verify-*.py` checks old path removed + new path present + parent dir creation
- Live probe: after enabling MCP on device, POST `/api/mcp {method:tools/list}` returns tool list, not 404
- SD path physically: `/sdcard/tactility/settings/mcp.properties` should contain `mcpEnabled=true`
## Related Bugs Same Session
- HttpServer ctrl_port collision (8080+6666 both 32768) → `config.ctrl_port = 32768 + (port % 1000)`
- DisplayIdle early return on `!supportsBacklightDuty()` → killed timer, broke MCP manual activation on RLCD
- `CONFIG_HTTPD_MAX_URI_HANDLERS=8` < 9 needed → bump to 20
- Video stream task stack 4096 → 8192 + idle delay 50ms
See `webserver-devserver.md` for full coexistence debugging.