회기 후속 검증과 표시 경계 보강
This commit is contained in:
parent
4cd354881f
commit
aa81af2958
10 changed files with 70 additions and 21 deletions
|
|
@ -238,7 +238,7 @@ test.describe("returned-practice browser-only DB closed loop", () => {
|
||||||
const firstButton = card.getByRole("button", {
|
const firstButton = card.getByRole("button", {
|
||||||
name: isDesktop(testInfo)
|
name: isDesktop(testInfo)
|
||||||
? "이번 회기를 독립 관찰로 반영"
|
? "이번 회기를 독립 관찰로 반영"
|
||||||
: "이번 회기를 독립 관찰로 반영",
|
: "반영 상태 다시 확인",
|
||||||
});
|
});
|
||||||
|
|
||||||
const postUrl = `/api/practice/${encodeURIComponent(
|
const postUrl = `/api/practice/${encodeURIComponent(
|
||||||
|
|
|
||||||
|
|
@ -625,6 +625,18 @@ test.describe("session review", () => {
|
||||||
};
|
};
|
||||||
response.summary =
|
response.summary =
|
||||||
"평가 AI는 [NAME]가 [ORG]에서 보인 조심스러운 반응을 분석했습니다.";
|
"평가 AI는 [NAME]가 [ORG]에서 보인 조심스러운 반응을 분석했습니다.";
|
||||||
|
response.goodMoments[0] = {
|
||||||
|
...response.goodMoments[0],
|
||||||
|
title: "[NAME]가 보인 강점",
|
||||||
|
body: "[ORG] 장면에서 내담자 [NAME]을 기다렸습니다.",
|
||||||
|
};
|
||||||
|
response.growthPoints[0] = {
|
||||||
|
...response.growthPoints[0],
|
||||||
|
title: "[NAME] 반응을 서두르지 않기",
|
||||||
|
body: "[ORG] 장면에서 내담자 [NAME]을 한 번 더 확인합니다.",
|
||||||
|
};
|
||||||
|
response.nextLine = "[NAME]의 말을 [ORG] 맥락에서 다시 확인할까요?";
|
||||||
|
response.clientFeedback = "[NAME] 이야기를 [ORG]에서 안전하게 다뤘어요.";
|
||||||
if (!response.caseWorksheet) {
|
if (!response.caseWorksheet) {
|
||||||
throw new Error("filled review fixture must include a worksheet");
|
throw new Error("filled review fixture must include a worksheet");
|
||||||
}
|
}
|
||||||
|
|
@ -661,8 +673,25 @@ test.describe("session review", () => {
|
||||||
);
|
);
|
||||||
await expect(summary).not.toContainText("[NAME]");
|
await expect(summary).not.toContainText("[NAME]");
|
||||||
await expect(summary).not.toContainText("[ORG]");
|
await expect(summary).not.toContainText("[ORG]");
|
||||||
|
await openReviewTab(page, "피드백");
|
||||||
|
const feedback = page.locator("#sr-panel-feedback");
|
||||||
|
await expect(feedback).toContainText("익명 내담자가 보인 강점");
|
||||||
|
await expect(feedback).toContainText("소속 기관 장면에서 내담자 반응을 기다렸습니다.");
|
||||||
|
await expect(feedback).toContainText(
|
||||||
|
"익명 내담자의 말을 소속 기관 맥락에서 다시 확인할까요?",
|
||||||
|
);
|
||||||
|
await expect(feedback).not.toContainText("[NAME]");
|
||||||
|
await expect(feedback).not.toContainText("[ORG]");
|
||||||
await openReviewTab(page, "워크시트");
|
await openReviewTab(page, "워크시트");
|
||||||
await expect(page.locator(".sr-ws-evidence").filter({ hasText: "[NAME]" })).toBeVisible();
|
const worksheetPanel = page.locator("#sr-panel-worksheet");
|
||||||
|
const worksheetEvidence = worksheetPanel.locator(".sr-ws-evidence").filter({
|
||||||
|
hasText: "저는 익명 내담자이고 소속 기관에서 상담받고 있어요.",
|
||||||
|
});
|
||||||
|
await expect(worksheetEvidence).toContainText(
|
||||||
|
"저는 익명 내담자이고 소속 기관에서 상담받고 있어요.",
|
||||||
|
);
|
||||||
|
await expect(worksheetPanel).not.toContainText("[NAME]");
|
||||||
|
await expect(worksheetPanel).not.toContainText("[ORG]");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("stores learner pre/post pilot evidence without effect claims", async ({ page }) => {
|
test("stores learner pre/post pilot evidence without effect claims", async ({ page }) => {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,15 @@ export function displayPiiSafeText(text: string) {
|
||||||
/\[NAME\]는 건지\s+잘 \[NAME\]는데요/g,
|
/\[NAME\]는 건지\s+잘 \[NAME\]는데요/g,
|
||||||
"되는 건지 잘 모르겠는데요",
|
"되는 건지 잘 모르겠는데요",
|
||||||
);
|
);
|
||||||
|
// 평가 문장에 이미 역할 명사가 있으면 이름 토큰을 다시 "익명 내담자"로
|
||||||
|
// 풀지 않는다. 특히 "내담자 [NAME]을 받을 여백"은 저장 마스킹 뒤
|
||||||
|
// 사람에게 "내담자 반응을 받을 여백"으로 보여야 한다.
|
||||||
|
next = next
|
||||||
|
.replace(/내담자\s+\[NAME\](?:을|를)/g, "내담자 반응을")
|
||||||
|
.replace(/내담자\s+\[NAME\](?:이|가)/g, "내담자가")
|
||||||
|
.replace(/내담자\s+\[NAME\](?:은|는)/g, "내담자는")
|
||||||
|
.replace(/내담자\s+\[NAME\]의/g, "내담자의")
|
||||||
|
.replace(/내담자\s+\[NAME\]/g, "내담자");
|
||||||
for (const [placeholder, label] of Object.entries(DISPLAY_PLACEHOLDERS)) {
|
for (const [placeholder, label] of Object.entries(DISPLAY_PLACEHOLDERS)) {
|
||||||
next = replacePlaceholderWithNaturalParticle(next, placeholder, label);
|
next = replacePlaceholderWithNaturalParticle(next, placeholder, label);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ import {
|
||||||
REVIEW_READY_POLL_INTERVAL_MS,
|
REVIEW_READY_POLL_INTERVAL_MS,
|
||||||
REVIEW_READY_POLL_LIMIT,
|
REVIEW_READY_POLL_LIMIT,
|
||||||
displayEvaluationRetryError,
|
displayEvaluationRetryError,
|
||||||
|
displayGeneratedReviewText,
|
||||||
displayReviewSummary,
|
displayReviewSummary,
|
||||||
displayTranscriptText,
|
displayTranscriptText,
|
||||||
noteAuthorLabel,
|
noteAuthorLabel,
|
||||||
|
|
@ -367,15 +368,15 @@ function SupervisorCallout({ note }: { note: ReviewNote }) {
|
||||||
>
|
>
|
||||||
<div className="sr-note__head">
|
<div className="sr-note__head">
|
||||||
<Icon name={iconName} size={13} strokeWidth={2.2} />
|
<Icon name={iconName} size={13} strokeWidth={2.2} />
|
||||||
{note.title}
|
{displayGeneratedReviewText(note.title)}
|
||||||
<span className="sr-note__tag">{tag}</span>
|
<span className="sr-note__tag">{tag}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="sr-note__body">
|
<div className="sr-note__body">
|
||||||
<ReviewMarkdown text={note.body} />
|
<ReviewMarkdown text={displayGeneratedReviewText(note.body)} />
|
||||||
{note.quote ? (
|
{note.quote ? (
|
||||||
<blockquote className="sr-note__quote">
|
<blockquote className="sr-note__quote">
|
||||||
<span>발화 근거</span>
|
<span>발화 근거</span>
|
||||||
<p>{note.quote}</p>
|
<p>{displayGeneratedReviewText(note.quote)}</p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -430,9 +431,11 @@ function JumpablePoint({
|
||||||
return (
|
return (
|
||||||
<div className="sr-point">
|
<div className="sr-point">
|
||||||
<div>
|
<div>
|
||||||
<div className="sr-point__h">{point.title}</div>
|
<div className="sr-point__h">
|
||||||
|
{displayGeneratedReviewText(point.title)}
|
||||||
|
</div>
|
||||||
<div className="sr-point__d">
|
<div className="sr-point__d">
|
||||||
{point.body}
|
{displayGeneratedReviewText(point.body)}
|
||||||
{point.jumpTo && targetTs ? (
|
{point.jumpTo && targetTs ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
@ -619,7 +622,7 @@ function CaseWorksheetCard({
|
||||||
onClick={() => onJump(evidence.turnId)}
|
onClick={() => onJump(evidence.turnId)}
|
||||||
>
|
>
|
||||||
{evidence.speaker === "learner" ? "학습자" : "내담자"}{" "}
|
{evidence.speaker === "learner" ? "학습자" : "내담자"}{" "}
|
||||||
· {evidence.quote}
|
· {displayGeneratedReviewText(evidence.quote)}
|
||||||
</button>
|
</button>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2026,7 +2029,9 @@ export default function SessionReview() {
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="sr-nextline__lab">다음에 시도할 문장</div>
|
<div className="sr-nextline__lab">다음에 시도할 문장</div>
|
||||||
<div className="sr-nextline__q">{data.nextLine}</div>
|
<div className="sr-nextline__q">
|
||||||
|
{displayGeneratedReviewText(data.nextLine)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -2044,7 +2049,7 @@ export default function SessionReview() {
|
||||||
{data.clientFeedback ? (
|
{data.clientFeedback ? (
|
||||||
<>
|
<>
|
||||||
<p className="sr-feedback__quote">
|
<p className="sr-feedback__quote">
|
||||||
“{data.clientFeedback}”
|
“{displayGeneratedReviewText(data.clientFeedback)}”
|
||||||
</p>
|
</p>
|
||||||
<div className="sr-feedback__src">
|
<div className="sr-feedback__src">
|
||||||
<Icon name="info" size={13} strokeWidth={2} />
|
<Icon name="info" size={13} strokeWidth={2} />
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,11 @@ export function displayReviewSummary(summary: string) {
|
||||||
) {
|
) {
|
||||||
return "저장된 축어록은 확인했지만 deep-loop 평가 AI 산출물을 표시하지 못했습니다. AI 평가 재시도가 필요합니다.";
|
return "저장된 축어록은 확인했지만 deep-loop 평가 AI 산출물을 표시하지 못했습니다. AI 평가 재시도가 필요합니다.";
|
||||||
}
|
}
|
||||||
return displayPiiSafeText(summary);
|
return displayGeneratedReviewText(summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function displayGeneratedReviewText(text: string) {
|
||||||
|
return displayPiiSafeText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function displayEvaluationRetryError(message: string) {
|
export function displayEvaluationRetryError(message: string) {
|
||||||
|
|
|
||||||
|
|
@ -686,7 +686,7 @@ route fixture는 90건이므로 전체 숫자를 실제 회기 폐루프로 부
|
||||||
검증한 뒤 sentinel resource·PID·listener·temp 잔여 0을 영수증으로 남긴다. 공개 8001/55432/9099와 NAS는 금지다.
|
검증한 뒤 sentinel resource·PID·listener·temp 잔여 0을 영수증으로 남긴다. 공개 8001/55432/9099와 NAS는 금지다.
|
||||||
최초 세 full run은 SSE body replay, durable turn 전 종료, 비종료 SSE `response.finished()` 대기를 차례로 찾아 모두
|
최초 세 full run은 SSE body replay, durable turn 전 종료, 비종료 SSE `response.finished()` 대기를 차례로 찾아 모두
|
||||||
fail-closed했다. 4차 clean run은 reload 뒤 persisted episode를 `학습자만 실행`으로 표시하는 제품 결함을 검출했다.
|
fail-closed했다. 4차 clean run은 reload 뒤 persisted episode를 `학습자만 실행`으로 표시하는 제품 결함을 검출했다.
|
||||||
5차 clean run은 핵심 동일 학습자 SSE·review·G4/G5 `0→1→1`을 처음 통과했지만, 같은 fixture를 후속 desktop/mobile 회귀가 재사용해 2/4 실패했다. 다섯 run 모두 public/NAS/active DB 접촉 0, cleanup GREEN이다. UI ledger hydration은 route fixture 6/6을 통과했고, runner는 핵심 폐루프와 후속 회귀에 별도 zero-state fixture를 준비한다(unit 12/12). 리뷰 요약 literal privacy 토큰과 `익명 내담자가고` 조사도 표시층에서 수정했다. 아직 **전체 receipt GREEN은 없으므로** 자동 full runner 등록은 금지다.
|
5·6차 clean run은 핵심 동일 학습자 SSE·review·G4/G5 `0→1→1`을 연속 통과했다. 5차는 fixture 공유, 6차는 desktop이 적재한 성공 상태를 mobile G4 테스트가 최초 버튼으로 찾은 locator 결함 때문에 전체 receipt가 FAILED다. 여섯 run 모두 public/NAS/active DB 접촉 0, cleanup GREEN이다. runner는 핵심 폐루프와 후속 회귀에 별도 zero-state fixture를 준비하고(unit 12/12), mobile은 성공 상태의 `반영 상태 다시 확인`을 누른다. 리뷰의 요약·강점·개선점·다음 문장·내담자 반응·워크시트 근거 literal privacy 토큰도 표시층에서 자연어화했다. 아직 **전체 receipt GREEN은 없으므로** 자동 full runner 등록은 금지다.
|
||||||
|
|
||||||
## 9. 핵심 변경 파일
|
## 9. 핵심 변경 파일
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@
|
||||||
> 6.3분 안에 동일 학습자 SSE·review ready·G4 원장 0→1까지 통과한 뒤, full reload가 persisted episode를 화면에 hydrate하지
|
> 6.3분 안에 동일 학습자 SSE·review ready·G4 원장 0→1까지 통과한 뒤, full reload가 persisted episode를 화면에 hydrate하지
|
||||||
> 못해 이미 기록된 상태를 `학습자만 실행`으로 되돌리는 제품 결함을 잡았다. 네 번 모두 public/NAS/active DB 접촉 0·자원 잔여 0이다.
|
> 못해 이미 기록된 상태를 `학습자만 실행`으로 되돌리는 제품 결함을 잡았다. 네 번 모두 public/NAS/active DB 접촉 0·자원 잔여 0이다.
|
||||||
> 현재 UI는 현재 session+prescription의 persisted episode를 `현재 원장 상태`로 복원하며 route fixture desktop/mobile 6/6을 통과했다.
|
> 현재 UI는 현재 session+prescription의 persisted episode를 `현재 원장 상태`로 복원하며 route fixture desktop/mobile 6/6을 통과했다.
|
||||||
> 5차 clean run은 핵심 동일 학습자 SSE·review·G4/G5 `0→1→1`을 처음 통과했지만, 같은 fixture를 재사용한 후속 returned-practice 회귀가 이미 적재된 1건을 초기 0으로 오인해 2/4 실패했다. 전체 receipt는 FAILED이며 public/NAS/active DB 접촉 0·cleanup GREEN이다. runner는 두 브라우저 게이트마다 별도 zero-state fixture를 만들도록 수정했고 unit 12/12·Ruff·compile을 통과했다. 실패 화면에서 리뷰 요약 literal `[NAME]`/`[ORG]`과 `익명 내담자가고`도 발견해 표시 자연어화·복합 조사 보정을 추가했다. 다음 clean single run의 **전체 GREEN receipt** 전에는 자동 full runner를 등록하지 않는다.
|
> 5·6차 clean run은 핵심 동일 학습자 SSE·review·G4/G5 `0→1→1`을 연속 통과했다. 5차는 같은 fixture를 후속 회귀가 재사용해 2/4 실패했고, fixture를 분리한 6차는 desktop G4/G5 2건이 통과한 뒤 mobile G4가 이미 적재된 성공 상태에서 최초 버튼을 찾아 timeout됐다. 두 receipt 모두 FAILED이며 public/NAS/active DB 접촉 0·cleanup GREEN이다. mobile은 `반영 상태 다시 확인`을 누르도록 교정했고, 실패 화면에서 추가로 확인한 강점·개선점·다음 문장·내담자 반응·워크시트 근거의 literal `[NAME]`/`[ORG]`도 표시층에서 자연어화했다. focused session-review desktop/mobile 2/2·typecheck·live spec collection 4건은 통과했지만, 다음 clean single run의 **전체 GREEN receipt** 전에는 자동 full runner를 등록하지 않는다.
|
||||||
> 증거: [배포 증거](./ops/nas-preview-deployment-evidence-2026-08-07.md), [기계 판독 증거](./ops/evidence/nas-preview-current-deploy-2026-08-07.json), [브라우저 증거](./ops/evidence/nas-preview-live-turn-2026-08-07.png). 이는 G7 물리 마이크·독립 사람 평가 증거를 대체하지 않는다.
|
> 증거: [배포 증거](./ops/nas-preview-deployment-evidence-2026-08-07.md), [기계 판독 증거](./ops/evidence/nas-preview-current-deploy-2026-08-07.json), [브라우저 증거](./ops/evidence/nas-preview-live-turn-2026-08-07.png). 이는 G7 물리 마이크·독립 사람 평가 증거를 대체하지 않는다.
|
||||||
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·모델 탐색·설정 저장 경로는 구현 완료. 남은 게이트는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 뒤 같은 프롬프트의 live 응답·계량·오류 표면화를 비교하는 것이다.
|
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·모델 탐색·설정 저장 경로는 구현 완료. 남은 게이트는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 뒤 같은 프롬프트의 live 응답·계량·오류 표면화를 비교하는 것이다.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -301,10 +301,10 @@
|
||||||
<div class="pulse-main">
|
<div class="pulse-main">
|
||||||
<span class="eyebrow">project pulse</span>
|
<span class="eyebrow">project pulse</span>
|
||||||
<h1>지금 위치: <em>G0~G6 · G8 DONE</em>. <em>G7 external proof</em>를 닫는 중.</h1>
|
<h1>지금 위치: <em>G0~G6 · G8 DONE</em>. <em>G7 external proof</em>를 닫는 중.</h1>
|
||||||
<p class="pulse-state"><b>현재:</b> G8·공개 앱은 정상이다. 주기 실회기 핵심 SSE→review→G4/G5 <code>0→1→1</code>은 통과했지만 회귀 fixture 오염으로 전체 receipt는 FAILED다. <b>다음:</b> 격리 보정 후 clean single run 전체 GREEN → G7 물리 마이크·human pack·checker exit 0 순서로 닫는다.</p>
|
<p class="pulse-state"><b>현재:</b> G8·공개 앱 정상. 주기 핵심 폐루프 2회 GREEN, 전체 receipt는 FAILED다. <b>다음:</b> mobile locator 보정의 clean single run → G7 mic·human pack·checker exit 0.</p>
|
||||||
<details class="pulse-details">
|
<details class="pulse-details">
|
||||||
<summary>운영 복구·watchdog·배포 상세 보기</summary>
|
<summary>운영 복구·watchdog·배포 상세 보기</summary>
|
||||||
<p class="pulse-state">G8 clean-head NAS-origin은 candidate 112/112·실제 origin 112/112로 DONE이다. 공개 API는 detached-clean <code>b34623f3…</code>·tree <code>32cc85c7…</code>에서 OpenAPI 126, <code>/admin/voice-runtime</code>, <code>local_whisper/small</code> + <code>melotts/melotts-korean</code>, WS queue 4를 제공하며 fresh provenance receipt <code>9f8d1941…a21bf</code>가 passed다. 공개 Web은 Pages entry <code>index-CGZK4CHD.js</code>와 review chunk <code>SessionReview-D4ScnJ5q.js</code>에서 literal <code>[NAME]</code> 0·시간 만료·모바일 44px/자동 스크롤 보정을 노출한다. 주기 실회기 5차의 전체 receipt 실패 뒤 회귀 fixture를 분리했으며 다음 clean single run 전에는 자동 승격하지 않는다. 마이크를 열지 않은 G7 rehearsal은 인증 WSS·runtime·Windows topology 세 leg 7-sample로 통과했다. 다음 외부 gate는 실제 mic·동시 high-water·human evidence다.</p>
|
<p class="pulse-state">G8 clean-head NAS-origin은 candidate 112/112·실제 origin 112/112로 DONE이다. 공개 API는 detached-clean <code>b34623f3…</code>·tree <code>32cc85c7…</code>에서 OpenAPI 126, <code>/admin/voice-runtime</code>, <code>local_whisper/small</code> + <code>melotts/melotts-korean</code>, WS queue 4를 제공하며 fresh provenance receipt <code>9f8d1941…a21bf</code>가 passed다. 공개 Web은 Pages entry <code>index-BrpoQ5nP.js</code>와 review chunk <code>SessionReview-E03q-dGb.js</code>에서 literal <code>[NAME]</code> 0·시간 만료·모바일 44px/자동 스크롤 보정을 노출한다. 주기 실회기 6차는 서로 다른 zero-state fixture와 핵심 폐루프를 통과했지만 desktop이 적재한 성공 상태를 mobile G4 테스트가 최초 버튼으로 다시 찾는 locator 결함을 fail-closed했다. 다음 clean single run 전에는 자동 승격하지 않는다. 마이크를 열지 않은 G7 rehearsal은 인증 WSS·runtime·Windows topology 세 leg 7-sample로 통과했다. 다음 외부 gate는 실제 mic·동시 high-water·human evidence다.</p>
|
||||||
<p class="pulse-state">2026-08-07 공개 DB는 recovered named volume으로 전환했고 원본 container·dump·volume을 보존했다. 2026-08-09 public DB incident 뒤 fresh custom dump <code>f1fd569c…5f64</code>를 만들고, 누락된 forward migration 14만 단일 트랜잭션으로 적용해 runtime schema 8/8을 복구했다. 공개 API·cloudflared는 clean release <code>b34623f3…</code>로 fresh 교체했고 public health 3/3·db/engine true·OpenAPI 126·local voice exact를 통과했다. 로그온·5분 watchdog 두 task도 같은 root/commit/tree/script SHA에 pin해 명시 실행 결과 0을 확인했다. 공개 runtime receipt는 <code>9f8d1941…a21bf</code>, 무마이크 rehearsal evidence는 voice <code>61af2e98…</code>·runtime <code>c6e8670e…</code>·topology <code>89f1cb86…</code>이며 UUID/email literal 0이다. G7은 배포가 아니라 실제 mic·동시 high-water·human evidence만 남은 external GATE다.</p>
|
<p class="pulse-state">2026-08-07 공개 DB는 recovered named volume으로 전환했고 원본 container·dump·volume을 보존했다. 2026-08-09 public DB incident 뒤 fresh custom dump <code>f1fd569c…5f64</code>를 만들고, 누락된 forward migration 14만 단일 트랜잭션으로 적용해 runtime schema 8/8을 복구했다. 공개 API·cloudflared는 clean release <code>b34623f3…</code>로 fresh 교체했고 public health 3/3·db/engine true·OpenAPI 126·local voice exact를 통과했다. 로그온·5분 watchdog 두 task도 같은 root/commit/tree/script SHA에 pin해 명시 실행 결과 0을 확인했다. 공개 runtime receipt는 <code>9f8d1941…a21bf</code>, 무마이크 rehearsal evidence는 voice <code>61af2e98…</code>·runtime <code>c6e8670e…</code>·topology <code>89f1cb86…</code>이며 UUID/email literal 0이다. G7은 배포가 아니라 실제 mic·동시 high-water·human evidence만 남은 external GATE다.</p>
|
||||||
</details>
|
</details>
|
||||||
<div class="phase-rail" id="phase-rail" aria-label="Phase 진행 추정"></div>
|
<div class="phase-rail" id="phase-rail" aria-label="Phase 진행 추정"></div>
|
||||||
|
|
@ -673,6 +673,7 @@
|
||||||
<p class="dg-note">194차 적용(2026-08-10): 주기 runner의 최초 세 full run은 모두 전용 clean stack에서 fail-closed했다. 1차는 성공 SSE의 body를 다시 읽는 CDP 오류, 2차는 UI 2턴만 보고 durable 저장 전에 종료해 리뷰가 기록 없음, 3차는 서버가 의도적으로 닫지 않는 SSE에 <code>response.finished()</code>를 기다려 20분 상한에 도달한 하네스 결함이었다. 세 번 모두 fixture review-ready·stack health는 통과했고 공개 8001·실 DB 55432·engine 9099·NAS 접촉 0, container/volume/network/listener/temp 잔여 0이었다. 현재 소스는 자동 retry 0과 <code>GET /sessions/{id}</code> durable turn 2개 poll만 완료 신호로 사용한다. 같은 단계 반복 원칙에 따라 이번 턴에는 더 실행하지 않으며, 다음 clean single run의 첫 GREEN receipt 전까지 주기 실회기 검증을 완료로 표기하지 않는다.</p>
|
<p class="dg-note">194차 적용(2026-08-10): 주기 runner의 최초 세 full run은 모두 전용 clean stack에서 fail-closed했다. 1차는 성공 SSE의 body를 다시 읽는 CDP 오류, 2차는 UI 2턴만 보고 durable 저장 전에 종료해 리뷰가 기록 없음, 3차는 서버가 의도적으로 닫지 않는 SSE에 <code>response.finished()</code>를 기다려 20분 상한에 도달한 하네스 결함이었다. 세 번 모두 fixture review-ready·stack health는 통과했고 공개 8001·실 DB 55432·engine 9099·NAS 접촉 0, container/volume/network/listener/temp 잔여 0이었다. 현재 소스는 자동 retry 0과 <code>GET /sessions/{id}</code> durable turn 2개 poll만 완료 신호로 사용한다. 같은 단계 반복 원칙에 따라 이번 턴에는 더 실행하지 않으며, 다음 clean single run의 첫 GREEN receipt 전까지 주기 실회기 검증을 완료로 표기하지 않는다.</p>
|
||||||
<p class="dg-note">195차 적용(2026-08-10): clean HEAD <code>13d1c39a…</code>·tree <code>beb93801…</code>의 4차 수동 full run은 앞선 SSE hang을 제거해 6.3분에 종료했고, 실제 동일 학습자 SSE·review ready·G4 첫 POST와 원장 count 0→1까지 통과했다. 이후 full page reload에서 이미 저장된 현재 회기 episode를 카드가 hydrate하지 않아 <code>원장 반영됨</code> 대신 <code>학습자만 실행</code>으로 되돌아가는 제품 정직성 결함을 fail-closed했다. receipt <code>periodic-learner-e2e-20260810-003150.json</code>은 공개·NAS·active DB 접촉 0과 container/volume/network/listener/temp 잔여 0을 증명한다. UI는 현재 session+prescription의 persisted episode를 read model에서 복원해 <code>현재 원장 상태</code>와 재확인 행동을 표시하도록 수정했고, 실제 reload를 포함한 자기주도 route fixture desktop/mobile <b>6/6</b>·typecheck를 통과했다. full runner는 재시도하지 않았으므로 첫 GREEN receipt와 자동 주기 승격은 여전히 미완료다.</p>
|
<p class="dg-note">195차 적용(2026-08-10): clean HEAD <code>13d1c39a…</code>·tree <code>beb93801…</code>의 4차 수동 full run은 앞선 SSE hang을 제거해 6.3분에 종료했고, 실제 동일 학습자 SSE·review ready·G4 첫 POST와 원장 count 0→1까지 통과했다. 이후 full page reload에서 이미 저장된 현재 회기 episode를 카드가 hydrate하지 않아 <code>원장 반영됨</code> 대신 <code>학습자만 실행</code>으로 되돌아가는 제품 정직성 결함을 fail-closed했다. receipt <code>periodic-learner-e2e-20260810-003150.json</code>은 공개·NAS·active DB 접촉 0과 container/volume/network/listener/temp 잔여 0을 증명한다. UI는 현재 session+prescription의 persisted episode를 read model에서 복원해 <code>현재 원장 상태</code>와 재확인 행동을 표시하도록 수정했고, 실제 reload를 포함한 자기주도 route fixture desktop/mobile <b>6/6</b>·typecheck를 통과했다. full runner는 재시도하지 않았으므로 첫 GREEN receipt와 자동 주기 승격은 여전히 미완료다.</p>
|
||||||
<p class="dg-note">196차 적용(2026-08-10): clean HEAD <code>9a7dd98c…</code>·tree <code>1dce20c5…</code>의 5차 단일 full run은 실제 동일 학습자 브라우저 단계에서 SSE·review ready·G4 <code>0→1→1</code>·G5 <code>0→1→1</code>을 78초에 모두 통과해 핵심 폐루프 첫 GREEN을 만들었다. 다만 같은 fixture를 이어서 사용한 returned-practice desktop/mobile 회귀가 이미 적재된 G4/G5를 초기 0으로 오인해 2/4 실패했고 전체 receipt <code>periodic-learner-e2e-20260810-010038.json</code>은 정직하게 FAILED다. 공개·NAS·active DB 접촉 0, container/volume/network/listener/temp 잔여 0은 유지했다. runner는 핵심 폐루프와 후속 회귀 전에 각각 새 학습자·회기 fixture를 준비하도록 분리했고 unit <b>12/12</b>·Ruff·compile을 통과했다. 실패 화면에서 리뷰 요약 literal <code>[NAME]</code>/<code>[ORG]</code>과 <code>익명 내담자가고</code> 조사 결함도 발견해 표시 전용 자연어 치환과 복합 조사 우선순위를 보강했고 session-review desktop/mobile <b>2/2</b>·typecheck·build를 통과했다. 전체 GREEN receipt 전에는 자동 full runner를 등록하지 않는다.</p>
|
<p class="dg-note">196차 적용(2026-08-10): clean HEAD <code>9a7dd98c…</code>·tree <code>1dce20c5…</code>의 5차 단일 full run은 실제 동일 학습자 브라우저 단계에서 SSE·review ready·G4 <code>0→1→1</code>·G5 <code>0→1→1</code>을 78초에 모두 통과해 핵심 폐루프 첫 GREEN을 만들었다. 다만 같은 fixture를 이어서 사용한 returned-practice desktop/mobile 회귀가 이미 적재된 G4/G5를 초기 0으로 오인해 2/4 실패했고 전체 receipt <code>periodic-learner-e2e-20260810-010038.json</code>은 정직하게 FAILED다. 공개·NAS·active DB 접촉 0, container/volume/network/listener/temp 잔여 0은 유지했다. runner는 핵심 폐루프와 후속 회귀 전에 각각 새 학습자·회기 fixture를 준비하도록 분리했고 unit <b>12/12</b>·Ruff·compile을 통과했다. 실패 화면에서 리뷰 요약 literal <code>[NAME]</code>/<code>[ORG]</code>과 <code>익명 내담자가고</code> 조사 결함도 발견해 표시 전용 자연어 치환과 복합 조사 우선순위를 보강했고 session-review desktop/mobile <b>2/2</b>·typecheck·build를 통과했다. 전체 GREEN receipt 전에는 자동 full runner를 등록하지 않는다.</p>
|
||||||
|
<p class="dg-note">197차 적용(2026-08-10): clean HEAD <code>4cd35488…</code>·tree <code>001a7c74…</code>의 6차 full run은 핵심 폐루프를 두 번째로 통과했고, 별도 returned-practice fixture도 초기 G4/G5 0을 증명했다. desktop G4/G5 2건은 GREEN이었지만 desktop이 만든 성공 상태를 mobile G4가 다시 읽을 때 테스트가 <code>반영 상태 다시 확인</code> 대신 최초 버튼을 찾아 5분 timeout, mobile G5는 serial 중단됐다. receipt <code>periodic-learner-e2e-20260810-012909.json</code>은 FAILED이며 public/NAS/active DB 접촉 0·exact cleanup GREEN이다. mobile 성공-state locator를 교정했고 실패 화면에서 추가로 확인한 강점·개선점·다음 문장·내담자 반응·워크시트 근거의 literal <code>[NAME]</code>/<code>[ORG]</code>도 저장/API는 유지한 채 표시층에서 자연어화했다. focused session-review desktop/mobile <b>2/2</b>·typecheck·live spec 4건 collection을 통과했지만 full runner는 재실행하지 않았다.</p>
|
||||||
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
<div class="dg-principles" aria-label="디자인 생성 가드레일">
|
||||||
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
<div><b>래스터만 사용</b><span>이미지 생성 도구 산출물은 PNG 기반 시안이다. SVG·벡터·와이어프레임·로고 시트로 해석하지 않는다.</span></div>
|
||||||
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
<div><b>기능 우선</b><span>메인 라우트의 실제 액션과 정보 구조를 먼저 반영한다. 장식은 기능을 가리지 않는 수준에서만 쓴다.</span></div>
|
||||||
|
|
@ -1160,7 +1161,7 @@
|
||||||
<tr><td>Docker image smoke</td><td><code>docker build -f apps/api/Dockerfile .</code> / <code>docker run ... python -c "import app.main"</code> / <code>docker build -f apps/web/Dockerfile apps/web</code></td><td>API/Web image build and API import smoke pass after packaging cleanup. Web build uses npm lockfile and ignores host <code>node_modules</code>; API image excludes local <code>.env</code> files.</td></tr>
|
<tr><td>Docker image smoke</td><td><code>docker build -f apps/api/Dockerfile .</code> / <code>docker run ... python -c "import app.main"</code> / <code>docker build -f apps/web/Dockerfile apps/web</code></td><td>API/Web image build and API import smoke pass after packaging cleanup. Web build uses npm lockfile and ignores host <code>node_modules</code>; API image excludes local <code>.env</code> files.</td></tr>
|
||||||
<tr><td>Deploy preflight</td><td><code>python scripts\check-deploy-preflight.py --skip-db --env-file infra\.env.example --allow-placeholder-secrets</code> / DB mode with local <code>DATABASE_URL</code></td><td>Passed: exact-pinned API requirements, live coaching <code>data/kb</code> source pack, env template keys, and DB readiness (<code>current_user=vignette</code>). DB mode can additionally check app-role DSN with <code>--require-app-role</code> and now verifies session read-model columns including <code>app.turns</code> voice metadata columns(<code>audio_ref</code>/<code>silence_ms</code>/<code>speech_rate</code>/<code>barge_in</code>/<code>provider_events</code>) plus worksheet review columns.</td></tr>
|
<tr><td>Deploy preflight</td><td><code>python scripts\check-deploy-preflight.py --skip-db --env-file infra\.env.example --allow-placeholder-secrets</code> / DB mode with local <code>DATABASE_URL</code></td><td>Passed: exact-pinned API requirements, live coaching <code>data/kb</code> source pack, env template keys, and DB readiness (<code>current_user=vignette</code>). DB mode can additionally check app-role DSN with <code>--require-app-role</code> and now verifies session read-model columns including <code>app.turns</code> voice metadata columns(<code>audio_ref</code>/<code>silence_ms</code>/<code>speech_rate</code>/<code>barge_in</code>/<code>provider_events</code>) plus worksheet review columns.</td></tr>
|
||||||
<tr><td>Fresh compose smoke</td><td><code>docker compose -p vignette-packaging-smoke -f infra/docker-compose.yml up -d --build</code> + proxy <code>/api/health</code></td><td>Passed with dummy production-safe env: API healthy, DB healthy, web/proxy up, <code>http://localhost:18080/api/health</code> 200 with <code>db:true</code>, <code>engine:true</code>, <code>engine_mode:"claude_cli"</code>. Smoke volumes/network removed after run.</td></tr>
|
<tr><td>Fresh compose smoke</td><td><code>docker compose -p vignette-packaging-smoke -f infra/docker-compose.yml up -d --build</code> + proxy <code>/api/health</code></td><td>Passed with dummy production-safe env: API healthy, DB healthy, web/proxy up, <code>http://localhost:18080/api/health</code> 200 with <code>db:true</code>, <code>engine:true</code>, <code>engine_mode:"claude_cli"</code>. Smoke volumes/network removed after run.</td></tr>
|
||||||
<tr><td>격리 NAS 프리뷰 · 회기 E2E 자동화</td><td><code>http://100.116.83.60:8088</code> / <code>vignette-e2e</code> 매시간 heartbeat / <a href="./ops/nas-preview-deployment-evidence-2026-08-07.md">배포 증거</a> / <a href="./ops/evidence/nas-preview-current-deploy-2026-08-07.json">기계 판독 증거</a></td><td>current clean source HEAD <code>61a41d1f…6af</code>·tree <code>87dec55d…3b77</code>의 archive <code>4d15d055…119d4d</code>를 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. candidate 112/112와 실제 NAS-origin 112/112는 통과했지만 영수증은 실 API/DB 22건과 route fixture 90건을 분리한다. 자동화는 공개/NAS 화면과 health를 읽기 전용으로 매시간 보여준다. material milestone의 동일 학습자 SSE→review→G4/G5 actual 폐루프는 clean HEAD/tree·전용 engine/DB/API/Web·보호 포트 거부·sentinel·exact cleanup runner가 소유한다. 1~3차는 하네스 결함, 4차는 reload ledger hydration 제품 결함을 fail-closed했다. 5차 핵심 동일 학습자 브라우저는 SSE·review·G4/G5 <code>0→1→1</code>을 처음 통과했지만 같은 fixture를 재사용한 후속 회귀 2/4 실패로 전체 receipt는 FAILED다. cleanup 5/5는 GREEN이다. 두 브라우저 게이트를 서로 다른 zero-state fixture로 분리했고 unit 12/12를 통과했지만 다음 clean single run의 전체 GREEN 전에는 자동 실행을 등록하지 않는다. release gate·NAS preflight·배포 SHA 변경 때만 프리뷰를 갱신한다. G7 외부 mic/provider/human 증거는 별도다.</td></tr>
|
<tr><td>격리 NAS 프리뷰 · 회기 E2E 자동화</td><td><code>http://100.116.83.60:8088</code> / <code>vignette-e2e</code> 매시간 heartbeat / <a href="./ops/nas-preview-deployment-evidence-2026-08-07.md">배포 증거</a> / <a href="./ops/evidence/nas-preview-current-deploy-2026-08-07.json">기계 판독 증거</a></td><td>current clean source HEAD <code>61a41d1f…6af</code>·tree <code>87dec55d…3b77</code>의 archive <code>4d15d055…119d4d</code>를 전용 Compose 프로젝트·포트·네트워크·named volume에 승격했다. candidate 112/112와 실제 NAS-origin 112/112는 통과했지만 영수증은 실 API/DB 22건과 route fixture 90건을 분리한다. 자동화는 공개/NAS 화면과 health를 읽기 전용으로 매시간 보여준다. material milestone의 동일 학습자 SSE→review→G4/G5 actual 폐루프는 clean HEAD/tree·전용 engine/DB/API/Web·보호 포트 거부·sentinel·exact cleanup runner가 소유한다. 1~3차는 하네스, 4차는 reload ledger hydration 제품 결함을 fail-closed했다. 5·6차 핵심 동일 학습자 브라우저는 SSE·review·G4/G5 <code>0→1→1</code>을 연속 통과했다. 5차는 fixture 공유, 6차는 mobile 성공-state locator 결함으로 후속 회귀가 중단되어 전체 receipt는 여전히 FAILED다. cleanup 6/6은 GREEN이고 fixture·locator는 수정했지만 다음 clean single run의 전체 GREEN 전에는 자동 실행을 등록하지 않는다. release gate·NAS preflight·배포 SHA 변경 때만 프리뷰를 갱신한다. G7 외부 mic/provider/human 증거는 별도다.</td></tr>
|
||||||
<tr><td>G7 외부 증거 3-artifact 동시 시간창 오케스트레이터</td><td><code>scripts/run-g7-external-proof-window.py</code> · <a href="./ops/outcome-os-g7-external-proof-readiness-2026-08-07.md">준비 문서</a></td><td>soak·runtime·topology 세 캡처를 같은 host의 공통 3,000초 시간창으로 실행하고 checker까지 잇는다. 공개 배포·프로세스·source pin은 clean commit <code>b34623f3…</code>와 fresh receipt <code>9f8d1941…</code>로 완료했다. Cloudflare가 Python 기본 User-Agent를 403으로 거부하는 경계도 브라우저 호환 header와 회귀 4/4로 고정했다. 30초 rehearsal은 세 leg 7 samples를 모두 통과했으며 마이크를 열지 않았다. <b>남은 선행 조건:</b> 사용자의 물리 장치 선택과 명시 동의, 독립 human voice-gain pack이다.</td></tr>
|
<tr><td>G7 외부 증거 3-artifact 동시 시간창 오케스트레이터</td><td><code>scripts/run-g7-external-proof-window.py</code> · <a href="./ops/outcome-os-g7-external-proof-readiness-2026-08-07.md">준비 문서</a></td><td>soak·runtime·topology 세 캡처를 같은 host의 공통 3,000초 시간창으로 실행하고 checker까지 잇는다. 공개 배포·프로세스·source pin은 clean commit <code>b34623f3…</code>와 fresh receipt <code>9f8d1941…</code>로 완료했다. Cloudflare가 Python 기본 User-Agent를 403으로 거부하는 경계도 브라우저 호환 header와 회귀 4/4로 고정했다. 30초 rehearsal은 세 leg 7 samples를 모두 통과했으며 마이크를 열지 않았다. <b>남은 선행 조건:</b> 사용자의 물리 장치 선택과 명시 동의, 독립 human voice-gain pack이다.</td></tr>
|
||||||
<tr><td>G7 external GATE · 최종 감사 정정</td><td><code>run-g7-external-proof-window.py</code> → <code>check-g7-external-proof.py</code></td><td><b>상태는 external GATE 유지.</b> runner 프로세스 exit는 canonical checker <code>exit 0</code>·<code>gate_closed=true</code>에 결속되고, browser Origin과 API transport host는 별도 allowlist로 검증된다. 세 artifact 교집합은 <code>≥3000s</code> + 120초 margin이고 Windows proof는 detached-clean HEAD/tree·tool SHA·<code>psutil==6.1.1</code>을 pin한다. clean-source 배포와 무마이크 rehearsal은 완료했다. 남은 것은 물리 마이크 3,120초, 동시 runtime/topology, 독립 human pack을 합친 4-artifact 실증뿐이다.</td></tr>
|
<tr><td>G7 external GATE · 최종 감사 정정</td><td><code>run-g7-external-proof-window.py</code> → <code>check-g7-external-proof.py</code></td><td><b>상태는 external GATE 유지.</b> runner 프로세스 exit는 canonical checker <code>exit 0</code>·<code>gate_closed=true</code>에 결속되고, browser Origin과 API transport host는 별도 allowlist로 검증된다. 세 artifact 교집합은 <code>≥3000s</code> + 120초 margin이고 Windows proof는 detached-clean HEAD/tree·tool SHA·<code>psutil==6.1.1</code>을 pin한다. clean-source 배포와 무마이크 rehearsal은 완료했다. 남은 것은 물리 마이크 3,120초, 동시 runtime/topology, 독립 human pack을 합친 4-artifact 실증뿐이다.</td></tr>
|
||||||
<tr><td>G8 DONE · clean-head NAS 기준선</td><td><code>vignette-preview-20260807</code> · <code>run-outcome-os-release-agent.py</code></td><td><b>상태는 DONE.</b> active archive <code>4d15d055…119d4d</code>, source HEAD/tree <code>61a41d1f…6af</code>/<code>87dec55d…3b77</code>, API/Web <code>d5021950…e4b1</code>/<code>9796c092…4b36</code> exact running. candidate 112/112와 actual NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했다. fresh dump <code>36ec8748…24db8</code> 1,097,100 bytes, TOC 1,738 / TABLE DATA 129와 previous active <code>6030a677…c611</code>을 보존했다. release agent의 DB restore 비소유·image/Compose/active-state rollback 경계는 유지한다.</td></tr>
|
<tr><td>G8 DONE · clean-head NAS 기준선</td><td><code>vignette-preview-20260807</code> · <code>run-outcome-os-release-agent.py</code></td><td><b>상태는 DONE.</b> active archive <code>4d15d055…119d4d</code>, source HEAD/tree <code>61a41d1f…6af</code>/<code>87dec55d…3b77</code>, API/Web <code>d5021950…e4b1</code>/<code>9796c092…4b36</code> exact running. candidate 112/112와 actual NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했다. fresh dump <code>36ec8748…24db8</code> 1,097,100 bytes, TOC 1,738 / TABLE DATA 129와 previous active <code>6030a677…c611</code>을 보존했다. release agent의 DB restore 비소유·image/Compose/active-state rollback 경계는 유지한다.</td></tr>
|
||||||
|
|
|
||||||
|
|
@ -281,10 +281,11 @@ NAS-origin 112건은 전부 실 API/DB 회기가 아니다. release receipt는 `
|
||||||
`scripts/run-periodic-learner-e2e.py --execute`가 전용 disposable engine/DB/API/Web에서만 실행한다.
|
`scripts/run-periodic-learner-e2e.py --execute`가 전용 disposable engine/DB/API/Web에서만 실행한다.
|
||||||
이 runner는 clean HEAD/tree, 로컬 npipe Docker, loopback 동적 포트, sentinel과 exact cleanup을 강제하며
|
이 runner는 clean HEAD/tree, 로컬 npipe Docker, loopback 동적 포트, sentinel과 exact cleanup을 강제하며
|
||||||
공개 8001·DB 55432·engine 9099와 NAS를 구조적으로 거부한다. 최초 세 full run은 하네스 결함, 4차는 reload 뒤
|
공개 8001·DB 55432·engine 9099와 NAS를 구조적으로 거부한다. 최초 세 full run은 하네스 결함, 4차는 reload 뒤
|
||||||
persisted episode를 UI에 hydrate하지 못하는 제품 결함을 각각 fail-closed로 검출했다. 5차는 핵심 동일 학습자
|
persisted episode를 UI에 hydrate하지 못하는 제품 결함을 각각 fail-closed로 검출했다. 5·6차는 핵심 동일 학습자
|
||||||
SSE·review·G4/G5 `0→1→1`을 처음 통과했지만 같은 fixture를 재사용한 후속 returned-practice 회귀가 2/4 실패했다.
|
SSE·review·G4/G5 `0→1→1`을 연속 통과했다. 5차는 fixture 공유, 6차는 desktop 성공 상태를 mobile G4가 최초 버튼으로
|
||||||
다섯 실행 모두 보호 runtime 접촉 0·자원 잔여 0이다. runner는 두 브라우저 게이트마다 별도 zero-state fixture를
|
찾은 locator 결함 때문에 후속 회귀가 중단됐다. 여섯 실행 모두 보호 runtime 접촉 0·자원 잔여 0이다. runner는 두
|
||||||
준비하며 unit 12/12를 통과했다. 리뷰 요약의 literal privacy 토큰도 표시 전용 자연어로 낮춘다. **전체 GREEN receipt** 전에는 full runner를 스케줄 등록하지 않는다.
|
브라우저 게이트마다 별도 zero-state fixture를 준비하고 mobile 성공 상태의 재확인 행동을 검증한다. unit 12/12,
|
||||||
|
표시층 privacy 자연어화 desktop/mobile 2/2를 통과했지만 **전체 GREEN receipt** 전에는 full runner를 스케줄 등록하지 않는다.
|
||||||
|
|
||||||
- **병렬 시나리오**: 166 tests (desktop 83 + mobile 83)
|
- **병렬 시나리오**: 166 tests (desktop 83 + mobile 83)
|
||||||
- **`@single-run` 직렬 시나리오**: 49 tests (DB 영속화·세션 MVP·음성 성공경로·인증 시각 테마·회기말 평가 저장·교수자 명시 재평가 저장·교수자 턴 재평가 저장·교수자 UI 평가 재시도·교수자 사용자별 분석·source pack sync·이론모드 저장·동의 철회 후 voice 차단·브라우저 stream PII 마스킹·음성 transcript 저장 실패 UI 표면화 등)
|
- **`@single-run` 직렬 시나리오**: 49 tests (DB 영속화·세션 MVP·음성 성공경로·인증 시각 테마·회기말 평가 저장·교수자 명시 재평가 저장·교수자 턴 재평가 저장·교수자 UI 평가 재시도·교수자 사용자별 분석·source pack sync·이론모드 저장·동의 철회 후 voice 차단·브라우저 stream PII 마스킹·음성 transcript 저장 실패 UI 표면화 등)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@
|
||||||
무마이크 rehearsal은 완료됐지만 물리 마이크·human pack을 대체하지 않는다.
|
무마이크 rehearsal은 완료됐지만 물리 마이크·human pack을 대체하지 않는다.
|
||||||
격리 NAS 프리뷰 `http://100.116.83.60:8088`은 전용 Compose 프로젝트·포트·네트워크·볼륨에 배포했고, 실제 브라우저 회기와
|
격리 NAS 프리뷰 `http://100.116.83.60:8088`은 전용 Compose 프로젝트·포트·네트워크·볼륨에 배포했고, 실제 브라우저 회기와
|
||||||
review API 저장 축어록 2턴을 확인했다. 기존 프로젝트 중단·재생성 명령은 실행하지 않았다. 매시간 `Vignette 앱 상태·회기 E2E 정기 검증`
|
review API 저장 축어록 2턴을 확인했다. 기존 프로젝트 중단·재생성 명령은 실행하지 않았다. 매시간 `Vignette 앱 상태·회기 E2E 정기 검증`
|
||||||
heartbeat(automation id `vignette-e2e`)는 ACTIVE이며 공개/NAS 화면·health는 읽기 전용으로 보여주고, material milestone에서만 전용 disposable 회기 E2E를 실행한다. NAS 112건은 실 API/DB 22와 route fixture 90으로 영수증에서 분리한다. 동일 학습자 SSE→review→G4/G5 actual 폐루프 runner의 clean HEAD/tree·보호 포트 거부·sentinel·exact cleanup과 unit 12/12는 완료됐다. 최초 세 full run은 하네스 결함, 4차는 reload ledger hydration 제품 결함을 fail-closed했다. 5차 핵심 폐루프는 SSE·review·G4/G5 `0→1→1`을 처음 통과했지만 같은 fixture를 재사용한 후속 회귀가 2/4 실패해 전체 receipt는 FAILED다. 다섯 번 모두 public/NAS/active DB 접촉 0·자원 잔여 0이다. runner는 핵심 폐루프와 후속 회귀에 별도 zero-state fixture를 준비하도록 수정했지만 다음 clean single run의 전체 GREEN receipt가 남았다. release gate·NAS preflight·배포 SHA 변경 때만 프리뷰를 갱신한다. 2026-08-07 SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`의 localhost 108/108과 평문 origin UUID 24건 실패는 역사 기준선으로 보존한다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 현재는 source HEAD/tree `61a41d1f…6af`/`87dec55d…3b77`의 active archive `4d15d055…119d4d`, exact API/Web `d5021950…e4b1`/`9796c092…4b36`이 실행 중이다. candidate 112/112와 실제 NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했고 fresh dump `36ec8748…24db8` 1,097,100 bytes·TOC 1,738/TABLE DATA 129와 previous `6030a677…c611`을 보존했다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
|
heartbeat(automation id `vignette-e2e`)는 ACTIVE이며 공개/NAS 화면·health는 읽기 전용으로 보여주고, material milestone에서만 전용 disposable 회기 E2E를 실행한다. NAS 112건은 실 API/DB 22와 route fixture 90으로 영수증에서 분리한다. 동일 학습자 SSE→review→G4/G5 actual 폐루프 runner의 clean HEAD/tree·보호 포트 거부·sentinel·exact cleanup과 unit 12/12는 완료됐다. 최초 세 full run은 하네스 결함, 4차는 reload ledger hydration 제품 결함을 fail-closed했다. 5·6차 핵심 폐루프는 SSE·review·G4/G5 `0→1→1`을 연속 통과했지만 5차 fixture 공유와 6차 mobile 성공-state locator 결함으로 전체 receipt는 계속 FAILED다. 여섯 번 모두 public/NAS/active DB 접촉 0·자원 잔여 0이다. fixture·locator와 리뷰 표시 privacy 토큰은 수정했지만 다음 clean single run의 전체 GREEN receipt가 남았다. release gate·NAS preflight·배포 SHA 변경 때만 프리뷰를 갱신한다. 2026-08-07 SHA `6030a677af7e87cbfabc422b553d108d53414fd3c446548734a13b036d35c611`의 localhost 108/108과 평문 origin UUID 24건 실패는 역사 기준선으로 보존한다. 실제 receipt-bound rollback은 같은 프리뷰에서 별도 helper로 두 번 실행해 종료했다([런북](./nas-preview-g8-rollback-proof-runbook.md), [기계 판독 증거](./evidence/nas-preview-g8-actual-rollback-2026-08-07.json)). 현재는 source HEAD/tree `61a41d1f…6af`/`87dec55d…3b77`의 active archive `4d15d055…119d4d`, exact API/Web `d5021950…e4b1`/`9796c092…4b36`이 실행 중이다. candidate 112/112와 실제 NAS-origin 112/112, health ok·db/engine true·auth 401·OpenAPI 126을 통과했고 fresh dump `36ec8748…24db8` 1,097,100 bytes·TOC 1,738/TABLE DATA 129와 previous `6030a677…c611`을 보존했다. 증거: [배포 증거](./nas-preview-deployment-evidence-2026-08-07.md),
|
||||||
[브라우저 증거](./evidence/nas-preview-live-turn-2026-08-07.png). 명시 동의 물리 마이크와 독립 human voice-gain 증거는 아직 없으므로 G7은 external GATE로 유지한다.
|
[브라우저 증거](./evidence/nas-preview-live-turn-2026-08-07.png). 명시 동의 물리 마이크와 독립 human voice-gain 증거는 아직 없으므로 G7은 external GATE로 유지한다.
|
||||||
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·Anthropic `/v1/models` 탐색·지원 추론 강도·관리자 fail-closed 저장 경로는 구현 완료. 남은 범위는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 live 응답·계량·오류 표면화 비교다. Claude CLI·Codex CLI(Terra/Medium)·Agy CLI(Gemini 3.6 Flash/High)는 로컬 live probe를 통과했다.
|
- [ ] **claude_cli ↔ Anthropic API live 동일성** — provider 라우팅·Anthropic `/v1/models` 탐색·지원 추론 강도·관리자 fail-closed 저장 경로는 구현 완료. 남은 범위는 연구팀/기관 `ANTHROPIC_API_KEY`를 게이트웨이 호스트에 주입한 live 응답·계량·오류 표면화 비교다. Claude CLI·Codex CLI(Terra/Medium)·Agy CLI(Gemini 3.6 Flash/High)는 로컬 live probe를 통과했다.
|
||||||
- [ ] **stable-source 재부팅 후 watchdog smoke** — watchdog·로그온 boot는 detached-clean `b34623f3…` stable
|
- [ ] **stable-source 재부팅 후 watchdog smoke** — watchdog·로그온 boot는 detached-clean `b34623f3…` stable
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue