From 7c50c0441f46ac8d959a5efe13205a824c7770cb Mon Sep 17 00:00:00 2001 From: "Claude Opus 4.8" Date: Wed, 29 Jul 2026 15:31:26 +0200 Subject: [PATCH] docs: link file references to their sources Turn backticked paths in the README, AGENTS.md, CONTRIBUTING.md and docs/ into relative markdown links so each reference opens the file, directory or package it names. Rebased onto current master: driver source now lives in srcfl/device-drivers and the in-repo drivers/*.lua tree is gitignored, so driver .lua references stay plain code spans rather than links that would 404, while srcfl/device-drivers is linked to its GitHub repo. Runtime-only files (state.db, config.yaml), release artifacts (manifest.json) and wire identifiers also stay code spans. Every relative link target was verified to exist. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com> --- AGENTS.md | 46 +++++++++++++++++++++----------------- CONTRIBUTING.md | 5 +++-- README.md | 8 +++---- docs/architecture.md | 36 ++++++++++++++++------------- docs/caldav-integration.md | 10 ++++----- docs/development.md | 3 ++- docs/ha-integration.md | 2 +- docs/nibe-local.md | 3 ++- docs/nova-integration.md | 2 +- docs/operations.md | 11 +++++---- docs/rpi-image.md | 2 +- docs/safety.md | 9 ++++---- docs/self-update.md | 7 +++--- docs/writing-a-driver.md | 5 +++-- 14 files changed, 85 insertions(+), 64 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 17623f89..b8d99d95 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,13 +7,17 @@ drivers and an optional Python/CVXPY optimizer. The repository has three explicit modules: -- **Core** — `go/cmd/ftw`, `go/internal` and `web`. Core owns state, - telemetry, control, safety, API and UI. -- **Drivers** — `drivers/*.lua`, hosted by `go/internal/drivers`. Drivers own +- **Core** — [`go/cmd/ftw`](go/cmd/ftw), [`go/internal`](go/internal) and + [`web`](web). Core owns state, telemetry, control, safety, API and UI. +- **Drivers** — `drivers/*.lua`, hosted by + [`go/internal/drivers`](go/internal/drivers). Drivers own vendor protocols and are the only place power signs are converted. This tree - is a generated snapshot of `srcfl/device-drivers`, pinned in - `drivers/BUNDLED_SOURCE.json`; change a driver there, not here. -- **Optimizer** — `optimizer`, behind the contract in `go/internal/mpc`. + is a generated snapshot of + [`srcfl/device-drivers`](https://github.com/srcfl/device-drivers), pinned in + [`drivers/BUNDLED_SOURCE.json`](drivers/BUNDLED_SOURCE.json); change a driver + there, not here. +- **Optimizer** — [`optimizer`](optimizer), behind the contract in + [`go/internal/mpc`](go/internal/mpc). It proposes plans; core validates them and retains a Go fallback. Keep new functionality in core unless it has a narrow versioned contract, @@ -32,11 +36,12 @@ Read [docs/architecture.md](docs/architecture.md) for the system map and - A failed/stale driver receives its autonomous default mode. - Every clamp protects a quantified hardware or control risk. - Persistent device state is keyed by stable hardware identity, not a YAML name. -- SQLite queries stay in `go/internal/state`. +- SQLite queries stay in [`go/internal/state`](go/internal/state). ## Drivers -`go/internal/drivers/lua.go` is the source of truth for the Lua host API. +[`go/internal/drivers/lua.go`](go/internal/drivers/lua.go) is the source of +truth for the Lua host API. Every driver implements `driver_init`, `driver_poll`, `driver_command` and a safe `driver_default_mode`; `driver_cleanup` is optional. Declare catalog metadata in the driver's `DRIVER` block and request only required capabilities. @@ -51,7 +56,7 @@ Drivers are hot-editable and ship without a compilation step. See - Prefer explicit mutexes and simple ownership over clever atomics. - Keep packages cohesive; depend on narrow interfaces. - Put tests beside Go code as `_test.go`. -- Keep full-stack tests in `go/test/e2e`. +- Keep full-stack tests in [`go/test/e2e`](go/test/e2e). - Treat code, types, tests and driver metadata as the detailed documentation. - Add prose only for architecture, safety invariants or operator steps the code cannot explain. @@ -80,7 +85,7 @@ landed on somebody else's finished work. area, whoever — or whatever — wrote it. Planning documents, design specs, task breakdowns and agent scratch notes -stay out of the repository; `.github/check-no-planning-docs.sh` enforces +stay out of the repository; [`.github/check-no-planning-docs.sh`](.github/check-no-planning-docs.sh) enforces this. Commit the change, its tests and a changeset; put the reasoning in the PR description, where it is read during review and then archived. @@ -102,8 +107,9 @@ Lua drivers have syntax and contract checks in the Go test suite. ## Releases Changesets drive versioning. Every user-visible code change needs a -`.changeset/*.md` entry; documentation- and CI-only changes are auto-exempt. -Do not edit `package.json` version or `CHANGELOG.md` manually. +[`.changeset/*.md`](.changeset/) entry; documentation- and CI-only changes are +auto-exempt. Do not edit [`package.json`](package.json) version or +[`CHANGELOG.md`](CHANGELOG.md) manually. Only two release channels exist: @@ -118,14 +124,14 @@ independently but follow the same beta-to-stable progression. See | Concern | Source | |---|---| -| Process wiring and control tick | `go/cmd/ftw/main.go` | -| Configuration schema | `go/internal/config`, `config.example.yaml` | -| HTTP routes | `go/internal/api/api.go` | -| Lua host and registry | `go/internal/drivers` | -| Safety/control | `go/internal/control`, `go/internal/telemetry` | -| Persistence | `go/internal/state` | -| Planner contract/fallback | `go/internal/mpc` | -| Optional optimizer | `optimizer` | +| Process wiring and control tick | [`go/cmd/ftw/main.go`](go/cmd/ftw/main.go) | +| Configuration schema | [`go/internal/config`](go/internal/config), [`config.example.yaml`](config.example.yaml) | +| HTTP routes | [`go/internal/api/api.go`](go/internal/api/api.go) | +| Lua host and registry | [`go/internal/drivers`](go/internal/drivers) | +| Safety/control | [`go/internal/control`](go/internal/control), [`go/internal/telemetry`](go/internal/telemetry) | +| Persistence | [`go/internal/state`](go/internal/state) | +| Planner contract/fallback | [`go/internal/mpc`](go/internal/mpc) | +| Optional optimizer | [`optimizer`](optimizer) | | Driver catalog | `DRIVER` blocks in `drivers/*.lua` | When behavior looks wrong, inspect the source and its tests before adding a new diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f6808e28..54005349 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,8 @@ Thanks for helping build the local energy coordination layer. This document covers the legal bits — for how the code is organized and how to add a driver, -start with `AGENTS.md` and `docs/writing-a-driver.md`. +start with [`AGENTS.md`](AGENTS.md) and +[`docs/writing-a-driver.md`](docs/writing-a-driver.md). ## Website @@ -80,4 +81,4 @@ By making a contribution to this project, I certify that: - Keep PRs focused on one logical change. - New code needs tests; `make verify` must pass before review. - User-visible changes need a Changeset entry (`npx changeset`) — see the - Release Process section in `README.md`. + Release Process section in [`README.md`](README.md). diff --git a/README.md b/README.md index 54ae431d..a79ee482 100644 --- a/README.md +++ b/README.md @@ -142,8 +142,8 @@ workflows. ## Configuration -`config.example.yaml` and the validation types in `go/internal/config` are -the configuration reference. Copy the example for a native development setup: +[`config.example.yaml`](config.example.yaml) and the validation types in +[`go/internal/config`](go/internal/config) are the configuration reference. Copy the example for a native development setup: ```bash cp config.local.example.yaml config.local.yaml @@ -194,8 +194,8 @@ metadata are the detailed reference. - [Home Assistant](docs/ha-integration.md) - [CalDAV](docs/caldav-integration.md) -Other files under `docs/` are focused installation or external-integration -guides. +Other files under [`docs/`](docs/) are focused installation or +external-integration guides. ## Contributing diff --git a/docs/architecture.md b/docs/architecture.md index 8b85ec3b..19840a96 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -10,9 +10,9 @@ make dispatch unsafe. | Module | Source | Runtime | Responsibility | |---|---|---|---| -| Core | `go/cmd/ftw`, `go/internal`, `web` | One Go binary | Configuration, telemetry, state, API/UI, safety, control and fallback planning | -| Drivers | Editable source in `srcfl/device-drivers`; bundled recovery in `drivers/*.lua`; host in `go/internal/drivers` | One sandboxed Lua VM per configured device | Vendor protocol, sign conversion and device commands | -| Optimizer | `optimizer`, contract in `go/internal/mpc` | Optional Python service/process | Solve the long-horizon mathematical plan | +| Core | [`go/cmd/ftw`](../go/cmd/ftw), [`go/internal`](../go/internal), [`web`](../web) | One Go binary | Configuration, telemetry, state, API/UI, safety, control and fallback planning | +| Drivers | Editable source in [`srcfl/device-drivers`](https://github.com/srcfl/device-drivers); bundled recovery in `drivers/*.lua`; host in [`go/internal/drivers`](../go/internal/drivers) | One sandboxed Lua VM per configured device | Vendor protocol, sign conversion and device commands | +| Optimizer | [`optimizer`](../optimizer), contract in [`go/internal/mpc`](../go/internal/mpc) | Optional Python service/process | Solve the long-horizon mathematical plan | Core can run without the optimizer. Hardware cannot be accessed without a driver, but one failed driver is isolated from the others. Optional @@ -38,9 +38,10 @@ changing power math. ## Core -`go/cmd/ftw/main.go` is the composition root. It wires configuration, driver -registry, telemetry, persistent state, control, planning, API and integrations. -Packages under `go/internal` should stay cohesive and communicate through +[`go/cmd/ftw/main.go`](../go/cmd/ftw/main.go) is the composition root. It wires +configuration, driver registry, telemetry, persistent state, control, planning, +API and integrations. +Packages under [`go/internal`](../go/internal) should stay cohesive and communicate through narrow Go interfaces or data types instead of reaching into one another's storage. @@ -57,7 +58,8 @@ telemetry → control/planner → core validation and safety → driver command The in-memory telemetry store owns latest readings and driver health. SQLite owns durable configuration state, history, forecasts, prices, device identity -and learned model state. Database access stays in `go/internal/state`. +and learned model state. Database access stays in +[`go/internal/state`](../go/internal/state). The control loop computes a site target, allocates it across capable assets, applies safety constraints, then sends commands through the driver registry. @@ -73,7 +75,8 @@ may later consume an exact public commit for other products or a higher support level. Each Lua artifact still contains its own `DRIVER` metadata and implements the -FTW lifecycle. `go/internal/drivers/lua.go` is the source of truth for FTW's +FTW lifecycle. [`go/internal/drivers/lua.go`](../go/internal/drivers/lua.go) is +the source of truth for FTW's host API and capability sandbox. Network and protocol capabilities must be granted in configuration. @@ -107,7 +110,8 @@ Drivers and the optimizer release on their own schedules, so core cannot assume the version on the other side of either contract. Both use the same rule. Each side declares the **window** of contract versions it speaks — core in -`go/internal/components` and `go/internal/optimizercontract`, a driver in its +[`go/internal/components`](../go/internal/components) and +[`go/internal/optimizercontract`](../go/internal/optimizercontract), a driver in its `host_api_min`/`host_api_max` metadata, the optimizer in its handshake reply. An overlap of one version is enough. Declaring a single version means a window of one. @@ -140,19 +144,21 @@ relevant code are the detailed executable specification. ## Configuration and interfaces -`config.example.yaml` and the structs plus validation in -`go/internal/config` define the configuration schema. The handlers registered -in `go/internal/api/api.go` define the HTTP surface. Driver metadata defines +[`config.example.yaml`](../config.example.yaml) and the structs plus validation +in [`go/internal/config`](../go/internal/config) define the configuration +schema. The handlers registered in +[`go/internal/api/api.go`](../go/internal/api/api.go) define the HTTP surface. Driver metadata defines the device catalog. These sources replace manually duplicated reference docs. Some startup bindings cannot be hot-reloaded, including state paths, API listener and selected integration transports. Normal device and control -configuration is reloaded through `go/internal/configreload`. +configuration is reloaded through +[`go/internal/configreload`](../go/internal/configreload). ## Future remote access boundary Remote Sourceful access is not implemented by the LAN/API hardening layer. The -central request policy in `go/internal/api` is the expansion point: a future +central request policy in [`go/internal/api`](../go/internal/api) is the expansion point: a future protected remote request must present a locally verifiable principal and access proof there before an API handler runs. @@ -234,7 +240,7 @@ There is no edge channel. See [self-update.md](self-update.md). 1. [site-convention.md](site-convention.md) 2. this document -3. `go/cmd/ftw/main.go` +3. [`go/cmd/ftw/main.go`](../go/cmd/ftw/main.go) 4. the package or driver being changed and its colocated tests 5. [writing-a-driver.md](writing-a-driver.md) for hardware support 6. [operations.md](operations.md) for deployment and recovery diff --git a/docs/caldav-integration.md b/docs/caldav-integration.md index 93138f3c..cf7febaa 100644 --- a/docs/caldav-integration.md +++ b/docs/caldav-integration.md @@ -9,13 +9,13 @@ network. FTW **hosts its own CalDAV server**, in-process, and also runs a CalDAV **client** against it: -- The **server** (`go/internal/caldavserver`) is pure-Go, built on +- The **server** ([`go/internal/caldavserver`](../go/internal/caldavserver)) is pure-Go, built on [`emersion/go-webdav`](https://github.com/emersion/go-webdav) (MIT). It ships inside the single FTW binary — no sidecar, no second container — and persists calendar objects in `state.db`. It binds `:5232` on your LAN so your phone or desktop calendar app can subscribe. Because it's in-process it runs everywhere FTW does, **including a single-container Home Assistant add-on**. -- The **client** (`go/internal/calendar`) polls a collection on that server and +- The **client** ([`go/internal/calendar`](../go/internal/calendar)) polls a collection on that server and maps events onto planner machinery. ``` @@ -87,7 +87,7 @@ languages. What FTW parsed is visible at `GET /api/caldav/status`. **Recurring events work fully.** A weekly *Away* or a daily *Charge car* expands into its individual occurrences server-side (RFC 4791 `CALDAV:expand`, via -`caldavserver/expand.go`), so the planner sees every occurrence inside its +[`caldavserver/expand.go`](../go/internal/caldavserver/expand.go)), so the planner sees every occurrence inside its horizon — not just the first. RRULE, RDATE and EXDATE are all honoured, and if you edit or delete a single occurrence in your calendar app (a per-instance `RECURRENCE-ID` override or cancellation) that one occurrence is updated/removed @@ -121,7 +121,7 @@ collections are kept distinct so FTW never re-reads its own output as input. ## Config -See the `caldav:` block in `config.example.yaml`. The password is stored in +See the `caldav:` block in [`config.example.yaml`](../config.example.yaml). The password is stored in `state.db` (key `caldav_password`), never written to `config.yaml`. URL, credentials, keywords and intervals hot-reload; toggling `enabled` needs a restart. `listen` (default `:5232`) sets the server's bind address. @@ -135,7 +135,7 @@ deploy mode with nothing extra to install: server binds `:5232` directly on the host. Subscribe at `http://:5232/…`. - **docker-compose on macOS (bridge networking):** the main service publishes - `5232:5232` (see `docker-compose.macos.yml`) so phones reach it; keep + `5232:5232` (see [`docker-compose.macos.yml`](../docker-compose.macos.yml)) so phones reach it; keep `caldav.url: http://localhost:5232` (the in-container loopback). - **Home Assistant add-on (single container):** it just works — there is no sidecar at all, so no deploy-mode is gated off. diff --git a/docs/development.md b/docs/development.md index 1b29a681..d17e1d5e 100644 --- a/docs/development.md +++ b/docs/development.md @@ -43,7 +43,8 @@ Set `FTW_PROXY_READONLY=0` only for an intentional live write session. ## Containers `docker compose up -d` mirrors the Linux production topology: core, optimizer, -updater and MQTT broker. Use `docker-compose.macos.yml` on macOS. Local Compose +updater and MQTT broker. Use +[`docker-compose.macos.yml`](../docker-compose.macos.yml) on macOS. Local Compose overrides are machine-specific and untracked. ## Generated files diff --git a/docs/ha-integration.md b/docs/ha-integration.md index 12ee004a..1a24f3d8 100644 --- a/docs/ha-integration.md +++ b/docs/ha-integration.md @@ -23,7 +23,7 @@ homeassistant: After FTW connects, Home Assistant discovers site power/energy, mode, planner, driver health, structured DER readings and emitted diagnostic metrics. The -exact entity set is generated by `go/internal/ha`; it is not maintained +exact entity set is generated by [`go/internal/ha`](../go/internal/ha); it is not maintained manually in this document. Power values keep FTW's site convention: diff --git a/docs/nibe-local.md b/docs/nibe-local.md index 2971325f..f78a0905 100644 --- a/docs/nibe-local.md +++ b/docs/nibe-local.md @@ -130,7 +130,8 @@ thermal/load models consume `hp_power_w` etc. as twins. ## Testing A live integration test exercises the driver against a real pump -(`go/internal/drivers/nibe_local_test.go`, `TestNibeLocalLive`), skipped unless +([`go/internal/drivers/nibe_local_test.go`](../go/internal/drivers/nibe_local_test.go), +`TestNibeLocalLive`), skipped unless `NIBE_LIVE=1`: ```bash diff --git a/docs/nova-integration.md b/docs/nova-integration.md index fed50235..13966f53 100644 --- a/docs/nova-integration.md +++ b/docs/nova-integration.md @@ -8,7 +8,7 @@ This bridge is an engineering integration, not a public remote-access path. ## Boundary -`go/internal/nova` snapshots registered DER telemetry, maps it to the selected +[`go/internal/nova`](../go/internal/nova) snapshots registered DER telemetry, maps it to the selected Nova schema and publishes over MQTT. An ES256 identity signs short-lived MQTT JWTs. Claim and provisioning use HTTPS. diff --git a/docs/operations.md b/docs/operations.md index 093cf3d0..8e95e98a 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -70,14 +70,15 @@ changing ownership or deleting any SQLite sidecar files. ## Configuration -`config.example.yaml` and `go/internal/config` define the current schema. +[`config.example.yaml`](../config.example.yaml) and +[`go/internal/config`](../go/internal/config) define the current schema. Edits are validated before application. A rejected hot reload leaves the previous live configuration intact. Driver set and most control values reload live. Listener addresses, state paths and some integration transports are startup bindings; restart after changing them. When unsure, inspect the restart classification in -`go/internal/config/restart_required.go`. +[`go/internal/config/restart_required.go`](../go/internal/config/restart_required.go). ## LAN and API access @@ -181,7 +182,8 @@ driver configuration. ### Configuration rejected -Read the validation error, compare with `config.example.yaml`, fix the file and +Read the validation error, compare with +[`config.example.yaml`](../config.example.yaml), fix the file and save again. Do not delete `state.db` to resolve a YAML error. ### Port already in use @@ -196,7 +198,8 @@ Stop the conflicting service or change the configured API port, then restart. `make build-arm64` and `make build-amd64` produce static Core and `ftw-backup` binaries. -`deploy/ftw.service` is the reference systemd unit. A conventional layout is: +[`deploy/ftw.service`](../deploy/ftw.service) is the reference systemd unit. A +conventional layout is: ```text /opt/ftw/ binary, web, bundled drivers, optional optimizer diff --git a/docs/rpi-image.md b/docs/rpi-image.md index ba370402..00285a84 100644 --- a/docs/rpi-image.md +++ b/docs/rpi-image.md @@ -110,7 +110,7 @@ beta/stable updater. ## Build the image -Image provisioning lives under `deploy/pi-gen`: +Image provisioning lives under [`deploy/pi-gen`](../deploy/pi-gen): ```bash deploy/pi-gen/build.sh diff --git a/docs/safety.md b/docs/safety.md index c1c646e2..01371c4d 100644 --- a/docs/safety.md +++ b/docs/safety.md @@ -27,8 +27,8 @@ normal dispatch resumes only after the required readings recover. Source and executable specification: -- `go/internal/telemetry` -- the control tick in `go/cmd/ftw/main.go` +- [`go/internal/telemetry`](../go/internal/telemetry) +- the control tick in [`go/cmd/ftw/main.go`](../go/cmd/ftw/main.go) - watchdog and stale-meter tests beside those packages ## Dispatch limits @@ -49,8 +49,9 @@ The reactive fuse saver may bypass normal slew limiting because preventing a fuse trip is the higher-priority quantified risk. It must still respect available battery energy and hardware power capability. -The implementation and tests in `go/internal/control` are authoritative. -Planner-side limits in `go/internal/mpc` reduce infeasible plans but do not +The implementation and tests in [`go/internal/control`](../go/internal/control) +are authoritative. Planner-side limits in [`go/internal/mpc`](../go/internal/mpc) +reduce infeasible plans but do not replace runtime enforcement. ## Planner and model containment diff --git a/docs/self-update.md b/docs/self-update.md index ac5da20d..2a10ff67 100644 --- a/docs/self-update.md +++ b/docs/self-update.md @@ -13,13 +13,14 @@ migrated to `beta`; no edge releases are published or accepted. ## Release progression User-visible changes land with a Changeset. The Changesets workflow opens the -Version Packages PR and updates `package.json` plus `CHANGELOG.md`. +Version Packages PR and updates [`package.json`](../package.json) plus +[`CHANGELOG.md`](../CHANGELOG.md). After that PR merges: -1. run `beta.yml` with `vX.Y.Z-beta.N`; +1. run [`beta.yml`](../.github/workflows/beta.yml) with `vX.Y.Z-beta.N`; 2. validate that immutable build on real sites; -3. manually dispatch `release.yml` from that same commit; +3. manually dispatch [`release.yml`](../.github/workflows/release.yml) from that same commit; 4. stable promotion verifies that a matching beta tag resolves to the exact stable candidate commit; 5. release assets publish `vX.Y.Z` and move the stable aliases. diff --git a/docs/writing-a-driver.md b/docs/writing-a-driver.md index 35b7be49..7a2628b3 100644 --- a/docs/writing-a-driver.md +++ b/docs/writing-a-driver.md @@ -86,7 +86,8 @@ the driver is a bug. ## Host capabilities -`go/internal/drivers/lua.go` is the complete, current host API. It exposes +[`go/internal/drivers/lua.go`](../go/internal/drivers/lua.go) is the complete, +current host API. It exposes telemetry, diagnostic metrics, identity, time/JSON helpers and capability-gated MQTT, Modbus, HTTP, WebSocket and raw TCP operations. @@ -141,7 +142,7 @@ instead of hand-rolling the byte loop. before enabling automatic dispatch. 7. Add configuration example only when the integration needs non-obvious operator input. -8. Add Go-hosted Lua tests beside `go/internal/drivers`. +8. Add Go-hosted Lua tests beside [`go/internal/drivers`](../go/internal/drivers). Useful checks: