legal batch: LEGAL footers + self-hosted fonts + license owner, and the sidecar-exchange first-contact consent gate - #3897
Conversation
…ts, license owner, measured tune time - footer LEGAL column (ToS / Privacy Policy / BSD-3 license) on every daslang.io page incl. the blog template and Sphinx docs layout, and on all dasllama.io pages - Google Fonts CDN dropped: Inter Tight + JetBrains Mono vendored as variable woff2 (all subsets) under files/fonts/ per site and doc _static; zero requests to fonts.googleapis.com / gstatic remain - LICENSE copyright owner: Gaijin Games Kft. (legal entity; site footers keep the Gaijin Entertainment brand), install/README.md aligned - the ~20-minute tune claim re-stated as ~12 min from a measured first boot (M1 Max, 11:46 wall incl. cold JIT codegen, 42-family race, re-exec, model load) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
… it was the one cookie-setting embed on the site Removes the embed from build_blog.py (all 44 posts regenerated without it), the orphaned wrapper CSS, and the README row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
… an expressed choice An explicit exchange_* config key (TOML or env) is itself the choice; on the zero-config path a <stem>.consent sidecar-sibling file governs. Unset: a real terminal asks inline (Enter = Accept); a supervised boot emits `@sidecar consent state=needed` and tunes locally - the watchdog raises a native Accept/Decline dialog (osascript / MessageBoxW; the answer lands in the consent file the event names, and Accept mid-tune rides the stop rail as the new non-sticky "consent" mode), and the control page carries the same banner via POST /exchange/consent (accept on an untuned box drains and exits 4 to run the lookup on relaunch). Notice text is a placeholder pending legal; the das constant and the watchdog copy point at each other. Proven end-to-end: an unconsented non-TTY dasllama-server boot emits the consent event with zero lookup lines; a recorded "accepted" runs the lookup. dastest suite extended (consent round-trip, garbage file reads as never-asked, policy.configured from TOML); 25/25 pass. Also: ask.tune.json refreshed by today's measured tune; remaining ~20-min claims in comments/docs restated as ~12 min. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
… gate) - the "no prompts" claim was stale Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
- utils/watchdog/test_consent.py: the @sidecar consent fold, the non-sticky env-free "consent" stop mode, and handle_consent_needed's contract with the dialog mocked at the show_consent_dialog seam - accept writes the file and stops a running tune, decline only records, walking away answers nothing - tests/consent.spec.js (8 specs): banner on the zero-config path only, POST bodies both ways, declined as the change-your-mind path, configured policy and pre-consent servers render no banner, failed POST re-enables the buttons - control.html: the banner additionally gates on consent_notice presence, so a server predating the consent API renders exactly as before Full playwright suite 104/104; both watchdog pytest files green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
…oundary, mints nothing test_tune_interrupt spawns _interrupt_probe (one interrupt_gate() between a BEFORE and an AFTER print) in a child process - the env is load-once, so a child is the only honest way to flip it. Present control file: the @tune abort event fires, "no sidecar written" prints, nothing past the gate runs. Negative controls: a named-but-absent file and an unset env both pass through. Registered in the model-free roster; the full gate ran green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
…nts root, audits' checklist defects, gate tests
The review round's confirmed findings, one batch:
- consent bypass closed both ways: an AUTHORITATIVE (control-page-written) toml's
exchange keys are no longer consent (the page persists them on every save);
submit=always without an expressed choice degrades to the ask offer; the page's
always-share toggle and an explicit sidecar apply now record the consent they
express. The gate composition is extracted as exchange_may_contact and tested
(mutation-controlled: deleting the gate now reds the suite)
- override announce: env/config-expressed consent prints the knob that waived
the question; a recorded accept names the consent file; both env docs extended
and ENVIRONMENT.md regenerated
- deploy breaker: pages.yml staged site-dasllama/files with a non-recursive cp -
the new files/fonts/ subdirectory would have failed the deploy step (cp -R now)
- blog fonts 404: the template's fonts link lacked {{root}} - every generated
blog/news page lost the self-hosted fonts; fixed + regenerated
- sidecars page: the transcript is relabeled "first start after Accept" (the
promptless boot it shows), the measured-comment matches the run (40 raced of
42 planned), the config-skips-question claim now names the hand-written-config
and env arms only
- /exchange fixture RE-CAPTURED from a real zero-config boot (carries consent /
consent_notice / policy.configured); consent specs are fixture-first + two new
(accept-without-restart, always-share-records-consent); README rows list the
new keys; dasllama_server.rst gains the /exchange/consent row (REVIEW.das green)
- ~20-min stragglers in control.html and dasllama-io.js -> ~12 min
- test_tune_interrupt: the "unset env" cell now explicitly unsets
DAS_TUNE_CONTROL in the child (proven leak from a poisoned parent), and the
tautological same-print assert is gone
- watchdog tests: stream_child dispatch (once per consent path), darwin dialog
parse arms (Accept/Decline/gave-up/Escape), stdlib threading no longer mutated
- checklist self-review defects fixed in place: site, site-dasllama, dasLLAMA,
dasLLAMA tests, dasllama-server, utils, workflows checklists + three new rules
in performance/REVIEW.md (notice sync, gate coverage, no-stdin-block) and the
web/examples/ui checklist restored after an accidental clobber (dragon catch)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
…act, dragon N0 residuals, sibling-path dedup - the resolver refactor had silently missed (a raw-newline artifact broke the replacement) - exchange_scope_resolver now genuinely calls exchange_may_contact, and the two embedded raw newlines are proper \n - performance/REVIEW.md gate rules name the public seam; the notice-copy rule is self-maintaining (a new copy names itself there); utils/REVIEW.md routes watchdog consent-wording diffs to it; the dragon's N0 residuals across the eight rule docs (arch-doc census -> property, per-folder path form, rewraps) - dupes triage: exchange_consent_path/exchange_state_path share the extracted sidecar_sibling_path helper Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
There was a problem hiding this comment.
Pull request overview
This PR applies a legal/compliance batch across daslang.io + dasllama.io + docs, and introduces a first-contact consent gate for dasLLAMA’s sidecar-exchange so an unconfigured box won’t contact dasllama.io until the operator explicitly accepts/declines once.
Changes:
- Add a GDPR-style first-contact consent decision path for the sidecar exchange across the boot resolver, watchdog (native dialog), and control page (
POST /exchange/consent), plus tests/fixtures. - Self-host Inter Tight / JetBrains Mono (remove Google Fonts), and add LEGAL footer blocks (Terms/Privacy/License) across sites and Sphinx docs; remove Disqus from the blog.
- Update review checklists + CI staging (recursive copy for dasllama.io
files/subdirs) and refresh various “~20 min” claims to “~12 min”.
Reviewed changes
Copilot reviewed 54 out of 114 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| web/examples/ui/REVIEW.md | Adds a Playwright-run reporting rule for runtime-path changes in UI samples. |
| utils/watchdog/watchdog.py | Implements supervised-session consent dialog handling and a non-sticky “consent” stop mode. |
| utils/watchdog/test_consent.py | Adds unit tests for consent event folding, stop-mode semantics, and dialog dispatch. |
| utils/watchdog/README.md | Documents the new @sidecar consent event and its watchdog handling. |
| utils/REVIEW.md | Clarifies cross-tool checklist applicability and consent-wording checklist linkage. |
| utils/dasllama-server/tests/fixtures/exchange.json | Updates captured /exchange fixture to include consent and configured fields. |
| utils/dasllama-server/tests/consent.spec.js | Adds Playwright coverage for the control-page consent banner and POST flow. |
| utils/dasllama-server/test_exchange_client.das | Adds dastest coverage for policy configured-flag and consent gating/recording. |
| utils/dasllama-server/REVIEW.md | Tightens fixture capture rule and aligns route/fixture expectations for tests/control page. |
| utils/dasllama-server/README.md | Documents new /exchange/consent endpoint and first-contact consent behavior. |
| utils/dasllama-server/openai_server.das | Adds /exchange/consent handler; records consent when applying a sidecar. |
| utils/dasllama-server/main.das | Updates startup comments to reflect ~12 minute tuning figure. |
| utils/dasllama-server/control.html | Adds consent banner UI + POST handler; records consent when enabling always-share. |
| site/REVIEW.md | Narrows the Playwright evidence rule and updates runtime-artifact scope list. |
| site/README.md | Removes Disqus reference from blog behavior description. |
| site/playground/placeholder.html | Switches to self-hosted fonts CSS. |
| site/playground/index.html | Switches to self-hosted fonts CSS. |
| site/performance.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/index.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/files/forge.css | Removes Disqus styling and expands footer grid for legal links. |
| site/files/fonts/fonts.css | Adds local @font-face rules for self-hosted fonts. |
| site/examples.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/downloads.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/daspkg.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/dasllama.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site/blog/template.html | Switches blog template to self-hosted fonts CSS and adds legal footer column. |
| site/blog/build_blog.py | Removes Disqus embed generation from blog build. |
| site/benchmarks.html | Switches to self-hosted fonts CSS and adds legal footer column. |
| site-dasllama/sidecars.html | Switches to self-hosted fonts, updates ~12 min copy, adds legal footer column. |
| site-dasllama/REVIEW.md | Strengthens provenance requirements for numbers and adds deploy-step verification rule. |
| site-dasllama/ladder.html | Switches to self-hosted fonts and adds legal footer column. |
| site-dasllama/index.html | Switches to self-hosted fonts and adds legal footer column. |
| site-dasllama/files/fonts/fonts.css | Adds local @font-face rules for self-hosted fonts on dasllama.io. |
| site-dasllama/files/dasllama-io.js | Updates the empty-state tuning duration copy to ~12 min. |
| site-dasllama/files/dasllama-io.css | Expands footer grid for legal links column. |
| modules/dasLLAMA/tests/test_tune_interrupt.das | Adds end-to-end test for the supervisor stop rail via child process. |
| modules/dasLLAMA/tests/run.das | Registers the new tune-interrupt test in the suite list. |
| modules/dasLLAMA/tests/_interrupt_probe.das | Adds probe program used by the tune-interrupt test. |
| modules/dasLLAMA/REVIEW.md | Clarifies architecture-doc indexing, override announce scope, and stdin-prompt ban. |
| modules/dasLLAMA/performance/REVIEW.md | Adds explicit “consent wording sync” and exchange_may_contact routing rules. |
| modules/dasLLAMA/ENVIRONMENT.md | Documents env vars as an expressed exchange-consent choice. |
| modules/dasLLAMA/dasllama/dasllama_exchange.das | Implements consent state file, console prompt, @sidecar consent event, and exchange_may_contact. |
| modules/dasLLAMA/dasllama/dasllama_env.das | Updates env knob docs to reflect consent-skipping behavior. |
| modules/dasLLAMA/ARCHITECTURE_MEASUREMENT.md | Defines boundary for “override announce” rule (timing knobs vs numeric-impact knobs). |
| modules/dasLLAMA/ARCHITECTURE_ENGINE.md | Documents consent gate behavior in engine architecture doc. |
| LICENSE | Updates copyright owner to “Gaijin Games Kft.” |
| install/README.md | Updates license/copyright line to match LICENSE header. |
| doc/source/reference/utils/dasllama_server.rst | Documents new /exchange/consent endpoint. |
| doc/source/_templates/layout.html | Adds LEGAL links block to every docs page footer. |
| doc/source/_static/fonts/fonts.css | Adds local @font-face rules for docs self-hosted fonts. |
| doc/source/_static/custom.css | Switches from Google Fonts import to local fonts and styles legal links block. |
| .github/workflows/REVIEW.md | Tightens gate-step failure requirements and adds “state a local run” rule. |
| .github/workflows/pages.yml | Fixes dasllama.io staging to copy files/ recursively (for fonts subdir). |
| .github/workflows/extended_checks.yml | Adds watchdog consent tests to extended_checks lane. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Ctrl+D / closed stdin at the console prompt returned "" which stripped to the Enter-defaults-to-Accept arm - a walkaway counting as consent. Enter arrives as a newline (non-empty before the strip), so an empty read is EOF alone: record nothing, tune locally, ask again next boot. Riding along: the consent route answers malformed JSON with "invalid JSON: <err>" instead of the missing-field message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FkBKGJisiiHXEgSsGjpqLm
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 114 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (2) — in code that hasn't changed since the last review.
utils/watchdog/watchdog.py:702
handle_consent_neededwrites the consent file usingPath(path_text)directly. If the consent path is relative (common when the child emitsmain.tune.consent), this resolves against the watchdog process CWD, not--cwd(the child’s working directory). Running the watchdog from another directory (e.g.python /abs/path/watchdog.py) can therefore record consent into the wrong location and leave the real gate unanswered, causing repeated prompts.
utils/dasllama-server/control.html:2376- When
/exchange/consentreplies withrestarting:true,postConsentimmediately callsloadExchange(). In a real restart this fetch will often fail (server is draining), and the catch inloadExchangeoverwrites the user-facing status withcould not load /exchange, hiding the intended “restarting…” message.
Behavior change: the first dasLLAMA server boot on an unconfigured box now asks once before contacting dasllama.io. Until a choice is recorded, nothing leaves the box and it tunes locally.
Gaijin legal reviewed daslang.io and dasllama.io. This branch lands everything actionable from that review. Both sites and the docs get a LEGAL footer block (Terms of Service, Privacy Policy, the BSD-3 license). Google Fonts is gone: Inter Tight and JetBrains Mono are vendored as variable woff2 per site and in the docs static tree, so no visitor request reaches Google. The GitHub LICENSE names the legal entity, Gaijin Games Kft.; site footers keep the Gaijin Entertainment brand. Disqus is removed from the blog - zero comments over its whole history, and it was the one cookie-setting embed.
The sidecar exchange gets a first-contact consent gate. The lookup sends the box identity (platform, CPU model, OS build - hardware-class data, no serials or user names), and legal asked for an Accept/Decline step before the first request. An explicit
exchange_*key in a hand-written config or the environment is itself the expressed choice, and the boot announces which knob expressed it. A control-page-written (authoritative) config does not count - the page persists exchange defaults on every save. With no expressed choice, a real terminal asks inline; a supervised boot emits@sidecar consent state=needed, the watchdog raises a native Accept/Decline dialog, and the control page carries the same banner (POST /exchange/consent). Accept mid-tune rides the existingDAS_TUNE_CONTROLstop rail: abort at the kernel-family boundary (minting is end-gated, nothing is written), relaunch, run the lookup. The notice wording is a placeholder until legal delivers the final text.The "~20 minutes" tune claim is re-stated as ~12 min from a measured first boot, everywhere it appears.
Where to look:
modules/dasLLAMA/dasllama/dasllama_exchange.das(exchange_may_contactis the whole decision),utils/watchdog/watchdog.py(dialog + theconsentstop mode),utils/dasllama-server/openai_server.das(handle_exchange_consent),control.html(banner + the always-share toggle now records consent),.github/workflows/pages.yml(the dasllama.io stagingcpis now recursive - the fonts subdirectory would have failed the deploy).Validation, claims, ledger
Validation
--grep-invert '@wasm'(the per-PR lane's filter) against the checked-in runtime artifacts; the diff'smodules/touch (dasLLAMA) is not part of the playground WASM runtime.run.das -- --suite model-free: green end to end (the runner tees full logs;test_tune_interrupt4/4 inside it).test_exchange_client.das(dastest): 28 passed, 0 skipped - download gate, privacy strip, submit rails, and the consent additions; the consent gate is mutation-controlled (deleting it reds the suite)./exchangefixture (a real zero-config boot; carriesconsent/consent_notice/policy.configured).dasllama_exchange.das,performance/REVIEW.md); the delta was revalidated with targeted gates (compile, lint, format --verify, the 28-test suite) rather than a second full preflight.preflight --fullfailed its three test gates on 5 pre-existing failures intests/mcp/(test_popen_argv_pipe,test_mcp_jsonrpc) - deterministic on this box, byte-identical test files vs master, a binary built before this branch existed, and a diff that touches nosrc/,daslib/,utils/mcp, ortests/mcppath. The other 17 gates passed.Claims - stated, not tested
exchange_scope_resolvercallingexchange_may_contact) is proven by the e2e boots above; the composed decision is unit-tested, the private call site is not.@sidecar consentevent's producer side is verified in the e2e transcripts only; the watchdog consumer side is unit-tested.DASLLAMA_EXCHANGE_URL/_ACCEPTsettingconfigured) is unpinned in-process (load-once env); the TOML arms are tested per key, and the e2e announce line covers the env arm./exchange/consenthandler branches (loopback guard, bad-body, the restart condition) have no route-level test - the guard is theis_loopback_reqpattern every operator route shares, none of which do.set "VAR="spellings intest_tune_interrupt, and the pipe-handle behavior ofis_terminalunder a supervisor. A false-positive TTY probe under a supervisor would block the boot; proven not to on darwin only.test_tune_interruptran on macOS only; its cells are platform-guarded, not skip-armed.Not done
EXCHANGE_CONSENT_NOTICE(das),CONSENT_TEXT(watchdog), and the captured fixture carry PLACEHOLDER wording - Gaijin legal delivers the final text; the copy-sync duty isperformance/REVIEW.md's new rule.REVIEW.dascomparing the das and Python constants) - recommended by the review round, not built.dasllama.tune.json(an old-format relic) is untouched, awaiting a ruling.🤖 Generated with Claude Code