How Chris Pollett likes development to run, written for an LLM working with him on Yioop. Most of this is shared with his other projects; what is specific to Yioop — the gold/work sync, the Createdb check, the locale and library rules — is marked. When something says "always" or "never," treat it as a hard rule.
Yioop is developed against a two-clone discipline, not a shared remote you push to:
confirmed-head tag marks that point. Your local gold
hashes are your own and will not match Chris's hashes.work_directory restored into it.gold is a vault, not a workbench. Exactly two operations ever
touch gold: cloning it to make work, and fast-forwarding it with
git apply of a patch Chris has confirmed. Nothing else, ever.
Never run a build, a test, a golden regeneration, a Createdb,
a ConfigureTool, or any other command inside gold; never
create a work_directory there; never hand-edit, hand-copy, or
hand-commit a file into it. Every build, test, and regeneration happens in
a work clone, and a regenerated golden reaches gold only inside a patch.
Reconciling a divergence is no exception: rebuild the wanted state in work,
cut a patch, and apply that to gold. If you ever find yourself about to go
into gold to run something — even just to check a result or to get
back in sync — that urge is the process breaking; stop, and do it in
work.
The loop each round, and this must be done prior to any new
work: the moment Chris confirms a patch by pasting its hash, and
before writing or changing a single line for the next task, apply Chris's
latest confirmed patch to gold, commit it, move the
confirmed-head tag, re-clone work, and restore
work_directory. Starting new work on a gold that has not been
synced is the most common way the two lines drift: the next cut is taken
against a stale confirmed-head and silently double-includes
the patch Chris already confirmed. If you notice that a round's work has
already begun on an unsynced gold, do not re-clone work — that would
discard the in-progress changes. Instead advance gold to the confirmed
commit, move confirmed-head to that commit in both gold and
work, and only then cut. Cut a delivery as git diff
confirmed-head..HEAD, and dry-run it with git apply
--check on a fresh clone checked out at confirmed-head
before presenting. Apply patches forward only — never reverse-diff.
Never assume anything is committed until Chris reports the hash. Name a
patch for what it does in plain terms, never a sequence number or a
plan-item number.
Beyond the gold/work clones above, a work clone needs a configured
work_directory before it will build or run. Restore the
configured work_directory into the fresh work clone, point Yioop
at it with php8.3 src/configs/ConfigureTool.php work-dir, then
build the two SQLite databases with
php8.3 src/configs/Createdb.php, which should print
“Create DB succeeded.” Without a configured
work_directory, Config.php stops short of defining
its later constants (for example MAX_GROUP_PAGE_LEN) and Createdb
dies with an undefined-constant error — that failure is the
environment, not your patch. Createdb also regenerates
work_directory/app/locale; since LOCALE_DIR prefers
that copy, delete it after Createdb so translations fall back to the current
src/locale when you render a page.
src/executables/CodeTool.php is the general-purpose tool.
Run a test script with php src/executables/CodeTool.php unit
<TestFile> (or unit list /
unit ClassName methodName to target cases); check docblocks
with needsdocs <file>; and check the 80-column rule with
longlines <file> (multibyte-aware, so it is the
authority over a raw byte count).lynx and curl for fetching and reading pages as
text, plus node and npm. Rather than say don't
have the tool you want, try to install it.You have used a headless renderer here before:
wkhtmltoimage and wkhtmltopdf are installed and turn a
page or an HTML fragment into a PNG or PDF with no X server. They are built on
WebKit, which descends from Konqueror's KHTML, so they draw a page much as a
browser would — enough to eyeball a layout or confirm a fragment
renders. The bundled /usr/bin/chromium-browser, by contrast, is a
snap wrapper that cannot launch here and errors that it “requires the
chromium snap.” For scripted measurement of computed styles (reading
getBoundingClientRect), a scriptable browser is needed: install a
Chrome-for-Testing build with npx @puppeteer/browsers install
chrome and drive it from puppeteer-core with
--no-sandbox and --disable-dev-shm-usage (not
--single-process, which breaks the DevTools pipe); its download
host is not in the default network allowlist, so add it to the sandbox network
settings first. When no scriptable browser is set up, render to an image with
wkhtmltoimage, reason about the cascade, and ask Chris to confirm
the pixels.
src/configs/PublicHelpPages.php, are the
exception).if, else,
and loop body./* ... */); never
//.Config.php for an existing one to reuse first.i,
j, k, in both PHP and JavaScript._arr,
_str, _int, _obj,
_bool, _list, _secs,
_ms, _bytes, ...). Put the unit in the
docblock: HEARTBEAT_POLL, not
HEARTBEAT_POLL_SECS. (_info, _max,
_name read as words and are fine.)tl(); extract the key to a variable first.
The locale extractor only matches tl('literal_key').src/library/ may touch a model or the database, call
tl(), or escape/sanitize. Components, controllers, and
helpers may. The atto servers (WebSite.php,
MailSite.php) go further: no L\ or
C\ references and no Config reads at all, so
keep any conversion constants local to the file, named
X_PER_Y with units in the docblock. Know decimal versus
binary: metricToInt is decimal (powers of 1000) and does
not match PHP's binary memory_limit "M" (1024×1024).src/configs/Createdb.php; positional seeds break on a
fresh install.DATABASE_VERSION and added a migration has
been confirmed gold, do not edit that version's tables, migration, or
Createdb seeds to make a further schema change. Bump
DATABASE_VERSION again and write a new migration, so a
database already upgraded to the confirmed version can move forward. A
confirmed version's database exists in the wild (at least on Chris's
machine); its version already matches, so an in-place edit never re-runs
and strands that database. Put differently: whenever something that
involved a DB change is called gold, the next schema change gets its own
version bump and migration. When unsure whether a change touches an
already-gold schema, add a new version.htmlspecialchars-escaped text; that breaks functional use
and double-escapes at display. Controllers escape for the view at
render time.VARCHAR widths matter
off SQLite. There are two SQLite databases, public_default.db
and private_default.db — never say just
"default.db."PublicHelpPages.php is autogenerated —
never patch it directly. Deliver wiki help source as a
.txt file for Chris to paste through the wiki UI.As code is developed, unit tests should be created. Unit tests live
in tests/; a case is a method named
<name>TestCase() (not test<Name>) and
asserts with assertEqual, assertTrue, and the
like. Run one with php src/executables/CodeTool.php unit
<TestFile>, and list or target cases with unit
list / unit ClassName methodName. Pull pure logic out
so it can be checked on its own. Flag any test over about
0.01 seconds — it usually means unintended I/O. When patching a
locale, edit src/locale/<tag>/configure.ini directly; it
takes effect immediately, no extraction step. Call the data a test stands
up its "test set-up" or "test data," never a "fixture."
Run these as separate checks and report PASS or FAIL for each. The CodeTool commands used are noted where they apply.
php -l on every touched file.CodeTool.php needsdocs <file> returns 0 —
every class and method has a docblock.i/j/k.// comments.i/j/k.CodeTool.php longlines <file> clean — no line
over 80 columns in a touched file.if body is braced.php src/configs/Createdb.php against a configured
SQLite work directory prints "Create DB succeeded" (verify the USERS
column count and the root/public rows).devlog/v10/yioop_v10_todo.html at the
same time: mark or adjust the matching phase and backlog-item entry
there (its box/part/checked markers) so the
arc plan and the master list never drift out of sync.Work is logged under devlog/v10/ in dated arc folders named
YYYY-MM-DD Arc Name/, each with an HTML plan document. The
devlog is append-only: a finished item keeps its checkmark and note as a
permanent record, and a later hotfix goes under the current arc's plan as
"other work accomplished," not a new dated folder. The only circumstance
where a new arc should be created is Chris says so
first; until he does, everything, including work that feels like a separate
concern such as a production hotfix, goes under the current arc's plan as
other work. Before presenting, check that the plan's numbering is internally
consistent and that what is marked done lines up with what is next. Try
to follow the rules for traditional outlining call for outlines to begin
with Roman numerals (I, II, III) and to proceed through letters
(A, B, C, and a, b, c) and Arabic numerals (1, 2, 3).
Getting oriented at the start of a session: if you are new to the v10
to-do and its development phases, read the master list
devlog/v10/yioop_v10_todo.html first, then the current arc's
plan. Each finished arc from 2026-06-16A Mail Refactor Arc
onward carries a three-to-four sentence “Arc Post-Mortem Summary”
paragraph just under its title that says what the arc accomplished, so you can
catch up quickly without reading every item. When an arc is finished, add such
a summary to its plan so the next co-implementer gets the same head start.
These words and phrasings should be excluded from all of your output even transient.
!).
timeout -s
KILL 12 php index.php ... </dev/null >/tmp/log 2>&1
&, and give curl a --max-time.fuser -k
8080/tcp; a stale server on the port answers with old code and
hides the change under test.