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.
This commit is contained in:
Nexoniarz 2026-08-09 14:27:22 +02:00
commit 3182da3965
47 changed files with 2810 additions and 0 deletions

View file

@ -0,0 +1,51 @@
---
name: schunix-wall-clock
description: The clock read 1970 because nothing ever called cmostime(); kernel/core/time.c now reads the RTC at boot.
metadata:
node_type: memory
type: project
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
modified: 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.