All checks were successful
PR checks / checks (pull_request) Successful in 9m9s
PLAN.md §13 phase 4: /features/, /architecture/, /modules/, /integrations/, and /community/ — plus the two scope items the phase table never assigned to anyone. Six decisions taken by the org lead before coding, recorded in PLAN.md §10 as D20-D25: - D20 /features/ is the homepage's list with a `detail` line, not a second list. One data file, two renderings, so they cannot disagree about what exists. - D21 /architecture/ draws reasons, not reference: three new inline SVGs, one per boundary. No endpoint tables, no config keys — those are phase 8's and stay canonical in docs/. - D22 The deliberate absences of §2 become one tagged data file, rendered on the three pages that promise them. - D23 Phase 4 absorbs /community/ (specified in §10 and §14 N3, linked from the header since phase 1, built by no phase) and checkLinks.mjs. - D24 `needsModule`: writing the Teams detail exposed a false claim phase 3 shipped. Teams are module-sourced only — teams.module_id is NOT NULL, there is no create route, sync is gated on providerModuleId() — so the Community group no longer says a bare core does all of it. - D25 The per-capability demo affordance brand.json had promised since phase 2 is a deep link, filled at boot from data-demo-path. checkLinks.mjs reads the built HTML rather than src/, because half these links are assembled from data files and template literals. Its PLANNED_ROUTES list is checked in both directions, so it cannot rot into a permanent exemption. applyBrand.mjs gained a pass that recomputes deep links from their immutable path, making it idempotent and reversible; checkBrand.mjs lifts that pattern out and runs it against the stock markup so the two cannot drift. Both proved against a real mount, in both directions. Fixes a cascade bug the checks could not see: [data-demo-url=''] and a scoped component class are both specificity 0,1,0, so .demo-link's `display` beat the hide rule and twelve links to a nonexistent demo rendered, each resolving to the current page. The rule is now !important. The four diagrams' shared SVG vocabulary moved to src/styles/diagram.css. Verified from a clean checkout: npm ci, all five checks, astro check (0 errors), production build, and a live browser pass at desktop and 390px. Co-Authored-By: Claude <noreply@anthropic.com>
289 lines
9.7 KiB
Plaintext
289 lines
9.7 KiB
Plaintext
---
|
|
import Base from '../layouts/Base.astro';
|
|
import PageHeader from '../components/PageHeader.astro';
|
|
|
|
import platform from '../data/platform.json';
|
|
import { brand } from '../lib/brand.mjs';
|
|
|
|
/**
|
|
* `/community/` — PLAN.md §10 and §14 N3, built in phase 4.
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* WHY IT IS IN THIS PHASE AT ALL
|
|
* ---------------------------------------------------------------------------------------
|
|
* §13's phase table never assigned it one. §10 specifies the page and §14 N3 specifies its
|
|
* contents, and the header nav and footer have both linked it since phase 1 — so it was a
|
|
* page the site pointed at and no phase built. The org lead folded it into phase 4 on
|
|
* 2026-08-20 rather than leaving it to be discovered by the link checker (D23). It is a
|
|
* marketing page with no new machinery, so this is where it fits.
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* THE HONEST SPLIT, WHICH IS THE WHOLE POINT OF THE PAGE
|
|
* ---------------------------------------------------------------------------------------
|
|
* §14 N3 is explicit: this page describes a split rather than a single channel, because the
|
|
* obvious sentence — "found a bug? open an issue" — is currently false. Gitea registration
|
|
* is disabled on this instance, so the code is publicly readable and nobody outside the org
|
|
* can file anything against it. Discord is therefore the front door in fact, not just in
|
|
* preference (D10), and saying so is cheaper for a reader than letting them find the
|
|
* sign-up page and its refusal.
|
|
*
|
|
* N3 also records that this page is written the same way whether or not registration is
|
|
* later reopened — only one sentence changes. That sentence is marked below, so whoever
|
|
* changes the Gitea configuration can find it without rereading the page.
|
|
*
|
|
* The security address comes from `brand.json` and appears nowhere in this file. D13
|
|
* publishes a personal address on the understanding that moving to a role address later is
|
|
* an edit to a mounted file, and `checkFacts.mjs` fails the build if an address is typed
|
|
* into any source file — including this one, which is the file most likely to want to.
|
|
*/
|
|
const title = 'Community';
|
|
const description =
|
|
'Where to ask, where the code is, and how to report a security problem.';
|
|
|
|
const gitea = `${platform.gitea.base}/${platform.gitea.org}`;
|
|
---
|
|
|
|
<Base title={title} description={description}>
|
|
<PageHeader eyebrow="Getting in touch" title="Three doors, and which one to use">
|
|
<p>
|
|
This is a small project run by people with day jobs. There is no support desk and no
|
|
ticket queue, which is worth knowing before you choose where to put a question — one of
|
|
these channels answers in minutes and one of them may not answer at all.
|
|
</p>
|
|
</PageHeader>
|
|
|
|
<section class="page section chan">
|
|
<ul class="chan__grid">
|
|
<li class="panel chan__card chan__card--primary">
|
|
<div class="chan__head">
|
|
<h2>Discord</h2>
|
|
<span class="chip chip--live">The front door</span>
|
|
</div>
|
|
<p class="chan__lede">
|
|
Questions, bug reports, help getting an install working, and where the Android beta
|
|
is announced. No account with us to make, nothing to be approved for, and the
|
|
fastest way to reach somebody who has run this software.
|
|
</p>
|
|
<p class="chan__use">
|
|
<span class="chan__use-label">Use it for</span>
|
|
Anything you would otherwise open an issue for, and everything you would not.
|
|
</p>
|
|
<a class="btn btn--primary" href={brand.discordInvite} rel="noopener noreferrer">
|
|
Join the Discord
|
|
</a>
|
|
</li>
|
|
|
|
<li class="panel chan__card">
|
|
<div class="chan__head">
|
|
<h2>The code</h2>
|
|
<span class="chip">Read freely</span>
|
|
</div>
|
|
<p class="chan__lede">
|
|
Every repository is public and readable without signing in to anything — the
|
|
website, the bridge, the game plugin, the installer, the module, the app and all of
|
|
the documentation. Clone it, read it, run it.
|
|
</p>
|
|
<p class="chan__use">
|
|
<span class="chan__use-label">One caveat</span>
|
|
{/*
|
|
THE SENTENCE §14 N3 SAYS WILL CHANGE. If Gitea registration is reopened —
|
|
manual confirm, Turnstile, no repository creation by default — this becomes
|
|
"issues and pull requests are open to anyone with an account", and nothing else
|
|
on the page moves.
|
|
*/}
|
|
Registration on our Gitea is closed at the moment, so filing an issue needs an
|
|
account we would have to create for you. Ask on Discord and it will reach the same
|
|
place.
|
|
</p>
|
|
<a class="btn btn--ghost" href={gitea} rel="noopener noreferrer">Browse the source</a>
|
|
</li>
|
|
|
|
<li class="panel chan__card">
|
|
<div class="chan__head">
|
|
<h2>Security</h2>
|
|
<span class="chip chip--draft">Private</span>
|
|
</div>
|
|
<p class="chan__lede">
|
|
If you have found something that should not be discussed in a public channel, email
|
|
it. You will get a human, not a form, and there is no bounty programme to game —
|
|
just an acknowledgement and a fix.
|
|
</p>
|
|
<p class="chan__use">
|
|
<span class="chan__use-label">Use it for</span>
|
|
Anything that would let somebody reach a deployment, an account or a game server
|
|
they should not.
|
|
</p>
|
|
<a class="btn btn--ghost" href={`mailto:${brand.contactEmail}`}>{brand.contactEmail}</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="page section contrib">
|
|
<div class="panel contrib__panel">
|
|
<p class="eyebrow">Contributing</p>
|
|
<h2>What is useful, in order</h2>
|
|
|
|
<ol class="contrib__list">
|
|
<li>
|
|
<h3>Run it and say what broke</h3>
|
|
<p>
|
|
The install path is the priority of this whole project, and the most valuable
|
|
thing anyone outside it can do is walk it on a machine we have never seen and
|
|
report where it stopped making sense.
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<h3>Build a module for another game</h3>
|
|
<p>
|
|
There is one module and it is Ultima Online, so the claim that this platform is
|
|
game-agnostic is currently an argument rather than a demonstration. The
|
|
<a href="/modules/">Integration Kit</a> exists to be followed by somebody outside
|
|
this project — and it stays marked draft until it has been.
|
|
</p>
|
|
</li>
|
|
<li>
|
|
<h3>Fix the documentation</h3>
|
|
<p>
|
|
Documentation is versioned alongside the code it describes and a change is not
|
|
finished until the docs match it. If something you read was wrong, that is a bug
|
|
of the same kind as any other.
|
|
</p>
|
|
</li>
|
|
</ol>
|
|
|
|
<p class="contrib__note">
|
|
All of it is free software under the GPL-3.0-or-later, and contributions carry one
|
|
house rule worth knowing before you start: work done with AI assistance has to say so
|
|
— a box on the pull request and a trailer on the commit. Undisclosed AI-generated
|
|
contributions get closed. Every repository's <code>CONTRIBUTING.md</code> has the
|
|
details.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
</Base>
|
|
|
|
<style>
|
|
.chan__grid {
|
|
display: grid;
|
|
gap: 1rem;
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
|
|
}
|
|
|
|
.chan__card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: clamp(1.25rem, 3vw, 1.75rem);
|
|
}
|
|
|
|
/* The one channel that actually answers gets the portal edge — the live signal, used
|
|
here for the same reason it is used on a running shard. */
|
|
.chan__card--primary {
|
|
border-color: color-mix(in srgb, var(--portal) 40%, transparent);
|
|
}
|
|
|
|
.chan__head {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
margin-bottom: 0.85rem;
|
|
}
|
|
|
|
.chan__card h2 {
|
|
margin: 0;
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.chan__lede {
|
|
margin: 0;
|
|
color: var(--muted);
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
/* Takes the slack so the button sits at the foot of every card in the row. */
|
|
.chan__use {
|
|
flex: 1;
|
|
margin: 1rem 0 1.5rem;
|
|
color: var(--dim);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.chan__use-label {
|
|
display: block;
|
|
color: var(--muted);
|
|
font-size: 0.72rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.11em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.chan__card .btn {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.contrib__panel {
|
|
padding: clamp(1.5rem, 4vw, 2.75rem);
|
|
}
|
|
|
|
.contrib h2 {
|
|
margin: 0 0 1.5rem;
|
|
font-size: clamp(1.5rem, 3vw, 2rem);
|
|
}
|
|
|
|
.contrib__list {
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
counter-reset: item;
|
|
}
|
|
|
|
.contrib__list li {
|
|
position: relative;
|
|
padding-left: 3.25rem;
|
|
counter-increment: item;
|
|
}
|
|
|
|
.contrib__list li + li {
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.contrib__list li::before {
|
|
content: counter(item);
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
display: grid;
|
|
place-items: center;
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
border: 1px solid var(--gold-deep);
|
|
border-radius: var(--radius-pill);
|
|
color: var(--gold);
|
|
font-family: var(--display);
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.contrib__list h3 {
|
|
margin: 0.3rem 0 0.4rem;
|
|
font-size: 1.06rem;
|
|
}
|
|
|
|
.contrib__list p {
|
|
margin: 0;
|
|
max-width: var(--measure);
|
|
color: var(--muted);
|
|
}
|
|
|
|
.contrib__note {
|
|
margin: 2rem 0 0;
|
|
padding-top: 1.25rem;
|
|
border-top: 1px solid var(--line-soft);
|
|
max-width: var(--measure);
|
|
color: var(--dim);
|
|
font-size: 0.9rem;
|
|
}
|
|
</style>
|