16 Commits
Author SHA1 Message Date
timka123 d280101bce disallow to disable guard during focus
docker / image (push) Failing after 24s
lint / ruff (push) Failing after 28s
tests / backend (push) Successful in 37s
tests / desktop (push) Successful in 22s
tests / android-core (push) Successful in 49s
tests / web (push) Successful in 1m20s
android / apk (push) Successful in 8m49s
2026-10-28 09:50:58 +03:00
timka123 1bdd65cc76 implement ota updates
lint / ruff (push) Successful in 14s
tests / desktop (push) Successful in 22s
docker / image (push) Successful in 43s
tests / web (push) Successful in 39s
tests / android-core (push) Successful in 1m1s
tests / backend (push) Successful in 2m12s
android / apk (push) Successful in 6m50s
2026-10-22 21:11:04 +03:00
timka123 2aaa58af0b include glyph sdk to release build
tests / backend (push) Successful in 37s
tests / desktop (push) Successful in 21s
lint / ruff (push) Successful in 1m0s
tests / web (push) Successful in 32s
docker / image (push) Failing after 1m34s
tests / android-core (push) Successful in 7m13s
android / apk (push) Successful in 12m36s
2026-10-22 19:25:44 +03:00
timka123 8c260feb78 cicd fixes and desktop fix
docker / image (push) Failing after 5s
lint / ruff (push) Successful in 40s
tests / desktop (push) Successful in 20s
tests / web (push) Successful in 33s
tests / android-core (push) Successful in 39s
tests / backend (push) Successful in 6m52s
android / apk (push) Successful in 4m51s
2026-10-22 19:08:07 +03:00
timka123 6ae1a1a73d fix image ci job 2026-10-22 18:52:42 +03:00
timka123andClaude Opus 5 8415c8995b ci: the runner is docker-mode, and a guide for setting one up
`runs-on: ubuntu-latest` stays what it is everywhere else — a disposable
container — and `docker-compose.ci.yml` is now what makes that true: the label
maps to `catthehacker/ubuntu:act-24.04`, job containers share the runner's
network so the cache server is reachable at all, and the host's docker socket is
passed through (`docker_host: automount`) so the image workflow can build.

The failure this is replacing is worth naming, because the symptom does not
point at it: with a **host**-mode runner answering the same label, every job runs
directly on the server as the runner's own user, so the workflows' `apt-get
install` steps fail on permissions — and had they succeeded, CI would have been
installing packages on a host. A workflow cannot tell which kind of runner it
got; only the label map can say.

`docs/gitea-runner.md` is the separate guide: why docker mode, the prerequisites
including Gitea's two `app.ini` settings, the install, how to remove a host-mode
runner (and what to check before you do, since a runner is shared), how to
verify a job really is in a container, why labels cannot be changed without
re-registering, and a table of the failures with their causes.

Also `workflow_dispatch` on the test workflow, so a fresh runner can be proved
from the UI without inventing a commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 18:35:20 +03:00
timka123andClaude Opus 5 38f346883c docs: the counts, and two claims that are now true
android / apk (push) Failing after 59s
lint / ruff (push) Failing after 3s
tests / backend (push) Failing after 2s
tests / desktop (push) Failing after 2s
tests / web (push) Successful in 27s
tests / android-core (push) Failing after 3s
docker / image (push) Canceled after 8s
274 Android tests, not 234; 73 backend, not 71. The new suites named alongside
the others, since that table is how anyone finds out what is already covered.

Two sentences in the Android README described the intent rather than the code,
and both now describe the code: "offline, the server's last answer keeps
working" (it is on disk, so it survives the process — and the process Android
starts for the accessibility service is the one that needed it most), and
"offline writes go to an outbox" (they do now; it says which guarantees that
buys, and what happens to an interval that cannot be filed honestly). The `:app`
module never contained Room, either.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 18:02:04 +03:00
timka123andClaude Opus 5 8e659cd315 desktop: an agent and a mini timer, not an application window
The design of the full desktop app was still bad, and the reason it stayed bad
is that it had no business existing. Five pages — Timer, Tasks, Blocker, Report,
Settings — each a second view of the *server's* data: a second place to be
wrong, a second place to style, and a second place to keep in step with a web
app that already does lists, rules and reports properly in a browser, on any
device. So it is gone, along with 2,100 lines of QML and the controller surface
that fed it.

What is left is what only a program on your machine can do, plus the two
surfaces worth having in front of you:

* **the tray menu — unchanged**, as asked. All fourteen entries, plus
  "Settings…". "Open Kadence" now brings the countdown up, because that is what
  it meant all along.
* **the mini timer**, which is now the whole of this app's own interface and so
  finally carries the house style on its own: warm surface, hairline instead of
  shadow, a letterspaced eyebrow, the progress lane as a rule, and exactly one
  filled button — whichever action is the obvious next one. The countdown is set
  in the display serif on a **fixed advance**, because Fraunces has no tabular
  figures and ships no `tnum`: each glyph now sits in a box as wide as the widest
  digit, instead of the clock shifting sideways on every tick. The phone client
  documents the same trap; the desktop had it too and nobody had looked.
  It also says OFFLINE when the backend is away — the count and the clock are
  both the server's, and this is the only window left to say it in.
* **a settings dialog**, because a fresh install has to be pointed at a backend
  somewhere. One column, hairline sections: the backend and its token, where the
  mini timer sits, what this machine enforces, and what it does to the session.
  It edits `desktop.json` and nothing else — with one honest exception, view-only
  mode, which is a server-side flag the other clients have to honour too.

