SchOS/.claude/memory/schunix-wall-clock.md
Nexoniarz 3182da3965 Carry project memory in the repo, with tools/memory-sync.sh
Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:

  - D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
    invisible until moved across by hand.
  - Development happens from Windows and from Linux, and each looks in a
    different place for the same checkout.

Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.

The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
2026-08-09 14:27:22 +02:00

2.7 KiB

name description metadata
schunix-wall-clock The clock read 1970 because nothing ever called cmostime(); kernel/core/time.c now reads the RTC at boot.
node_type type originSessionId modified
memory project 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 2026-08-08T19:44:10.022Z

Fixed 2026-08-08. Nexoniarz reported "time has stopped, or it's just too slow — type date, wait 10 seconds, it's still the same or pretty close."

The clock was not stopped. ticks advanced correctly (measured: uptime 19.81 → 34.51 over ~14 real seconds, identical at -smp 1 and -smp 2). The bug was the origin: sys_gettimeofday, sys_clock_gettime and sys_time all returned ticks / 100 — seconds since boot, presented as seconds since the epoch. So every machine believed it was 1 Jan 1970 and the date crawled at the rate of its own uptime. cmostime() in kernel/arch/x86_64/lapic.c could read the hardware clock the whole time and had no caller.

kernel/core/time.c now reads the RTC once in main() (after the timer is running, before userspace), converts it with Howard Hinnant's days_from_civil, and stores boot_epoch. wallclock(&sec, &usec) is the one place that answers "what time is it", so the three syscalls cannot disagree.

  • days_from_civil, not a loop over years — branchless, exact for any year, no table. Accumulating 365/366 since 1970 gets leap centuries wrong unless you remember 1900 was not a leap year and 2000 was.
  • RTC read once, not per call — it is polled over two I/O ports and must be sampled twice to catch it mid-update.
  • Interpreted as UTC. A PC that has had Windows on it usually keeps the RTC in local time, but there is no timezone store and nowhere to ask. date shows UTC; Nexoniarz is UTC+2 in summer, so it reads two hours behind local. When a settings store exists, the offset goes in time_init().
  • CLOCK_MONOTONIC (clkid 1) subtracts boot_epoch; everything else is treated as realtime.
  • Implausible CMOS readings leave boot_epoch at 0, i.e. the old behaviour, rather than putting the machine in the Bronze Age.

Verified: Sat Aug 8 19:36:43 UTC 2026, advancing 14s in 14 real seconds, host at 21:37 CEST.

The stale-image trap, now guarded

A running QEMU holds build/fs.img open; on Windows rm then fails with "Device or resource busy", and make stops after the kernel has already relinked. The next test boots the previous kernel and the change looks like it did nothing. This cost several cycles across the session. The fs.img rule now prints what is wrong and how to clear it. When a fix appears to have no effect, check schunix.img's timestamp before debugging the code.