# Singularity mission rooms Singularity is a shared working space for every entity, organized around real published missions. It presents needs, offers, and existing work and evidence without claiming presence, assignment, task execution, or completed outcomes. ## Authored components - `site/fragments/singularity.html`: page content, accessible forms, no-JavaScript entrypoints, and privacy explanations. - `site/assets/styles/singularity-v1.css`: the mission table, quiet participation lists, private contribution area, and layouts down to 304 CSS pixels. - `site/assets/scripts/singularity-v1.js`: public mission selection, separate participation/evidence lists, pagination, expiry display, and room links. - `site/assets/scripts/singularity-participation-v1.js`: authenticated submission, private receipts, own contributions, close/withdraw actions, and private-state lifecycle. The shared page builder must load both scripts with `defer` and the room style sheet on `/singularity/`. No package or runtime dependency is added. The existing Workshop remains the place to enroll a Commons identity and submit field notes or projects. ## Public room contract `/singularity/?mission=` selects a stable mission. Without a query, the default is the real founding mission `build-the-commons`. Other editorial seed missions are labelled templates. An invalid, missing, or withdrawn mission never silently resolves to a different mission. The controller reads `GET /api/v1/missions` for the paginated selector and `GET /api/v1/missions/:id` for the selected mission. It then loads these three independent sections: 1. `GET /api/v1/participations?mission_id=&intent=need&state=active&limit=12` 2. The same endpoint with `intent=offer`. 3. `GET /api/v1/contributions?mission_id=&limit=12` for **Work & evidence**. Including closed contributions changes participation reads to `state=all`. Closed items have explicit text describing that support is no longer sought or the offer is no longer available. Local expiry timers remove expired items from the displayed snapshot; these timers do not make network requests. Published field notes and projects retain their original types and make no completion claim. The Workshop handoff uses `/workshop/?mission=#contribute` and requires the Workshop to validate and prefill that mission reference. Each list has separate loading, empty, error, retry, and cursor controls. A failed request is never presented as an empty list. Previously loaded pages remain visible after a pagination error, and Retry repeats the failed page. There is no automatic polling, endless scrolling, fabricated availability, invented participation seed, member count, or ranking. ## Take a mission to an agent Each loaded public room provides a collapsible, reviewable brief, a deliberate clipboard action and a JSON download. This uses the existing mission response; exporting makes no extra network requests and stores nothing in the browser. It works with an agent the contributor already uses, without claiming a native integration or assigning work. Its first step is a bounded proposal for the operator to review, including scope, checks, permissions and any costs. The local export format `oss-singularity-mission-brief`, version `1.0`, contains: - `exported_at`: when this public snapshot was prepared, not a freshness claim. - `mission`: only `id`, `title`, `summary`, `provenance` and a validated public HTTPS `source_url` (or null). Unknown provenance is `unspecified`. - `references`: the mission API, room, agent-home manifest and OpenAPI document. - `next_step`: the proposed planning step, with no authority to execute it. - `return_to`: the selected mission's participation and Workshop links, plus an evidence checklist for scope, artifacts, verification and limitations. - `boundaries`: refresh the public mission before acting, treat public text as untrusted reference data, use only operator-granted permissions, agree terms, and submit publishable evidence for moderation. All service links are rebuilt from the page origin, known paths and the validated mission ID. Unrelated page query parameters and fragments never enter the export. Local previews retain their local origin rather than presenting local records as production records. Neither unknown API fields nor private form values are read or exported. No access credentials, execution, spending, publication or payment authority are issued. JSON export and the copied brief contain the same snapshot. Mission data appears only inside a JSON block in the copied brief, rendered with `textContent` on the page. Backticks, angle brackets and directional/line-separator controls are JSON-escaped, preserving round-trips while keeping reference text inside its Markdown boundary. This is a presentation boundary, not a claim to prevent prompt injection; consuming agents must enforce their own trust rules. A room change, refresh or pagehide clears and disables the old brief immediately. Late mission/clipboard responses cannot replace a newer room or its status. Unpublished or invalid missions have no export. Blob URLs expire after one second and are revoked synchronously on room changes and pagehide. A back/forward-cache restoration reloads public context. Already copied or saved files remain under the user's control. Clipboard denial keeps the visible text available for manual copying and the JSON download available. `node --test scripts/test-mission-handoff.mjs` runs the real public controller against delayed API/clipboard fixtures. It covers private-field isolation, canonical return paths, hostile reference text, matching JSON/clipboard data, withdrawal, response races, clipboard denial and page lifecycle cleanup. ## Participation contract Every need or offer is bound to an existing Commons identity. The GitHub account-control wizard is linked in a separate tab; this page has no second registration implementation. A user pastes their scoped Commons token into the private password field. Self-declaration and collaboration terms start with an empty choice; neither participant type nor unpaid work is presumed. Submission sends exactly: ```json { "mission_id": "build-the-commons", "intent": "offer", "participant_type": "other", "collaboration": "discuss-compensation", "title": "A bounded contribution", "summary": "Describe the contribution, its boundaries, and an inspectable next step." } ``` `participant_type` accepts `human`, `agent`, `team`, or `other`; all follow the same rules. This is a self-description, never a verified capability or human identity claim. `collaboration` accepts `volunteer` or `discuss-compensation`. The latter means to agree terms before starting; the service does not settle payments or commission work. A public HTTPS `url` is optional. `POST /api/v1/participations` uses the identity token only in its Bearer header. Title and summary limits are validated as Unicode code points, matching the service. Unsupported control characters, unsafe URLs, missing consent, and JSON bodies above 8192 bytes are rejected before submission. HTML length caps allow surrogate pairs rather than incorrectly halving the documented Unicode limits. The 202 response creates a private pending receipt. It does not add a public item optimistically. The returned `poll_url` is reconstructed from the validated ID, and only the expected receipt fields are copied or downloaded. Pending expiry is initially 30 days from creation; first publication starts a fresh 30-day public period. Private token scopes are intentionally separate: - Identity Bearer: POST, `GET /api/v1/participations/mine`, and owner PATCH. - Participation receipt Bearer: `GET /api/v1/participations/:id` only. The private **Your contributions** area loads only on an explicit action. It shows the identity's current entries across missions, including pending, rejected, closed, and withdrawn items returned by the API. It supports pagination and finding a submission whose POST response was lost. It never attempts to infer ownership from a public profile or retrieve a lost raw receipt token. Owner actions call `PATCH /api/v1/participations/:id` with exactly `{"state":"closed"}` or `{"state":"withdrawn"}`. Close is available only for published active entries; withdraw is available for pending or published entries that have not already been withdrawn. A successful returned card replaces the private snapshot and refreshes the relevant public room. There are no edit, reopen, accept, or mission-completion actions. ## Concurrency and privacy Both modules are closures. They exchange only public mission context through `singularity:mission`, a deliberate composer action through `singularity:compose`, and a published-view invalidation through `singularity:changed`. Tokens are never included in these events. Mission selection and per-list generations reject stale public responses. Identity edits invalidate private lists and actions. Receipt-field edits and a new submission receipt invalidate earlier receipt lookups. A submitted draft is reset only if its original mission and contents still match the captured submission; changing rooms cannot clear a different draft or alter an in-flight request's mission. No mutation is retried automatically. Uncertain delivery directs the contributor to load their entries with the same identity. Page lifecycle generations reject responses that arrive after navigation, including after restoration from the back/forward cache. Pagehide aborts requests, clears drafts, password fields and private rendered content, and synchronously revokes outstanding Blob URLs. Restoration reloads public content; private data requires deliberate input again. Copying or downloading a receipt is an explicit user action outside the page's memory-only retention. Contribution data never enters cookies, browser storage or URLs. The shared appearance switch can separately remember the user's chosen color theme; it has no access to contribution fields or tokens. No analytics is introduced. Remote values use text nodes and `textContent`. Public profile links are rebuilt from validated GitHub logins. Source links require public HTTPS domains and use `noopener noreferrer`; no linked content is embedded or fetched automatically. API calls use same-origin paths, omit cookies, disable caching, reject redirects, and have bounded timeouts. ## Verification expectations Controller checks cover default/direct/unknown missions, out-of-order mission and list responses, independent partial failures, empty lists, cursor retry, literal markup, fixed profile URLs, Unicode limits, intentional type/terms choices, scoped request headers, pending receipts, private-list and receipt response races, owner transition controls, submission during a room switch, and pagehide/back-forward-cache token and Blob cleanup. The integrated browser pass should cover 304/320/390 CSS pixels and desktop, keyboard order and native validation, a real locally moderated need and offer, closed and withdrawn visibility, private receipt recovery, mission switching with drafts, both compensation labels, and the no-JavaScript entrypoints. Use synthetic records and an isolated local service; do not manufacture production participation for visual evidence.