import Link from 'next/link'; import { Callout, DocSection, Prose } from '@/components/meta/prose'; import { Unavailable } from '@/components/ui/unavailable'; import { fmtDateTime, fmtInt } from '@/lib/format'; import { routes } from '@/lib/site'; import type { MethodologyPayload } from '@/lib/types'; /** Data-driven sections of /methodology rendered from `api.methodology()`: constellation rules, owner codes, metric definitions. */ export function ConstellationRulesSection({ rules }: { rules: MethodologyPayload['constellation_rules'] | null }) { return (

Membership is assigned in two tiers. CelesTrak GP groups first: an object listed in a thematic group mapped to a constellation (for example starlink, oneweb, kuiper) is a source-backed member (method: celestrak_group). Documented name patterns second: payloads not covered by a group are matched against the regular expressions of the curated registry (method: name_pattern, derived). Membership history is kept with since/until dates; a member is never removed just because a group response was short.

The table below is the live registry โ€” the exact patterns and groups the classifier uses right now.

{rules === null ? ( ) : (
{rules.map((r) => ( ))}
Constellation Service CelesTrak groups Name patterns
{r.name} {r.service_type ?? 'โ€”'} {r.celestrak_groups.length === 0 ? ( none ) : ( {r.celestrak_groups.map((g) => ( {g} ))} )} {r.match_patterns.length === 0 ? ( none ) : ( {r.match_patterns.map((p) => ( {p} ))} )}

{fmtInt(rules.length)} constellations in the registry.

)}
); } const KIND_LABELS: Record = { country: 'Countries', organization: 'Organizations', joint: 'Joint programmes', intergovernmental: 'Intergovernmental', agency: 'Agencies', unknown: 'Unknown / unassigned' }; const KIND_ORDER = ['country', 'intergovernmental', 'agency', 'organization', 'joint', 'unknown']; export function OwnerCodesSection({ owners }: { owners: MethodologyPayload['owner_codes'] | null }) { const groups = new Map(); for (const o of owners ?? []) { const list = groups.get(o.kind) ?? []; list.push(o); groups.set(o.kind, list); } const kinds = [...groups.keys()].sort((a, b) => (KIND_ORDER.indexOf(a) === -1 ? 99 : KIND_ORDER.indexOf(a)) - (KIND_ORDER.indexOf(b) === -1 ? 99 : KIND_ORDER.indexOf(b))); return (

SATCAT identifies the responsible party of every object with a short owner code. We map each code to a country (ISO 3166) and, when the owner is an organization, agency or intergovernmental body, to an operator entity. Joint programmes map to several countries’ programmes but a single primary country when one is defined. Unmapped codes raise an UNKNOWN_COUNTRY quality flag instead of guessing.

{owners === null ? ( ) : (
{kinds.map((kind) => { const rows = groups.get(kind) ?? []; return (
{KIND_LABELS[kind] ?? kind} {fmtInt(rows.length)} codes
{rows.map((o) => ( ))}
Code Name Country
{o.code} {o.name} {o.country_code ?? 'โ€”'}
); })}

{fmtInt(owners.length)} owner codes mapped.

)}
); } export function MetricsSection({ metrics }: { metrics: MethodologyPayload['metrics'] | null }) { return (

Every derived value on the site points at one of these definitions. The version changes whenever the rule changes; the inputs list the exact canonical columns the rule reads. None of them is a safety metric.

{metrics === null ? ( ) : metrics.length === 0 ? ( No metric definitions are published yet. ) : (
{metrics.map((m) => (

{m.name}

{m.key} ยท v{m.version}

updated {fmtDateTime(m.updated_at)}

{m.methodology}

Inputs

    {m.inputs.map((i) => (
  • {i}
  • ))}
))}
)}
); }