Skip to content

chore(sync): merge upstream 0.1.41 into fork (WIP, sanitized relay defaults) - #15

Merged
zeeshanhaque21 merged 306 commits into
mainfrom
sync/daily-2026-10-07-haiku
Oct 8, 2026
Merged

zeeshanhaque21 merged 306 commits into
mainfrom
sync/daily-2026-10-07-haiku

Conversation

@zeeshanhaque21

@zeeshanhaque21 zeeshanhaque21 commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Sanitized upstream sync through v0.1.41

Reviewed and tested head: b1b55c1a03518458bf881724ff661ebb023ccf72.
Pinned upstream parent: 9d7fc7ac005ce32271444cb302fb6860d019276c.
This includes the v0.1.41 release commit 3507091ced84cdb61eb6b6d08e0315cfd9d357cc and 136 later upstream commits.
PR #15 merged at df0b3e35986496aa27d48388d5cc24b205252e6e, with a source tree identical to the reviewed head.
Published APK-only release: https://github.com/zeeshanhaque21/nanoMuse/releases/tag/v0.1.41-fork.1.
Mac installation is verified at 0.1.41; the private Jetson relay is upgraded and healthy at 0.22.0.

Changes

  • Removes active upstream backend defaults and owned website links while retaining configurable private relay hosting and historical attribution/legal documents.
  • Preserves Google Calendar, credential-vault MCP headers, signed-out relay editing and bundled Chromium.
  • Repairs offline packaged service activation, Android blank privacy actions, stable unconfigured-relay errors, DST scheduling and gateway token-log redaction.
  • Fixes cross-account push/pull isolation, stale acknowledgments, sign-in timing and coalesced pull triggers with deterministic regressions.
  • Requires captured request identity at every sync error-handler boundary, so old-account errors cannot pause or disable the current account.
  • Serializes SQLite shutdown with notice transactions and waits for completed audit writes in its asynchronous regression.
  • Preserves runtime and browser framework links at both copy stages.
  • Keeps fork-tag build/test jobs but blocks automatic desktop/harness release uploads and Docker latest changes.
  • Fixes the timeout test's proc-reaping diagnostic without weakening real child-liveness assertions.

Verified evidence

  • Exact-head CI: 15 successful checks, one tag-only release skip, no failed or pending checks.
  • Local root suite before the final test-only delta: 803 passed, 5 skipped, one live test deselected; the final 35-test tool suite passes and exact-head Python matrix CI is green.
  • Cloud 165, gateway 43, web 94 and harness 267 tests pass; repository lint, formatting, compileall and 138-file mypy pass.
  • Every fix has an independently audited contract and old-fail/new-pass proof where a regression was reproduced.
  • Native Opus verified account-bound keys and generation checks and approved this exact final head for merge after independently rerunning 114 focused tests and 15 cloud tests.
  • Final Mac ZIP extracted on local disk passes deep/strict signature verification, auth/route smoke (401, 303, cloud/rooms 200), packaged runtime health and real bundled Chromium navigation.
  • Whole-app endpoint scan: zero nanomuse.cn or demo.nanomuse.dev matches.
  • DMG SHA-256: 0636f395d8accb2dd279d003a4ed2cba0977fddf00cf6b88add5f365130b2dab.
  • ZIP SHA-256: 5a18405cdb6f3ce8207cc4e714f1b9de8addc7830e560d8294be577b2ccd59c7.
  • Its product/build inputs are identical between frozen build 74deaee269 and this final head.

Release boundaries and limitations

The freshly tag-built, release-signed Android APK and its checksum are now published as v0.1.41-fork.1.
Its public download was independently rechecked for the fork certificate, archive integrity, endpoint absence and SHA-256 dd9a699075c355ae8545165ea1726e0119f2ef7c8ffb4418969a9cc097766584.
Older v0.1.38-v0.1.40 counterparts are assetless drafts targeting the sanitized merge, with no pushed tag refs or misleading old-version Docker images.
All four tag workflows passed, including all four desktop platforms; both automatic release-upload jobs skipped and only APK/checksum assets are attached.
Mac artifacts are ad-hoc signed, not notarized, and are not advertised as signed/notarized release assets.
Full iOS native build, signed Windows artifacts, phone installation and operator/UI permission checks remain unverified.
Mac deployment preserves two verified 0.1.37 backups and passes installed-runtime health and Chromium checks; the separate local port-8787 relay remained running.
Jetson deployment preserves credentials and private exposure, with integrity-checked DB backups and the old 0.19.0 container retained.
The new 0.22.0 relay and old-image rollback startup passed isolated migrated-snapshot checks first.
The owner explicitly approved the brief disconnect after the active-connection gate deferred the first attempt; shutdown was graceful without forced kill and the old container exited 0.
The private external /healthz returns HTTP 200, ok: true, version 0.22.0, and Docker reports healthy.
The main-only Website workflow is blocked by missing fork GitHub Pages configuration, not a failing tag artifact check; no public site was enabled.
The stock release helper's apksigner-37 certificate-display parser needs a separate compatibility fix; this release used independently verified manual finalization without altering the immutable tag.