Structurally, `app.py` no longer loads a QML root at all: a plain `QQmlEngine`
with the mini timer as a component, and the dialog built on demand. Everything
that used to ask "is the main window visible?" is simpler for not having one —
the mini timer's auto-show rule is now just "an interval is running", which is
what the setting always claimed.

The window-only controller surface goes with the pages: tasks and task writes,
stats, the daily report, the settings document, the presets, the cycle and
interruption counters, and their worker jobs and API calls. Two things were kept
deliberately: the task list is still read once a minute for one fact — which task
is pinned, so "Start focus" from the tray attaches what you pinned this morning —
and `timerEnabled` now comes from the timer state, which already carries
`timer_enabled`, rather than from a second fetch of a document this app can no
longer edit.

Also: `--settings` and a `Settings` D-Bus verb; toasts go to the tray's balloon
now that the snackbar is gone; the launcher entry starts the agent and grew a
Settings action; a systemd user unit is shipped rather than only written at
runtime; and both READMEs say what this piece is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 18:00:52 +03:00
timka123andClaude Opus 5 bc6ff3c489 ci: gitea actions for the suites, the image and the APK
Four workflows, none of which existed:

* `tests.yml` — backend, desktop, `:core` and the web app's typecheck+build, in
  parallel. `:core` needs no Android SDK at all, because settings.gradle.kts
  leaves `:app` out when there is none; `:app`'s Robolectric suite therefore
  lives in the APK workflow, where the SDK is installed anyway. Nothing needs a
  running backend — the suites feed on committed fixtures, which is the whole
  reason they were written that way.
* `lint.yml` — ruff, which the repo had been configured for and never run.
* `docker.yml` — builds the image, *starts* it against SQLite and waits for
  /api/health before pushing, then publishes `:latest` and `:<short-sha>` to
  git.timka.su. A tag nobody can run is worse than no tag.
* `android-release.yml` — the release APK on every push to master, signed from
  secrets, with `apksigner verify --print-certs` reporting which key was really
  used and failing if it was the debug one.

Three things the repo needed before any of that could work:

`versionCode` was pinned at 1, so every APK looked like the same version to the
phone and would not install over its predecessor. It now comes from the run
number, with `versionName` from a property, both via one `setting()` helper that
reads a Gradle property or an environment variable.

The release build fell back to the *debug* signing key whenever the keystore was
missing — silently. That is a trap: the APK installs, looks fine, and can never
be upgraded by a properly signed build. `KADENCE_REQUIRE_SIGNING=1` makes that
case fail the build instead, and the release job sets it whenever a keystore
secret is present.

The compose files built an image with no name, so there was nothing to pull.
`image:` now names the registry tag while `up --build` still builds locally.

Also: `android/ci/install-sdk.sh` (idempotent, cacheable, and the one place that
knows `platforms;android-37.0` is spelled with the minor version), the runner as
a compose service with its config and a setup checklist, six make targets, and a
README section. No runner is registered yet — `make ci-runner` is step two of
that checklist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 17:33:27 +03:00
timka123andClaude Opus 5 5facf41f10 blocker: an exception reads like a block, so make it behave like one
`youtube.com` on the block list catches `music.youtube.com` — a plain entry
covers its subtree. `youtube.com` on the *allow* list caught only itself,
because exceptions were glob-matched whatever they looked like and a plain name
has no metacharacters. The same text in two boxes of the same form meant a
subtree in one and a single hostname in the other, with nothing in the UI
saying so.

A plain exception now covers its subdomains; a masked one is glob-matched
exactly as before, which is still how you spare something narrower than a whole
subtree (`docs.*`, `*.docs.google.com`).

Three enforcers decide this and all three had to move together, or an exception
would work in the browser and not at the resolver: `blocker_service.is_allowed`,
`:core`'s `isAllowed`, and the desktop DNS stub's own allow check, which was
reusing the generic glob matcher. Parity tests on both sides, plus the two
sentences in the web UI and the README that promised the old behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 17:24:17 +03:00
timka123 5c6710ae3d docs: make targets and counts for the guard
desktop-test runs the 39 desktop tests in the backend venv (an absolute
path, so pytest does not warn about sys.prefix); the root README explains
that a '*' in the expansion list means every subdomain, and that the desktop
app answers the zones a hosts file cannot express.
2026-10-19 18:45:50 +03:00
timka123andClaude Opus 5 632dee1f12 docs: four pieces, not three
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 16:17:38 +03:00
timka123andClaude Opus 5 738734d485 docs: the phone client, and the Glyph traps worth not re-learning
The android README still described the app as unbuilt and pointed `glyph/libs/`
at a path that does not exist. It now covers what the app is made of, how to get
the SDK, and the three Glyph behaviours that cost an afternoon — two of which
fail with no exception and no log at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 16:17:25 +03:00
timka123 2be074f191 add auth 2026-10-19 09:41:32 +03:00
timka123 77b26757bf refactor: enhance TimerPage layout and functionality; update theme and tokens for improved design consistency
- Refactored TimerPage component to improve layout and user experience.
- Updated theme configuration to utilize design tokens for better maintainability.
- Introduced new tokens for colors and typography to align with design principles.
- Simplified state management and improved timer functionality.
- Enhanced accessibility and responsiveness of UI elements.
2026-10-18 23:57:36 +03:00
timka123 17a0a3c4c9 init 2026-10-18 18:02:05 +03:00