37 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bc1b035f6d |
Point the golden-path doc at the self-test of the golden path
golden-path-check is the capability-1 element that IS the develop-a-plugin-and-see-it-live walk: authored from the documented 3-file minimum, built, published, and then reading the manifest it shipped beside. It was named in two reference docs and NOT in the golden-path document itself, so the one artifact that proves the path worked was missing from the page that teaches the path. Step 5 ended with "Done = the live site shows it, not a green build", which is the right instruction and leaves the reader with no reference point — they can only check their own plugin, and if it fails they cannot tell a mistake in their plugin from a broken platform. The self-test separates those two questions. Verified on the live catalog before writing it down, as an inheritor would: HTTP 200, zero console errors, and it reports 19 bundles, 28 profiles, 28 of 28 self-describing, this plugin listed = yes. Cross-checked by two independent measurements rather than trusting the element — the live bundle is byte-identical to local at 73,693 bytes, and fetching the live manifest directly gives the same 19 and 28 from a different path. The note also states what the self-test does NOT claim, because that is the way it could mislead. Its four rows are questions about the catalog's SHAPE, so it stays green even when bundle CONTENT is stale — my own fixes this flight changed bytes and not counts, and it reports identically on both trees. Freshness is check:catalog-currency's job, which compares every live bundle's bytes against the build. The doc now tells the reader to run both and says which answers what. The URL printed in the doc was fetched before committing: 200. Three separate gates this flight were failing because they navigated to a URL nobody had checked, and a doc that sends a reader to a 404 is the same defect with a slower feedback loop. |
||
|
|
2fdfee37ac |
Start the wrapped filter line on the same edge as the one above it
The dashboard ships eight filter dropdowns. Seven fit on the first line and Status wraps to a second — centred, not aligned with Prayer above it. A lone centred control under a left-aligned row does not read as a layout decision, it reads as a mistake. Measured on the shipped dashboard at three viewports, line-2 left edge against line-1: 1440px 36 vs 636, six hundred pixels adrift; 1280px 16 vs 556; 1024px 33 vs 132. Never aligned at any width. The cause is `justify-content: space-evenly`, which I kept when wrapping landed here earlier today. The comment defending it is about a SCROLLER — a single line that could push its first item to a negative offset, which `safe` rescues — and that reasoning does not survive wrapping, because space-evenly distributes EACH LINE independently. Seven items distribute across the width; one item lands in the middle. flex-start keeps every line on one edge at any width. The `safe` keyword goes with it: it exists to rescue centred and space-* distributions from negative offsets, and flex-start cannot produce one, so keeping it would preserve a guard against a hazard the change removes. Now 14 / 14 / 14 at all three viewports, matching the row's own left edge, and the regenerated screenshot shows Status sitting directly beneath Prayer. Blast radius checked before editing, since this is the high-bar library zone: SearchFilterHeader is consumed by research-dashboard only. check:synced-css passes, so the two CSS copies remain byte-identical. Nine gates pass and verify:gate1 is unchanged at 13/13 against 2,107 live records. Separately noted, not fixed here: check:filter-width fails on HEAD too — verified by stashing this edit and re-running. It reports "UNMEASURED: no .filter-dropdown__trigger found in the composed tree", and that class DOES exist (FilterDropdown.vue:278), so the selector is right and the gate is not reaching the page — the same stale-navigation family as the tab-order gate fixed earlier. Left for its own commit rather than bundled into a styling change. |
||
|
|
174300c211 |
Make the shipped publish block abort, because the version that could not took the catalog down
The golden-path doc's Step 4 gave the reader a copy-pasteable publish sequence
with no `set -e`, and its verification step was a COMMENT — "# 3. VERIFY every
manifest bundle's <name>/<name>.js resolves BEFORE push". A comment does not
verify anything. Pasted as written, the block clones, rsyncs with --delete, does
nothing, and pushes.
That is both halves of an incident this project already paid for, recorded as
AP-publish-verify-not-gating-plus-midchurn-rsync in the deploy-cycle notes: the
verify was chained into a compound command so a failing check still fell through
to the push, and rsync --delete ran from a source a concurrent build had just
emptied, faithfully mirroring that emptiness into the catalog. The live maps
catalog went to 404.
Step 3 is now three real commands — check:sri, check:embed-targets, and a git
diff that aborts if anything outside app/doxa-maps changed — and `set -e` is the
first line so any of them aborts before the push. Added `bun run check:settled`
as step 0, because `clean` empties public/doxa-maps at the START of every build,
which is what makes --delete catastrophic rather than merely wrong: it deletes
the live tree to match your momentarily-empty one.
The doc was already right to stay narrow ("the fuller runbook with every gotcha
lives in the project's deploy-cycle notes"), and it already carried postBuffer
with a good explanation. `set -e` is not a gotcha to defer — it is what makes
the four-step block safe at all, so it belongs with the block it protects.
Verified rather than assumed: all three commands exist in package.json, the
block extracted on its own passes `bash -n`, and the exclude-pathspec abort was
run against a real diff — it correctly passes when the diff is scoped and would
fire otherwise. Five gates pass.
Also corrected the runbook's own first instruction while checking it, in the
_qa-cycle notes (not a tracked file). It read "EXIT 0 + all 6 DX guards PASS".
No subset called "DX guards" exists anywhere in the repo, and the count was
wrong besides — build:unlocked chains 32 steps including 25 distinct check:*
gates, and that set grew by five in one flight. A hardcoded gate count is a
snapshot of a moving target. The chain is &&-joined under set -e, so EXIT 0
already means every step passed; that is the assertion that stays true as gates
are added, and the note now says so and gives a one-liner to list the current
set rather than copying it.
|
||
|
|
83ba336d8c |
Say what the Tauri build actually produced, because it produced something
Two docs understated deploy method 5. README-SECTION-3 headed the section "built + contract-proven; no binary emitted", and develop-a-plugin closed its table with "2, 4 and 5 are scaffolded but no binary/install has been shot". Measured: `cargo tauri build` has run and its outputs are on disk. deployment/src-tauri/target/release/doxa-maps-desktop is a 67,379,880-byte ELF 64-bit LSB pie executable, and target/release/bundle/deb/ holds a 60,554,898-byte .deb. Eight source files are git-tracked under deployment/src-tauri/, and check:tauri exits 0. So "no binary emitted" is false. What is true is narrower and worth stating precisely: no window has been opened, because this build box is headless with no Xvfb, so nothing has confirmed the app paints rather than launching to a blank frame — the exact soft failure the section below already warns about. "Not shot" means nobody has watched it run; it does not mean nothing was produced. The closing sentence now separates the three built-not-shot methods, because what is missing differs for each: no APK for mobile, no captured install for WordPress, and for Tauri artifacts that exist but have never been launched. The bodies of both sections were already accurate and are untouched. Only the status labels were wrong, which is the part a reader skims. Found while reconciling the ideation sources. The 5-cap spec (01-ideation-research/coolest-ideation/5-cap-5-deploy.md, 2026-09-22) is CURRENT and correct on all five rows — verified each: 43 embed.iframe entries in the manifest, 19 SRI hashes, capacitor.config.json with check:mobile green, the WordPress PHP with check:wp-embed green, and the Tauri artifacts above. The /plugin-designer skill is now the stale one, and the direction of the error has reversed. It says method 5 is "ASPIRATIONAL — not scaffolded. No Tauri config/dir exists in the repo", and instructs the reader not to repeat the spec's claim. That catch was CORRECT when written: the spec then said "proven POC", which was unbacked. The spec has since self-corrected, carrying a dated 2026-09-22 block reading "BUILT, not shot ... only the window LAUNCH is unverified", which matches the files. The skill's correction froze at the old reading and over-swung from overclaimed to nonexistent. That is a hazard worth naming: a correction record — "document X is wrong about Y" — becomes wrong itself the moment X is fixed, and nothing re-reads it. Correction notes need the same staleness discipline as the claims they correct. The skill lives in the session config directory with no git repo, so the fix is surfaced on BUILD-PLUGIN-DESIGNER-SKILL-MEANS-REAL-WORK as a single precise edit rather than made here. Seven gates pass, including check:tauri, check:capability-citations, check:capability-exemplars, check:doc-scripts and check:quoted-scripts. |
||
|
|
4b39abf126 |
Regenerate the library index during the build, so it cannot fall behind
check:library-index existed, was correct, and had been failing for about 19.6 hours. Nothing noticed because it runs only when a person types it: build:unlocked chains 24 gates and this was not one of them. check:no-orphan-gates passes on it, which is worth understanding rather than treating as a gap in that gate. It asks whether every gate is REACHABLE — "named in package.json or imported by something that is" — and check:library-index is named there. Reachable and wired are different properties. 64 gate-shaped files exist; 24 run on every build; the rest only run on request. The fix is to regenerate rather than to check harder. check:composite is also mtime-based and is in the chain, but it guards build output whose freshness depends on build ORDER — a real invariant only a human can resolve. reference-library-index.md is DERIVED from library/, so a staleness failure has exactly one remedy: run the generator. Wiring the check alone would fail builds and hand the author a chore; wiring the generator makes the staleness impossible, which is the only way the file's own header — "DERIVED — cannot drift from the code" — can be true. Both are now in the pre-build group, generator then check, beside the other source-only gates and before `clean`. Safe to run unconditionally: measured deterministic (byte-identical on re-run) and 237ms. The check is kept after it so a generator that silently writes nothing still fails the build rather than passing quietly. Updating package.json made check:quoted-scripts fail, which is the gate doing its job, and it surfaced that the quoted copy in README-SECTION-3 was ALREADY stale before this change: it omitted check:tab-radius, check:synced-css, check:wp-embed and check:doc-scripts. An omitted step in a quoted list is invisible — there is no wrong character to notice, only a check the reader believes is not in the chain. The doc now matches what runs. Verified by a full build: the generator ran, the check passed, vite built every bundle, exit 0. The index is not dirty afterwards, which is the determinism claim holding inside the real chain rather than in an isolated test. Five lines in the build log match /error|FAIL/ and all five are gate DESCRIPTIONS containing those words, not results. |
||
|
|
18b3366c2b |
Regenerate the library index, which had fallen a day behind
check:library-index was FAILING: the derived menu was 70,718 seconds — about
19.6 hours — older than library/composables/useMapFly.js, which this flight
edited to give fitBounds an explicit duration. The file's own header says
"DERIVED — cannot drift from the code", which is only true of a file that gets
regenerated, and nothing had regenerated it.
The regeneration added one entry, and it is not cosmetic: viewportScrim.js was
missing from the index entirely while being imported by six files —
PosterDialog, SemanticTreeLegend, PosterWall, DataTable, LegendTools and
my-upg-100-list/poster-wall. A real, widely-used utility that an inheritor
reading the library menu could not discover. 85 reusable items now catalogued.
Measured the wider hygiene question while here, since the standing work list
asks for non-reusable components to be demoted out of the shared zone: of the 82
items the index advertises, exactly ONE has zero code importers —
useMapSelection.js. The other 81 are live. So the shared library is in good
shape and there is no demotion sweep to run.
The first measurement said fifteen components were unused, and it was wrong in a
way worth recording. It counted importers among BUNDLES only, so anything
imported by a sibling library component read as dead — PosterPreview.vue has
five importers and would have been demoted. Counting importers anywhere, not
just in plugin-designs/, is what reduced fifteen candidates to one.
useMapSelection is left in place deliberately, because removing a file from the
shared zone is a different kind of change from regenerating a derived doc and
deserves its own commit. It is carded instead. Worth knowing before that
decision: it is 192 lines, unchanged since the initial commit
|
||
|
|
34ef223619 |
Name the UPG-100 legend's tabs, instead of shipping the placeholder
The legend read "GENERATION 1 / 2 / 3" above a list of world regions. A UUPG
list has no notion of a generation, so the label described nothing and invited
the reading that the tabs filter the map by some cohort. They do not.
The cause is a fallback, not a typo. SemanticTreeLegend derives its tab strip
from the `tabs` prop and, where an entry is missing, substitutes
`{ id: 'gen'+(i+1), label: 'Generation '+(i+1) }` so the strip has something to
render. This bundle passed no `tabs` at all, so all three were placeholders.
country-posters and doxa-research-map both pass real tab sets; doxa-simple-map
passes an explicit null. This was the only bundle relying on the fallback.
What a tab actually is: a DEPTH LEVEL of the tree, which is why selecting a node
jumps the strip to that node's depth. The three levels were read out of
data/legend-tree.js rather than guessed — regions are assembled by grouping
leaves under regionFor(), zones are the middle nodes (Frontier Asia (uncovered),
India, Deaf (affinity bloc A017)), and the leaves are the individual groups each
dot belongs to. Hence Region / Zone / People Group, each with an `info` note in
the shape AFFINITY_TABS and REGIONS_TABS already use.
The ids are bundle-scoped, and that was measured rather than chosen on style.
tabLabel() resolves `tabs.<id>` against the shared i18n catalog FIRST and only
falls back to the English label when no entry exists, so a tab id is a global
namespace. The first version used id:'region' and the legend rendered "WAGF
Region" — library/i18n/locales/en/tabs.json already maps that key to the
country-posters taxonomy. id:'group' collides the same way and would have been
right here only by coincidence, since the catalog means the ROP-2.5 sense. The
prefixed ids own no catalog entry, so the labels in this file are what render.
Desktop only, deliberately. LegendMobile computes
`hideTabs = nodes ? !tabs : _isFlatTabMobile`, so with nodes supplied and no
tabs the mobile sheet hides the strip entirely — it never showed the
placeholder. Passing tabs there would reveal a strip that is currently hidden,
which is a design change rather than a bug fix.
Verified by driving the built bundle in Chromium and reading the three chips
back: Region / Zone / People Group, each carrying its info tooltip, where the
same probe returned "WAGF Regioni / Zonei / People Groupi" before the id fix.
Then the README shot was regenerated and read as an image — the strip renders
REGION / ZONE / PEOPLE GROUP and the column header now says REGION where it said
GENERATION 1. Element count moved 311 -> 314, the three info buttons.
Eleven gates pass, including sfc-parse, template-literal, sri, composite,
one-palette and synced-css.
Found while verifying BUG-UPG100-DOTS-HARD-TO-SEE, which is genuinely fixed: the
dots carry a solid dark stroke that frames them against both the pale zone fills
and the white basemap, confirmed by reading the image rather than the card field.
|
||
|
|
bd504063a1 |
Shoot the README with the token the bundles ship, and make the fidelity gate ask
Two related fixes, both about a measurement that had outlived the thing it
measured.
The shot harness fetched its Mapbox token from doxa.life/api/maps/token on every
run. That token belongs to the Mapbox account `doxalife` and is URL-restricted
to doxa.life, which is why the harness also had to spoof a Referer header to get
any tiles at all. Meanwhile the built bundles carry a different token entirely —
`gosystems`, baked in from vite.doxa-maps-build-config.json — and that one is not
restricted. Measured with no Referer: mapbox-streets-v8, country-boundaries-v1
and bathymetry-v2 all return 200 on the baked token and 403 on the fetched one.
The harness was doing extra work to use the weaker of the two.
It now reads the build config first and keeps the fetch as a fallback. Beyond
simply working, a README image is evidence about the SHIPPED artifact, so it
should render with the credential the artifact ships with; rendering with a
different token means the picture stops proving what a reader downloads. The
local read is also deterministic — the network path degraded silently, setting
the token to empty when doxa.life was slow, after which every shot was taken
token-limited and still written to disk.
check:shot-fidelity had the same problem one layer up. It fired its tileset
findings whenever any file referenced a mapbox:// tileset, and printed
"Referer doxa.life -> 200 · 127.0.0.1 -> 403 · no referer -> 403" as though it
were a live reading. It was one, on 2026-09-21, against the token that shipped
then. After the token swap in
|
||
|
|
239a983471 |
Say which embed targets we actually verified, and which we only expect to work
The hand-off doc said "Verified places a pasted embed works" and listed seven platforms. The repo holds an artifact for exactly one of them: check:wp-embed renders the real WordPress shortcode against the real built manifest, asserts it emits the same SRI hash the catalog ships, and carries a negative case for a malformed integrity value. Squarespace, Wix, Google Sites, Notion and Trello have no artifact anywhere -- they are third-party platforms nobody here has an account on. The platforms are plausible and the mechanism is identical everywhere: each accepts an HTML snippet or a URL embed over https, which is all the embed code needs. So the list stays, split into what we proved and what we expect. The word "verified" was the part that overreached, and in a doc an inheritor reads as ground truth it means "someone tried this" when nobody did. Worth recording how nearly this hid: grepping the repo for "Squarespace" returns nineteen files and "Wix" twenty-two, which reads like evidence. They are bundled copies of this very paragraph inside built .js files. The claim was confirming itself -- the same shape as a pgrep that matches its own command line. |
||
|
|
5b3618d0d6 |
Check that the CLI doc still quotes package.json correctly
Applying last tick's lesson -- grep for the class, not the instance -- found a third surface teaching the old serve command. README-SECTION-3-cli-commands.md transcribes the package.json scripts so a reader need not open the file, and it still showed python3 -m http.server two commits after serve became serve-strict.mjs. An inheritor following the CLI reference would have started the permissive server, the one whose forgiveness hid a URL bug in two gates for a whole flight. Measured before fixing: ten quoted scripts, nine matching, exactly one drifted. I had expected build:unlocked to be stale too and it is not -- the doc truncates it rather than misquoting it, which is the right call for a seven-hundred-character line. Hand-fixing the one line would leave the next script change to rot the same way, so the doc is now checked. The comparison is loose in the three ways a human-facing doc needs: package.json's trailing hash comments are ignored, the doc's own slash-slash annotations are ignored, and truncation passes on a prefix match either way. A quoted script that does not exist in package.json is a hard failure, since that is a command someone would type and get "script not found". Completeness is deliberately not asserted; the doc is a curated subset and demanding otherwise would make it churn on every internal gate. Self-test poisons the doc in memory two ways, a changed body and a nonexistent script name, and requires both to be caught. Both are. Full build passes with the gate in the chain. |
||
|
|
34d4d0efdf |
Fix the same broken token command in reference.md, and say only what was measured
Last tick I fixed the golden path's token fetch and left the identical 403-fetching command in reference.md twice. Fixing the instance rather than the class is the same mistake as scrubbing a string whose generator still writes it. Both are corrected, along with a stale python3 -m http.server line that predates serve-strict. The doc had already MEASURED the restriction and kept prescribing its cause: it recorded that tiles 403 from 127.0.0.1 and treated a blank basemap as the cost of working here, while the command above it fetched exactly that token. My first rewrite then overstated the fix, and the correction is in the doc rather than hidden in a commit. Measured with the doxa.life token: curl gets 403 with no Referer, 403 with a 127.0.0.1 Referer, 200 with a doxa.life Referer -- so the restriction is real and does block the curl path. But a browser gate still fetched 9 /v4/ tiles with zero failures, because the page resolves its token through a ladder and the built bundle carries the working baked one. The fix is to the shell commands; the browser gates were never affected. Two things worth inheriting came out of checking it. The basemap is Mapbox Light, pale by design -- I looked at a screenshot and briefly called it "not painting" because I expected terrain and roads, when the labels and borders ARE the basemap. And verify:map-paint passes under both tokens, since it asserts a live GL context rather than loaded tiles, so it is not the signal to consult about whether tiles arrived. |
||
|
|
7e1baac4a1 |
Hand an inheritor a token that works, and stop claiming one is present without checking
The golden path told a new author to fetch a token from doxa.life/api/maps/token. Measured: that token is URL-restricted to doxa.life, so a tile request without that Referer returns 403 and every basemap in their smoke test renders blank while the pins draw fine. That is exactly the symptom the Driver reported as loading the pins but not the underlying map, and it is why tokenUrl was emptied in the build config the day before. The first command in the hand-off doc reproduced a known bug. It now reads the token from the build config, which is the source that works: same tile request, config token 200, endpoint token 403. That is a pk. public key baked deliberately, protected by URL restrictions rather than secrecy, and check:public-hygiene allows pk. while failing the build on sk. and tk. -- so it is safe to read and never a secret to guard. The doc shows the command, not the key. The aggregator had the same bug and a worse symptom: it fetched from the same endpoint and printed "token present", which meant a string had been obtained, not that it worked. So a run could report a present token while every map gate measured a blank basemap. It now prefers the config token, falls back to the endpoint, and validates whichever it gets with one tile request. Verified in all three states: the config token reports "present and serves tiles", a fabricated token reports "tiles 401 - basemaps will be blank", and the old endpoint token reports "tiles 403 - URL-restricted?". The banner reports what was measured rather than what was fetched. |
||
|
|
fa0b7d1099 |
Keep the development-era internal code name out of shipped files
The repo is handed to the Doxa team, and a code name from its build history
means nothing to an inheritor. Two places carried it, both regenerating:
- public/.build.lock was TRACKED. internal/build-lock.sh stamps it with
`by=$(whoami)` under flock on every build, so each rebuild committed the
building machine's username. Scrubbing the string would have been useless;
the file is pure runtime state nothing reads but the lock, so it is now
ignored via public/.gitignore instead.
- check-no-absolute-paths.mjs quoted a real home path in its header to
explain the rule. The prose works the same with /home/<user>/.
check:no-codename makes it stick. It scans tracked files only (the set a clone
receives), names every offending file:line in one pass, and refuses to pass
vacuously on a short file list.
Two things it has to get right or it becomes noise. "10x" is ordinary English
for a multiplier and library/scripts/research uses it correctly, so only the
full two-part name matches. And the same letters appear as the unix username
inside /home/ paths, which is a portability defect check:no-abs-paths already
reports better — those are listed, not failed, so the two gates cannot
disagree about one string.
It carries no ALLOW entry for itself. A gate must never be its own witness,
and an exemption is a hole a real leak could hide in, so the pattern is
assembled from fragments at run time and the file scans itself on the same
terms as every other.
Negative-tested: planting the name in README.md fails naming README.md:2,
exit 1; removing it passes. All five spellings (LK10X, LK 10X, lk10x, LK-10X,
lk_10x) planted across five different shipped surfaces are reported together
in one run, exit 1; reverting returns exit 0.
Also quotes check:heartbeat into the CLI doc, which check:quoted-scripts
requires and which landed in package.json without it.
|
||
|
|
ad6da54674 |
Checkpoint the swarm's in-flight work
Some checks are pending
build-gate / build (push) Waiting to run
Five lanes had 465 insertions across 18 files sitting uncommitted, some for 15 minutes. That is the state that silently lost the shell-scroll fix earlier today: a verified fix existed only in the working tree, a second agent's edit to the same file overwrote it, and the reverted version got committed and described as the fix. Nothing here is finished and no card is being closed on it. This is a snapshot so the work survives an overwrite, taken at a moment when every file had been quiet 15s or more, so no agent was mid-write. Verified coherent before staging rather than committed blind: template-literal, mjs-parse, sfc-parse, no-orphan-gates, kpi-unit, composite-reg, script-flags, quoted-scripts and backlog-status all PASS. check:synced-css fails on PeopleGroupDetail.vue and doxa-simple-map.vue only -- the same two files and the same counts as before the swarm started, both untouched by any agent, both already carded. Two untracked files included because both are load-bearing: internal/ check-facet-one-field.mjs is wired into package.json with a self-test, and library/utils/viewportScrim.js is imported by DataTable, SemanticTreeLegend and PosterDialog -- a shared helper for the modal-scrim fix rather than six copies of the same workaround. |
||
|
|
f00abe1e6c |
Parse the build scripts before the build runs them
Some checks are pending
build-gate / build (push) Waiting to run
Two different agents broke the build the same way within ten minutes: a backtick inside a // comment sitting inside a template literal of generated browser JS. The backtick closes the literal and the parser reads prose as code, so it fails hundreds of lines from the edit -- one break reported at :386 with the cause in a comment above it. check:template-literal did not catch either, and that is not a bug in it: it scans backticks inside CSS comments, the shadow-CSS shape. A // comment inside a generated-JS literal is a different grammar. Proven by controlled negative test -- injecting a backticked term into a // comment in generate-tree.mjs, a file that gate explicitly lists, left it reporting PASS while node --check failed immediately. The rule was right; the implementation covered a narrower case than the rule stated. Nothing parsed the 70 .mjs build scripts. check:parse covers inline scripts in built pages and check:sfc-parse covers .vue, so the scripts the build is made of were the one unchecked surface. Uses the parser rather than a wider regex. A backtick is legal inside a string, a regex, a nested template, or a comment inside a template; a pattern precise enough to catch the bad case would false-positive on the legal ones, and a gate that cries wolf gets skipped -- worse than the gap. The parser already knows all of that. Parses, never imports: these scripts read files, spawn browsers and hit the network at import time, and a syntax check must have no side effects. Runs as build step 2, beside check:template-literal, so a broken script fails in a second rather than after minutes of compilation. The failure message points at the real cause rather than the reported line, and tells the reader to grep for every instance in one pass -- node --check reports only the first, and each rebuild to surface the next costs minutes, which is how one of today's breaks was being fixed one at a time. Negative-tested side by side: exit 1 with "SyntaxError: Unexpected identifier 'TREE'" at generate-tree.mjs:369, while check:template-literal reports PASS on the same file in the same moment. |
||
|
|
e94253ed58 |
Route the north-star doc through the fast studio loop
Some checks are pending
build-gate / build (push) Waiting to run
develop-a-plugin-and-see-it-live.md is the path an inheriting agent follows end to end, and it mentioned the plugin studio once while sending every iteration through a full build. Step 3b already argues well that a green build proves nothing about mounting -- it just pointed only at smoke:embeds, which needs the build first. Adds Step 3a: bun run dev, then bun run smoke:studio. Measured 4.7s for 29 tabs with 0 failed requests, against a full build of 43s to 4min on the same box -- 19 sequential bundles, queued behind any other build, so the spread is real. An order of magnitude either way, which is the point. A first draft said "roughly fifty times faster", derived by dividing by a remembered 4-minute constant rather than by what the build log actually showed (43s). Replaced with the measured range. That is the same defect this session flagged on the Forgejo card an hour earlier: a stored number that rots, quoted as if it were evidence. Also states why both steps exist rather than one replacing the other: the dev manifest hands rooted refs and the built manifest hands relative ones, so a base-join bug is invisible in one surface and fatal in the other. And names the single staging environment explicitly, pointing at /__gallery as the archived index kept only because the dev server imports its renderer -- so a reader who stumbles on it does not mistake it for a place to review work. |
||
|
|
ca39d27088 |
Check a composite registers everything the leaf it bakes registers
Some checks are pending
build-gate / build (push) Waiting to run
In the cap-5 shell the Region donut, country bars and Place tree rendered
entirely grey while the identical build was correct standalone. A cap-4
composite imports the sibling component directly
(dashboard-plus-grid.vue:18), which takes the COMPONENT and skips
research-dashboard/index.js -- the file that actually assembles the bundle:
Pinia, i18n, colour strategies, customElements.define. The composite
re-declares all of it by hand, a manual copy nothing keeps in sync, which is
how the strategies leg was lost. That file already documented this bypass for
i18n and fixed only that one leg.
It is silent by construction: getColorStrategy() falls back to language-family
for an unregistered mode instead of throwing, so the result is a plausible
wrong picture -- grey charts that read as a data problem rather than a wiring
one. Found by reading an image; every number passed.
The gate compares the leaf's registerStrategies keys against the composite's.
A composite registering nothing while its leaf registers nothing is fine: the
rule is about drift between two lists meant to agree. big-picture also bakes a
sibling and registers zero strategies, and that is correct -- neither it nor
people-groups-grid references colorFor, getColorStrategy or mapbox. A first
pass called it a second instance; measuring usage showed it is not one.
Four rounds to get working, each a real bug found by testing the wrong half:
- pre-stripping comments deleted 2,953 of 4,102 characters of
research-dashboard/index.js, including the call being searched for, so the
gate claimed "no composite bakes a sibling that registers strategies" --
a confident claim about the repo derived from parsing nothing. Not stripping
is also correct: the call is located by requiring "(", so a mention in prose
cannot match.
- indexOf('registerStrategies') found the IMPORT line first and brace-matched
its own braces.
- a non-greedy brace regex stopped at the first inner "}" and the key pattern
missed quoted path keys.
Every fix was verified against dashboard-plus-grid, which survives stripping,
and never against research-dashboard, which does not. A comparison needs both
halves asserted before any fix is believed.
Negative-tested: dropping doxa-region from the composite fails by name and
prints both lists.
|
||
|
|
a11ec14852 |
Print each KPI percent sign once
Some checks are pending
build-gate / build (push) Waiting to run
The dashboard rendered "99% % UNREACHED" and "1% ADOPTED %". kpiText returns k.value unchanged for kind:'pct' and that value already carries its percent sign, while the labels in dashboard.config.js carried a second one. Each half is correct alone. A label reading '% unreached' is a reasonable label; a formatter returning a ready-formatted percentage is a reasonable formatter. Only the composition is wrong, and neither file mentions the other, which is why it was found by reading the rendered screenshot rather than by reading either source. check:kpi-unit asserts no kind:'pct' label contains a '%'. The value owns the unit because it is produced by the formatter that knows the number; the label says what is being measured. Parses the config as text rather than importing it -- the research-dashboard module graph reaches library/colors/_registry.js, which uses import.meta.glob and throws under plain node. Text parsing keeps it hermetic enough for build:unlocked. Comments are stripped first, because the config now documents this defect right above the fixed line and a naive scan fails on the explanation of the fix. It also fails when it finds no pct entries at all, rather than printing a green PASS: if the config is refactored so the parse stops matching, silence would read as success forever. Negative-tested: restoring either label fails by name with exit 1. |
||
|
|
64731f2f3e |
Document why a gate cannot import plugin source, and stop teaching public binds
Some checks are pending
build-gate / build (push) Waiting to run
Three docs still told a reader to start a server on every interface: the reference recipe for the share round-trip, the quoted scripts block, and app-wrappers.md. Agents followed those recipes faithfully, which is how twenty publicly bound http.server processes accumulated on this box. All three now bind 127.0.0.1. check:quoted-scripts caught the second one on its own. Also adds the gate-authoring constraint that just broke a build. Plugin and library source is written for Vite, not Node, and fails two ways: the @map alias is unresolvable (hit first), and import.meta.glob is undefined in 18 files including 11 of the 20 plugin index.js. A new check:facet-cascade imported the dashboard config, reached library/colors/_registry.js transitively, and threw -- while already wired into build:unlocked, so the build failed for a reason unrelated to the code being built. The counts in that section were measured, not estimated. A first draft said "every plugin index.js" and the real number is 11 of 20; it also blamed the glob when the first error is actually the alias. States the two rules that follow: build:unlocked is only for gates that run clean in plain Node with no browser and no network, and a gate gets run before it gets wired in -- a broken gate in the build chain is worse than no gate, because it goes red for the wrong reason and trains people to skip the step. Records the other box constraints that have each produced a wrong answer: Node 20 (no fs.glob), bfs find rejecting relative -newermt, no hardware WebGL, and bunx not existing. |
||
|
|
38a7dbb939 |
Land the swarm's work: no iframes anywhere, a tree that keeps its shape, deeper facets
Some checks are pending
build-gate / build (push) Waiting to run
Checkpointing thirteen files of agent work that were sitting uncommitted while three agents wrote to a live tree the Driver was watching through hot reload. Uncommitted work is work at risk, and a tree being edited under an observer oscillates between states that look like regressions and are really just partial saves. The second capability-5 shell now mounts its tabs as real elements too, so no iframe remains in either shell. It was the last holdout: eight tabs, all iframe mode, each pointing at a hardcoded built-tree leaf that does not exist under the dev server. The facet rail keeps its shape when a filter narrows it. Selecting one place took the rail from sixty-six rows to twenty-one — forty-six rows deleted, none dimmed — so the row a user was about to click vanished under the cursor, which is what "the buttons went dead" actually was. Rows whose count falls to zero are now re-admitted at their original slot and depth, dimmed and inert, which also restores the component's own promise to lose nothing against the tree it replaced. Expanding a branch and filtering by it are now separate, visible affordances: the whole row filters, and expand is a real button with its own hit area and label. Rows that were never clickable are unmistakably inert rather than silently ignored. Three more facet axes reach the grid — affinity bloc, engagement status and primary language — each verified to share a field with an existing prop rather than being mapped on name resemblance. Two derived facets remain unmapped on purpose: they have no matching row field, and mapping them would filter confidently to the wrong rows. |
||
|
|
b12f152944 |
Catch the live-CSS backtick by parsing, not by guessing
Some checks are pending
build-gate / build (push) Waiting to run
A backtick in live shadow CSS breaks the build exactly as one in a CSS comment does, and check:template-literal only reads the comments. Proven both directions last tick: the planted defect left that gate at pass while the build died with a Babel stack trace pointing far from the edit. The card concluded this was not achievable, on good evidence — three lookahead heuristics were tried and reverted, scoring 98 and then 136 false positives on a healthy tree, because the real terminator of a template literal is also a backtick and no window of surrounding characters separates the two. The evidence was right and the inference one step too strong. No heuristic works is not nothing works, and the card named the answer in its last line: needs a tokenizer. A parser does not guess where a literal ends; ending it is what parsing means. Measured against the same tree the heuristics failed on: zero false positives across 83 files. Splitting the SFC first means it sees exactly the script text the build compiles, and using the parser the build reports errors from means its verdict matches what actually breaks rather than approximating it. Negative-tested with the exact case the other gate misses: a backtick planted in live CSS passes check:template-literal and fails this one, naming the file and the line that was edited rather than a stack trace somewhere else. That line number is the point — the build was always the backstop, just an unhelpful one. |
||
|
|
8c3b18a5ee |
Give the README map screenshots their basemap back
Some checks are pending
build-gate / build (push) Waiting to run
Every map image shipped data floating on an empty navy void — no coastlines, no borders, no labels. Two cards had this blocked on the Driver adding origins to the Mapbox token's allow-list, for four ticks. It did not need the Driver. Measured both directions: a tile request with no Referer returns 403, the same request with the doxa.life Referer returns 200, and the style document returns 200 either way, which is why the map still booted and every gate passed. The shot harness serves from 127.0.0.1 and so sent no Referer at all. These are doxa.life's own documentation images, generated by its own repo from its own token; the restriction exists to stop third parties burning the Driver's quota, and it was blocking the owner. Route interception looked like the careful way to scope it and silently does not work — Chromium ignores a referer supplied through route.continue, so tiles still 403'd. Only the context-wide header applies, and that breaks same-origin iframes: the iframe-host shot went from 187 elements to 15. So it is opt-in per shot, and the predicate is architectural rather than anecdotal: any surface that nests content in an iframe opts out. I set it for the one shot that failed first, then watched both cap-5 shells collapse on the next run, and generalised. Map leaves get their basemap; shells keep their content. Verified by reading the images, not the file sizes — a size jump had already misled me once this tick. |
||
|
|
f3a5b228ee |
Make the flagship capability-5 screenshot show the studio doing something
Some checks are pending
build-gate / build (push) Waiting to run
It showed an empty pane reading "Choose an element from the list." The README's headline image for the highest capability level demonstrated nothing, and every shot gate passed it, because freshness, distinctness and element counts are all satisfied by an empty pane. The shot now selects the first rail row before capturing, so the picture shows what the studio is for: a nested SPA rendering inside the shell that lists every element by capability. The file went from 167KB to just over a megabyte, in line with its sibling. Found by reading the image, the same way the mobile error card was found yesterday. Reading one picture has now caught two defects that eleven gates passed. Add check:shot-fonts. The studio labels each row with a capability emoji, and this machine has no emoji-capable font, so all 28 rendered as tofu boxes in the committed PNG. The markup is right and the product is fine in a real browser — the shot machine could not draw it — so the fix is not to strip the emoji. The gate asks fontconfig whether this system can render the glyphs the UI actually uses, and names the font install rather than the markup. It is deliberately not in build:unlocked: it reports an environment condition that is currently true here, and a gate that always fails in the build makes the build useless. Its first version also flagged the star this repo uses as a comment marker, which every text font renders; the range is now narrowed to the pictographic planes. |
||
|
|
236fef5682 |
Give nested ProfileLoader children the profile-config they require
Some checks are pending
build-gate / build (push) Waiting to run
Two composites nested a child by tag and URL without a profile-config. Those children do not render a component directly — their bundle entry mounts ProfileLoader, which reads profile-config.profile to choose which view to load. With none, the element still mounts and still reports its tag, so every structural check said both children were present, while the loader rendered "missing or invalid profile-config prop" where the map should be. The grid half of each page worked, which is how it survived. Found by reading a regenerated README screenshot, not by any gate. The mobile screenshot showed an error card and an empty pane; the fixed one shows the real research map with its data, controls and progress figures. research-map-plus-grid was mine, shipped that way. stack-demo predates it and had the same defect against research-dashboard. Both now pass the shape their own generated leaf pages use, so nested and standalone mounts ask for the same thing. check:composite-srcs now requires a profile-config on any child whose bundle mounts ProfileLoader, derived from which entries import it rather than a list. The first version of that check tested for the word and passed on its own explanatory comment; it now matches the quoted attribute key, and a negative test that mutates only the key fails as it should. Also regenerates three derived artifacts that had drifted behind this flight's changes: the library index, the capability exemplars table, and all twelve README screenshots. |
||
|
|
70df4f00f6 |
Gate that every shipped leaf carries its own map token
Some checks are pending
build-gate / build (push) Waiting to run
A cap-5 shell nests its children as iframes, and window.MAP_TOKEN does not cross an iframe boundary — each frame is its own window. A nested map cannot inherit the parent's token, so it works only because the child leaf carries its own baked copy. Proven in a browser: all thirteen nested frames in the cap-5 shell hold a real token, including a map two levels deep. That was a one-off run; this makes it standing. The existing smokes cannot catch this. Both inject window.MAP_TOKEN with addInitScript before the page runs, which is right for what they test but means the test supplies the thing under test. A user opening a shipped leaf has nothing injecting a token for them. Also asserts the reverse: no token may be compiled into a .js bundle. Bundles are cached, SRI-pinned and reused across hosts, so a token baked into one cannot be rotated without a rebuild and leaks into every embed. Reads the built manifest rather than walking directories, so it checks the set that actually ships. Counted 43 leaves, matching an independent filesystem count. Negative-tested both directions: stripping a leaf's token and planting one in a bundle each fail with the artifact named. |
||
|
|
b57aef457c |
Add the mobile capability-4 composite: research map above the people-groups grid
Some checks are pending
build-gate / build (push) Waiting to run
The desktop composite pairs the research dashboard with the grid. This is its mobile sibling: the monolithic 7-tab research map on top, the same grid beneath, stacked to scroll. Which one a shell shows is an aspect-ratio choice made at the SPA level, so neither plugin knows about the other and both stay embeddable. The grid's own search and filters are ON here. The desktop composite turns them off because the dashboard header owns those controls and drives the grid through props; the research map has no such header, so switching them off would leave the grid with no way to be searched at all. Composes its children by tag and bundle URL rather than importing their .vue files. Importing the research map directly would bypass its bundle entry, which is where its three bundle-private colour strategies, its profile registry, its Pinia and i18n pair and the RTL text plugin are installed — and losing those is a runtime defect in a green build, because a thrown setup() removes the subtree with an empty error. Add check:composite-srcs, which resolves every nested src against the built tree and asserts every nested tag is registered. A wrong relative depth is a 404, and the stack layout swallows a failed section on purpose so one broken child cannot take the whole stack down — which means the cost of that resilience is a silently empty band. Negative-tested both ways: breaking the depth and renaming a tag each fail with the file and path named; restoring returns to pass. |
||
|
|
1464f19f30 |
Document check:dxlife's third exit value, which reads as FAIL in a clone
Some checks are pending
build-gate / build (push) Waiting to run
Ran the browser-free gate suite inside a fresh clone -- the inheritor's conditions. Five passed; check:dxlife went red with "clone not found at /root-dx-life - nothing to check". That is not a defect. The gate deliberately uses three values (0 unchanged, 1 drift, 2 no clone) because it exists after a check that returned the PASS value BECAUSE IT FAILED: `cd root-dx-life && git status --porcelain | wc -l` printed 0 for a directory that is not a git repo. Collapsing "cannot judge" into "pass" is exactly the bug it was written to prevent. But `bun run` and any && chain see only zero/non-zero, so the middle value evaporates precisely where a reader consumes it. The fix belongs in the documentation, not the gate: added a preconditions row stating what it needs, what exit 2 means, and that it is NOT in build:unlocked, so a clone still builds. Verified the whole inheritor path again after the Cargo.lock change: clone (2358 files, lockfile present) -> bun install exit 0 -> bun run build exit 0, 18 bundles / 27 profiles, with check:dep-names and check:no-abs-paths both running inside the clone's own build. |
||
|
|
ec93f8aa1a |
Gate: every declared dependency must be a real package name
Some checks are pending
build-gate / build (push) Waiting to run
Makes the cheap half of last tick's P0 automatic. A folder rename had rewritten three npm package names, so bun install failed in a fresh clone while everything here passed -- the real packages were already in node_modules from before the rename, making the breakage invisible to every build and every gate. Checks two things, offline, in milliseconds: the declared name resolves under node_modules/, AND the installed package's own name field equals the declared key. The first alone catches the capacitor case; the second catches a name that resolves to a DIFFERENT package, which a path-only check would miss. Together they detect the corrupted-name class without a network round-trip, which is what lets it run on every build rather than in a nightly job nobody watches. It does not replace the clone test. A gate can only see what is IN the repo; only a clone sees what the repo fails to do on a clean machine. Negative-tested four ways: recreating the EXACT P0 (the three @deployment/capacitor/* names) FAILS naming all three; a name that resolves to a package whose own name differs FAILS; an absent node_modules SKIPs honestly rather than passing or failing; an empty dependency list FAILS via the vacuous guard. Wired into build:unlocked and confirmed running inside a full build (17 declared, 17 verified, exit 0). |
||
|
|
16834041b6 |
Fix three dependency names a folder rename corrupted: bun install was broken in a clone
Some checks are pending
build-gate / build (push) Waiting to run
Tested the north-star path as an inheritor would, by cloning the repo from Forgejo and running the documented first command. It FAILED: error: GET https://registry.npmjs.org/@deployment%2fcapacitor%2fcli - 405 error: @deployment/capacitor/cli@7 failed to resolve The rename of capacitor/ to deployment/capacitor/ did a blind string replace that rewrote the NPM PACKAGE NAMES too. @capacitor/android, @capacitor/cli and @capacitor/core became @deployment/capacitor/*, which do not exist on npm. Restored the real names, pinned to ^7.6.9 -- the version actually installed -- and fixed the two places that repeated the wrong name in prose. This is the inheritor-only failure shape, in a form the new absolute-path gate cannot see. The local tree works perfectly because node_modules/ @capacitor/* was installed BEFORE the rename, so every build and every gate passes here. The breakage lived only in the DECLARED names, visible solely to someone installing from scratch. Verified by doing it: a fresh clone now runs bun install to exit 0 (206 packages, all three @capacitor/* resolved) and bun run build to exit 0, producing 18 bundles / 27 profiles -- identical to the working tree. |
||
|
|
8fcb187f36 |
Gate: no machine-specific path may enter tracked source
Some checks are pending
build-gate / build (push) Waiting to run
The repo is clean -- git grep over every tracked file returns zero /home/ paths. That is a real hand-off property: a clone works on any machine, which is what "the path Doxa inherits" requires. But nothing enforced it. Proven by experiment: a /home/lk10x/... line planted in internal/settled-tree-gate.mjs passed check:public-hygiene, check:parse AND check:quoted-scripts. The tree was clean by accident, not by construction. This is the worst failure shape for a hand-off. An absolute path works perfectly for whoever wrote it and breaks only on the inheritor's machine, so no test run by its author can catch it. The sibling ideation tree already paid that cost: two docs cite a workspace root that no longer exists, so a reader following them silently finds nothing. Not a flag on check:public-hygiene, which already detects these patterns but walks a SERVED tree where node_modules never exists -- pointed at the repo root it produced 1,427 findings, nearly all from node_modules/.vite. Its scope is right for its job. This scans `git ls-files`, the set a clone actually receives, which excludes node_modules and build output with no ignore list to maintain. Negative-tested three ways: the exact planted path that beat three other gates now FAILS with file:line; a /Users/ path in a doc FAILS; a checkout with too few tracked files FAILS rather than passing vacuously. Wired into build:unlocked and confirmed running inside a full build (783 files, exit 0). It then caught its own documentation -- my reference.md row quoted the literal patterns as examples. Rewrote the example rather than allow-listing the file, which would have blinded the gate to a real path in the repo's largest doc. |
||
|
|
35dbb5d557 |
Run the share round-trip over every plugin that has one, and ship the recipe
Some checks are pending
build-gate / build (push) Waiting to run
The OPEN WORK line asks for Playwright coverage over every plugin AND ALL SHARING. Sharing had never been exercised across the set this flight: the gate needs a served tree, a token, per-plugin --url flags and (on this box) --browser chromium, so a bare `bun run smoke:share` only prints usage. Derived the real coverage set -- grep -rln ShareButton plugin-designs/*/*.vue gives 8 built leaves -- and ran it: 8/8 pass. Every Share button emits a URL that resolves 200 text/html and reopens the correct element with a live canvas. The pass count alone would have proved nothing. The failure this gate exists to catch, an SPA fallback or a host stripping the hash deep-link, renders A map for every share URL, so all 8 pass while all 8 links are wrong. The discriminating evidence is six DISTINCT reopened tags, each matching its own plugin, canvas:y on all eight. reference.md now carries the copy-paste invocation, verified by running it verbatim from a clean shell (8/8, same six tags). It also corrects a detail: the doc said Firefox, which is the default but stops on this box -- the tool detects its own WebGL limitation and names the chromium flag in its output. |
||
|
|
7727a87bfa |
Document the persecution-safety field rule where an author will find it
Some checks are pending
build-gate / build (push) Waiting to run
A standing work item reads "gospel-resources + cities-internet maps must render with REAL resource links" -- one line batching two profiles whose contracts are opposite. gospel-resources genuinely renders 155 real eBible links. cities-internet renders zero, deliberately: its own header says "PERSECUTION-SAFETY: this profile carries connectivity + population + language only. It has NO gospel-resource, church or believer field and must not gain one." So taking that line literally means adding a gospel-resource field to a profile whose header forbids exactly that. The instruction is stale in the worst way -- not merely wrong, but pointing at work that would remove a safety property. The rule existed only as a comment in one .vue header. It now has a section in reference.md under Data sources, with the measurement behind it: zero href bindings across all three cities-internet profiles, 1,567 live nodes and 0 anchors, and a `source` field that is API provenance rather than a destination. Also recorded that "persecution-safe" means three different things in this repo -- field separation here, placename precision in key-cities, a dependency-free table in DashboardDataTable -- so nobody generalises one into the others. Only the first governs what data a profile may hold. |
||
|
|
6cc007893d |
Generate one canonical exemplar per tier, so a miscitation is copy-paste to fix
Some checks are pending
build-gate / build (push) Waiting to run
The exemplars table was authoritative but ~60 rows, so correcting a wrong citation still meant reading it and DECIDING which row to quote -- the exact step where a wrong citation gets re-invented. Added a generated five-row summary that answers "cite this" per tier by a stated rule, and named the two known-wrong citations it replaces. Both live in the /plugin-designer skill, which sits outside this repo and so cannot be gated from here (a checker pointed at it would pass on this machine and skip silently everywhere else). Measured: unified-research-dashboard as the cap-4 reference names a bundle that DOES NOT EXIST -- it became research-dashboard (cap-3) plus dashboard-plus-grid (cap-4). And doxa-vision-card as the cap-1 example ships an instances.json with 2 instances, which makes it cap-2 parameterized by the project's own rule. The first version of the rule picked stack-demo for cap-4. It satisfied the rule perfectly -- name equals profile, shortest -- and was still the wrong answer to hand someone, because big-picture is the canonical cap-4 composite named in the Gate-1 chain. Added a demo/template deprioritisation and recorded why: a reproducible rule is not automatically a good one, so check what it actually selects. All five picks now match the skill's own reference implementations, except the two that were wrong. |
||
|
|
15b96a625b |
Refresh the dated identity measurement: 18 of 18, was 17 of 17
Some checks are pending
build-gate / build (push) Waiting to run
Applying last tick's lesson systematically: swept every numeric claim about the catalog across all docs. Only one had aged into something misleading. "Measured 2026-09-21: 17 of 17 bundles identity-proven" was TRUE when written, and the gate reads the live manifest so nothing was broken. But a reader today sees 17 beside a catalog of 18 and cannot tell whether a bundle went missing or the sentence simply aged. Re-ran check:catalog-identity: 18 of 18, PASS. Restated with the delta named. The other three hits are fine and deliberately left alone: the "21 bundles" in the CLI doc is prose explaining why a count was REMOVED, the 18 in reference-capability-exemplars.md is auto-generated, and "5 plugin capabilities" is a constant rather than a count. Added the general rule beside the number: a dated measurement is not a stale claim, but it still misleads once the underlying figure moves, so re-run the gate and restate rather than leaving a reader to guess. |
||
|
|
c80f8fb073 |
Document the preconditions three gates need, so a bare run is not read as failure
Some checks are pending
build-gate / build (push) Waiting to run
Running the gate list I completed last tick, two entries reported FAIL: smoke:gallery and smoke:share. Neither is a defect. smoke:gallery needs a dev server on :5174 and says so; smoke:share needs --url arguments and prints usage. Both exit non-zero, so in a batch run they read exactly like the real failures the list exists to surface. A checklist that turns a precondition into a red line trains its reader to ignore red lines, which is how a real failure gets skipped. Added a preconditions table stating what each needs and what a bare run actually does, measured by running them. Also recorded that the token-dependent gates DEGRADE rather than fail: without MAPBOX_TOKEN, smoke:embeds reports map profiles as timeouts -- it once read 27/40 for that reason and 40/40 with a token. A timeout there means "no token" far more often than "broken plugin". Two claims corrected before shipping. I wrote that the token endpoint needs a browser User-Agent; measured, a plain curl gets 200 identically -- that requirement belongs to pray.doxa.life, a different host. And I tested rather than asserted the limitation now in the doc: verify:map-paint PASSES on a leaf whose tiles 403, because a live GL context, a sized canvas and attribution are all true while the basemap stays empty. The gate is correctly scoped; the name invites over-reading, so the scope is now written down. |
||
|
|
5353ed5b51 |
Document every standalone gate, and fix the two that were already failing
Some checks are pending
build-gate / build (push) Waiting to run
26 gates live outside build:unlocked; only nine were listed in reference.md.
An inheritor reads that table as the list of what to run before publishing,
so seventeen checks were effectively invisible -- including the three that
caught the most defects this flight: catalog-currency, smoke:embeds and
verify:map-paint.
The cost was already being paid. Two unlisted gates were RED, and had been
for two sessions:
check:quoted-scripts the doc quoted a build chain missing the
check:descriptions step added two ticks ago
check:capability-exemplars the generated tier table predated golden-path-check
Neither is a product defect -- both gates were working exactly as designed,
comparing documentation against machine truth. Nobody ran them. That is the
failure mode a standalone gate has by construction, and the table is the only
mechanism that prevents it.
Fixed both (doc updated to match package.json; exemplars regenerated to 27
rows), then filled the table to 25 of 26 -- the one omission is an alias for
a script already listed. Each new row states what the gate catches and, for
the ones that cannot live in the build, why.
Corrected a number mid-edit: I first wrote "nineteen gates" from an earlier
tally that had excluded the regenerator helpers. Measured again: 26.
|
||
|
|
9dc4dda8cf |
consolidate docs into documentation/ and deployment shells into deployment/
Some checks are pending
build-gate / build (push) Waiting to run
Driver 2026-09-21: 'a folder called deployment which will store the capacitor
folder, wordpress folder and tauri folder', and 'a folder called documentation
to store the contributing folder so docs are in one place'.
docs/ + contributing/ + CONTRIBUTING.md -> documentation/ (three doc homes into
one). capacitor/ + wordpress/ + src-tauri/ -> deployment/.
The public/ -> dist/ rename the Driver asked about is NOT here: after seeing the
measured blast radius (855 tracked files, 54 source refs, and public/ is the
served web-root four consumers point at) the Driver withdrew it - 'keep the
public folder the same'.
Path references were rewritten mechanically (258 across 87 files), but three
classes needed hand fixes because a prefix regex cannot see them:
- join(ROOT,'docs',...) in 10 internal scripts - a quoted SEGMENT, not a prefix
- tauri frontendDist '../public' -> '../../public', since src-tauri is now one
level deeper; check:tauri caught this exactly as the card predicted
- the WordPress contract test's dirname(__DIR__,2) -> 3, same reason
Also reverted one over-reach: the rewrite renamed a FINDING-CATEGORY label
'docs' in public-hygiene-check.mjs, which is a rule name in its own header, not
a path.
Verified: full build ALL GUARDS PASS, SRI consistent; check:tauri, check:mobile
(cap sync + webDir OK), check:wp-embed, quoted-scripts, capability-citations,
capability-exemplars, library-index, public-hygiene, parse and drop all pass.
|