Earlier claims that all checks passed on cd4d593506 and that twelve identical pristine-upstream failures could waive checks were disproved and are not used here.
The actual backend scan retains 13 previously classified historical/attribution, maintenance or negative-test files; it does not imply zero repository literals.
Canonical detailed evidence is local under maintenance-evidence/run-2026-10-07/haiku/ in the primary checkout and is not claimed to be published through this PR.

lgy0404 and others added 30 commits October 6, 2026 19:13
… deleted account removes it

A 401 on the key refresh — Sign out everywhere from another device, a relay
reset, a relay bug — used to take the keep=false path on both phones and delete
the account's side chats, memory, goals and routines with no question and no
way back. It now takes the keep path: the chats stay hidden behind their owner
rows, the files, routines and preferences go aside under accounts/<hash>/, the
relay key is removed, and the sign-in page says "Your sign-in on this phone was
ended — sign in again to continue; your chats are kept on this device until
then" until the next sign-in; the same account id restores everything, another
account sees nothing of it.

Only when the relay answers 401 account_deleted may the phone delete, since
there is nothing to come back to. The relay did not have such a code: deleting
an account removed its key rows, so a device still holding one heard bad_key.
It now keeps the SHA-256 hashes of the deleted account's keys in a deleted_keys
table for 90 days (no account id, address or device with them) and answers
account_deleted on every call that takes a key; a client that does not know
the code sees the same 401 as before. The rule is AccountScope.keepOnRefusedKey
(Android) and NanoMuseAccountData.keepOnRefusedKey (iOS), unit-tested; the
relay's delete test checks the new code on /v1/me, /v1/models, /v1/sync/state
and /v1/chat/completions, and a new test checks that a revoked key, an unknown
key and an aged-out tombstone stay bad_key.

Strings in all 17 Android locales and the 9 iOS languages. docs/sync.md gains
a "Key refused" column in both tables and a paragraph on the tombstones;
docs/cloud.md, docs/android.md, docs/ios.md, docs/every-device.md and
cloud/README.md follow.

Checks: Android compileDebugKotlin clean, 194 unit tests (192 + 2); relay ruff
clean, 103 tests (102 + 1); docs build OK; rebrand clean.

Signed-off-by: lgy0404 <2528379691@qq.com>
Resolved: the relay schema (0.22's daily/controls tables and C12's deleted_keys
both stay), docs/cloud.md (the switches' codes and the account_deleted
paragraph), docs/index.md (the parity link and the sync link), and
Localizable.xcstrings (main's 44 new entries plus this branch's 6, two replaced;
re-merged entry by entry, in place).

Signed-off-by: lgy0404 <2528379691@qq.com>
feat(phones): nothing of one account for the next — owners, the sign-out question, a clean reinstall (C12)
Signed-off-by: lgy0404 <2528379691@qq.com>
One Unreleased block for 0.1.40: the six merged pull requests' bullets by
area (Cloud, Runtime, Desktop, Android, iOS, Project) with this branch's
three, follow-ups folded into the bullet they amend.

Signed-off-by: lgy0404 <2528379691@qq.com>
fix(ios): the bottom bar is a row, not a safe-area inset — the composer was behind it; fix(desktop): Windows starts after a moved install
…relay's guidance, the iOS allowance card and catalogue

docs/parity.md items 32–35, the phones' side.

Both phones (nano-muse#35): a turn nanoMuse Cloud refuses is one plain sentence in the
phone's language and the button that fits — never a status code, the relay's
JSON or upstream's "Rate limited". RelayRefusal (Kotlin) / NanoMuseRelayRefusal
(Swift) classify the reply where the body is still whole, store a canonical
`nm_relay:` line in the message, and the chat draws it as a card: 413 too_large
→ New chat; 401 bad_key / account_deleted → Sign in; 403 not_invited /
account_disabled / signup_closed and 404 model_not_offered → Open Settings;
429 too_many_in_flight / locked / rate_limited and provider_busy (with the wait
from retry_after), 503 service_paused / sync_paused / hub_paused and any 5xx →
Try again. `describe` knows every code on both phones; 429 allowance_exhausted
with paused: true leads the allowance card with "paused on this relay for now —
not used up". Strings in all 17 Android locales and all 9 iOS locales, inserted
in place.

Android (nano-muse#34): AllowanceWaysCard and Settings → nanoMuse Cloud read the relay's
spend.guidance first (Guidance.kt, Ways.resolve) and the bundled catalogue only
when the relay sent none.

iOS (nano-muse#33): NanoMuseAllowance pins NanoMuseAllowanceCard under the header the way
the star card is pinned (no new link on AIChatView.body) when a turn is refused
for the allowance — the lead, the ways on, Try again — and shows the 80 %
heads-up above the composer once per pool size. The ways on come from
spend.guidance (kept between runs), the catalogue when none.

iOS (nano-muse#32): NanoMuseCatalogue reads the bundled providers.json; NanoMuseWaysList
is the one list on the pinned card, the account page and the own-key sheet;
NanoMuseVendorSheet takes a key, a local server's address, or runs the plan
sign-in — ChatGPT through upstream's CodexOAuthManager, Claude, OpenRouter,
Kimi's device code — with the line about OpenAI's terms under the ChatGPT row.

Checked: Android compileDebugKotlin + processDebugResources; 215
io.github.nanomuse.* unit tests pass (RelayRefusalTest, GuidanceTest added).
iOS: MinisTests/NanoMuseRound7Tests.swift added; the device build runs in CI.

Signed-off-by: lgy0404 <2528379691@qq.com>
Localizable.xcstrings merged at entry level: the three keys of nano-muse#102 kept in place beside ours.

Signed-off-by: lgy0404 <2528379691@qq.com>
…nce card and catalogue

Signed-off-by: lgy0404 <2528379691@qq.com>
feat(phones): every relay refusal as one sentence, the ways from the relay's guidance, the iOS allowance card and catalogue
App 0.1.40 (Android versionCode 41, iOS build 41), relay 0.22.0, desktop shell
and plugin 0.1.40. CHANGELOG's Unreleased moves under 0.1.40 · Clear with a
lead paragraph; release notes in docs/releases/v0.1.40.md; README, the
translations and docs/index.md point at the 0.1.40 assets; the roadmap's
past-releases table gains the row; docs/parity.md's "next release" cells say
0.1.40; CITATION.cff carries today's date.

Checked: ruff, pytest (547 passed), cloud pytest (123 passed), rebrand.py
clean, connectors-json and providers-json --check, website docs:build.

Signed-off-by: lgy0404 <2528379691@qq.com>
REVIEW_ADDRESSES (e-mail addresses) and REVIEW_CODE (six digits), both empty
by default: a code request for one of those addresses sends nothing and
answers 204 as if it had, and /v1/auth/verify accepts exactly REVIEW_CODE for
it. The code row is written like anyone's, so the lifetime, the attempt limit
and the rate limits are the same; the address may sign in on a private relay
and while sign-ups are paused, since the operator named it. The account is an
ordinary one with the usual allowance; the admin console tags it `review` in
the People table and on the account page (a grey pill) and leaves it out of
the sign-up counts on the Overview, the People chart and the Stats page's
accounts_daily `new` column. With either value empty nothing changes.

Documented in cloud/README.md (settings table, "For App Store review"),
cloud/.env.example, docs/cloud.md and CHANGELOG.md. Tests in
cloud/tests/test_review.py: off unless both values are set, the fixed code
and nothing sent, a wrong code refused, other addresses unchanged, the tag and
the counts in /v1/admin/..., the attempt limit, the private relay.
cloud: 127 passed; ruff check clean; docs build clean.

Signed-off-by: lgy0404 <2528379691@qq.com>
feat(relay): a review sign-in for the app store's reviewer
nanomuse.cn opens on the demo in an <iframe> of https://demo.nanomuse.dev/?embed=1.
In that mode the page hides its own header (the site's bar is above it) and does
not turn the phone on until the visitor taps its screen, so opening the homepage
starts nothing on the server or in the visitor's memory. The site passes its
language and theme on first load (&lang=, &theme=dark) and afterwards over
postMessage from the project site's origins only; links out of the page open in
the top window. Dark is the page's own surfaces in the site's dark tokens;
MobileGym's keys, dock and drawer keep their look, their captions lifted to read
on the dark stage. The boot label is bilingual. README and docs/every-device.md
say so. node --check on page.js; the showcase workflow builds the image on the PR.

Signed-off-by: lgy0404 <2528379691@qq.com>
An address that is not there on nanomuse.cn answers with the repository's
404.html (the site's bar and theme), the status kept. Validated with
`caddy validate` against Caddy 2.11.

Signed-off-by: lgy0404 <2528379691@qq.com>
…t in the nav

The VitePress nav opens with Home (nanomuse.cn) and Try it (demo.nanomuse.dev),
the same two the homepage's bar leads with. The brand colour is docs/brand.md's
one accent (#015CFB on white, #58A6FF on dark), the type the same system stack,
the surfaces the same near-white and near-black (website/.vitepress/theme). The
logo and the favicon are a copy of assets/brand/nanomuse-icon.svg under
website/public, served with the site, instead of a raw.githubusercontent.com
link a reader in China cannot load. The home page's tagline says what the
homepage says, and its actions gain Try it in the browser.
`npm run docs:build` is clean (the dead-link check included).

Signed-off-by: lgy0404 <2528379691@qq.com>
feat(showcase): an embed mode for the frame on the project site
docs(site): the docs site in the homepage's look, with Home and Try it in the nav
…ount

`test_the_switch_and_the_relays_refusals` timed out on the macOS and Windows
runners of the main push for 9ef54bd (`wait_for` at the last account switch,
"condition not met in time"). The test waited on `owner_of(MAIN_THREAD)`, which
the engine sets in `_cid_for`/`_adopt` — before the request reaches the relay,
with a sync.json write (GIL released) in between — then swapped the fake relay's
store under the push still on the wire. The first account's main chat landed in
the second account's store and the engine, correctly, adopted its id, so the
"new cid" the test waited for never came.

* tests/test_sync.py: a `settled()` helper (no push or pull scheduled or on
  the wire) that every wait which reads the engine's owner/cid state — or that
  precedes `relay.switch_account` — now includes; the same assertions stay, and
  the relay's main conversation is checked against the engine's cid.
* nanomuse/sync/engine.py: `push_soon()` reads `PUSH_DELAY_S` when it is called
  rather than binding the default at import, so the fixture's 0.05 s applies to
  the pushes after a sign-in and the switch too (they waited two seconds each).

Checked: tests/test_sync.py ten times in a row (19 passed each), the whole
suite once (547 passed), and the module under an artificial 100 ms sync.json
write that fails the test before the change; ruff check/format, mypy on
linux, darwin and win32.

Signed-off-by: lgy0404 <2528379691@qq.com>
test(sync): wait for the push to land before swapping the relay's account
PRs nano-muse#112 and nano-muse#113 were merged without their CHANGELOG lines; this adds the
bullets from their PR bodies under Unreleased → Project. Nothing else changes.

Signed-off-by: lgy0404 <2528379691@qq.com>
docs(changelog): the showcase embed mode and the docs site's look
tests/test_hub.py::test_delegate_tool_asks_the_device_and_passes_the_answer_on errored
once in teardown on the macOS runner: FakeRelay.stop() timed out after 5 s in
Server.wait_closed(). That wait is for every accepted TCP connection to close and every
handler to return, and both depend on the peer — an open connection gets a close handshake
the peer must answer, a connection whose HTTP request never completed is kept until the
peer closes it — each for up to 10 s (websockets' close and open timeouts). The peer is
the runtime under test, whose event loop is already gone when the fixture tears down, so
a connection it left half-open holds the stop past the budget.

The runtime left such a connection: the `working` presence note a turn sends once its push
is through waited on the push task with `suppress(CancelledError)`, so when stop()
cancelled the push (or the task itself) it carried on and opened a POST to the relay in
the middle of shutdown, racing `CloudClient.close()`; the hub's background tasks and the
profile's push and pull were cancelled but not waited for. A trace of the fixture showed
that POST's socket still open on the relay when stop() began.

- ConversationSync.stop() cancels and waits for the timer, the push, the pull and the
  one-off presence and delete tasks; a `_stopped` flag refuses anything scheduled on the
  way out; `_send_working` waits for the push with asyncio.wait() so a cancelled push does
  not read as its own cancellation and its own cancellation ends it.
- HubService.leave() waits for the tasks it cancels; HubService.stop() ends the profile's
  push and pull (new ProfileSync.stop()) before closing the HTTP client.
- FakeRelay keeps its own list of TCP connections (a ServerConnection subclass; websockets
  lists only the ones whose handshake succeeded) and aborts whatever is still open when it
  stops, so stopping no longer depends on the peer. The 5 s budget stays.

A scratch peer that leaves a silent connection and a sent POST open reproduced the exact
TimeoutError with the old stop() and returns in 1 ms with the new one. tests/test_hub.py
ran ten times in a row (23 passed each), the whole `-m "not live"` suite once (547 passed);
ruff check and format, mypy (linux, darwin, win32) clean.

Signed-off-by: lgy0404 <2528379691@qq.com>
fix(hub): end every request to the relay before the cloud client closes
…, the console's files are revalidated on every load

The round-9 audit of the relay, the admin console and the showcase against
cloud/, a dev relay and the live demo page found the relay's 0.22 switches,
thresholds, pool batch, stats, GitHub series, review sign-in, samples export
and the showcase's dock shortcut, keyboard handling, come-back and embed mode
in place. What it changes:

- the web console (app.js) had no sentence for signup_closed, service_paused,
  sync_paused, hub_paused, account_deleted, too_many_in_flight, rate_limited
  and channel_unsupported, and said "allowance used up" for a paused
  allowance; both languages now, a paused allowance is said as paused, a
  deleted account signs the page out, a hub closed with 4003 hub_paused is
  shown as such and retried every 30 s; the "e-mail" label is translated
- both console pages set <html lang> to the language they draw instead of
  the fixed zh-CN in the markup
- the console's files under /app/ go out with Cache-Control: no-cache (an
  ETag answers 304), so a deploy reaches the next reload instead of waiting
  out a browser's heuristic freshness
- docs/cloud.md: the star policy sets when, not what; docs/privacy.md: the
  operator's page shows an address's country, province and city from an
  offline ip2region copy (computed when drawn, not stored) — the page said
  the relay does not look an address up; cloud/README.md: a key left out of
  PUT /v1/admin/nudges goes back to its default; the nanomuse-hk deploy
  README points at the 0.22 Controls switches instead of .env edits and
  restarts, and names ADMIN_EMAIL and ALERT_URL

Checked: cloud tests 129 passed (2 new in tests/test_console.py), ruff check
clean, the docs site builds, the headers and the paused codes exercised with
curl on a dev relay.

Signed-off-by: lgy0404 <2528379691@qq.com>
The apps speak Chinese; the docs did not. docs/zh/ now holds a Chinese page
for every page of the site (34 pages and a home page, same file names), and
website/.vitepress/config.mts carries two locales: English at / and 简体中文
at /zh/, each with its own nav, sidebar, outline label, prev/next, edit link,
footer, 404 text and search strings. VitePress's language menu switches
between the two copies of the current page. Headings in the Chinese pages
carry the English anchors (## 安装 {#install}) so every cross-page link and
the sidebar's deep links work in both languages; commands, paths, URLs,
numbers and code blocks are identical to the English pages, which are not
edited.

The nav gets a GitHub link with the repository's star count
(theme/GitHubStars.vue): the same localStorage key (nm-stars), one-hour TTL
and 1.2k formatting as the homepage's site.js, so a visitor coming from
nanomuse.cn sees the number without another request. The GitHub social icon
goes; Discord stays. calls.md leaves the sidebars with the two other
non-pages; links to it from either language open the file on GitHub.

CONTRIBUTING.md gains "Translating the docs": where the pages live, the
same-PR rule for an English change and its Chinese twin, the anchor and path
rules, and a glossary of the words the apps already use.

Checked: npm run docs:build and DOCS_BASE=/docs/ npm run docs:build both
pass with the dead-link check; the built zh pages have lang="zh-Hans",
Chinese nav/sidebar/search/footer, edit links to docs/zh/, resolved
screenshots and the explicit anchors. Every Chinese page was diffed against
its English source for headings and ids, fenced blocks, table rows, links,
inline code, URLs, numbers and anchors.

Signed-off-by: lgy0404 <2528379691@qq.com>
…polish

Android
- The hands' lights breathe, none runs (nano-muse#236.4.6, desktop 0.1.40): the
  stage's comet round the rim and the scan line are gone, the edge glow
  breathes at 4.8 s (2.4 s in, 2.4 s out); the capsule's ring breathes
  (scale and brightness, the gradient still) instead of turning, the four
  bars breathe at the same pace 0.6 s apart; everything holds still under
  the system's reduce-motion setting (animator scale 0).
- The first run's Hands page carries the app's mark, as the sign-in does
  (nano-muse#224.6).
- The first conversation's fourth line says where the messages go with
  sync on: to the model, and — signed in — to the other devices through
  nanoMuse Cloud, with Data controls switching that off; 17 locales.
- A stale comment on the steps default in AppearanceScreen.

iOS
- The sign-in sheet opens with the app's mark; the field says "Mainland
  China phone number or e-mail" and a number with another country code
  gets the e-mail sentence under the field while it is typed, before any
  tap (nano-muse#224.3, nano-muse#224.6); the footer carries Android's fine print and links
  the privacy policy instead of the GitHub page.
- The first run's Notifications page carries the mark (nano-muse#224.6).
- After a new face is picked with a video model set, the reply says the
  four clips follow in the background, as on Android.
- The account's Devices list: an offline device shows when it was last
  seen; a tap on an online one opens the main chat with "@name " in the
  composer (the Reach sheet's Ask this device), as on Android (nano-muse#224.1).
- NanoMuseRoot draws nothing until it knows whether the setup is due, so
  a fresh install never shows the shell for a frame before the welcome
  page.
- The first conversation's fourth line as on Android; 9 languages.

Docs: android.md (hands' lights, permissions, what the relay keeps),
ios.md (the round's note), parity.md (breathing lights row, iOS devices).

Signed-off-by: lgy0404 <2528379691@qq.com>
…recipe after the audit

README.md and the nine translations: badges for the browser demo and the website,
the open-source line linked to CONTRIBUTING, a Browser row that says it is a demo,
the public TestFlight link with Apple's review caveat, a News section with the last
three releases; the translations' literal sentences rewritten by native readers,
facts unchanged.

docs: parity.md no longer describes a live stage on the desktop (gone in 0.1.40),
ios.md's status is build 13 submitted to beta review, privacy.md covers the computer
runtime too and links the site's copy, index.md matches the README's install table,
roadmap.md carries the three cells of the paper's roadmap it lacked, the release
notes template names the real assets.

CONTRIBUTING.md: the two trees are android/ and ours; the release section is the
recipe as it is run, with scripts/release-bump.sh and scripts/release-docs.py
brought into the repository so that it is reproducible from here. AGENTS.md and
THIRD_PARTY_NOTICES.md updated to match.

Signed-off-by: lgy0404 <2528379691@qq.com>
… page, zh wording

The presence line under another device's prompt read "kwai is working…" in both
languages — a developer's host name baked into the dictionaries; it now carries the
device's name. The settings sidebar called the nanoMuse Cloud page "Account" while every
pointer string and the docs say "Settings → nanoMuse Cloud"; the override is gone and the
five strings that pointed at "Models", "Account" or "nanoMuse" say the same. The usage card's
"Upgrade" link to the homepage is removed (nothing is for sale). Stale sentences corrected:
chats no longer "stay on this computer" (sync is on by default), the look can be changed
here and not only on the phone, the update check reads nanomuse.cn before GitHub, the
thanks row names the UI-TARS-desktop operator. Chinese: 在线 for "online", 快捷聊天, 非营利,
形象工坊, 操作屏幕 for the hands, 「」 quotes throughout, 图像与视频模型.

tests/locales.test.mjs keeps it so: same holes in both dictionaries, no English left in zh
except names, no “” or " in zh, no host name. docs/parity.md marks the web's first
conversation partial (note 39) and the Settings pointers in docs follow the one name.
Checked: pnpm build, typecheck, 196 tests; connectors/providers --check.

Signed-off-by: lgy0404 <2528379691@qq.com>
lgy0404 and others added 27 commits October 8, 2026 07:22
…desktop

fix(desktop): the main chat follows the chat model pick; hub task carries the UI language
…e-ios

feat(ios): hub task carries the phone's language
The desktop and Android pages still quoted the capability gate with the dash
that PR nano-muse#183 replaced by a colon; the quotes now read as the strings do, in
English and Chinese.

Signed-off-by: lgy0404 <2528379691@qq.com>
docs: quote the current runtime sentences
The runtime joins the provider list to the pictures-unavailable sentence
with a colon; the Chinese configuration page and the docstring example in
nanomuse/llm/catalogue.py still quoted the older dash form. Both now match
what unavailable_sentence() returns.

Signed-off-by: lgy0404 <2528379691@qq.com>
docs: match the pictures sentence everywhere
Merge upstream main 9d7fc7a (v0.1.38 to v0.1.41)
into the fork. Hand-resolved conflicts keep fork-only capabilities: Google Calendar,
vault-backed MCP header secrets, cloud.base_url / NANOMUSE_CLOUD_BASE_URL, signed-out
relay editing, and bundled Playwright/Chromium packaging.

Relay and backend defaults are emptied at the owning configuration layer. An empty
relay raises relay_unconfigured before any network I/O. The private override remains
valid. Emptied URL consumers are guarded (iOS guide link, Android requireBaseUrl,
blank privacy rows). Fork self-update and repository links point at
zeeshanhaque21/nanoMuse; the Electron proxy no longer adds a default relay host.

The static web bundle is regenerated from the merged web source with Node 22.
Legacy desktop/ Python package and scripts/build-desktop.py are removed upstream.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Upstream removed the account page's usage link to the homepage, so drop the fork's copy and its unused constant. Sections takes its own DOCS_URL and no longer imports the one from panels, which conflicted with it.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
next_check_in built each candidate with the input's fixed offset, so a check-in computed before a DST change fired an hour off afterwards. Candidates now take the local zone of their own date. The config tests read the host's app settings and config from ~/.nanomuse, so they now point the data directory at a temp dir.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Active docs, the README translations, the Pages site and the docs website linked the upstream homepage, the demo host, the download mirror and the upstream repository. Each now points at this fork's repository, its GitHub releases and its own docs, or describes the upstream service in prose without a link. The release-notes template and the zh privacy page are updated where they named a backend. tests/test_fork_docs_sanitization.py fails on any upstream backend link in the active tree and leaves history, the runbook and the legal text alone.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
The new test failed ruff format in CI. The glob also picked up website/node_modules when present locally, so dependency and build folders are now excluded explicitly.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
The newer test client and HTTP stack log requests on httpx2 and httpcore2, which the token filter did not cover, so a ?token= URL reached the log in clear. The filter now covers those loggers as well.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
…nstead

The relay constructor threw on an empty origin, so the cloud service never activated in the packaged app: nanomuse-rooms waited on it and the nanoMuse routes never mounted (404 on /nanomuse/cloud/status and /nanomuse/rooms/state, reproduced by starting the packaged app). The guard now lives in the fetch wrapper: an unconfigured relay refuses each call with relay_unconfigured before any request leaves, and the service starts. Tests cover refusal with no request and a configured origin.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
files() rejected any path with a leading dot in its absolute parts, so a skill kept under a dot-named folder (a home directory such as .nanomuse, or a checkout under a dot directory) listed no scripts, references or assets. The check now looks only at the path inside the skill folder.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Shell warnings for each dangerous pattern now have a regression test, with safe everyday commands as the counterpart. The sync pull test waits for the engine's startup pull before it writes to the fake relay; the wait is a mitigation for an intermittent failure whose cause is not isolated.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
…and local iOS config

The runbook's pytest ignore list, four-file remainder and ten-failure baseline described an older tree and environment. It now gives the isolated venv procedure, the classified remainder, and how to prove a baseline on a pinned commit. The reviewer scratch directory and the operator-local iOS customization are git-ignored.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
… its code

The Settings privacy row opened the blank policy link. It now stays dimmed and inert while no link is configured, through a shared helper with unit coverage. The empty-relay refusal raised a bare IllegalStateException; it now raises the relay's relay_unconfigured code before any request, through requireRelay with unit coverage.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
The DST regression test called time.tzset to force a zone, which Windows does not have, so the windows-latest job failed with AttributeError. The DST behavior now has a portable check that runs on every platform, and the forced-zone check is skipped only where tzset is absent, with the reason stated. Reproduced by deleting time.tzset in-process: the previous test fails with the same AttributeError, the new file skips the forced-zone test and passes the rest.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
…d-pull trigger

A pull's page is applied only if the account is the one it was requested for; a switch while a page is on the wire drops it, and the new account pulls from zero. A trigger that arrives during a pull now schedules one more pull instead of being dropped. The startup push test waits for the push and the pull it schedules before writing rows.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
With no relay origin the policy read now refuses with relay_unconfigured before a request leaves, instead of failing on a relative URL.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
The check runs in the process zone; it exercises a DST change only where that zone observes DST. The forced-zone test covers it wherever tzset exists.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
… made for

Sync requests carry the key captured with their account generation instead of the shared client key, so a round started under one account cannot go out under another. A push round whose account switched while on the wire is discarded: its answer is not applied and its rows stay unsynced for the new account's own push. A main chat with unsynced text from the previous account keeps that text on re-home instead of letting the next account adopt it. Regressions: a note written during a mid-round switch, an A to B to A bounce, and a sign-out during a push.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Guard connection shutdown with the transaction lock and wait for final notice audits in the asynchronous test. Add a deterministic close-lock regression for the CI SQLite crash.

Normalize two existing SQL expressions to ruff 0.16.10 formatting without changing their behavior.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Require a captured generation and token for every error handler, covering push, pull, scheduled delete and status polling. Preserve current-account refusals and public error responses.

Cover late 401, sync-off and network errors across account switches and same-account key renewal with deterministic gates and successful subsequent sync.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Preserve relative PyInstaller links as well as the existing bundled browser links. Extend the platform-aware packaging regression to prove both copy stages retain valid links.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Keep fork-tag builds and checks, but prevent automatic desktop and harness release uploads and prevent fork tags from moving Docker latest. Keep normal version tags and manual release behavior unchanged.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
Keep real child-liveness and zombie checks while treating ENOENT and ESRCH during proc stat reads as reaping. Cover running, zombie, vanished and permission-error diagnostics deterministically.

Signed-off-by: Zeeshan Haque <zhaque.mail@gmail.com>
@zeeshanhaque21
zeeshanhaque21 marked this pull request as ready for review October 8, 2026 23:26
@zeeshanhaque21
zeeshanhaque21 merged commit df0b3e3 into main Oct 8, 2026
16 checks passed
@zeeshanhaque21

Copy link
Copy Markdown
Owner Author

Post-release follow-ups

Verified outcomes and artifact provenance are in completion.md.
The fork's issues are disabled, so this list is also recorded on PR #15 rather than claiming issue creation.

APK helper compatibility

scripts/release-apk.sh:69 assumes legacy Signer #1 certificate output and exits after successful apksigner 37 verification because that version prints V2 Signer: certificate.
Repair the parser in a separate signed-off PR; do not move v0.1.41-fork.1 or overwrite its artifacts.
Reproduce against the same signed APK under build-tools 34 and 37, prove both return success with the same certificate, and ensure an Android Debug certificate still fails the default guard.
Include explicit diagnostics when certificate display parsing fails.
The current published APK has independently passed signature checks under both tool versions.

Native toolchain preflight

Require a usable readelf implementation before native symbol metadata generation, and fail if an upstream pipeline stage fails.
The missing command initially generated offset zero while the build still succeeded; the same loader processed by NDK llvm-readelf yields offset 1120.
The current APK was rebuilt with corrected metadata, and both packaged native copies match the regenerated binary.
The project-local shim is retained in the implementation lease; future builds should use a qualified NDK implementation rather than assuming GNU readelf exists on macOS.

Hosting and remaining platform prerequisites

The main Website workflow fails at configure-pages because the fork's GitHub Pages site is not configured.
Choose deliberately whether the fork should host a public site before enabling Pages or changing its deployment policy.
Full iOS native builds need the missing native frameworks, and notarized macOS/signed Windows release artifacts need the appropriate signing credentials.
Phone installation and logged-in desktop operator/UI exercises require a separate authorized test pass.

Local failure memory

Sanitized local MisakaNet candidates were submitted as contrib_0de7e092b4 for verifier-output parsing and contrib_2881f9829c for silent native pipeline failures.
They are pending local review, not published remote lessons.

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