Commit Graph

299 Commits

Author SHA1 Message Date
79eaa9ceef Merge pull request 'docs(android): M12 phase 5 as landed' (#117) from docs/android-theming-nav-phase-5 into edge
Reviewed-on: #117
2026-08-08 12:12:20 +00:00
02d1663c1a docs(android): M12 phase 5 as landed
§6.2 corrected and amended, §6.1 given the Home decision, and a "Phase 5 as
landed" section added with the  in §8.

**§6.2's table was in the wrong order, and the order is now load-bearing.** The
three news categories were listed Five on Friday / Newsletter / Screenshots; the
website's array has them Screenshots / Five on Friday / Newsletter. That did not
matter while the table was only a mapping, but a stored `order` is an index into
the site's nav, so a row the admin never moved takes its sort key from this list —
and a key from the wrong list scrambles a partially-overridden nav. The rows are
now numbered 0-15 and the order is called out as verbatim.

Also written into §6.2: the `feature` values are deliberately **not** mirrored
into the table (`APP_MENU` stays the app's own source of truth for gating), and
the "no `APP_MENU` row" rule covers **seven** entries rather than four — the
three news categories as well as the hub boards.

Two decisions the org lead settled before code, both recorded where the rule
lives rather than only in the phase notes:

- **The news categories are ignored for the drawer**, on the same rule as the hub
  four. The mapping still exists for phase 6's added links, where a category tab
  is a perfectly good destination because the admin named it by path. The
  alternative — surfacing such a row only when overridden — keeps AC-1 but lets an
  override introduce navigation after all, so it was rejected.
- **An admin may hide the Home row**, mirroring the website, where `/` is hideable
  from the public header. Home stays the start destination and stays reachable by
  back-press. The contrast that makes it safe is with `/admin/navigation`, which
  has three guards because hiding it would strip the only way to undo an override;
  nothing about a hidden Home row is unrecoverable.

"Phase 5 as landed" records the sort-key finding above as the thing the spec did
not settle, that an untouched instance gets `APP_MENU` back by **identity** so
AC-1's drawer claim is an `assertSame`, that the app's own rows are partitioned
off rather than sorted (and what that would mean for a future interleaved row),
that `group`/`section` are read and dropped, and why `Routes.NEWS_ROUTE` is
declared beside `Routes.NEWS` rather than replacing it — with the consequence that
`destination.route` now carries a query and is compared on `substringBefore('?')`.

Android-app: RunicGateway/Android-app#38.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 07:09:44 -05:00
67e110201b Merge pull request 'docs(android): M12 phase 4 as landed' (#116) from docs/android-theming-nav-phase-4 into edge
Reviewed-on: #116
2026-08-08 11:51:19 +00:00
317dc310fa docs(android): M12 phase 4 as landed
§5.6 gains the three decisions the drafted section left open — the top bar's
text fallback, the hero's fixed cropped band, and the accessibility split
between a decorative logo and a named one — each with the reasoning that
settled it, so none is re-litigated from scratch. Adds the 32dp/24dp logo
heights, the six-times-height width cap, and the note that the app takes no
fallback hero image where the website substitutes its own emblem.

"Phase 4 as landed" records how the empty-slot rule is enforced by layout
rather than by a conditional at each call site, why the blank check runs on
both sides of the resolver, and the honest limit of the phase's tests: the
drawing cannot be tested here at all, since the app has no Robolectric and no
androidTest source set, so only the decision of whether to draw is pure.

Phase 4 ticked in §8.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 06:14:36 -05:00
6526f32a42 Merge pull request 'docs(android): M12 phase 3 as landed' (#115) from docs/android-theming-nav-phase-3 into edge
Reviewed-on: #115
2026-08-08 10:51:22 +00:00
d95632db04 docs(android): M12 phase 3 as landed
§5.3 rewritten against what the build measured rather than what the plan
estimated, plus a "Phase 3 as landed" section and the  in §8.

Three corrections to the drafted section:

- The APK estimate was wrong by more than 3×. "Roughly 1.5-2.5 MB" becomes a
  measured before/after table: 5,031,411 B → 13,574,703 B, +8.15 MiB. The
  section now records that Merriweather is 6.08 MiB of that on its own, and the
  two cheaper options the org lead costed and declined, so the number is not
  re-litigated from scratch later.
- Family lookup must be global, never scoped to the role's option list. Two of
  the three presets publish a font their own role's dropdown does not offer,
  because resolveThemeTokens copies preset tokens verbatim — the same bypass
  §5.4 already records for --shadow-card.
- Italics mirror the website's four rather than being skipped, and every
  bundled family supplies the four weights the scale asks for (with IM Fell
  English's single weight and Cinzel's untouched 500/600/700 as the two
  documented exceptions), because a family is not confined to the role its
  dropdown lives in.

Android-app: RunicGateway/Android-app#TBD

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 05:46:02 -05:00
ea8c5b9a42 Merge pull request 'docs(android): M12 phase 2 as landed' (#114) from docs/android-theming-nav-phase-2 into edge
Reviewed-on: #114
2026-08-08 10:16:48 +00:00
1746ad4e37 docs(android): M12 phase 2 as landed
The radii-and-shadow phase, shipped as Android-app#35. Seven notes, of which
three are amendments to the spec rather than records of it.

Section 5.4's elevation map is not a no-op and now says so, with section 2 and
AC-1 amended to match. Material3's filled Card is Level0 and FeatureCard drew
none of the shadow its own KDoc claimed, so the app has been flat since M5 -
while the runic-gateway preset it was drawn from selects the "Default" shadow.
The org lead chose to apply the map as written rather than rebase it on the flat
baseline, because rebasing would have collapsed none/Soft/Default onto 0dp and
left only Deep doing anything on the phone. The radius half is untouched by this
and remains a provable no-op.

Section 5.4's exact-string match is corrected to a nearest-blur match. The
fantasy preset's own --shadow-card is not one of SHADOW_OPTIONS' four values,
because resolveThemeTokens copies a preset's tokens verbatim and they never pass
through the admin form's dropdown.

Section 5.2 gains the rule the pill needed: CircleShape is a percentage, so it
has no shipped dp for a ratio to scale and the resolved px is taken as dp below
the 500px floor. It is a literal because there is no app scale to preserve, not
as an exception to the ratio rule.

Also recorded: why all 24 Card( call sites became ShardCard( (Material takes
elevation as a default argument, not from the theme), that --radius-pill reaches
only StatusPill, and that Shapes - unlike ColorScheme - does implement equals,
so the structural no-op proof is one assertion rather than a reflection walk.

PLAN.md 9's M12 entry records phase 2 landed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 05:15:21 -05:00
afe0e9245f Merge pull request 'docs(android): M12 phase 1 as landed' (#113) from docs/android-theming-nav-phase-1 into edge
Reviewed-on: #113
2026-08-08 09:57:00 +00:00
a860557e91 docs(android): M12 phase 1 as landed
The colors phase, shipped as Android-app#34. Seven notes, of which three are
departures the build forced rather than choices it made:

ColorScheme has no equals in material3 1.3.0, so AC-1's "full equality, not a
spot check" is a field-by-field compare by reflection over all 36 color roles,
against a verbatim copy of the pre-M12 scheme held in the test.

ShardPillFg is derived from --accent-bright rather than being a sixteenth
token, under the rule 5.1 already states for ShardOnCta.

An instance with an env accent and no theme_visual row loses its accented CTA
buttons, because brand.accent now seeds --accent alone. That is 5.1's "wrong
twice over" being corrected, and it is the one thing on screen that moves.

PLAN.md 9's M12 entry records phase 1 landed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 04:55:11 -05:00
beae4b21dd Merge pull request 'docs(android): M12 phase 0 as landed, and phase 7 cancelled' (#112) from docs/android-theming-nav-phase-0 into edge
Reviewed-on: #112
2026-08-08 07:01:55 +00:00
11999c4bcd docs(android): M12 phase 0 as landed, and phase 7 cancelled
Two records, one milestone.

Phase 7 (reading nav_admin / nav_player) is cancelled. Section 6.4 already
made the case against it and scheduled it last so the call could be taken on
its merits with the rest working: the two authenticated navs reach four app
rows between them, which does not pay for a new authenticated fetch, a
session-keyed cache and its teardown. The app therefore makes no authenticated
settings call at all, and the player and staff drawer rows keep their coded --
and so localized -- labels, which is the one thing given up. Section 4's locked
decision, section 3's endpoint row and section 5.5's lifecycle paragraph are
amended to match so the doc does not contradict itself, and section 8 gains
the standing "Phase 7, cancelled" note.

Phase 0 (the contract and the appearance store) landed as Android-app#33, and
its "as landed" notes record two departures from what section 8 specified:

- `theme` is modeled as a raw JsonElement rather than Map<String,String>?.
  kotlinx fails the decode of the whole object on a value of an unexpected
  kind, and `theme` shares its payload with `brand` and `push` -- so one odd
  token would have blanked the branding and dropped the push relay URL, the
  opposite of section 2. It is coerced field-by-field instead.
- A failed refresh keeps the last good appearance rather than falling back to
  NONE. Section 5.5 said best-effort; the distinction it did not draw is that
  a moment of no connectivity on resume must not repaint a themed shard back
  to the shipped defaults.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 01:58:48 -05:00
e7dea29885 Merge pull request 'docs(android): build contract for admin theming and navigation parity (M12)' (#111) from docs/android-theming-nav-plan into main
Reviewed-on: #111
2026-08-08 06:45:16 +00:00
b150e354a8 Merge pull request 'docs(tree): sync website/PROJECT_TREE.md' (#110) from chore/sync-website-tree into main
Reviewed-on: #110
2026-08-08 06:45:00 +00:00
e86b04567f docs(android): build contract for admin theming & navigation parity (M12)
The website merged runtime admin theming, brand assets and nav overrides to
main (website#126 / docs#109). The app reads exactly one field of it --
brand.accent -- and renders a hardcoded APP_MENU, so an admin who re-skins the
site and restructures the header sees none of it on the phone.

Adds docs/android/THEMING_AND_NAV.md as the design of record for M12, and the
PLAN.md §9 entry that anchors it. Plan only: no app code, no backend work.
Everything consumed is already live on website/main.

The points that shaped it:

- The app's ui/theme/Color.kt palette is already, value for value, the
  runic-gateway preset -- M5 was drawn from the same theme.css the preset was
  later extracted from. So "an untouched instance is unchanged" carries over as
  a testable ColorScheme equality assertion, not an approximation.
- Radii apply as a ratio against that baseline, not as literal dp. The app's
  Shapes came from the M5 mockup and genuinely differ (medium 12dp vs
  --radius-card 10px); a literal mapping would restyle the untouched app the
  day this ships, and copying the app's scale into the server would be a second
  source of truth.
- Fonts are bundled, not downloadable: the Play Store font provider makes a
  de-Googled device fall back silently. Seven families join the bundled Cinzel.
- Nav overrides are keyed by website paths, so the app needs a path -> route
  table -- the one new cross-repo coupling here. An override for a path the app
  does not surface in its menu is ignored: a nav override may never introduce
  navigation.
- The gates are untouched. MenuAccess and MenuEntry.feature still run after the
  merge, so hidden:false cannot un-hide what a role or the shard's visibility
  config withholds.
- Read the resolved theme/brand fields, never the raw theme_visual/brand_assets
  rows that ride along in the same payload -- re-deriving a palette from them
  would be a second resolveThemeTokens in Kotlin, guaranteed to drift.

Nine phases into a fresh edge in both repos, reaching main as one edge -> main
merge, the same shape the website side used. Phase 0 must change nothing on
screen. Phase 7 (the authenticated navs) is marked optional: nav_player reaches
two app rows and nav_admin two, which is a thin return for a new authenticated
fetch and its cache teardown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TgfKv5cz5pbY3dPeofSE5a
2026-08-08 01:39:52 -05:00
runic-docs-bot
3aed6bca17 docs(tree): sync website/PROJECT_TREE.md from RunicGateway/website@265042e [skip ci] 2026-08-08 06:19:50 +00:00
518f1e0449 Merge pull request 'docs(website): theming &amp; navigation, complete (edge → main)' (#109) from edge into main
Reviewed-on: #109
2026-08-08 06:09:06 +00:00
2e955f1e9c Merge pull request 'docs(website): phase 10 — public nav sections and added links, and the §7 amendment' (#108) from docs/theming-nav-phase-10 into edge
Reviewed-on: #108
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-08-08 06:00:18 +00:00
2784cad6e4 docs(website): phase 10 — public nav sections and added links, and the §7 amendment
Records the capability asked for before the edge -> main cutover: dropdown
sections in the public header, with the coded entries organised into them and
admin-authored links alongside.

§7 is amended rather than quietly contradicted. It said the override layer
"cannot introduce a `to` that is not already in the hardcoded NAV array"; that
remains true of every CODED entry, and the restated constraint spells out what an
added link may be — a same-origin path, carrying no gate of its own, advertising
a route rather than granting one — plus why the property is structural: coded
entries live in a map keyed by routes the base array declares, and everything
that can name an arbitrary path lives in `links`, where the rule is applied.

§6.4 gains the { items, sections, links } wrapper, including the two properties
worth knowing: a bare map still reads as the items map, and a nav with no
sections still stores one.

§9 gains three acceptance criteria — the empty dropdown does not render, an added
link cannot leave the origin, and deleting a section returns its entries to the
top level rather than removing them.

"Phase 10 as landed" records why the Public tab needed its own tree editor, why
moving between containers stayed a dropdown rather than a cross-container drag,
why the menu opens on click and its trigger is not a link, and the palette-vs-full-nav
bug this surfaced in the phase 6-8 save path.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 00:42:39 -05:00
d7dd0e5078 Merge pull request 'docs(website): theming phases 6-8 as built, and phase 9 cancelled' (#107) from docs/theming-nav-phase-6-8 into edge
Reviewed-on: #107
2026-08-08 05:11:45 +00:00
5c24ff1378 docs(website): theming phases 6-8 as built, and phase 9 cancelled
Marks the nav wiring and the builder UI landed, and records the five places the
build differed from the design:

- The server had no way to store a nav row. The design scoped 6-8 as client
  work, but updateSettings would have written a nav object as "[object Object]"
  — a save that 200s and does nothing, for ever.
- The server deliberately cannot check that a `to` exists: the base NAV arrays
  are client constants, and a server-side copy would be a second source of truth
  for navigation. Shape is the server's question, membership the client's.
- `hidden: false` is accepted and never stored, so hiding stays subtractive.
- The nav editor cannot be hidden, enforced in three places.
- Orders are written only when something actually moved, compared against the
  base restricted to the rows the editing admin can see.

Phase 9 (hue-carrying rgba literals + Parchment) is cancelled rather than
deferred. The finding that motivated it is kept as the record: those literals
carry a hue, so they are a rough edge in the three dark presets and not only a
blocker for a hypothetical light one.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 00:02:46 -05:00
2a8b9d5748 Merge pull request 'docs(website): theming phase 5 as built — brand assets and the cached HTML shell' (#106) from docs/theming-nav-phase-5 into edge
Reviewed-on: #106
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-08-08 02:05:03 +00:00
97cb8be2d5 docs(website): theming phase 5 as built — brand assets and the cached shell
Records Phase 5 of THEMING_AND_NAV.md as landed and documents the new route
and the shell lifecycle in BACKEND_DESIGN.md.

Where the build differed from the design: the upload is one admin-only call
that writes the settings row too (rather than the generic staff upload plus a
PUT, which would leave unreferenced files and let editors change the site's
identity); brand_assets needed a validator of its own because these are the
only settings values written straight into HTML as URLs; the shell cache
carries a TTL as well as explicit invalidation because it is per process; and
the logo went into all six MoonDot surfaces rather than three.

Also notes what was deliberately left alone: the shell's title and description
still come from BRAND_NAME rather than the admin-set site_title, and fixing
that would change the served shell for instances with no brand_assets row —
which is exactly what the phase's acceptance criterion forbids.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 20:10:16 -05:00
ce6f5b8788 Merge pull request 'docs(website): theming & nav phases 3-4 as built' (#105) from docs/theming-nav-phase-3-4 into edge
Reviewed-on: #105
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-08-08 00:21:41 +00:00
e669243aca docs(website): theming phases 3-4 as built
Records where the build diverged from the design and why.

- The presets do not live in theme.css as [data-theme] blocks. Section 6.2 is
  marked superseded and a "Phases 3-4 as landed" section explains the inline
  --accent precedence problem that forced server-side resolution.
- Section 4.5's accentInt fix is struck through: getPublic() never exposed
  accentInt, and Discord embeds are colored by a separate process reading env,
  so there was nothing per-request to recompute. Replaced with what was
  actually done -- the bot fetching the effective accent.
- Phase 9 re-scoped. Applying Fantasy on a live instance showed the section 4.8
  rgba literals carry a hue, not just a light/dark assumption, so the dark
  presets need that promotion too.
- BACKEND_DESIGN.md: the new /settings/theme/options route, the `theme` block
  on /public/settings, effective values in the brand block, theme_visual
  validation on PUT /admin/settings, route count 225 -> 226.
- api-route-inventory.json regenerated from the manifest.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 19:16:47 -05:00
09e6ffd67c Merge pull request 'docs(website): theming & nav phases 0-2 as built' (#104) from docs/theming-nav-phase-0-2 into edge
Reviewed-on: #104
2026-08-07 23:25:06 +00:00
252733644b docs(website): theming & nav phases 0-2 as built
Matches RunicGateway/website's phases 0-2 of THEMING_AND_NAV.md.

BACKEND_DESIGN.md:
- The new /settings router group and its one route, plus why it is a fifth
  group rather than a route on an existing one.
- DELETE /admin/settings/:key in the admin route table, with the allowlist and
  why reset deletes instead of writing.
- The five unseeded theming/nav keys under the settings schema: absence of the
  row is the "use the default" state, values are TEXT so consumers parse, and
  malformed reads as absent.
- Route count 215 -> 225.

THEMING_AND_NAV.md:
- Phases 0-2 marked landed, with an "as landed" section recording the three
  things the design left open: where /settings/nav lives, where
  parseJsonSetting lives, and the exact 23-declaration radius promotion.
- The nav merge util's ordering rules, settled by the implementation: an
  untouched item keeps its index as its sort key, an explicit order wins a tie
  against a coincidental index, equal explicit orders keep code order, and
  `group` is honored only when it names an existing section.
- All four PR pairs target `edge`; the feature reaches `main` as one merge.

api-route-inventory.json: resynced from server/routes.manifest.json. Picks up
the two new routes plus eight that were already missing from the mirror since
the Protocol 3.0 cutover (shard clilocs, market, points).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 18:15:50 -05:00
6398285a13 Merge pull request 'docs(website): add theming & nav build contract' (#103) from docs/theming-and-nav-plan into main
Reviewed-on: #103
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-08-07 22:07:47 +00:00
d33064e8d7 docs(website): add theming & nav build contract
Corrects the design doc against the current codebase and locks the open
decisions, in the same shape as HERO_EDITOR.md (locked decisions ->
corrections to reality -> phased build).

Blocking gaps found in the design doc:
  - no delete path exists for a settings row, which every "reset to
    defaults" in the feature depends on
  - editors/moderators/players have no endpoint to read their own nav
    overrides (GET /admin/settings is admin-only)
  - renderIndexHtml runs once at boot, not per request
  - settings values are JSON strings, not objects
  - getPublic().brand is a cross-repo contract the Android app and
    Discord embeds theme from; new keys would silently bypass it

Locked: effective values resolved server-side into getPublic().brand;
radius + shadow tokens only (spacing/border cut); radius tokens seeded at
today's real values so the promotion is a no-op; three dark presets in v1
with Parchment deferred; 12-option font shortlist across 8 web families
in one request; PNG-only favicons.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 17:04:54 -05:00
6d25279b32 Merge pull request 'docs(installer): lead with the installer now that it is released' (#102) from docs/installer-first-setup into main
Reviewed-on: #102
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-08-07 21:34:25 +00:00
5197c2c281 docs(installer): lead with the installer now that it is released
v0.1.0 shipped on 2026-08-07, so every doc that said "not released yet"
was wrong the moment the cutover merged.

- INSTALL.md: replace the pre-release status banner. The installer is the
  path the guide leads with; Appendix A is reframed as supported-not-
  deprecated, for hosts that cannot run the binary, operators who want to
  place files themselves, and development from a working tree.
- PLAN.md: status is Shipped, both cutover gates recorded as met (incl.
  the Windows 1053 handshake bug the real SCM run found), Phase 5 table
  and 5.4 closed out.
- README.md: point anyone setting up a shard at INSTALL.md first.
- link/link-README.md: mark the pre-split snapshot as historical, so its
  deploy.ps1 instructions stop reading as the setup path.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 16:05:39 -05:00
c73db117f5 Merge pull request 'docs(tree): sync installer/PROJECT_TREE.md' (#101) from chore/sync-installer-tree into main
Reviewed-on: #101
2026-08-07 20:29:37 +00:00
runic-docs-bot
22cf093f97 docs(tree): sync installer/PROJECT_TREE.md from RunicGateway/installer@1173a10 [skip ci] 2026-08-07 20:28:25 +00:00
4a35e86bc8 Merge pull request 'docs(installer): bundles publish to a branch, releases only tag' (#97) from docs/bundles-branch into main
Reviewed-on: #97
2026-08-07 19:15:33 +00:00
9889eefb0f Merge pull request 'docs(tree): sync installer/PROJECT_TREE.md' (#100) from chore/sync-installer-tree into main
Reviewed-on: #100
2026-08-07 19:15:06 +00:00
bc97e5d221 Merge pull request 'docs(tree): sync link/PROJECT_TREE.md' (#99) from chore/sync-link-tree into main
Reviewed-on: #99
2026-08-07 19:12:55 +00:00
runic-docs-bot
5c9aa2891c docs(tree): sync installer/PROJECT_TREE.md from RunicGateway/installer@09eafe2 [skip ci] 2026-08-07 19:12:12 +00:00
runic-docs-bot
c54dcb47f5 docs(tree): sync link/PROJECT_TREE.md from RunicGateway/link@67d7800 [skip ci] 2026-08-07 18:53:32 +00:00
18dfceae73 Merge pull request 'docs(installer): correct the Windows service decision, and the 1053 advice' (#98) from docs/windows-service-1053 into main
Reviewed-on: #98
2026-08-07 18:52:35 +00:00
7c1a88febb docs(installer): correct the Windows service decision, and the 1053 advice
PLAN.md §8 recorded that `sc create` against the plain console sidecar worked
and needed no change to `link`. The first Windows install disproved it: 1053,
"a timeout was reached (30000 milliseconds) while waiting for the service to
connect", with SERVICE_EXIT_CODE 0.

The premise was a false symmetry with systemd. systemd supervises any
foreground process; the Windows SCM supervises only one that calls
StartServiceCtrlDispatcher within ~30 seconds. Record the reversal and what it
costs: link gains a Windows service entry point, kept at the edges so the whole
sidecar stays shared and Cargo builds neither Windows crate for Linux.

INSTALL.md:
- Troubleshooting gains a 1053 row naming the real cause (a sidecar older than
  v1.2.0) and the two tell-tales that distinguish it from a crash: exit code 0,
  and a foreground run of the same binary working fine.
- The existing "stops immediately" row said the same wrong thing; it now covers
  the genuine-crash case only, and points at the log file and journalctl.
- §3 and Appendix A4 document the service log, and A4 states the version floor.
- Fixes a literal 0x08 byte in the backups path row, which rendered as
  `%ProgramData%\RunicGatewayackups\` — the backslash had been eaten.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 13:37:29 -05:00
ddcfb5de29 docs(installer): bundles publish to a branch, releases only tag
Two corrections to §7.1, both forced by the first compose run that ever
had a bundle to write (org lead, 2026-08-05).

The section said bundles are committed to `main` and that this "needs no
new branch-protection exception: release.yml's version-bump commit
already requires the CI user to be able to push to main". Both halves
were wrong. `main` is protected and declines the push, and release.yml
had never pushed anything: its bump step has never executed in any repo
carrying it, because an empty template expression written literally in
one of its comments makes the runner fail to build the step and skip it
without failing the job. The tags exist because Gitea's release API
creates one when it publishes. The assumption that a working push path
already existed had never been tested by anything.

Bundles now publish to a `bundles` branch at its root, which keeps every
property the original choice was for -- reviewable diff, git history of
the compat matrix, plain anonymous raw URLs, no credentials on the shard
host -- and needs no exception. The release workflows are tag-only for
the same reason, as servuo-plugins has always been: the version is still
written into Cargo.toml before building so a released binary
self-reports correctly, but is not committed back.

Also updated: the raw URLs in INSTALL.md Appendix A1 and in Phase 0.3's
as-built note.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 17:22:16 -05:00
00d476c00b Merge pull request 'docs(installer): Phase 5 — scope, the §5.3 correction, and the operator guide' (#96) from docs/installer-phase5-scope into main
Reviewed-on: #96
2026-08-05 17:52:20 +00:00
ad7defd471 docs(installer): document aarch64 and the upgrade backup in INSTALL.md
PLAN.md §5.4. The operator guide is the specification of the run, so
these are part of building the phase rather than a write-up after it.

- The download list gains runicgateway-installer-linux-aarch64, with
  `uname -m` as the way to tell, and says plainly that there is no macOS
  and no Windows-on-arm build: the shard dials the sidecar out on
  loopback, so the two share a host, and no ServUO host is either.
- Appendix A3 names the arm64 sidecar asset for the by-hand path, and
  points at the bundle from A1 for the version rather than the one
  written in the example.
- §7's `update` says what a backup is, when one is taken and when one is
  not, and that restoring is the operator's to do -- the guide already
  promised their Bridge.cfg edits survive, and this is the same promise
  for the .cs file they edited that gets overwritten by design.
- --no-backup joins the flags table; --purge's row and the uninstall
  table now name backups alongside the config, the database and the
  cached patch set.
- The paths tables and doctor's sample output gain the backups
  directory and its row.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:53:11 -05:00
e18eec8957 docs(installer): correct §5.3's trigger and record what building §5.2 found
Two things this section got wrong, both found by implementing it.

§5.3 said a backup is taken for an update and for an install over an
existing record, because "a first install overwrites nothing". That is
not true of a tree deployed by hand per INSTALL.md Appendix A2 -- the
path this project recommends while the binary is unreleased. There the
first install finds .cs files that differ, plans them as Change, and
overwrites them with no prior record anywhere to notice. The rule is
now the direct one: back up whenever the run is about to overwrite
something. A genuine first install onto a clean tree still writes
nothing, because there is nothing to copy.

Also records that sidecar.toml joins a backup rather than causing one
(nothing rewrites it, so triggering on it would leave a dated directory
after every no-op update), and that the directory is created lazily
with the manifest written last, so an interrupted run can neither be
mistaken for a backup nor evict a good one.

§5.2 gains what its cross-builds turned up: neither crate builds with
the arm64 compiler alone. gcc-aarch64-linux-gnu only recommends
libc6-dev-arm64-cross while both release workflows install with
--no-install-recommends, so the C in each crate -- bundled SQLite under
sqlx, ring under ureq's rustls -- fails on a missing libc header while
every Rust dependency compiles fine.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:49:09 -05:00
38f83ad2e0 docs(installer): settle Phase 5's scope
Phase 5 was sketched as four items; two of them are dropped rather than
deferred, because what stops them is an ownership conflict that does not
improve with time (org lead, 2026-08-05).

- No .deb and no MSI (5.1). A .deb under link's release would own the
  binary, the systemd unit and the service user -- the three things
  service.rs writes, hardens and removes and install.json records, so
  uninstall would leave a dpkg-installed-but-broken package and an
  apt upgrade would make doctor report drift nobody caused. The
  binary-only variant buys apt-managed upgrades of one file, which
  update already does from a protocol-checked bundle. An MSI
  contradicts "the installer does not install itself" and adds a second
  uninstall path beside the verb that owns install.json, the cached
  patch set and the ServUO-tree report.
- Linux aarch64 for both components (5.2), in the order the bundle CI
  forces: it hard-fails on an unrecognized link asset name and asserts
  the platform keys present, so the name is taught on main first, link
  publishes, the key becomes required, and only then does the crate on
  edge learn it. bundle.yml is never edited on edge, so the cutover
  merge has nothing to conflict over.
- Backup before overwrite (5.3), scoped by what cannot be fetched
  again: not the binary or the overlay files, and not the database
  (store.rs is CREATE TABLE IF NOT EXISTS over shard state the sweeps
  repopulate -- a cache with a schema), but an operator's edits to a
  deployed .cs file, which Phase 1 overwrites by design, and
  sidecar.toml, whose token the website already holds.
- The docs a first release invalidates (5.4), including the repo README
  still announcing Phase 1 four phases later.

Also records that the Windows SCM smoke was attempted on 2026-08-05 and
stopped at its first check on an unelevated shell, so that half remains
entirely unexecuted.

Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit 8818c06f1d)
2026-08-05 05:20:26 -05:00
3eb9fa8653 Merge pull request 'docs(installer): put Phase 5 before the cutover, and flag Windows SCM as untested' (#95) from docs/installer-polish-before-cutover into main
Reviewed-on: #95
2026-08-05 10:12:17 +00:00
3a6b9196fa docs(installer): put Phase 5 before the cutover, and flag Windows SCM as untested
Two decisions from the org lead, recorded in the design of record.

Phase 5 (packaging polish) now runs BEFORE the edge -> main cutover
rather than after it. The original order assumed the cutover would cut a
v1 and packaging would follow as a v1.x, but this phase changes the
release layout itself: shipping first would mean a first release that is
immediately superseded, and operators who downloaded a bare binary being
told to re-download a package. Deferring costs nothing — nothing is
published from `edge`, and INSTALL.md's Appendix A is the supported path
meanwhile.

The cutover therefore has two entry criteria, stated in the status
header and at Phase 5: packaging polish, and the Windows SCM half being
verified on a real host. The second is called out explicitly because
`sc create`, the virtual service account, the failure actions and the
token-file ACL have still never been executed anywhere — and running the
systemd half for real is precisely what turned up a bug no unit test
had. Nothing should be released while the only untested code is the half
that registers a service.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 05:00:45 -05:00
c47e0fb303 Merge pull request 'docs(installer): record the first real systemd verification' (#94) from docs/installer-systemd-verified into main
Reviewed-on: #94
2026-08-05 09:30:29 +00:00
33fadbd254 docs(installer): record the first real systemd verification
Phase 2 shipped service registration that had never been executed: a
relocated test run deliberately skips it, `sc create` needs elevation,
and systemd needs a Linux host. It has now been run for real on a
privileged Debian 12 container with systemd as PID 1 — unit written and
enabled, service up as the unprivileged runicgateway user, sidecar.toml
600 and owned by it, database under /var/lib (so the UOLINK_DB_PATH pin
works), /health answering protocol 3, and uninstall taking the service,
unit, binary and account away while leaving the config, the database and
the whole ServUO tree alone.

That surfaced one bug only a real service host could show — user_created
was recorded per-run rather than as state, so an identical re-run
rewrote install.json and uninstall silently left behind the account the
installer had created (installer#8). Recorded here with the reason it is
invisible on Windows.

Also notes what is still unverified: the Windows SCM half, which needs an
elevated shell this machine's automation does not have.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 04:02:16 -05:00
1dc6084bf7 Merge pull request 'docs(installer): record Phase 4 — doctor, update and uninstall as built' (#93) from docs/installer-phase4 into main
Reviewed-on: #93
2026-08-05 08:49:12 +00:00