feat(pipeline): keycloak-session-store 25편·virtualization 59편을 S3→S5→S6 으로 돌린다

기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만
있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문
종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다.

SSOT 결함 둘을 고쳤다.

- kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이
  「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽
  `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다.
- virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데
  원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다.

기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은
둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더
든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다).

계약을 셋 고쳤다.

- kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/**
  28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는
  것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온
  것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수
  대조했을 때 가장 가까운 커밋도 28개가 어긋났다).
- virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고
  재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다.
- kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고
  넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다.

style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져
(실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고,
engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고
있었다(Question 기록에서 11.94 → 3.86).

verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-17 11:01:55 +09:00
co-authored by Claude Opus 5
parent d473609e0a
commit 2109f726fe
574 changed files with 159654 additions and 1551 deletions
@@ -0,0 +1,256 @@
{
"schemaVersion": 2,
"runId": "2026-09-16-2002",
"project": "keycloak",
"record": "docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"startedAt": "2026-09-16T20:02:42+09:00",
"finishedAt": "2026-09-16T20:15:36+09:00",
"stages": [
{
"id": "S1",
"name": "코드베이스 → SSOT",
"skill": "analyzing-codebase-for-tech-log",
"runBy": "ssot-analyst",
"status": "SKIPPED",
"skipReason": "SSOT(docs/keycloak/final/document.md)가 이미 있고 이 기록의 근거를 그 안에서 찾을 수 있다. 이번 런은 기록의 칸 배치를 고치는 것이라 SSOT 를 건드리지 않았다.",
"skillEcho": "",
"skillRevision": "edd45dfec66d155a621cd41186b83b5582f2244c",
"inputs": [],
"outputs": [],
"gates": [],
"notes": "",
"finishedAt": "2026-09-16T20:05:54+09:00",
"finishedBy": null
},
{
"id": "S2",
"name": "SSOT → 분해 계약",
"skill": "deriving-tech-log-root-tree",
"runBy": "tree-deriver",
"status": "SKIPPED",
"skipReason": "이 글감은 tech-log-tree.json 에 PROMOTE·CONFIRMED 로 이미 있다. 계약이 아니라 기록 파일 안의 문단 위치를 고쳤다.",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
"notes": "",
"finishedAt": "2026-09-16T20:05:54+09:00",
"finishedBy": null
},
{
"id": "S3",
"name": "글감 → 기록",
"skill": "writing-tech-log-records",
"runBy": "record-writer",
"status": "DONE",
"skipReason": "",
"skillEcho": "| 본문 밖 칸에 백틱 | 평문이다. 백틱을 뺀다 |",
"skillRevision": null,
"inputs": [],
"outputs": [
"docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md"
],
"gates": [
{
"cmd": "python3 scripts/studio-body.py docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md -o /tmp/sb.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:05:54+09:00"
},
{
"cmd": "node --experimental-transform-types .agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/sb.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:05:55+09:00"
},
{
"cmd": "node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:05:55+09:00"
},
{
"cmd": "node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak --repo",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:05:55+09:00"
},
{
"cmd": "python3 scripts/check-required-content.py --file docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:05:55+09:00"
}
],
"notes": "둘째 문단 175자 중 셋은 지우고 하나만 옮겼다. 재시작 뒤 로그인 유지는 미지수 1번과 가정 2번, 인스턴스가 바뀌어도 세션을 찾는 것은 미지수 2번, 토큰까지 찾는 것은 제약 2번, 인스턴스가 여럿이라는 전제는 가정 1번에 이미 있었다. 「여러 인스턴스에 흩어진 세션과 토큰을 함께 지우는 법」만 미지수의 로그아웃 항목 끝에 붙였다 — 그 항목이 이미 로그아웃 시점의 상태 삭제를 다루고 있어 범위만 넓히면 됐고, 새 항목을 만들면 같은 질문이 둘로 갈린다.",
"startedAt": null,
"generation": 1,
"owner": null,
"elapsedSeconds": 1,
"finishedAt": "2026-09-16T20:05:55+09:00",
"finishedBy": null
},
{
"id": "S4",
"name": "기록 → 그림",
"skill": "technical-visualizer",
"runBy": "diagram-maker",
"status": "SKIPPED",
"skipReason": "그림이 필요 없다. 이번 런은 제목 아래 문단을 알맞은 칸으로 옮기거나 지운 것이라 새로 그릴 소재가 생기지 않았다.",
"skillEcho": "",
"skillRevision": "edd45dfec66d155a621cd41186b83b5582f2244c",
"inputs": [],
"outputs": [],
"gates": [],
"notes": "",
"finishedAt": "2026-09-16T20:05:55+09:00",
"finishedBy": null
},
{
"id": "S5",
"name": "AI 티 제거",
"skill": "rewriting-technical-prose-naturally",
"runBy": "prose-rewriter",
"status": "DONE",
"skipReason": "",
"skillEcho": "| One sentence runs through two subjects, scales, or results | Split it at the subject change |",
"skillRevision": null,
"inputs": [],
"outputs": [
"docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md"
],
"gates": [
{
"cmd": "node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:10:30+09:00"
},
{
"cmd": "node .agents/skills/rewriting-technical-prose-naturally/scripts/style_profile.mjs docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 1,
"session": null,
"generation": 1,
"at": "2026-09-16T20:10:30+09:00"
},
{
"cmd": "node --experimental-transform-types .agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/s5.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:10:30+09:00"
},
{
"cmd": "node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak --repo",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:10:30+09:00"
}
],
"notes": "라틴 표기 둘만 고쳤다 — 선택지 3 제목의 token→토큰, 제약 3번째의 host→호스트. 식별자가 아니라 자리잡은 외래어이고 같은 문서가 본문에서 이미 「OAuth 토큰」을 쓴다. 8081 은 안 건드렸다. 미지수의 로그아웃 항목(세 물음 한 덩어리)은 읽히므로 안 고쳤다 — 셋째의 규모 전환은 가정 첫 줄이 받친다. 선택지 2 의 제목 session affinity 와 본문 Sticky Session 은 둘이 같다는 판정이 필요해 못 합쳤다.",
"startedAt": null,
"generation": 1,
"owner": null,
"elapsedSeconds": 1,
"finishedAt": "2026-09-16T20:10:30+09:00",
"finishedBy": null
},
{
"id": "S6",
"name": "일한 사람의 목소리",
"skill": "writing-as-the-person-who-did-it",
"runBy": "voice-writer",
"status": "DONE",
"skipReason": "",
"skillEcho": "**쓸 수 있는 것은 자료가 기록한 사람의 행동과 판단뿐이다.** 넣은 문장마다 그것이 어느 파일,",
"skillRevision": "edd45dfec66d155a621cd41186b83b5582f2244c",
"inputs": [],
"outputs": [
"docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md"
],
"gates": [
{
"cmd": "node .agents/skills/writing-as-the-person-who-did-it/scripts/check_voice.mjs docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:15:36+09:00"
},
{
"cmd": "node .agents/skills/rewriting-technical-prose-naturally/scripts/check_prose.mjs docs/keycloak/tech-log-studio/oauth-oidc-auth-boundary/question/question-multi-instance-session.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:15:36+09:00"
},
{
"cmd": "node --experimental-transform-types .agents/skills/writing-tech-log-records/scripts/check_body.mjs /tmp/s6.md",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:15:36+09:00"
},
{
"cmd": "node .agents/skills/writing-tech-log-records/scripts/check_evidence.mjs keycloak --repo",
"exit": 0,
"session": null,
"generation": 1,
"at": "2026-09-16T20:15:36+09:00"
}
],
"notes": "제약 첫 항목에 확인 범위의 한계를 넣었다 — 학습 환경이라 레플리카 사이 세션 조회·failover 를 안 만들었고 장애 복구 시간과 비밀값 교체 절차도 확인 범위 밖이라는 것. 근거는 SSOT:97 과 :109 이고 source 앵커(#문제를-어렵게-만든-제약-학습-환경) 안이다. 계약의 unknown 에 「장애 복구 시간은 얼마인가」가 적혀 있는데 기록에는 그 자리가 비어 있었다. 접은 것 — 공유 저장소 대 session affinity 중 무엇을 골랐나는 SSOT:1455·253 이 둘을 나란히 적기만 하고 고른 기록이 없어, 선택지에 선호를 넣으면 지어낸 선택이 된다. 보고만 한 것 — 이 기록에 「닫는 조건」 줄이 없다. 계약에는 decision-criterion 이 있고 writing-each-kind.md:259 가 요구하는 칸인데 check-required-content.py 가 이것을 안 센다. S3 영역이라 손대지 않았다 — S6 에서 넣으면 검사기를 통과시키려고 계약 문구를 기록에 옮겨 적는 셈이다.",
"startedAt": null,
"generation": 1,
"owner": null,
"elapsedSeconds": 0,
"finishedAt": "2026-09-16T20:15:36+09:00",
"finishedBy": null
},
{
"id": "S7",
"name": "Studio 저장",
"skill": "publishing-tech-log-to-studio",
"runBy": "studio-validator",
"status": "SKIPPED",
"skipReason": "사용자가 Studio 반입을 요청하지 않았다. 이 기록은 이미 게시돼 있고 S7 은 저장까지인데, 저장하면 Studio 쪽 version 이 올라간다. 저장소와 공개본이 갈린 상태로 두고 사람이 판단할 일로 넘긴다. 게시는 어떤 경우에도 하지 않는다.",
"skillEcho": "",
"skillRevision": null,
"inputs": [],
"outputs": [],
"gates": [],
"notes": "",
"finishedAt": "2026-09-16T20:15:36+09:00",
"finishedBy": null
}
],
"revision": 23,
"updatedAt": "2026-09-16T20:15:36+09:00",
"sessions": [
{
"session": null,
"stage": "S3",
"generation": 1,
"beganAt": "2026-09-16T20:05:54+09:00"
},
{
"session": null,
"stage": "S5",
"generation": 1,
"beganAt": "2026-09-16T20:10:29+09:00"
},
{
"session": null,
"stage": "S6",
"generation": 1,
"beganAt": "2026-09-16T20:15:36+09:00"
}
]
}