chore(release): 7.0.10 출시일 정렬 + 공개 배포본 누출 정정

7.0.9 페이로드 대비 버전이 오른 패키지 31종과 코어의 CHANGELOG 최상단 절
날짜를 2026-09-04 로 맞췄다.

이어서 릴리즈 스크립트의 게이트를 브랜치 생성 없이 전수 실행한 결과, 파일
본문 누출 검사가 error 25건으로 릴리즈를 막고 있었다. 전부 이번 사이클에
들어온 것이다.

- 확장 문서·코어 주석 20건이 공개 배포본에 없는 를 가리켰다.
 공개되는 AGENTS.md 참조로 바꾸고, 참조된 9개 절의 실재를 전수 확인했다.
 유일하게 빠져 있던 「독립 레이아웃의 글로벌 호스트 컴포넌트」절은 원래
 동기화 대상이었으므로 AGENTS.md 에 반영했다.
- 시나리오 매니페스트의 내부 문서 포인터는 소비자가 하나도 없어 제거했다.
- 공개 테스트 3건이 내부 검사 스크립트를 열어 도구 간 리터럴을 대조하고
 있었다. tests/** 는 배포 페이로드에 포함되고 는 제외되므로
 배포본에서 원리상 성립할 수 없는 검사다. 대조를 그 도구 옆으로 옮기고
 공개 테스트에는 자기 동작 단언만 남겼다. 존재 가드로 무마하지 않은 것은
 그 경우 배포본에서 검사가 조용히 건너뛰어져 "검사하지 않음" 과 "통과" 가
 구분되지 않기 때문이다.

옮긴 대조는 돌지 않으면 잠그지 못하므로 대상 파일 편집 시 자동 실행되도록
등록했다. 누출 검사 자체도 릴리즈 시점에만 돌아 위반이 다음 릴리즈까지
드러나지 않았으므로, 룰을 복제하지 않고 실행 시점만 세션 종료로 앞당겼다.

같은 검증 중 드러난 선행 결함 하나(이커머스 문서의 집계 블록 드리프트)도
함께 정리했다.
This commit is contained in:
HeuJung
2026-09-04 12:18:22 +09:00
parent c305b56248
commit 482faf00be
55 changed files with 98 additions and 140 deletions
@@ -378,59 +378,44 @@ class ExtensionDocContractTest extends TestCase
}
/**
* 미채움 마커 목록이 PHP · 검사 스크립트 · audit 룰 세 곳에서 일치해야 합니다.
* 미채움 마커 목록이 5종으로 고정되고 서로 겹치지 않아야 합니다.
*
* 세 곳이 갈라지면 한쪽이 세지 않는 마커가 생기고, 그 자리는 영영 집계되지 않습니다.
* 이 목록은 자동 생성 블록이 채우지 못하는 사람 서술 자리를 가리킵니다. 항목이
* 늘거나 줄면 문서 완비 판정의 모집단이 조용히 바뀝니다.
*
* 내부 검사 도구(`check-extension-docs.cjs` · 미채움 audit 룰)가 같은 목록을
* 쓰는지는 그 도구들과 함께 있는 하네스 테스트가 대조합니다.
*/
public function test_todo_marker_list_is_consistent_across_tooling(): void
public function test_todo_marker_list_is_fixed_and_distinct(): void
{
$markers = ExtensionDocScaffolder::todoMarkers();
$this->assertCount(5, $markers, '미채움 마커는 5종으로 고정입니다.');
$script = (string) file_get_contents(base_path('.claude/scripts/check-extension-docs.cjs'));
$rule = (string) file_get_contents(base_path('.claude/scripts/audit/rules/extension-doc-unfilled-markers.cjs'));
$this->assertSame(
$markers,
array_values(array_unique($markers)),
'미채움 마커가 중복되면 그 자리는 두 번 세어집니다.',
);
foreach ($markers as $marker) {
$this->assertStringContainsString(
"'{$marker}'",
$script,
"check-extension-docs.cjs 에 마커 '{$marker}' 가 없습니다.",
);
$this->assertStringContainsString(
"'{$marker}'",
$rule,
"extension-doc-unfilled-markers.cjs 에 마커 '{$marker}' 가 없습니다.",
);
}
// 포함만 단언하면 방향이 하나뿐이라 JS 쪽 **초과** 항목(6번째 마커·오탈자 잔여)이
// 그대로 살아 있다. 두 도구가 세는 모집단이 갈라지면 잔량 집계가 서로 다른 답을
// 내는데, 그 차이는 어느 쪽 출력에도 드러나지 않는다.
foreach ([$script, $rule] as $i => $source) {
preg_match_all("/'(TODO: [^']+)'/u", $source, $m);
$found = array_values(array_unique($m[1]));
sort($found);
$expected = $markers;
sort($expected);
$this->assertSame(
$expected,
$found,
($i === 0 ? 'check-extension-docs.cjs' : 'extension-doc-unfilled-markers.cjs')
.' 의 마커 목록이 PHP SSoT 와 다릅니다 (초과 또는 누락).',
$this->assertStringStartsWith(
'TODO: ',
$marker,
"마커 '{$marker}' 가 공통 접두를 잃으면 잔량 검사가 그 자리를 놓칩니다.",
);
}
}
/**
* 빈 서술 축이 PHP 와 검사 스크립트 양쪽에 존재하고 같은 판정을 내려야 합니다.
* 빈 서술 축이 마커 잔량과 별개로 세어져야 합니다.
*
* 미채움은 두 축입니다 — `TODO:` 마커 잔량과, 마커를 지우고 서술을 쓰지 않은 빈
* `@intent` 블록. 후자가 한쪽에만 있으면 그 도구만 통과시키는데, 결과가 "다 채웠다"
* 와 구분되지 않아 비어 있는 문서가 완비로 집계됩니다.
* `@intent` 블록. 후자를 세지 않으면 스캐폴딩 직후 마커만 지운 문서가 골격·블록
* 검사를 전부 통과해, 비어 있는 문서가 완비로 집계됩니다.
*
* 내부 검사 도구가 같은 수를 세는지는 그 도구와 함께 있는 하네스 테스트가 대조합니다.
*/
public function test_empty_intent_axis_agrees_across_tooling(): void
public function test_empty_intent_blocks_are_counted(): void
{
$filled = '<!-- @intent START -->
서술이 있다.
@@ -445,19 +430,6 @@ class ExtensionDocContractTest extends TestCase
'.$empty));
$this->assertSame(1, ExtensionDocScaffolder::emptyIntentBlocks($filled.'
'.$empty));
$script = (string) file_get_contents(base_path('.claude/scripts/check-extension-docs.cjs'));
$this->assertStringContainsString(
'countEmptyIntentBlocks',
$script,
'check-extension-docs.cjs 에 빈 서술 축이 없습니다 — PHP 만 세면 하네스가 통과시킵니다.',
);
$this->assertStringContainsString(
"id: 'emptyIntent'",
$script,
'check-extension-docs.cjs 의 빈 서술 축 식별자가 없습니다.',
);
}
/**
@@ -614,50 +586,25 @@ class ExtensionDocContractTest extends TestCase
}
/**
* 프로세스 경계를 넘는 리터럴 2종이 PHP·JS 양쪽에서 일치하는지 단언합니다.
* 수집 실패 통지가 실제로 방출되는 블록마다 실려야 합니다.
*
* 코어 인덱스 스캐너는 `@generated:stats` 마커와 "확인하지 못했습니다" 문장을 **문자열로**
* 다시 적어 두고 파싱합니다. 생성기 쪽 문구가 바뀌면 스캐너가 조용히 실패해 인덱스가
* 다시 `훅 0 · 라우트 0` 을 사실로 싣습니다 — 미채움 마커 5종에는 이미 양방향 집합
* 단언이 있는데 같은 성질의 이 두 리터럴만 그 취급을 받지 못했습니다.
* 수집 실패는 수치가 아니라 문장으로 옵니다. 통지는 둘(표면 전체 실패 · 개별 getter
* 실패)이고, 어느 한쪽이 다른 어구로 시작하면 그 문서는 "발행 훅 N종 …" 같은 거짓
* 문장을 경고 없이 싣습니다.
*
* 그 어구와 집계 블록 마커를 내부 인덱스 스캐너가 그대로 알고 있는지는 그 스캐너와
* 함께 있는 하네스 테스트가 대조합니다.
*/
public function test_cross_process_literals_match_the_index_scanner(): void
public function test_surface_notice_reaches_every_measured_block(): void
{
$scanner = base_path('.claude/scripts/generate-docs-index.cjs');
if (! is_file($scanner)) {
$this->markTestSkipped('내부 스캐너가 없는 배포본입니다.');
}
$js = (string) file_get_contents($scanner);
// 생성기가 실제로 방출하는 마커에서 키 부분을 뽑아 스캐너 소스와 대조한다.
// (스캐너는 정규식으로 공백을 흡수하므로 마커 전문이 리터럴로 있지는 않다.)
// 집계 블록은 마커로 감싸여 방출된다 — 스캐너가 그 구간을 잘라 수치를 읽는다.
$wrapped = ExtensionDocScaffolder::wrap('stats', '**훅 수**: 1');
$this->assertStringContainsString('@generated:stats START', $wrapped);
$this->assertStringContainsString('@generated:stats END', $wrapped);
$this->assertStringContainsString(
'@generated:stats',
$js,
'인덱스 스캐너가 집계 블록 마커를 그대로 알고 있어야 합니다.',
);
// 수집 실패 문구 — 통지는 둘(표면 전체 실패 · 개별 getter 실패)이고 스캐너는 그
// 둘이 공유하는 어구를 찾는다. 소스에 리터럴이 있는지만 보면 "어딘가에 그 문자열이
// 있다" 만 확인할 뿐, **실제로 방출되는 문장**이 그것을 담는지는 보지 않는다 —
// 개별 getter 실패 통지가 다른 어구로 시작해 스캐너에 통째로 새던 것이 그 사각이다.
$marker = ExtensionDocScaffolder::SURFACE_NOTICE_MARKER;
$this->assertStringContainsString(
// 스캐너 정규식은 `**` 를 이스케이프하므로 그 형태로 대조한다.
str_replace('**', '\*\*', $marker),
$js,
'인덱스 스캐너가 표면 실패 판정 어구를 읽지 못하면 점검 불가가 0 으로 굳습니다.',
);
// 두 통지가 실제로 그 어구를 담고 방출되는지 렌더 결과로 단언한다.
$inventory = new ExtensionInventory;
$record = $inventory->find('module', 'sirsoft-board');
@@ -74,6 +74,3 @@ test_files:
- tests/Unit/Installer/RequirementsResponseUtf8Test.php
- tests/Unit/Installer/AddLogUtf8ScrubTest.php
- tests/Unit/Services/SettingsServiceCpuInfoTest.php
validation:
browser_matrix: .claude/docs/chrome-mcp-inspection-matrix.md