aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
diff options
context:
space:
mode:
Diffstat (limited to 'AGENTS.md')
-rw-r--r--AGENTS.md88
1 files changed, 88 insertions, 0 deletions
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..4a13a10
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,88 @@
+# slackware-changelog
+
+A widget for a [Homepage](https://gethomepage.dev) dashboard, one of the
+modules under `homepage-modules/`. Siblings share the shape described below.
+
+## Shape of a module
+
+Homepage's `customapi` widget fetches JSON and maps fields to labels. It
+cannot parse text, cannot do arithmetic, and refetches on every refresh
+interval. So each module is a small stdlib Python HTTP service on localhost
+that does the parsing and caching, plus:
+
+- `<name>.py` - the service, single file, stdlib only, executable
+- `test_<name>.py` - self-check, fakes the upstream, prints `ok`
+- `<name>.service` - systemd unit, `DynamicUser=yes`, config via `Environment=`
+- `homepage-services.yaml` / `homepage-settings.yaml` - snippets to merge
+
+No dependencies. No framework. `ThreadingHTTPServer` and `urllib`.
+
+## This module
+
+Serves the latest Slackware -current ChangeLog entry on `127.0.0.1:8098`.
+
+Endpoints: `/latest` (counts and summary), `/top` (the first `TOP_N` packages
+as flat fields), `/packages` (every package with its action), `/entry` (HTML
+page of the entry), `/refresh` (bypass the cache).
+
+### Why /top exists
+
+`customapi` maps a fixed list of field names to labels. It cannot iterate an
+array and cannot scroll, so a JSON list of packages renders as nothing. `/top`
+flattens the first `TOP_N` into `pkg1`, `pkg2`, ... which the widget can map
+one per row, and appends a `... and N more` row in the next slot when the entry
+is longer. The card links to the ChangeLog for the rest.
+
+Mapping more `pkgN` fields than the entry has packages is harmless: Homepage
+skips a field that is absent. So the snippet maps a fixed 9 rows regardless of
+how many that day's entry holds.
+
+### ChangeLog format
+
+The file is reverse-chronological. Entries are separated by a `+--------+`
+line, so the latest entry is everything before the first separator.
+
+Inside an entry:
+
+```
+Mon Sep 7 22:56:39 UTC 2026
+a/util-linux-2.42.3-x86_64-1.txz: Upgraded.
+ This update fixes bugs and security issues.
+ (* Security fix *)
+l/cryptopp-8.9.0-x86_64-3.txz: Rebuilt.
+ Fixed library location. Thanks to Petri Kaukasoina.
+```
+
+Gotcha: a package line is identified by **starting at column 0**, not by
+ending in a period. Note lines are indented and frequently end in a period
+too ("Thanks to Petri Kaukasoina."), so a period-based regex counts prose as
+packages. The test asserts this specific case.
+
+Actions seen: `Upgraded`, `Rebuilt`, `Added`, `Removed`. The parser counts
+whatever word it finds rather than matching a fixed list, so a new action
+verb shows up in `summary` instead of vanishing.
+
+Security fixes are marked by the literal `(* Security fix *)` on its own
+indented line. CVE ids appear in the notes as cve.org URLs.
+
+### Caching
+
+`CACHE_TTL` (default 1800s) governs upstream fetches; the widget's
+`refreshInterval` only redraws. A failed refresh keeps the previous entry and
+adds a `stale` field with the error, so an unreachable mirror degrades to an
+old entry rather than a blank card. `/refresh` forces a fetch but obeys the
+same fallback.
+
+### Rendering
+
+`/entry` escapes everything from the ChangeLog with `html.escape` before it
+reaches the page. Upstream text is not trusted markup; the test asserts no
+`<script` survives.
+
+## Conventions
+
+- GPLv2 only. Header notice in every source file.
+- Snippet YAML files are merged into Homepage's config by hand, never copied
+ over it; the comments at the top of each say so.
+- Layout groups: any group omitted from `layout:` falls to the bottom of the
+ page, so the snippet reminds the reader to list their existing groups too.