Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ Mossland 생태계를 탐색하는 지도와 Algora, AO, Bridge의 픽셀 아트

- **푸시 스트림이 아닌 폴링입니다.** 서비스 API는 이전 조회 주기가 끝난 뒤 15초, 상태 조회는 60초, 레지스트리는 10분 후에 갱신합니다. 요청 주기의 제한 시간은 10초입니다.
- **API 응답 여부와 서비스 상태는 다릅니다.** 신호 또는 통계 요청에 성공하면 해당 서비스에 `LIVE` 배지가 붙습니다. 세 서비스 중 하나라도 응답하면 전체 상태는 `LIVE`, 모두 응답하지 않으면 `OFFLINE`, 첫 판단 전에는 `Connecting…`입니다. 생태계 상태 피드는 별도로 상태를 측정합니다.
- **상태는 관측 근거로 판단합니다.** 브라우저가 레지스트리의 `statusUrl`을 직접 조회하며, 직접 측정값이 [city 상태 집계](https://city.moss.land/api/health)보다 우선합니다. HTTP 오류 응답에도 상태가 명시되어 있으면 유지합니다. 해석할 수 없는 5xx 응답은 `down`으로 처리하지만, 네트워크·CORS 실패나 유효한 판정이 없는 응답만으로 장애를 단정하지 않습니다. 알 수 없는 상태 문자열도 임의로 바꾸지 않습니다.
- **상태는 관측 근거로 판단합니다.** 브라우저가 레지스트리의 `statusUrl`을 직접 조회하며, 직접 측정값이 [city 상태 집계](https://city.moss.land/api/health)보다 우선합니다. HTTP 오류 응답에도 상태가 명시되어 있으면 유지합니다. 비어 있지 않은 문자열 `status`가 없는 5xx 응답(HTML 오류 페이지, 또는 해당 필드가 없는 JSON)은 `down`으로 처리하지만, 네트워크·CORS 실패, 응답을 읽는 도중 끊긴 경우, 유효한 판정이 없는 5xx 이외의 응답만으로는 장애를 단정하지 않습니다. 알 수 없는 상태 문자열도 임의로 바꾸지 않습니다.
- **움직임마다 의미가 다릅니다.** 지도 입자는 새로 수집된 신호에 반응하며 표시 개수에는 제한이 있습니다. 링 스윕은 완료된 상태 조회에 반응합니다. 은하 회전은 장식입니다. 벨트 이동, 단계 전환, Bridge의 제안·증명 애니메이션은 작업 흐름을 설명하며, 실행 추적 기록이나 작업 완료 증거가 아닙니다.
- **최신 조회보다 오래된 스냅샷이 남을 수 있습니다.** 개별 요청이 실패해도 상세 데이터 캐시는 유지되고, 상태 조회가 전부 실패하면 이전 스냅샷을 유지합니다. AO는 캐시된 아이디어를 다시 표시할 수 있습니다. 신뢰도나 성공률이 없으면 `—`를 표시하며, 없는 값을 측정된 0으로 해석해서는 안 됩니다. 이 앱은 관측용 뷰어이며 가동 시간이나 실행의 감사 기록이 아닙니다.
- **최신 조회보다 오래된 스냅샷이 남을 수 있습니다.** 개별 요청이 실패해도 상세 데이터 캐시는 유지되고, 상태 조회가 전부 실패하면 이전 스냅샷을 유지합니다. AO는 캐시된 아이디어를 다시 표시할 수 있습니다. 신뢰도나 성공률이 없을 때, 그리고 서비스가 숫자로 보내지 않은 사이드바 수치는 `—`로 표시하며, 없는 값을 측정된 0으로 해석해서는 안 됩니다. 이 앱은 관측용 뷰어이며 가동 시간이나 실행의 감사 기록이 아닙니다.

## 로컬 실행

Expand Down Expand Up @@ -77,7 +77,7 @@ npm test
npm run build
```

[GitHub Actions](.github/workflows/ci.yml)는 대상 브랜치와 관계없이 모든 풀 리퀘스트와, 병합이 끝난 `main`에 대해 같은 검사를 실행한 뒤 빌드가 올바른 `dist/health.json`을 생성했는지 확인합니다(`node scripts/check-health-json.mjs`). 풀 리퀘스트 없이 푸시한 브랜치는 자동으로 검사하지 않으므로, 초안 풀 리퀘스트를 열거나 워크플로를 직접 실행하세요. 테스트는 상태 응답 해석, 생태계 피드 동작, 그리고 오리진이 빌드를 서빙하는 방식(없는 파일은 404, 디렉터리 목록 없음)을 검증합니다. 해당 코드를 수정할 때는 `npm run test:watch`를 사용할 수 있습니다.
[GitHub Actions](.github/workflows/ci.yml)는 대상 브랜치와 관계없이 모든 풀 리퀘스트와, 병합이 끝난 `main`에 대해 같은 검사를 실행한 뒤 빌드가 올바른 `dist/health.json`을 생성했는지 확인합니다(`node scripts/check-health-json.mjs`). 풀 리퀘스트 없이 푸시한 브랜치는 자동으로 검사하지 않으므로, 초안 풀 리퀘스트를 열거나 워크플로를 직접 실행하세요. 테스트는 상태 응답 해석, 생태계 피드의 판정·병합·폴링, 서비스 데이터 폴러의 연결 상태·폴링 체인·신호 중복 제거, 사이드바와 지도 툴팁이 마크업에 쓰는 값, 그리고 오리진이 빌드를 서빙하는 방식(없는 파일은 404, 디렉터리 목록 없음)을 검증합니다. 해당 코드를 수정할 때는 `npm run test:watch`를 사용할 수 있습니다.

```bash
npm run preview # 프로덕션 빌드를 로컬에서 확인
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ See [the service overview](docs/mossland-services-overview.md) for responsibilit

- **Polling, not a push stream.** Service APIs refresh 15 seconds after the previous cycle finishes. Health sweeps refresh after 60 seconds; the registry after 10 minutes. Requests have a 10-second cycle timeout.
- **API reachability and health are different.** A service gets a `LIVE` badge when its signals or stats request succeeds. The aggregate is `LIVE` when any of the three responds, `OFFLINE` when none responds, and `Connecting…` before the first verdict. The ecosystem health feed has its own status readings.
- **Health comes from evidence.** The browser reads service `statusUrl` addresses from the registry; direct readings override the [city health aggregate](https://city.moss.land/api/health). A declared status survives an HTTP error response. An unparseable 5xx means `down`; network/CORS failures and responses without a usable verdict do not by themselves prove an outage. Unknown status strings stay untranslated.
- **Health comes from evidence.** The browser reads service `statusUrl` addresses from the registry; direct readings override the [city health aggregate](https://city.moss.land/api/health). A declared status survives an HTTP error response. A 5xx without a non-empty string `status` (an HTML error page, or JSON without the field) means `down`; network/CORS failures, reads cut off mid-response, and non-5xx responses without a usable verdict do not by themselves prove an outage. Unknown status strings stay untranslated.
- **Motion has different meanings.** Map particles are triggered by newly ingested signals and capped for display; a ring sweep follows a completed health refresh. Galaxy rotation is decorative. Belt travel, stage promotion, and Bridge's proposal/proof animation illustrate workflows; they are not execution traces or proof that work completed.
- **Snapshots can be older than the latest poll.** Detail caches survive individual request failures, and a wholly unsuccessful health sweep retains the previous snapshot. AO can replay cached ideas. Missing trust scores or an absent success rate display `—`; an unavailable value must not be interpreted as a measured zero. This is an observational viewer, not an uptime or execution audit log.
- **Snapshots can be older than the latest poll.** Detail caches survive individual request failures, and a wholly unsuccessful health sweep retains the previous snapshot. AO can replay cached ideas. Missing trust scores, an absent success rate, and sidebar figures a service did not send as a number display `—`; an unavailable value must not be interpreted as a measured zero. This is an observational viewer, not an uptime or execution audit log.

## Run locally

Expand Down Expand Up @@ -77,7 +77,7 @@ npm test
npm run build
```

[GitHub Actions](.github/workflows/ci.yml) runs these checks on every pull request, whatever its base branch, and on `main` after each merge, then confirms the build emitted a well-formed `dist/health.json` (`node scripts/check-health-json.mjs`). A branch pushed without an open pull request is not tested automatically; open a draft pull request or run the workflow by hand. Tests cover health-response interpretation, ecosystem feed behaviour, and how the origin serves a build (404 for missing files, no directory listing). Use `npm run test:watch` while working on those readers.
[GitHub Actions](.github/workflows/ci.yml) runs these checks on every pull request, whatever its base branch, and on `main` after each merge, then confirms the build emitted a well-formed `dist/health.json` (`node scripts/check-health-json.mjs`). A branch pushed without an open pull request is not tested automatically; open a draft pull request or run the workflow by hand. Tests cover health-response interpretation; the ecosystem feed's grading, merging and polling; the service-data poller's connection state, polling chain and signal deduplication; the values the sidebar and the map's tooltip write into their markup; and how the origin serves a build (404 for missing files, no directory listing). Use `npm run test:watch` while working on those readers.

```bash
npm run preview # inspect the production build locally
Expand Down
3 changes: 2 additions & 1 deletion src/services/algora-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const BASE = "/algora-api";

export async function fetchAlgoraSignals(limit = 20, signal?: AbortSignal): Promise<AlgoraSignal[]> {
const data = await getJSON<{ signals?: AlgoraSignal[] }>(`${BASE}/signals?limit=${limit}`, "Algora signals", signal);
return data.signals ?? [];
// A list or nothing: anything else here would throw mid-ingest.
return Array.isArray(data.signals) ? data.signals : [];
}

export async function fetchAlgoraIssues(limit = 20, signal?: AbortSignal): Promise<AlgoraIssue[]> {
Expand Down
7 changes: 2 additions & 5 deletions src/services/ao-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,15 @@ const BASE = "/ao-api";

export async function fetchAOSignals(limit = 20, signal?: AbortSignal): Promise<AOSignal[]> {
const data = await getJSON<{ signals?: AOSignal[] }>(`${BASE}/signals?limit=${limit}`, "AO signals", signal);
return data.signals ?? [];
// A list or nothing: anything else here would throw mid-ingest.
return Array.isArray(data.signals) ? data.signals : [];
}

export async function fetchAODebates(limit = 10, signal?: AbortSignal): Promise<AODebate[]> {
const data = await getJSON<{ debates?: AODebate[] }>(`${BASE}/debates?limit=${limit}`, "AO debates", signal);
return data.debates ?? [];
}

export async function fetchAODebateDetail(id: string, signal?: AbortSignal): Promise<AODebate> {
return getJSON<AODebate>(`${BASE}/debates/${id}`, `AO debate ${id}`, signal);
}

export async function fetchAOStatus(signal?: AbortSignal): Promise<AOStatus> {
return getJSON<AOStatus>(`${BASE}/status`, "AO status", signal);
}
Expand Down
3 changes: 2 additions & 1 deletion src/services/bridge-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const BASE = "/bridge-api";

export async function fetchBridgeSignals(limit = 20, signal?: AbortSignal): Promise<BridgeSignal[]> {
const data = await getJSON<{ signals?: BridgeSignal[] }>(`${BASE}/signals?limit=${limit}`, "Bridge signals", signal);
return data.signals ?? [];
// A list or nothing: anything else here would throw mid-ingest.
return Array.isArray(data.signals) ? data.signals : [];
}

export async function fetchBridgeStats(signal?: AbortSignal): Promise<BridgeStats> {
Expand Down
Loading
Loading