A repo with `.dep-freshness-no-workflow` at its root keeps the local pieces
(delegate script, pre-commit hook) but the installer no longer creates or
rewrites `.github/workflows/dependency-freshness.yml` there. teatr-pw
deleted its workflows on purpose (no GitHub Actions minutes on a private
repo; its pre-push hook runs `--all --strict`), and without this every
fleet sweep would put the workflow back.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8tUQzqQmiidbg8ZXmeQAg
TUTOR_FROM = 2026-10-10 (the day after the deploy). The automation tutor
keeps confirmed_on=None, so it stays "new".
Ladder floor: a rung comes off the floor only for a pure bonus (no
penalty_from) or an earner whose penalty has started -- the same
_penalised() rule (first_credits -> penalty_start, confirmed_on fail-closed
fallback, penalty_from without first_credits) the gaming base uses. Before
this, a never-credited tutor would have lowered 2026-10-10's floor by its
full 50-minute rung (19:50 -> 19:00) even with first_credits. Maturity can
only raise time: new property test against the 0.6.0 floor formula.
ruff 0.17.0 (dependency-freshness): CPY001 ignored -- personal repo, no
copyright policy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdfmsGMht9ypyvxGPmNF1V
CI's earned_time pre-commit flagged tests/_maturity_rows.py: the local
--all-files run predated git tracking of the new files. Same exclude as
steam-backlog-enforcer's tests/_*.py helpers. No package code changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdfmsGMht9ypyvxGPmNF1V
How far to trust a gate is computed from its signed ledger: new / maturing /
mature (>= 14 verified credit days, first >= 21 days old), with a read-only
CLI table. Only signed, non-manual, paying credits count; a gate's own
re-evaluation grant (detail.grant_of, book-guard bonus: rows) counts at
amount 0.
Earner.confirmed_on (hand-set) is new. penalty_start = max(penalty_from, day
after first real credit, day after confirmed_on); resolve/base_for take an
optional first_credits map and can only delay a penalty, never add one.
Omitted, every day resolves exactly as in 0.5.0.
Fail closed: a gate with confirmed_on and no credit on record (unreadable
key/ledger, deleted or emptied ledger) falls back to max(penalty_from, day
after confirmed_on), so deleting a ledger cannot lift a penalty.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WdfmsGMht9ypyvxGPmNF1V
freedays 0.3.0 pinned crdt-sync v0.9.1 exactly, so any consumer that
moved to crdt-sync 0.9.2 (screen-locker) could not resolve both in a
clean venv. No code change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RHwVPyiiQMPh4M3JQwQku
**FileCredentialStore.save wrote every save through one fixed `<cache>.tmp`.**
Two processes share a credential cache (a long-running stream and a periodic
sync), so one could rename the other's half-written temp file into place and
leave a truncated refresh token behind. Each save now gets its own mkstemp
file in the same directory -- created 0600 from the outset, so the token is
never world-readable -- and replaces the cache atomically. A failed write
unlinks its temp file and leaves the previous cache untouched.
Tests cover: no temp file left after a save, distinct temp names per write,
and an interrupted write keeping the old credentials with no leaked temp.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RHwVPyiiQMPh4M3JQwQku
~/src/daily-limits' user timer publishes today's limits for its phone app
and answers its requests, with a Firebase session of its own.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcxgxMaQHMSGWzLRpNSkuP
From LADDER_FROM (2026-10-10) shutdown is read down from sleep: ceiling =
WAKE_MINUTES (07:00) - 8h = 23:00, floor = ceiling - every earner's rung
(workout 110, LeetCode 50, reading 30, Anki 25, Automation 25) = 19:00.
A second workout earns nothing more. Earlier days resolve exactly as in
0.3.0 (frozen 23:00 ceiling); gaming is unchanged for every input.
WORKOUT gets a signed ledger (~/.local/share/workout_locker/ledger.json,
written by screen-locker) and workout_match; a rest_day row counts only if
declared before its day began. New API: first_credit_at, credit_units,
day_window, shutdown_minutes_for, extra_shutdown_minutes_for,
shutdown_ceiling_for, on_ladder, LADDER/Rung, LADDER_FROM, WAKE_MINUTES.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QrjUTo1yhgAsUUasdw4bpG
subosito/flutter-action's cache is the whole SDK, 1.6 GB: ~38 s to restore
on every Flutter job even on a hit, which alone kept them over a minute.
This composite action caches a depth-1 clone of the release tag with only
the host and web artifacts (393 MB zstd'd); verified locally that test,
analyze --fatal-infos and build web run on it without lazy downloads.
Release APK builds keep subosito, which they need for the Android engines.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBbkN4ZB4aUPJPzR2prRan
screen-locker and leetcode-guard each carried a near-identical copy of the
reader for wake-alarm's signed morning_session.json; book-guard is about to
need it too. The policy stays in wake-alarm -- this only answers "signed,
dated today, now < exempt_until". Path and retry budget are arguments so
each consumer's test redirect keeps working.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FBbkN4ZB4aUPJPzR2prRan
blocker split blocked_by on the first "@", so "transitive:@scope/name@1.2.3"
read the package name as "" and left "scope/name@1.2.3" stuck to the
version -- the format check then rejected the entry outright. rpartition
splits on the last "@" instead, which is always the version separator
even when the package name carries its own leading "@".
Found adding a typescript-eslint entry (blocked by its own
@typescript-eslint/scope-manager not having published 8.70.1 yet) in
signal-bot.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Zg7ULrGyB1gMcKC1mkbLb
Two forks per path (`git ls-files --error-unmatch`, `grep -qiE <<<`) made
the gate a 5 s step on a 2053-file repo. The tracked set now comes from a
single `git ls-files -z -- "$@"` and the extension match is bash's own
`=~` on the lower-cased path (same case-insensitive semantics as `grep -i`).
0.2 s on the same tree; verdicts unchanged (blocked / allowlisted /
untracked-ignored checked on a throwaway repo).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016P4KZw1E2sXJjYqfcZWf1x
Three findings from proving the JitPack fix on the TachiyomiSY gate:
- The first repository with a stable answer used to win. Central still
carries com.github.ben-manes:gradle-versions-plugin 0.11.1 (2016) while
the plugin portal has 0.54.0, so a 0.51.0 pin graded "current". Every
repository is now asked and the newest stable across the pooled lists is
the answer.
- ~900 lookups, eight at a time, each resolving the registry host (A+AAAA)
per connection: the LAN router's forwarder started dropping queries for
every client on the LAN and the gate degraded itself. socket.getaddrinfo
is memoised per (host, port, hints) for the run, and the per-host probe
runs under a lock so eight workers make one probe, not eight.
- A resolver EAI_AGAIN ("temporary failure") during the probe marked the
registry unreachable for the whole run. The probe retries it
HTTP_ATTEMPTS times; a permanent failure still fails immediately.
Both retry loops lost their unreachable `return` sentinel (http.py is at
100% branch now); the resolver tests live in test_http_resolver.py to keep
test_http.py under the 250-line cap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016P4KZw1E2sXJjYqfcZWf1x
Since ~2026-09-14 https://jitpack.io/.../maven-metadata.xml answers HTTP 401
"No access token" (artifact POM/AAR downloads still 200), which turned every
com.github.* coordinate into "undeterminable" (exit 3) in --strict CI.
JitPack is dropped from MAVEN_REPOS. A com.github.<owner>:<repo> name that no
HTTP repository knows is now answered from the GitHub repo's tags via
`git ls-remote --tags --refs` -- JitPack's version IS the tag name, so the
tags are compared as they are (v prefix included). The tag list goes through
the same stable-first selector as maven-metadata, so a newer pre-release tag
does not win. Not an allowlist entry, not a token.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016P4KZw1E2sXJjYqfcZWf1x
The packageManager field was bumped after the last install, so CI's
--frozen-lockfile refused it (ERR_PNPM_FROZEN_LOCKFILE_WITH_OUTDATED_LOCKFILE).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
The ruff bump surfaced one ANN001 in test_window.py. ts-core / web-ui /
ts-config workflows gain workflow_dispatch so a workflow-only change (like the
last commit, which touched no ts_core/** path and therefore never ran) can be
re-verified on demand.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
ts_core and web_ui depend on `link:../ts_config`. pnpm links the directory
and installs nothing inside it, and a flat config imports its plugins from
the preset's own node_modules, so CI died on "Cannot find package
'@eslint/js'" while the local tree (where ts_config had been installed by
hand) passed. Both workflows now `pnpm install` in ts_config first, and
trigger on ts_config/** too so a preset change re-verifies its consumers.
The same push turned the dependency-freshness job red on five pins that had
aged underneath it (ruff 0.16.8 in four requirements.txt,
flutter_secure_storage 11.2.0 in crdt_sync_flutter); bumped, pubspec.lock is
ignored in that package so nothing else moves.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
The two shared packages are the first repos on the preset (link:../ts_config
inside the monorepo; consumers outside use the git tag). Each eslint.config.js
is now the defineConfig() call plus one override with its reason: a public
API name (`nextChance`, `fuzzyMatch`) the boolean-prefix rule would rename,
which is a breaking change for the three repos importing it.
Everything else the preset flagged is fixed in code, not disabled:
key order (perfectionist), hex casing, sort compare functions in tests,
Iterator#map().toArray(), explicit returns on multiline arrows.
web_ui: react-refresh/only-export-components was a real finding --
range-slider.tsx exported `steppedValue` next to the component, which breaks
Vite HMR for the module. The keyboard contract moves to stepped-value.ts as
`steppedValue` + a new pure `keyTarget(key, values, current)`, which also
resolves unicorn/no-late-event-control: the handler now decides the target
first and calls preventDefault() only when it owns the key. Its tests move
with it (the 250-line gate caught the combined file). Minor bump for the new
export. dist/ rebuilt in both packages.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
Surfaced by making ts_core and web_ui the first consumers:
- base({ project }) uses an explicit tsconfig list instead of the project
service. Both packages exclude tests from tsconfig.json so they never land
in dist/, and widen the include in tsconfig.lint.json for lint only; the
project service would have reported every test file as "not found".
- unicorn/no-barrel-files is off for src/index.ts: a package entry point IS
a barrel, that is its job. Stays on everywhere else.
- unicorn/no-keyword-prefix is off in the React layer: `className` is
React's prop name and fires on every styled element.
- pnpm-workspace.yaml: allowBuilds for unrs-resolver set to false explicitly
(pnpm 12 had written a placeholder); the resolver's prebuilt binding is
used, no install script runs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
Evil Martians' "ten anti-AI-slop moves" as a package: strict tsconfig base
(noUncheckedIndexedAccess, exactOptionalPropertyTypes), type-checked
typescript-eslint strict+stylistic, unicorn/sonarjs/perfectionist, behaviour
rules for tests (expect-expect, no-focused-tests), React (hooks, @eslint-react,
jsx-a11y strict, you-might-not-need-an-effect), Playwright, and layer
boundaries via eslint-plugin-boundaries 7 with the article's default policies.
Knip / jscpd / Stryker bases; Stryker carries no break threshold on purpose.
Plugins are dependencies, not devDependencies: a flat config imports them from
this package's own node_modules, so a consumer installing from the git tag
gets them or the lint cannot run. No build step, so the tag needs no prepare.
test/fixture is a mini repo with one planted violation per rule family;
test/expected.js names the rule that must catch each, and the suite runs the
real CLI over it (in a child process -- v8 coverage merges natively-loaded
and transformed copies of the same module into "uncovered"). 35 tests, 100%
branch coverage on the factories.
eslint/fast.js is the type-free subset for a per-edit hook: 0.52 s cold on
one file, measured against 1.28 s for base() without types -- importing a
plugin is the cost, so it loads none, nor base.js which imports them.
Probed under ESLint 10: jsx-a11y 6.10.2 (peer says <=9) and boundaries 7.2.0
both run; the fixture pins that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnAYyNH1Sm4iJNidfYbrZJ
QEMU user-mode networking (libslirp 4.9.4 / qemu 11.1.1) freezes 2-4 of 5
concurrent 60 MB streams at ~10 MB until they time out, while one stream
does 45 MB/s. pacman's ParallelDownloads = 5 hit this on every guest
upgrade: a 90-package -Syu crawled at "500 KB/s" for minutes and then
finished in 12 s once set to 1. Window scaling and rmem do not help; a
tap/bridge NIC would but needs root, which vmbox avoids by design.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SS8M5wk88nJTXxDWz61KKk
The golden image logs in with agetty+startx and has no RGB controllers,
no NVIDIA card and no sound card, so the night-lockdown scripts — which
mask/stop lightdm.service and getty@.service and shell out to openrgb,
nvidia-smi and amixer — had nothing real to act on in a sandbox and
were being "verified" on the host instead.
- `vm lightdm <name>`: installs lightdm + lightdm-gtk-greeter into the
live overlay (pacman -Syu, not -S: the overlay's package database is
the image's and mirrors have rotated those versions out), configures
autologin -> i3 exactly like the host, turns tty1 back into a plain
getty, reboots the guest and proves lightdm.service is active with an
Xorg it owns. Never rebuilds the base: `vm build --force` re-seals a
new checksum while every existing sandbox still backs onto the old
file's name, which is silent qcow2 corruption.
- `vm shim <name> <tool> [exit[:N]|hang|sleep:S]`: a fake tool ahead of
the real one on PATH, every call logged with an epoch to
/var/log/vmbox-shim/<tool>.log so the host can assert what ran and in
what order. `hang` is the openrgb 1.0-2 that never returned on
2026-09-12 and defeated the curfew.
- The DISPLAY/XAUTHORITY resolver `vm run` sources becomes its own file,
guest/vmbox-x11.sh, installed by the image build and re-installed by
the lightdm switch; it now tests readability, not existence, and tries
~/.Xauthority before the startx serverauth glob, because lightdm's
Xorg -auth file is root-only.
- README: the worked example uses MIDNIGHT_SHUTDOWN_CONFIRM=y (the
installer refuses to run unattended now; `echo y |` no longer works),
and the "i2c RGB stays on the host" line is gone.
- bats: four host-side tests for shim.sh (exit, exit:N, hang, refusals).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015mLnUeoJ3mUwGRTowTUH6d