Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/limen-verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ on:
description: The verify matrix's runners, as a JSON list.
type: string
default: '["ubuntu-24.04", "ubuntu-24.04-arm", "macos-15", "windows-2025", "windows-11-arm"]'
submodules:
# A test suite that reads its fixtures from a git submodule (a
# conformance corpus kept upstream) has them only if the checkout
# fetches them. Forwarded to actions/checkout by the jobs that run
# tests, verify and fuzz; tools installs pins and never needs it.
description: Submodule checkout for the test jobs, as actions/checkout takes it ("false", "true" or "recursive").
type: string
default: 'false'

# No default token permissions: each job states what it needs, within what
# the caller grants (contents: read).
Expand Down Expand Up @@ -47,6 +55,7 @@ jobs:
# The token is not left behind in .git/config: nothing in this
# workflow talks to GitHub after checkout.
persist-credentials: false
submodules: ${{ inputs.submodules }}
# Full history and refs: `just do lint commits` validates the commit
# range against the PR's base branch, which a shallow clone lacks.
fetch-depth: 0
Expand Down Expand Up @@ -94,6 +103,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
submodules: ${{ inputs.submodules }}

- name: Install hermetic (pinned, verified)
uses: ./.github/actions/setup-hermetic
Expand Down
20 changes: 10 additions & 10 deletions .hermetic/aqua-checksums.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,28 @@
"algorithm": "sha256"
},
{
"id": "github_release/github.com/farcloser/limen/v0.15.1/limen_0.15.1_darwin_arm64.tar.gz",
"checksum": "B7D7EE4E515E2FDC64B89CA2A3C52FADF7A0DF35F6EFE172CFC337B69A51789B",
"id": "github_release/github.com/farcloser/limen/v0.16.0/limen_0.16.0_darwin_arm64.tar.gz",
"checksum": "A7AD03A9F501636531136B3A7DFFC1CEE4DFA412700BC541BE48DD236D8CF451",
"algorithm": "sha256"
},
{
"id": "github_release/github.com/farcloser/limen/v0.15.1/limen_0.15.1_linux_amd64.tar.gz",
"checksum": "4A2932003514F5C682FCB95F057DD9A5EE2932810DEF38CD08F0DC1D18B10B44",
"id": "github_release/github.com/farcloser/limen/v0.16.0/limen_0.16.0_linux_amd64.tar.gz",
"checksum": "7373510BEC311B1159733A2838ED1BBA1A0C691D1616E40EF2F0FE7B6717EF1A",
"algorithm": "sha256"
},
{
"id": "github_release/github.com/farcloser/limen/v0.15.1/limen_0.15.1_linux_arm64.tar.gz",
"checksum": "37E85C3D7C41A39D7E8BA3F98BFD29946C4F76305F7E6330FBFA196EB6154B3C",
"id": "github_release/github.com/farcloser/limen/v0.16.0/limen_0.16.0_linux_arm64.tar.gz",
"checksum": "2DEFD677A47496BF88CEFC7317F2463DE6104155E697720F71D369EA60235830",
"algorithm": "sha256"
},
{
"id": "github_release/github.com/farcloser/limen/v0.15.1/limen_0.15.1_windows_amd64.tar.gz",
"checksum": "D5B9D6D2F957A4D0380DC2A4DD1E37F8CF0E41633790E3231C4B2460A1B323D0",
"id": "github_release/github.com/farcloser/limen/v0.16.0/limen_0.16.0_windows_amd64.tar.gz",
"checksum": "A8025B69076A774C872E43F42AD358295930CF8A5CF07D03FBF2AE14C7434971",
"algorithm": "sha256"
},
{
"id": "github_release/github.com/farcloser/limen/v0.15.1/limen_0.15.1_windows_arm64.tar.gz",
"checksum": "B82FF644AB4DDFADDED490CF977770CD9BFDD717634F4B1679C646B8F2A64826",
"id": "github_release/github.com/farcloser/limen/v0.16.0/limen_0.16.0_windows_arm64.tar.gz",
"checksum": "F4603D256E221255F277C2087645828D778DC14B108A5360241115E637533524",
"algorithm": "sha256"
},
{
Expand Down
2 changes: 1 addition & 1 deletion .hermetic/hermetic.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ registries:
packages:
# The limen this repository runs, and with it every canonical file it
# writes: the one canonical pin kept here, where Renovate bumps it.
- name: farcloser/limen@v0.15.1 # renovate: depName=farcloser/limen
- name: farcloser/limen@v0.16.0 # renovate: depName=farcloser/limen
registry: local
# The Go toolchain is the repository's own pin, seeded from limen's own
# manifest once and Renovate's here afterwards: a standard-library fix
Expand Down
7 changes: 5 additions & 2 deletions .limen/just/build-go.just
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import 'lib.just'

# netgo/osusergo force the pure-Go resolver and user lookups. No-ops when
# CGO_ENABLED=0 (pure Go already); with CGO they keep DNS and user handling
# out of libc, which is exactly what makes `static` viable on glibc.
go_tags := 'netgo,osusergo'
# out of glibc, which is exactly what makes `static` viable there. Linux only:
# on macOS libc is the one road to Open Directory users (the pure-Go os/user
# reads /etc/passwd alone, so user.Lookup of a real account fails) and to
# scoped DNS, and there is no static libc to keep clear of.
go_tags := if os() == 'linux' { 'netgo,osusergo' } else { '' }

# CGO is off unless a project exports GO_CGO := '1' (or CGO_ENABLED=1 for one
# run); resolution in lib.just.
Expand Down
15 changes: 9 additions & 6 deletions .limen/just/lint-homebrew.just
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import 'lib.just'
# RuboCop would not know the formula cops. brew is not on the hermetic PATH on
# purpose — it is the subject under test, not a substitute for a pinned tool;
# BREW_BIN is captured from the ambient PATH in main.just. `style` (and its
# fixing twin) may run from another brew than the machine's: BREW_STYLE_BIN,
# exported by the project's .justfile, defaults to BREW_BIN and leaves `audit`
# and `test` on the machine's, which installs and audits for real.
# fixing twin) and `audit` may each run from another brew than the machine's:
# BREW_STYLE_BIN and BREW_AUDIT_BIN, exported by the project's .justfile,
# default to BREW_BIN. The machine's brew cannot run under a session sandbox
# (it writes under its own prefix and caches) and refuses a tap it already has
# installed, so without them `audit` is CI-only in a tap repository. `test`
# stays on the machine's, which installs for real.

default: style audit

Expand Down Expand Up @@ -89,7 +92,7 @@ audit: (_banner "lint homebrew" "audit")
exit 0
fi
# shellcheck disable=SC2154 # BREW_BIN is exported by the canonical .justfile (main.just).
brew="${BREW_BIN:-}"
brew="${BREW_AUDIT_BIN:-${BREW_BIN:-}}"
{{ _require_brew }}
{{ _tap_link }}
# shellcheck disable=SC2086 # deliberate word-split of the flags knob.
Expand All @@ -108,8 +111,8 @@ _skip_unless_macos := '''
'''

# Refuses when the brew the recipe chose ($brew) is empty: main.just's ambient
# capture came up empty and the project exported no BREW_STYLE_BIN. Twin in
# fix-homebrew.just.
# capture came up empty and the project exported no BREW_STYLE_BIN or
# BREW_AUDIT_BIN. Twin in fix-homebrew.just.
[private]
_require_brew := '''
if [ -z "${brew:-}" ]; then
Expand Down
8 changes: 2 additions & 6 deletions .limen/just/perf-go.just
Original file line number Diff line number Diff line change
Expand Up @@ -113,22 +113,18 @@ profile: (_banner "perf go" "profile") (_go-tool "dot" "github.com/forkcloser/do
docs="$LIMEN_BUILD/profiles-docs"
mkdir -p "$prof" "$docs"
# Rendered from pprof's -dot text rather than pprof's own -png, which
# shells out to a dot it expects on PATH. -o with a pre-created file: under
# a sandbox the WASM runtime can overwrite files but not always create
# them. Paths stay cwd-relative for the same reason (the runtime preopens
# only the working directory).
# shells out to a dot it expects on PATH.
render() {
src=$1; png=$2; shift 2
go tool pprof -dot -nodecount=20 "$@" "$src" > "$png.dot" 2>/dev/null
: > "$png"
"$LIMEN_BUILD/tools/dot" -Tpng -o "$png" "$png.dot"
rm -f "$png.dot"
echo " -> $png"
}
for pkg in $(go list ./...); do
# One directory per package keyed by the FULL import path: flattening
# collides (example/a__b vs example/a/b, '_' being legal in import
# paths). Paths stay cwd-relative for the WASM dot (see render).
# paths).
mkdir -p "$prof/$pkg" "$docs/$pkg"
echo "Profiling $pkg..."
go test -count=1 -o "$prof/$pkg/pkg.test" "$pkg" \
Expand Down
33 changes: 21 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ is the [engineering book](https://github.com/farcloser/limen/tree/main/book).
recipe covers the need — say so when you do it. `just --list` shows every task.
- Always name the recipe; module defaults are curated subsets, not "run everything".
- Never bump a tool version by hand: `just do tools set` / `just do tools update`.
- **Every git command is `git c …`, never raw `git`**, local commands included: the
harness sets `GIT_SSH_COMMAND`, which sends raw git's ssh to the system ssh and the
machine's own keys; the sessions' `c` alias (`env -u GIT_SSH_COMMAND git`) drops it so
the rig's ssh and the bot's key are used. A network command also takes the rig's
transport explicitly,
`git c -c core.sshCommand="$(git c config -f ~/.claude/gitconfig core.sshCommand)" fetch`,
and the same for `push` and `pull`. The alias composes with `-C`: `git -C <clone> c …`.

## Contributing

Expand Down Expand Up @@ -97,13 +104,14 @@ chapter; the procedure is limen's `skills/contribute`.
place — another transport, a variable set by hand per command, a manual step for a
recipe — carried on as if the rig worked: that hides the defect. A workaround is used
only after the breakage is reported and the human agrees, and is named as one every time.
- **A locked laptop is not broken tooling.** The bot's key answers only while the human's
session is unlocked: once the screen locks, the agent refuses to sign or authenticate. So
when signing or pushing fails after it worked earlier in the session, the human is away,
not the rig broken. No retry loop, no workaround: finish the work in the worktree — done,
`just lint` and `just test` green, the commit message written — ready to commit and push
when the human is back, and say so once. Signing that never worked in the session is
broken tooling (above).
- **A locked screen does not stop the key.** The bot's key is created to sign while the
screen is locked, from the human's first unlock after boot until the next boot (taciturn,
`--use locked`). So a signing or push failure that follows earlier successes in the session
is a defect and is reported as broken tooling (above), never waited out. Only a machine
still on Secretive refuses once the screen locks; there the human is away, not the rig
broken: no retry loop, no workaround, finish the work in the worktree — done, `just lint`
and `just test` green, the commit message written — ready to commit and push when the
human is back, and say so once.
- **Read what the work needs, never the whole disk.** A targeted read outside the
repositories is fine when the work calls for it; a filesystem-wide walk is not: no
`find /`, `find ~`, disk-wide `mdfind`, or recursive grep over `/` or `~`.
Expand Down Expand Up @@ -133,11 +141,12 @@ chapter; the procedure is limen's `skills/contribute`.
assessment or misreads the code, say so with the evidence and settle it between the two
sessions, the reviewer's 🛑 included (the reviewer may miss what the owner knows; the
owner may miss the cross-cutting picture). A deadlock goes to the human, as the exception.
- **No GitHub issues unless the human asks for one.** The issue tracker is the human's.
A defect or a request that belongs to another repository goes to the session that owns
that repository, as a message with what, why, and where; that session fixes it, and
the human hears about it as a pull request. An issue you opened on your own is closed
once the owning session has the work.
- **A follow-up is a GitHub issue; a defect in another repository is a message.** Anything
left for later, a follow-up or parked work, is filed as an issue in the repository it
belongs to, one per real item, written so its owner can act on it; never a scratch,
`TODO` or notes file, and never an issue for a passing note. A defect or a request in a
repository another session owns goes to that session as a message with what, why and
where; that session fixes it, and the human hears about it as a pull request.

## Code

Expand Down
Loading