excavate은(는) 무엇을 하나요?
레거시 리포지토리가 첫 번째 기능을 맹목적으로 실행하기 전에 스스로에 대해 이미 알고 있는 정보를 복구합니다. 출력은 오프-트리 두서 파일(핫스팟, 결합도, 복구된 결정사항, 누구에게 물어볼지 지도, 그리고 히스토리가 알려줄 수 있었던 것과 알려줄 수 없었던 것을 명시하는 Coverage 블록)과 docs/solutions/ 항목의 옵트인 PR(/compound의 형식 사용)입니다. 이는 /research, /write-a-prd, /execute가 이미 실행하는 키워드 grep을 통해 읽을 수 있도록 합니다.
두 가지 원칙이 이 프로세스의 정직함을 유지합니다. Tornhill의 방법은 버전 관리를 행동적 기록으로 분석합니다. 핫스팟은 변경 빈도와 복잡성의 교집합이며, 어느 한 축만으로는 정의되지 않고 고정된 기간으로 제한되며, 검사해야 할 확률이지 행동해야 할 판결이 아닙니다. 그리고 Grounding Rule(references/visual-rendering-core.md § 1)은 모든 아티팩트를 분할합니다. SHA, PR 번호, 경로, 개수, 인용된 근거는 git 및 gh 출력에서 복사되며 절대 다시 입력되지 않습니다. 모델은 그 주변의 산문만 작성합니다. '왜'가 모델의 추측인 결정사항은 복구된 것이 아니라 발명된 것이며, 리포지토리에서 제외됩니다.
호출 위치
사이드 루트, 사용자 호출 전용입니다. 사람이 /excavate를 입력할 때만 실행됩니다. disable-model-invocation: true는 다른 모든 세션의 컨텍스트에서 이 스킬의 설명을 유지하지 않도록 합니다. 이는 리포지토리당 한 번, 온보딩 시에 입력되며 자체 트리거될 이유가 전혀 없기 때문입니다. 계획 모드, HITL(인간-루프 내)입니다. AFK(자리 비움) 경로는 없습니다. 커밋에 바인딩된 모든 항목은 사람이 그 기반이 되는 인용문을 읽어야 하기 때문입니다.
새 워크스페이스의 첫 번째 메시지가 아닌 자체 메시지로 보내십시오. 호스트가 프리앰블을 추가하는 첫 번째 메시지에 입력된 숨겨진 스킬은 산문으로 읽혀 로드되지 않습니다. /lfg의 호출 위치는 이 현장 사고를 기록합니다(chrislacey89/skills#383). 두 번째 메시지로 다시 입력하십시오.
/help 2단계는 이 스킬로 시작하는 신호를 명시합니다. 파이프라인이 이미 복합화하는 리포지토리(거기서 /compound 실행), 개인의 성과 평가(§ 5 참조), 또는 결함 찾기(/qa 및 /triage-issue가 담당)에 사용하지 마십시오.
주 파이프라인과 나란히 서서 /shape에서 재연결됩니다. 첫 번째 기능은 두서 파일과 항목을 손에 쥐고 형성됩니다. 최상위 핫스팟이 작업 자체인 경우 /request-refactor-plan으로, 또는 히스토리가 종료되지 않은 수정 레이블 클러스터를 보여주는 경우 /qa로 핸드오프할 수 있습니다.
메서드 규칙
다음은 아래 프로세스가 운영하는 규칙입니다. 각 규칙은 출처를 가리키며, 이 파일의 다른 곳에서는 다시 설명되지 않습니다.
- 수정 횟수로 순위를 매기고, 코드 줄 수로 적격성을 판단하며, 절대 한 축만으로 판단하지 않습니다.
크기가 없는 빈도는 설정 파일을 플래그 지정하고, 빈도가 없는 크기는 수년 전에 디버깅되어 정확해진 이후로 전혀 수정되지 않은 코드를 플래그 지정합니다
(Tornhill, 1-4장).
wc -l을 통한 LOC가 복잡성 프록시입니다.cloc는 가정되지 않으며, LOC는 이 책이 기본값으로 사용하는 프록시입니다. - 고정된 기간으로 제한됩니다. 원시 수정 횟수는 기준 기간 없이 무의미합니다. 12개월이 기본값이며, 속도가 빠른 리포지토리의 경우 줄이십시오. 실제 기간을 출력하십시오.
- 멱법칙을 예상하고 무릎(knee)에서 자릅니다. 소수의 파일이 대부분의 비용을 부담합니다. 곡선이 평평해지는 지점에서 멈추고, 절대 반올림된 숫자에서 멈추지 않습니다.
- 추세에는 복잡성 과 크기가 필요합니다. 악화는 들여쓰기 복잡성이 LOC보다 빠르게 상승함을 의미합니다. 리팩터링됨은 둘 다 하락함을 의미합니다. 안정은 둘 다 평평함을 의미합니다(Tornhill, 5-6장). LOC만으로는 이러한 레이블을 생성할 수 없습니다.
- 결합도 보고 전에 비도메인 파일을 필터링하고, 모든 결합도를 보고하기 전에 분류합니다. 잠금 파일, 매니페스트, 생성된 코드 및 벤더링된 코드는 거짓 양성입니다. 그 대상과 결합된 테스트는 의도적이며 제거됩니다. 복사-붙여넣기, 비지원 경계, 생산자-소비자 결합도는 유지되고 레이블이 지정됩니다(Tornhill, 8-9장). 스쿼시된 히스토리는 커밋 수준의 결합도를 파괴합니다. PR 수준의 변경 집합을 사용하거나 섹션을 사용할 수 없다고 명시하십시오.
- 핫스팟은 확률입니다. 메커니즘을 명명하기 전에 파일을 열고, 열지 않은 파일에 대한 항목을 제공하지 마십시오.
- 소유권 데이터는 네 가지 합법적인 용도가 있습니다. 리뷰어 찾기, 온보딩, 테스트 위치 결정, 구조가 팀에 적합한지 판단하는 것입니다. 사람을 순위 매기거나 평가하는 데 절대 사용되지 않으며, 봇과 초기 임포트를 제외한
.mailmap을 통해 읽혀집니다(Tornhill, 12-14장). - 기존 문서는 열망적입니다. ADR, README 또는 CHANGELOG 주장은 현재 트리에 대해 확인된 후에야 유효한 결정사항으로 기록됩니다(Spinellis, Code Reading).
- 핫스팟에 대한 첫 번째 조치는 리팩터링이 아니라, 핀치 포인트에서의 특성화 테스트입니다(Feathers, Working Effectively with Legacy Code). 두서 파일의 "제안된 첫 번째 조치" 섹션은 핫스팟별로 이를 명시합니다.
- 핫스팟은 극단적이기 때문에 선택되므로, 순위의 일부는 노이즈입니다. 평균으로의 회귀는 귀무가설입니다. 한 기간에서 최상위였던 파일이 아무 작업 없이 다음 기간에서는 평균일 수 있으며, 리팩터링 후 냉각된 파일은 어쨌든 냉각되었을 것입니다(Galton; Kahneman). 기간의 양쪽 절반에서 지속성을 기준으로 순위를 매기고, 각 핫스팟에
persistent또는single-window태그를 지정하고, 지속적인 것만 첫 번째 조치 목록으로 라우팅하십시오. 순위는 진단 도구이지 목표가 아닙니다. 팀이 이를 목표로 삼으면 게이밍이 발생하고 품질이 저하됩니다(Goodhart). - 결정사항 옆에 가정을 기록하십시오. 근거는 가장 빨리 잊혀지며, 기록되지 않은 가정은 아무도 감히 바꾸지 못하는 숫자로 굳어집니다(Leveson, Engineering a Safer World, 10장). 각 복구된 결정사항은 인용문이 명시하는 제약 조건("Y 때문에 X를 선택했고, Z를 가정함")을 가지며, Z가 여전히 트리에 있는지 여부가 반전 탐지기가 됩니다.
- 결정사항 항목은 ADR의 형태와 컴플라이언스 라인을 가집니다. 제목, 상태, 컨텍스트, 이유를 포함한 결정사항, 결과, 그리고 이를 확인하는 방법이 필요합니다. 마지막 항목이 없으면 기록은 거버넌스가 아닌 고고학이 됩니다(Richards & Ford, Fundamentals of Software Architecture, 19장). 여기서의 검증은 현재 트리에 대해 실행된 grep 또는 경로 검사입니다.
- 두서 파일은 전문성의 저주 아래 있는 신규 참여자가 읽습니다. 짧은 용어집과 무릎 핫스팟으로 시작하고, 전체 테이블은 그 뒤에 배치하며, 첫 번째 환영 작업은 핫스팟의 복구된 결정사항을 이해하고 요약하는 것이지 수정하는 것이 아닙니다(Hermans, The Programmer's Brain).
프로세스
아래의 모든 펜스 블록은 작성된 대로 실행됩니다. 각 Bash 도구 호출은 새로운 셸이므로, 한 호출에서 다음 호출로 변수가 유지되지 않습니다. Step 0 변수 블록은 한 번 실행됩니다. 그 후의 블록은 모든 확인된 값을 $WORK/vars.sh에 씁니다. 이후의 모든 셸(사용자의 셸 또는 마이너의 셸)은 Step 0을 다시 실행하는 대신 . "$WORK/vars.sh"로 시작합니다. Step 0을 다시 실행하면 SINCE가 기본값으로 재설정되고 --since 또는 NONDOMAIN 확장이 무시되기 때문입니다. 파일을 건너뛰는 셸은 오류 없이 실패합니다. git log --since=""는 커밋을 반환하지 않고 grep -Ev ""는 아무것도 출력하지 않으며, 빈 출력은 "리버트 없음"이라는 유효한 답변으로 읽힙니다. WORK는 리포지토리에서 파생된 경로이므로, 새 셸은 Step 0의 WORK= 표현식으로 이를 다시 파생시키고 그 아래 파일을 소싱합니다. 마이너는 변수를 상속받지 않고 브리핑에서 vars.sh의 절대 경로를 전달받습니다. <path>는 유일한 플레이스홀더이며, 핫스팟별로 대체됩니다.
플래그 의미는 도구 자체의 문서입니다
(git-log,
git-shortlog,
git-rev-parse,
gh pr list). 이 파일은 이를 요약하지 않습니다. scripts/test-documented-git-commands.sh는 각 git 블록을 심어진 fixture에 대해 실행하므로, 유효한 호출이 아닌 블록은 CI에서 실패합니다.
0. 사전 점검
인수를 구문 분석합니다. --since <날짜>(기본값 12 months ago; git log --since가 허용하는 모든 형식) 및 --paths <pathspec>...(아래 모든 블록의 .를 대체). 그런 다음 공유 변수를 설정합니다.
SINCE='12 months ago'
WORK="${TMPDIR:-/tmp}/excavate-$(git rev-parse --show-toplevel | cksum | cut -d' ' -f1)"
mkdir -p -m 700 "$WORK"
START=$(git log --since="$SINCE" --no-merges --format=%cs HEAD </dev/null | tail -1)
NONDOMAIN=(':(exclude)*.lock' ':(exclude)*package-lock.json' ':(exclude)*pnpm-lock.yaml' ':(exclude)*yarn.lock' ':(exclude)*.min.*' ':(exclude)*.snap' ':(exclude)*.generated.*' ':(exclude)CHANGELOG.md' ':(exclude)*/vendor/*' ':(exclude)*/node_modules/*')
BOTS='[[]bot[]]|dependabot|renovate'
MAXFILES=30
export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.quotePath GIT_CONFIG_VALUE_0=false
GIT_CONFIG_* 내보내기는 이 실행의 모든 git 호출에 대해 core.quotePath=false를 설정합니다(git-config, Environment 참조). 따라서 비ASCII 바이트가 있는 경로는 wc 및 아래의 조인이 열거나 일치시킬 수 없는 인용된 이스케이프가 아닌 자체로 출력됩니다.
START는 기간 내 첫 번째 커밋 날짜이며, date에서 파생되지 않고 git에서 파생됩니다. date -d는 GNU 전용이고 date -v는 BSD 전용이기 때문입니다. NONDOMAIN은 시작 목록입니다. Step 1 전에 이 리포지토리의 생성된, 벤더링된, 스냅샷된 및 미러링된 경로로 확장하십시오(의심스러운 항목에 대해 git check-attr linguist-generated linguist-vendored 확인). MAXFILES는 변경 집합 크기 상한선으로, 이를 초과하는 커밋은 벌크 스윕(리포맷, 임포트, 대규모 이름 변경)으로 처리되어 빈도 및 결합도 계산에서 제외됩니다. Step 2의 공유 로그는 로그가 빌드될 때 한 번 적용합니다. 이후의 블록은 이를 반복하지 않습니다.
WORK는 HEAD가 아닌 리포지토리의 최상위 경로에 키가 지정됩니다. Step 8이 HEAD를 docs 브랜치로 이동하기 때문입니다. 이는 셸 간에 유효하며 Step 9에서 제거됩니다. 중단된 실행의 디렉토리가 이미 있는 경우, Step 2 블록 전에 삭제하여 오래된 파일이 이 실행의 출력으로 전달되지 않도록 합니다.
NONDOMAIN이 확장된 후(그리고 Step 1 게이트 이후 사용자가 기간을 변경하는 경우 다시) 확인된 값을 유지합니다. 파일을 소싱하고, SINCE를 설정하고, Step 0의 START= 라인을 다시 실행한 다음, 이 블록을 다시 실행합니다.
{
declare -p SINCE START NONDOMAIN BOTS MAXFILES WORK
declare -px GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0
} > "$WORK/vars.sh"
그런 다음 . "$WORK/vars.sh"로 시작하는 셸에서 커버리지를 측정합니다.
git rev-parse --is-shallow-repository
git rev-list --count --since="$SINCE" --no-merges HEAD
git log --max-parents=0 --format=%cs HEAD </dev/null | sort | head -1
git log --since="$SINCE" --no-merges --format=%s HEAD </dev/null | awk '{ t++ } /\(#[0-9]+\)$/ { s++ } END { printf "%d of %d subjects end in (#N)\n", s, t }'
네 줄은 다음과 같습니다. 얕은 리포지토리 여부, 기간 내 커밋 수, 루트 커밋 날짜(얕은 경우 얕은 경계이므로 "unknown" 출력), 스쿼시 비율입니다. 높은 스쿼시 비율은 각 커밋이 전체 PR임을 의미하므로 커밋 수준 결합도는 이미 PR 수준 결합도입니다. 낮은 비율과 다른 곳에서 스쿼시 병합된 PR이 있는 경우 주의해야 할 사례입니다.
그런 다음 이미 기간 내에 있는 변경 사항을 적용하는 커밋을 계산합니다. 릴리스 브랜치를 통해 제공되는 리포지토리는 동일한 변경 사항을 원본, 체리픽, 릴리스 또는 동기화 롤업으로 전달하므로, 아래의 모든 카운트는 각 복사본을 별도의 수정으로 봅니다.
git log --since="$SINCE" --no-merges --reverse -p --format='commit %H' HEAD -- . "${NONDOMAIN[@]}" </dev/null \
| git patch-id --stable | awk 'seen[$(1)]++ { print $(2) }' > "$WORK/dups.txt"
wc -l < "$WORK/dups.txt" | tr -d ' '
dups.txt는 이전 기간 내 커밋이 이미 가진 패치 ID(git-patch-id)를 가진 모든 커밋을 나열하므로, 가장 오래된 복사본이 목록에서 제외됩니다. Step 2의 공유 로그와 동일한 기간 및 경로 사양을 읽으므로, 순위가 읽는 커밋 중에서 복사본을 계산합니다. 변경 사항이 이미 메인 라인에 있는 병합된 브랜치는 둘 다에서 git log의 히스토리 단순화에 의해 건너뛰어집니다. 카운트는 Coverage 블록에 들어가고, Step 3은 이러한 커밋 없이 무릎을 다시 순위를 매깁니다. 이들은 카운트되며, 제외되지 않습니다. 재적용된 변경 사항은 여전히 누군가가 수행한 작업이며, 롤업은它所包含的 변경 사항의 유일한 기록이 될 수 있으며, 이후의 모든 블록은 자체 git log 또는 git rev-list를 통해 히스토리를 읽으므로 공유 로그에만 적용된 필터는 단계 간에 불일치를 남깁니다. 스코프 게이트에서 제목에 표시된 릴리스 또는 동기화 롤업을 명명하십시오. 이들도 제외되지 않습니다.
gh auth status와 리모트가 GitHub인지 확인합니다. gh: yes 또는 gh: no를 기록합니다. bash "$SC" path research로 두서 파일 디렉토리를 확인합니다. 여기서 SC는 이 스킬에 번들된 리졸버의 절대 설치 경로인 references/sidecar.sh입니다. 사이드카 경로를 직접 계산하지 마십시오. SIDECAR MISS에서 리졸버는 종료 코드 1로 종료되고 init 명령을 출력합니다. 이를 사용자에게 표시하고 사용자가 실행할 때까지 계속합니다. 사이드카 생성은 사람의 행위이기 때문입니다. 해당 디렉토리에서 이전 excavation-*.md와 기존 docs/solutions/을 찾습니다. 재실행은 항목을 복제하지 않고 업데이트하며, 이전 두서 파일의 persistent 태그를 새 기간에 대해 점수화하여(얼마나 많은 핫스팟이 유지되었는지) Coverage 블록이 자체 예측에 대한 적중률을 전달하도록 합니다.
리포지토리가 얕은 경우, 얼마나 많은 히스토리가 누락되었는지 말하고 git fetch --unshallow를 제안하십시오. 절대 실행하지 마십시오. .git을 다시 쓰기 때문입니다.
1. 범위 게이트 (HITL)
Coverage 블록을 두서 파일의 머리말로 정확히 출력합니다.
Window: <START> → today (<SINCE>)
Commits: <N> non-merge (<M> excluded as bulk > MAXFILES files)
Shallow: yes | no
Squash ratio: <s>/<t>
Duplicates: <d> commits repeat an earlier commit's patch ID (counted, not excluded)
gh: yes (<fetched> of <total> merged PRs; <closed> closed unmerged) | no
Fix labels: <k> of <N> commits match the fix pattern (a lower bound; filled in at Step 3)
Excluded: <NONDOMAIN list, one line>
Not visible: closed-unmerged PRs, force-pushed or squashed history, deleted files, departed contributors
Not visible 줄은 상용구가 아닙니다. 아래의 모든 것은 순위를 매깁니다...