Skip to content

Harden Worker, site shell, gate suite, and Loopy skill - #98

Open
trollbot2012 wants to merge 33 commits into
Forward-Future:mainfrom
trollbot2012:gauntlet/integration
Open

Harden Worker, site shell, gate suite, and Loopy skill#98
trollbot2012 wants to merge 33 commits into
Forward-Future:mainfrom
trollbot2012:gauntlet/integration

Conversation

@trollbot2012

Copy link
Copy Markdown

Summary

Hardening pass over the Worker, site shell, gate suite, and the Loopy skill. Every change was built in its own branch and independently re-verified: full gate suite rerun, each new check mutated to prove it fails when the fix is reverted, and a written review before merge. Baseline gates were green before this work except one: loop-library/scripts/check.mjs failed on any Windows clone with core.autocrlf=true (CRLF vs \n assertions).

Worker tests: 52 → 72, passing on Node 22 and Node 24.

Worker

  • Minimum completion time is now enforced server-side (form_elapsed_ms, 1200 ms suggestions / 800 ms weekly), mirroring the honeypot's fake-success response. Previously client-only, so a direct POST skipped it.
  • Vote writes are rate limited per signed-in account through the existing limiter binding (30/min), the one write path that had no limit.
  • Public rendered surfaces (homepage, detail pages, catalogs, feed, sitemap, llms.txt, public /api/loops) send public, max-age=60, stale-while-revalidate=600 instead of no-store. 404s, 5xx fallbacks, admin, session, vote and OAuth responses stay no-store. The homepage now copies only an allowlist of shell headers, so a shell Set-Cookie can never land in a cached response.
  • Atom feed timestamps are UTC (Z) instead of a hardcoded -07:00.
  • Real tests for LoopCatalog and VoteStore against SQLite via node:sqlite (both classes previously ran only behind in-memory mocks); tests for the 10/day and weekly 5/hour limits and the origin-suffix branch.
  • Dead helpers removed; npm run check now node --checks every source file.

Site

  • Server-rendered rows carry data-upvotes, so first paint is already in "Most popular" order instead of reshuffling after /api/votes returns.
  • Vote and share controls are role="group" with per-loop labels; copy/show-more buttons name the loop. Twitter card meta on learn/ and agents/. The form guard fails closed (buttons disabled, status shown) when the API origin is missing.
  • Inter and IBM Plex Mono are self-hosted (latin subsets, OFL licences included, font-display: swap); the CSS already named them but nothing loaded them.
  • Cache-bust tokens bumped for both styles.css and script.js (the Worker-rendered pages were on a different, stale script.js token; a new gate keeps them aligned).

