registry: MIP-1 생명주기 정책을 강제 가능한 스키마 계약으로 도입 - #21
Merged
Conversation
MIP-1 (공개 서비스·저장소 생명주기 정책, voting until 2026-09-02) names this
registry the single source of truth for each public service's lifecycle state.
The schema could not express that: `status` is an availability vocabulary with
no room for Core/Beta/Lab/Archive. This adds the axis and makes the policy fail
CI rather than live in a document.
Design: `lifecycle` is a new *orthogonal* field, not a split of `status`.
`status` is untouched — same enum, same values, same meaning — so moss.land,
Passport, City and WA see no change. Splitting `status` into availability plus a
derived projection is the tidier end state and is written up in RUBRIC.md §5 as
a considered alternative, but it re-interprets a field consumers already fetch,
which is a MAJOR rubric change. An orthogonal axis gets MIP-1 what it ratifies
at MINOR cost: rubricVersion 1.0.0 -> 1.1.0.
Added: lifecycle (core|beta|lab|archive), maintainer, secondMaintainer,
lifecycleReason, statusVerifiedAt, and document-level lifecycleReviewedAt.
`maintainer` is deliberately not the existing `owner`, which names the
domain-owning party and carries INV-PASSPORT-001 / CLAIM-PASSPORT-002.
Enforced in the schema's allOf and by CI, from the rubric's own declaration:
Art. 2 core => maintainer + secondMaintainer
Art. 3 core|beta, unstaffed => lifecycleReason (the recorded exception)
Art. 4 archive => lifecycleReason
Each `if` is guarded by required:["lifecycle"]. Without the guard an absent
lifecycle satisfies the condition vacuously: the unchanged registry then draws
150 errors across all 30 entries, asking Upbit and sitemap.xml for a maintainer.
.github/scripts/test-lifecycle-rules.py proves all of this on every push — eight
cases in both directions plus the guard, rather than a one-time capture.
Handles only, never names or emails: this is a public repository
(AGENTIC_ASSURANCE.md §9), pinned by schema pattern and by the validator.
No classification value is set. 17 of 17 in-scope services stay unclassified —
that is an owner decision, and the validator reports the gap on every run
(RES-LIFECYCLE-001, which also records that MIP-1 Art. 1's *display* obligation
needs the generator to render lifecycle, which it does not yet).
Corrected against live endpoints (2026-08-23, EV-STATUS-001):
alpha paused -> operational /api/health?strict=1 => status ok, db ok,
worst_status ok, 1642 pages, fresh sitemap
ao degraded -> operational /api/status => operational, api/database/
llm_router healthy, data endpoints HTTP 200
Both were recorded 2026-07-06 and stood wrong for 48 days, including across the
2026-08-21 registry edit. RES-STATUS-DRIFT-001 is re-reviewed accordingly: its
"re-verified when the registry is updated" mitigation was author discipline
described as a control and is withdrawn rather than restated; uncertainty
medium -> high. The acceptance itself stands.
No probe workflow. Automated health checks would reverse the owner-approved
non-goal in SYSTEM.md §2 ("not a live uptime monitor") — the very basis on which
RES-STATUS-DRIFT-001 was accepted — and MIP-1 Art. 4 asks for a monthly review,
not continuous monitoring. Recorded as a separate owner decision. statusVerifiedAt
strengthens that non-goal rather than weakening it: the age of an observation
becomes machine-readable instead of implied.
Generated output is byte-identical — `status` is registry-only metadata today and
never reaches the page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The drift check flagged three protected evidence/enforcement strings as removed. They were: the line span `102-111` had to move to `253-260` because this change grows the schema above it. The constraint itself is byte-for-byte identical. A line number is a reference that rots on every edit above it, and this repository has already been bitten by exactly that: AGENTIC_ASSURANCE.md §2 records a pin that went stale across two re-pins "because the validator does not read markdown". Rather than move the numbers and have the same finding recur on every future schema edit, the references now point at `$defs.service.allOf`, which does not move. The new lifecycle invariants get the same treatment from the start. Left alone: `ecosystem-registry.schema.json:12` in INV-RENDER-001 — still accurate, and re-citing it would open a gated finding for nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8 tasks
mikim
added a commit
that referenced
this pull request
Aug 24, 2026
The legend summary and the llms.txt lifecycle note were emitted unconditionally. With a value-less registry that is wrong twice: the page advertises "Chips & lifecycle" with no lifecycle rows under it, and llms.txt explains states that nothing carries. Both now key off the same guard as the markers and legend rows. Proof, generated from the merged #21 registry (rules present, no values) through this generator: llms.txt and sitemap.xml byte-identical to the committed output; index/embed differ only by the 21 inert .lc CSS lines, which match no element. With the applied values the committed output is byte-identical to a fresh run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Assurance policy change: Two gated changes, both intended, proposed by the drafting agent for owner decision at merge — nothing was accepted on the owner's behalf (PROFILE.md §15). (1) RES-STATUS-DRIFT-001 loses one recorded mitigation, "Statuses are re-verified against the live services when the registry is updated." It is withdrawn because it was demonstrably not operating: the 2026-08-21 registry edit (9c7b186) updated the registry and left alpha "paused" and ao "degraded", both wrong, 48 days after they were recorded (EV-STATUS-001). Keeping the line would assert a control that does not exist. The residual is NOT closed, its acceptance and rationale are unchanged, and uncertainty is raised medium to high with the evidence recorded in review_note. (2) CLAIM-PASSPORT-001 and INV-PASSPORT-001 have three evidence/enforcement strings rewritten, not weakened: "ecosystem-registry.schema.json:102-111" becomes "ecosystem-registry.schema.json $defs.service.allOf". The constraint they cite is byte-for-byte unchanged — only the line span moved, because this change adds schema above it. They are rewritten structurally rather than renumbered so the same finding does not recur on every future schema edit, which is the failure mode AGENTIC_ASSURANCE.md §2 already records for the upstream pin. The constraint is verified still present and enforcing by the negative test below.
Summary
MIP-1 (공개 서비스·저장소 생명주기 정책, 투표 마감 2026-09-02) 4조가 이 registry를 생명주기 상태의 유일한 기준으로 지정합니다. 그런데 현재 스키마로는 그 정책을 표현할 수 없습니다 —
status는 가용성 어휘이고 Core/Beta/Lab/Archive를 담을 필드가 없습니다. 가결 후 14일 내 반영이 요구되므로, 그 창을 지키려면 기계가 먼저 준비되어 있어야 합니다.이 PR은 축을 추가하고, 정책을 문서가 아니라 CI 실패로 존재하게 만듭니다. 분류 값 자체는 담지 않습니다 — 그건 owner 결정입니다.
Non-goals: 분류 값 확정, probe/모니터링 워크플로,
status의 의미 변경.승인이 필요한 결정 (Decisions for the owner)
algorapassportEligible: true,stampClass: lab입니다. Archive 서비스가 Passport 스탬프를 계속 뒷받침해도 되는지 MIP-1은 말하지 않습니다 (CLAIM-PASSPORT-002영역)signalorg/team만lifecycle도status도 페이지에 렌더링되지 않습니다. 값 반영 시 generator도 함께 고쳐야 1조를 충족합니다 (RES-LIFECYCLE-001)부속서 A 마이그레이션 초안 — 승인용 (draft, not committed)
담당자 칸은 의도적으로 공란입니다. 임의로 채우지 않았습니다.
mossdisclosurelinksagorapassportsignalmapalphawaaobridgemonitorcitynpcrecipemediaalgorasignal범위 밖 (lifecycle 없음) — 1조는 공개
*.moss.land서비스와 배포 연결 공개 저장소로 한정: 거래소 6개(third_party), 채널 2개(medium/x), 자료 파일 3개(registry-json/llms-txt/sitemap), GitHub 조직 2개(github-mossland/github-opendevs).MIP-1 조항 ↔ 스키마 대응
lifecycle— 선택 필드. 1조 범위(공개*.moss.land서비스·배포 연결 저장소)만 대상RES-LIFECYCLE-001maintainer,secondMaintainerallOf:lifecycle=core⇒ 둘 다 required. 예외 없음lifecycleReason+rubric.lifecycle.unstaffedCap: "lab"allOf:lifecycle∈{core,beta}∧secondMaintainer없음 ⇒lifecycleReasonrequiredlifecycleReasonallOf:lifecycle=archive⇒ requiredlifecycleReviewedAt(문서 레벨)maintainerallOf:lifecycle∈{core,beta}⇒ requiredstatusVerifiedAt정책은
rubric.lifecycle에 데이터로 선언되어 있고, 검증기가 MIP-1을 하드코딩하지 않고 그 선언(states[*].requires,unstaffedCap)을 읽어 강제합니다. 칩 규칙이 이미 쓰는 방식(RES-CURATION-002)을 두 번째 축에 적용한 것으로, 읽는 사람이 보는 약속과 CI가 적용하는 규칙이 같은 객체입니다.제약 3 —
status하위호환: 파생이 아니라 직교 추가status는 전혀 손대지 않았습니다. enum·값·의미 모두 그대로이므로 moss.land / Passport / City / WA가 보는 계약은 변하지 않습니다.lifecycle은 직교 축입니다.lifecycle은 유지보수 약속,status는 특정 시점의 관측된 가용성 — 서로 파생 관계가 아닙니다. Archive 서비스가operational일 수 있고(읽기 전용 보존 + 도달 가능), Core 서비스가degraded일 수 있습니다.검토했으나 채택하지 않은 대안:
status를lifecycle+availability로 쪼개고status를 파생값으로 남기는 안. 더 깔끔한 종착점인 건 맞습니다 — 현재 enum은 가용성(operational/degraded/paused/offline)과 생명주기성 값(beta/deprecated)을 실제로 섞고 있습니다. 하지만 이미 4개 소비자가 fetch하는 필드를 재해석하는 것이고, RUBRIC.md §3 기준 MAJOR rubric 변경 + 소비자 조율 문제입니다. 직교 추가는 MIP-1이 비준하는 것을 MINOR(rubricVersion1.0.0 → 1.1.0) 비용으로 얻습니다. 대안은assurance/RUBRIC.md§5에 기록해 두었고 별도 리뷰로 남아 있습니다.남는 흠은 숨기지 않고 적었습니다:
status의beta/deprecated는 그대로 남고,status: beta는 계속베타칩을 유발합니다(§4.1 규칙 6). 두 축이 어긋날 수 있는 지점에서는 MIP-1 목적상lifecycle이 권위입니다.probe 워크플로를 넣지 않은 이유
assurance/SYSTEM.md§2의 "Not a live uptime monitor" 는 2026-07-18 owner가 승인한 intent이고,RES-STATUS-DRIFT-001을 수용한 근거 그 자체입니다. 자동 헬스체크 도입은 이를 뒤집는 별도의 material change이므로 이 PR에 포함하지 않았습니다 (AGENTIC_ASSURANCE.md§3, §10 — 에이전트가 승인된 intent를 재정의 금지). MIP-1 4조가 요구하는 것도 월 1회 검토이지 연속 모니터링이 아닙니다.statusVerifiedAt은 이 non-goal을 약화시키는 게 아니라 강화합니다: "point-in-time이지 real-time이 아니다"가 암묵적 가정에서 소비자가 볼 수 있는 데이터가 됩니다. 관측이 얼마나 오래됐는지 문서 레벨generatedAt으로 추측하지 않고 항목 단위로 읽을 수 있습니다.같은 이유로 월 1회 검토 초과는 NOTE이지 실패가 아닙니다. 날짜로 터지는 하드 실패는 아무것도 바꾸지 않은 무관한 PR의 빌드를 깨뜨립니다. 내부 정합성 오류(파싱 불가한 날짜)만 실패입니다.
3조 강제 — 네거티브 테스트
.github/scripts/test-lifecycle-rules.py,.github/workflows/registry.yml에 연결되어 매 push/PR마다 실행됩니다. 2026-07-18 registry gate 선례는 수동 1회 캡처였지만, 이건 커밋된 스크립트라 규칙을 몰래 느슨하게 만들면 빌드가 빨개집니다.각 규칙을 양방향으로 검사합니다 — 위반은 거부되어야 하고, 준수 형태는 통과해야 합니다. 전부 거부하는 규칙도 여기서 떨어집니다.
3조 위반 시 실제 출력:
스키마(공개 계약)와 검증기(조항을 짚어주는 메시지) 양쪽에서 잡힙니다.
required: ["lifecycle"]가드가 핵심입니다JSON Schema의
if는 해당 속성이 없으면 공허하게 참입니다. 기존passportEligible규칙은tier가 required라 문제가 없었지만,lifecycle은 1조 범위 때문에 선택 필드입니다. 가드를 빼고 변경 없는 registry를 검증하면:업비트·빗썸·
sitemap.xml까지 담당자를 요구받습니다. 가드가 있으면 같은 registry가 0 오류입니다.alpha / ao 정정 — 실측 근거
2026-08-23 실측 (
EV-STATUS-001):alphapaused/api/health?strict=1→{"status":"ok","db":"ok","seo_pages":1642,"worst_status":"ok"}, sitemap에 당일 항목,GET /200operationalaodegraded/api/status→operational,api/database/llm_router전부healthy, 데이터 엔드포인트 HTTP 200operational두 값 모두 2026-07-06 기록 이후 48일간 그대로였고, 2026-08-21 registry 편집(
9c7b186)을 지나면서도 재검증되지 않았습니다.이건
RES-STATUS-DRIFT-001의 mitigation — "Statuses are re-verified against the live services when the registry is updated" — 이 작동하지 않았다는 직접 증거입니다. 통제로 서술된 저자 규율이었을 뿐이므로, 다시 적는 대신 철회했습니다. uncertainty medium → high. 수용 자체는 유지됩니다(사유 불변: 오래된 운영 상태는 안티피싱 보증을 약화시키지 않음). 대체물은 통제보다 약하고 그렇게 기술했습니다 —statusVerifiedAt은 드리프트를 보이게 할 뿐 막지 않습니다.note필드도 이번 실측 기준으로 다시 근거를 잡았습니다.Related issue or advisory
Related to Mossland DAO MIP-1 (agora.moss.land,
contentHash sha256:7d3cf4a4…, 투표 마감2026-09-02T02:19:25Z). 이 저장소에 대응 Issue 없음.Affected assurance IDs
INV-LIFECYCLE-001(Core는 담당자 2인),INV-LIFECYCLE-002(예외와 종료는 사유와 함께 기록),RES-LIFECYCLE-001(OPEN),EV-STATUS-001,EV-LIFECYCLE-001RES-STATUS-DRIFT-001(재검토, uncertainty ↑, mitigation 1건 철회),RES-CURATION-001(statusVerifiedAt으로 부분 완화)INV-PASSPORT-001,CLAIM-PASSPORT-002,CLAIM-REG-001,INV-REG-001SYSTEM.md§2 non-goal 4건 — 문구 그대로. 양립 이유만 §4에 기록Change classification
customHttp.ymlheaders, CORS, or trust boundaries렌더링 출력은 byte-identical 입니다 —
status는 오늘 registry 전용 메타데이터이고 페이지에 도달하지 않습니다. 변경은 기계가 읽는 계약에만 발생합니다.Verification evidence
c31d3e5기준, 전부 재현 가능:c31d3e5(EV-PROJECTION-001)EV-STATUS-001, 2026-08-23T01:01:52ZAGENTIC_ASSURANCE.md§8: 동일 컨텍스트가 단독 저자이자 감사자여선 안 됨). 특히 위 결정 Add MIT LICENSE #2(3조 독법)와 registry: verify live service statuses, add tier chips + stampClass #3(algora)은 사람 판단이 필요합니다.Residual impact
RES-LIFECYCLE-001신규(OPEN),RES-STATUS-DRIFT-001재검토,RES-CURATION-001부분 완화RES-STATUS-DRIFT-001은 닫지 않았습니다. 두 값을 고친 것이 드리프트를 만드는 조건을 없애지는 않습니다.Completion checklist
allOf+ CI 검증기 + 커밋된 네거티브 테스트EV-STATUS-001,EV-LIFECYCLE-001; 검증은c31d3e5에 바인딩SYSTEM.md§3/§3.1/§4/§7/§8/§9,INVARIANTS.yaml,RESIDUALS.yaml,EVIDENCE.md,RUBRIC.mdorg/team만 허용하도록 스키마 패턴 + 검증기가 이중으로 막습니다. 이 PR은 실명·이메일을 하나도 담지 않습니다부수 정리
README.md:lifecycle섹션 신설. 더불어 PR registry: Media 은퇴 반영 + Mossland Signal 등록 #20 이후 낡은 서술 정정 —media는 이미 은퇴(deprecated/hidden)했는데 "live but still data-seeding" 예시로 남아 있었습니다. 현재실험칩을 다는signal로 교체assurance/전반의 스키마 참조를 줄 번호에서 구조 참조로 변경 (schema.json:102-111→schema.json $defs.service.allOf). 이 PR이 스키마를 위쪽에 추가하면서 줄 번호가 밀렸고 drift 검사가 이를 정당하게 잡았습니다. 숫자만 바꾸면 스키마를 건드릴 때마다 같은 finding이 반복되므로 아예 안 썩는 참조로 바꿨습니다 —AGENTIC_ASSURANCE.md§2가 upstream pin에서 이미 기록한 실패 유형입니다 (c31d3e5)claude/absorb-media-register-signal삭제 (원격은 이미 정리됨)