Gate suite and CI

  • .gitattributes pins LF and check.mjs normalises CRLF on read, so the documented pre-commit checks run on Windows.
  • check.mjs now mirrors every skills/loopy/references/* file against skills/loop-library/references/* by directory listing, gates audit.md, validates examples/loop.json with the real normalizeLoopDocument, checks .dev.vars.example points at the .com callback host, and asserts every src/*.js is in the check script.
  • The CI git diff --check step ran on a clean tree after npm ci and inspected nothing; it is replaced with git diff --check $(git hash-object -t tree /dev/null) HEAD, pinned identically in ci.yml, AGENTS.md, README.md and check.mjs.

Loopy skill

  • One terminal-state vocabulary across SKILL.md and the references (the run-receipt enum); "stagnated" removed, "exhausted" added to the preflight.
  • publish.md gets the untrusted-content clause the other paths already had.
  • Loop Doctor gains a Not ready verdict for gaps that cannot be repaired from scoped evidence.
  • agents/openai.yaml advertises Find first instead of forcing the crafting interview.
  • The design rules are stated once (SKILL.md "Design the feedback cycle"); the preflight and Loop Doctor point at it. Both copies of SKILL.md are kept in step and gated.

Docs

  • .dev.vars.example pointed OAuth at the redirect-only .ai host; examples/loop.json carried a categoryLabel the renderer never shows; README publish checklist lacked llms.txt; the SEO audit presented a June snapshot as current; CHANGELOG backfilled and extended.

Not included

  • No deploy and no catalog data change. Live verification of the cache headers needs a Worker deploy from main per AGENTS.md.
  • The here.now proxy's handling of origin Cache-Control is undocumented; the headers are correct at the Worker boundary regardless.

Test plan

  • node --check loop-library/site/script.js
  • node loop-library/scripts/check.mjs (also on a fresh core.autocrlf=true clone)
  • npm --prefix loop-library/worker run check on Node 24.18 and Node 22.23 (72/72)
  • JSON manifests, git diff --check, and the new repo-wide whitespace check
  • diff -r skills/loopy/references skills/loop-library/references empty

🤖 Generated with Claude Code

justkidding2047 and others added 30 commits September 2, 2026 10:20
check.mjs asserted on embedded \n and failed on core.autocrlf=true clones.
Normalise CRLF on every text read and add .gitattributes so future
checkouts stay LF.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- .dev.vars.example: OAUTH_CALLBACK_ORIGIN uses the live .com callback host,
  not the redirect-only .ai host
- examples/loop.json: categoryLabel matches loop-schema's mapping for its
  category; AGENTS.md documents that requirement
- README: publish checklist includes llms.txt; layout line names
  loop-library/scripts/ and loop-library/audits/
- seo-geo audit: stop-state heading dated and page count at capture noted
- CHANGELOG: 2026-07-07 entry for popular sort, homepage shell fallback,
  .ai redirect docs, hardened harness checks

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…en node --check

- Vote handler now calls TURNSTILE_RATE_LIMITER keyed on the verified
  viewer id (vote:<sub>) and returns the same 429 shape as the forms.
- Atom feed <published>/<updated> use Z instead of a hardcoded -07:00,
  matching formatDate's UTC pin.
- Remove unreferenced randomUrlSafe and latestModified.
- package.json check covers loop-routes, catalog-store, render-loops,
  loop-schema.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…branch

Adds four FormGuard/handleRequest tests: suggestions 10/day, weekly
signups 5/hour and 10/day, and a suffix-allowed Origin that is still
rejected by Turnstile hostname verification. Daily caps seed the guard's
storage with 5h-old events rather than injecting a clock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Instantiate the real classes with a node:sqlite DatabaseSync adapter so the
schema, CHECK constraints, upsert and revision writes, and the export/restore
contract are executed instead of substituted by in-memory namespaces.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
styles.css already named both families but nothing loaded them, so
production rendered system fallbacks. Add @font-face rules (Inter
variable wght 100-900 latin subset from rsms/inter v4.1; IBM Plex Mono
400/600/700 Latin1 split subsets from @ibm/plex-mono 2.5.0; both SIL
OFL, licenses alongside), preload the sans face on the three shell
pages, bump the styles.css cache token to 20260902-self-hosted-fonts
(check.mjs pin and the Worker loop-page template updated in step), and
drop the unused --violet token.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rm fail-closed

- Worker reads VOTE_STORE totals when rendering the homepage and emits
  data-upvotes plus initial counts per row, so the client's first sort
  matches the post-/api/votes order.
- vote-controls and share-actions get role="group"; vote, copy, and
  show-more labels carry the loop title so each control is distinct.
- learn/ and agents/ pages gain twitter:card/title/description/image.
- Form protection fails closed with a visible status when no Worker
  origin is configured.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Round 1 shipped the fonts but nothing went red when a woff2 was renamed.
Assert every @font-face url() and every as="font" preload href resolves
under site/, >= 4 @font-face blocks each with font-display: swap, and
both OFL license files present.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Client sends form_elapsed_ms; Worker returns the honeypot fake-success
(202 {ok:true}) and writes nothing when it is missing, non-integer, or
below 1200 ms (suggestions) / 800 ms (weekly signups).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…validation, dev vars, node --check coverage, empty-tree whitespace check

- check.mjs mirrors every skills/loopy/references file into skills/loop-library
  via readdir + sorted listing equality instead of three hand-listed files
- gate SKILL.md -> references/audit.md and audit.md content fingerprints
- validate examples/loop.json with normalizeLoopDocument and categoryLabel
  (categoryLabel fixed to Engineering; same one-word edit as P3)
- assert .dev.vars.example uses signals.forwardfuture.com (same fix as P3)
- assert package.json check covers every src/*.js; widen script (same line as P7)
- replace vacuous post-npm-ci `git diff --check` with a diff from the empty
  tree so every tracked file is scanned; pinned identically in CI/AGENTS/README

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Assert the llms.txt publish phrase and loop-library/audits/ in README,
the dated stop-state heading in the SEO audit, and that the newest
CHANGELOG entry (2026-07-07) is the first heading. Wrap README line 301.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Homepage, loop detail, catalogs, llms.txt, sitemap, feed, and the public
catalog API now send `public, max-age=60, stale-while-revalidate=600`
instead of `no-store`. Nothing on those surfaces is per-viewer: vote
controls render as hidden placeholders and counts load from /api/votes.
Fallback 5xx homepages, admin, session, vote, and OAuth routes stay
no-store. AGENTS.md read-back step notes the 60s window.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…il-closed, warn on vote-store failure

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… ready verdict

- Canonicalize run.md's six receipt states across SKILL.md, audit.md, discover.md
- Add untrusted-content clause to publish.md
- Add 'Not ready' Loop Doctor verdict with Result guidance
- check.mjs: fingerprint the new Verdict line, forbid 'stagnated', assert the
  preflight bullet, publish.md 'untrusted', and audit.md '| Not ready'
- Mirrored byte-for-byte into skills/loop-library/references

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Missing-loop 404s (JSON and HTML) return to no-store so a slug probed
before publish is not served stale afterwards. The injected homepage now
copies only Content-Type, Content-Language, and Vary from the shell
origin, so a shell Set-Cookie or WWW-Authenticate cannot land in a
publicly cacheable response. The unreachable-shell 502 fallback is now
pinned no-store by test. AGENTS.md read-back step tells maintainers to
use a cache-busting query string.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The homepage uses U+2192 in two <option> labels; it was outside the
Inter subset and unicode-range, so it fell back to the system font.
Also trim the verbatim upstream trailing whitespace on line 21 of the
IBM Plex Mono OFL so the repo-wide whitespace gate stays green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eck.mjs

- Alias body now matches loopy/SKILL.md except name/description/header
- check.mjs: legacySkillSource joins the 'stagnated' loop; preflight bullet
  asserted on both trees via whitespace-tolerant regex so a re-wrap cannot
  break CI

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- openai.yaml (both skills): short_description leads with Find; default_prompt
  routes to a published loop before crafting instead of forcing the interview.
- SKILL.md: 'Validate every crafted loop' no longer restates the design rules;
  it points at 'Design the feedback cycle' + 'Keep every workflow grounded'
  and keeps only the preflight-unique items (one-shot fallback, Not ready).
- audit.md: 'Check for' bullets replaced by a pointer to the same normative
  list; run-evidence linkage and no-score/no-budget guidance unchanged.
  Mirrored byte-for-byte into skills/loop-library/references.
- check.mjs: pin the new default_prompt phrase and 'Find,' prefix; move the
  P8 terminal-state preflight pin onto the normative bullet; pin the new
  preflight and audit pointer phrases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
justkidding2047 and others added 3 commits September 2, 2026 10:46
…no-store headers, docs

- script.js changed (required form_elapsed_ms, a11y, fail-closed) without a
  cache-bust; old cached scripts posted without the field and got the silent
  202. Bump to 20260902-elapsed-a11y on all three shell pages, the Worker
  loop-page template (was stale 20260623-proxy-auth), and check.mjs pins.
- check.mjs now asserts render-loops.js carries the same script token as
  index.html so Worker pages cannot drift from the shell again.
- loop-routes.js: comment reflects embedded vote totals + 60s public cache;
  NO_STORE_HEADERS const replaces six inline literals.
- index.js: drop foreign ponytail: comment prefix.
- AGENTS.md: document per-account vote rate limit (30/min via
  TURNSTILE_RATE_LIMITER). CHANGELOG: 2026-09-02 entry; check.mjs first-heading
  pin moved to 2026-09-02.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… run

A 13-piece fan-out met every per-piece check yet 9 pieces served a goal the
operator had not asked for, because the piece list came from a whole-repo
survey. Add one sentence to run.md step 2 (mirrored to the loop-library
alias) and pin it in check.mjs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants