14. JAM Full E2E 평가 리포트
0. v0.40.0 / Metrics v14 재검증 (2026-07-15)
아래 §1~§7은 v0.10.6 당시의 역사적 기준선이다. 2026-07-15에는 같은 fixed-SHA 16개 코퍼스를 test-cli v0.3.0·security-cli v0.3.0·spec-cli v0.2.0과 v0.40.0 릴리즈 후보로 다시 측정했다. 원본을 수정하지 않은 .git 제외 복사본에서 full 점수를 비교하고, 별도로 .git을 보존한 복사본에서 security-only 성능을 검증했다. companion의 공개 릴리즈 바이너리로 최종 smoke를 반복해 동일 계약을 확인했다.
이번 재검증에서 반영한 수정:
- test-cli가 Rust nextest JUnit 경로를 프로젝트 설정이 아니라 격리된
--tool-config-file로 주입한다. - test-cli의 Python 자동 감지는 일반
scripts/*.py가 아니라 pytest 관례(test_*.py,*_test.py,tests/*.py)만 사용한다. - security-cli Gitleaks 기본은 working tree이며
--gitleaks-history만 전체 이력을 검사한다. 리포트의scanners[].mode가 실제 범위를 기록한다. - JAM은 jam-lite 무결성과 모든 요청 컴포넌트의 측정 가능성을 합쳐
decision=pass|fail|incomplete를 만들고, incomplete 숫자는score.status=provisional,score.usable=false로 표시한다. - 실제 생성된 companion 원본만
artifacts에 기록한다.
실행 예:
SECURITY_CLI_HOME=/tmp/security-cli-home \
jam full "$project" \
--out "/tmp/jam-v14-improved-results/$(basename "$project")" \
--json --sarif --quiet \
--test-cli /tmp/test-cli-improved \
--security-cli /tmp/security-cli-improved \
--full-timeout 600s
0.1 결과와 이전 v0.40 pre-fix 비교
| 샘플 | 이전 Full | 공개 릴리스 Full | score 상태 | decision | Test | Security |
|---|---|---|---|---|---|---|
c-how2heap-stress | 51/D | 51/D | final | fail | failed/no evidence | failed |
c-linenoise-best | 61/D | 62/D | final | fail | failed/no evidence | ok |
cpp-fmt-best | 91/A | 69/C | final | fail | failed/no evidence | ok |
cpp-fuzzer-test-suite-stress | 50/D | 47/F | provisional | incomplete | failed/no evidence | failed |
cs-dvcsharp-stress | 49/F | 44/F | provisional | incomplete | error | failed |
cs-stateless-best | 85/B | 87/B | provisional | incomplete | error | ok |
go-cleanhttp-best | 91/A | 92/A | final | pass | ok | ok |
govwa-stress | 48/F | 48/F | provisional | incomplete | error | failed |
java-dvja-stress | 51/D | 51/D | provisional | incomplete | error | failed |
java-gs-rest-best | 67/C | 67/C | provisional | incomplete | failed/no evidence | error (Maven 429) |
js-nodegoat-stress | 50/D | 50/D | provisional | incomplete | error | failed |
py-itsdangerous-best | 49/F | 49/F | final | fail | failed | failed |
py-pygoat-stress | 46/F | 46/F | provisional | incomplete | error | failed |
rust-hyperfine-best | 83/B | 85/B | final | pass | 102/102, quality 87/B | ok |
rust-rustlings-stress | 86/B | 81/B | final | pass | 13/13, quality 62/D | ok |
ts-is-best | 89/B | 91/A | provisional | incomplete | error | ok |
최종 열은 GitHub에 공개된 세 companion의 Linux amd64 자산을 내려받아 SHA256SUMS를 검증한 뒤 측정했다. test-cli v0.3.0(f4e0cf1), security-cli v0.3.0(3c70df3), spec-cli v0.2.0(726ca1a)을 사용했고, security-cli v0.3.0 자체로 격리 설치한 Trivy 0.72.0·Gitleaks 8.30.1·OSV-Scanner 2.4.0과 fresh DB를 사용했다. 같은 공개 바이너리를 522시간 stale DB 환경에서 실행하면 SEC-TOOLCHAIN=40/failed로 내려가 go-cleanhttp-best가 84/B가 됐다. 이는 환경 차이를 조용히 숨기는 점수 변동이 아니라 freshness gate가 의도대로 작동한 결과다.
spec 공개 바이너리는 REQ-CALC-001을 참조하는 통과 Go 테스트 fixture로 별도 통합 smoke를 수행했다. JAM은 spec-report.json·spec-trace.json·spec-summary.json 세 파일을 모두 보존하고 Functional (external) 100, SPEC-TRACE-GATE 100/ok, 최종 100/A·decision=pass로 판정했다.
계약·완전성 변화:
| 항목 | pre-fix | 수정 후 |
|---|---|---|
| exit 0 / 1 / 4 | 1 / 4 / 11 | 3 / 4 / 9 |
실제 test/report.json | 2/16 | 4/16 |
| 실제 security report | 16/16 | 16/16 |
decision-grade (usable=true) | 구분 불가 | 7/16 |
| Gitleaks 범위 증적 | 없음 | worktree 16/16 |
| 전체 순차 실행시간 | 약 112초 | 85초 |
이전 schema v1은 미측정 test를 합성에서 제외한 A/B 숫자도 최종 점수처럼 보였다. 예를 들어 cpp-fmt-best의 91/A와 ts-is-best의 89/B는 test evidence가 없었다. schema v2는 전자는 명시적인 no-test 품질 실패 68/C로, 후자는 89/B 잠정값(usable=false)으로 구분한다. Rust 두 샘플은 nextest JUnit 복구로 실제 테스트 수와 canonical quality가 합성되어 모두 최종 pass가 됐다.
진단 숫자만 놓고 보면 8개 언어군 모두 best 샘플이 대응 stress 샘플보다 높았다(8/8). 평균은 best 75.3, stress 52.3으로 23.0점 차이다. 다만 incomplete 9개는 포트폴리오 순위에 사용할 수 없으므로 이 값은 방향성 점검일 뿐 독립 타당화 결과가 아니다.
0.2 Git 이력 성능과 범위
.git을 보존한 16개 복사본에서 security-only를 실행했을 때 Gitleaks working-tree 실행은 16/16 성공했고 합계 5.712초(최대 0.673초)였다. pre-fix의 암묵적 history 실행은 결과가 남은 13개만 합계 2,547.343초(최대 362.515초)였다. 즉 기본 adapter 시간은 비교 가능한 결과 기준 400배 이상 줄었고 저장소 이력 크기에 좌우되던 fleet tail을 제거했다.
--security-history smoke test는 scanners[].mode=history를 기록하며 정상 pass했다. 따라서 일상 포트폴리오 측정과 전체 이력 secret 감사를 의도적으로 분리할 수 있다.
0.3 남은 해석 한계
py-itsdangerous-best는 project dependencyfreezegun이 설치되지 않아 collection error 2건이 발생했다. 자동 dependency bootstrap은 대상 실행·네트워크 변경을 수반하므로 JAM이 암묵적으로 수행하지 않는다. 재현 가능한 프로젝트 CI 환경에서 다시 측정해야 intrinsic test 품질로 해석할 수 있다.- 같은 샘플의
uv.lock에는 runtime dependency가 없고 개발/docs/test dependency만 있지만 현재 SCA producer는 lockfile 전체를 동일 정책 범위로 보고한다. 따라서 이 샘플의 security 0점은 배포 runtime 위험이 아니라 build/development supply-chain 위험까지 합친 값이다. dependency scope/VEX 계약이 생기기 전에는 둘을 분리해 주장하지 않는다. - Java
gs-rest의 Trivy Maven Central 429, C# SDK/solution 선택, Java multi-project 선택, C/C++ build/test artifact 부재는 계속 incomplete 또는 no-test evidence로 드러난다. provisional 점수로 숨기지 않고 환경 준비 또는 명시적 companion 설정이 필요하다.
평가일: 2026-06-11 · 도구:
jam v0.10.6· Metrics Spec:v2.3.4· 지표 세트:jam-full
이 문서는 13-e2e-sample-evaluation.md의 16개 샘플 코퍼스에 test-cli와 security-cli를 붙여 실행한 full E2E 결과다. 원본 샘플 저장소는 수정하지 않고 /tmp/jam-e2e-samples-v106-toolchain 복사본에서 테스트/빌드 산출물을 만들었다.
1. 실행 환경
설치·준비한 toolchain:
- OS 패키지:
python3-pytest,python3-pytest-cov,python3-pip,default-jdk,openjdk-17-jdk,maven,dotnet-sdk-8.0,cargo,rustc,pkg-config,libssl-dev - Python: user site
pytest 9.0.3,pytest-cov 7.1.0 - Node: nvm Node
v22.22.3, npm10.9.8, globaljest-junit 17.0.0 - Rust: rustup stable
rustc 1.96.0,cargo-llvm-cov 0.8.7,cargo-nextest 0.9.137,clippy - Security:
/tmp/security-cli-tools-jam-full에trivy 0.71.0,gitleaks 8.30.1,osv-scanner 2.3.8
실행:
go build -o /tmp/jam-toolchain-v106 ./cmd/jam
go build -o /tmp/test-cli-jam-full /home/vtopia/git/test-cli/cmd/test-cli
go build -o /tmp/security-cli-jam-full /home/vtopia/git/security-cli/cmd/security-cli
SECURITY_CLI_HOME=/tmp/security-cli-tools-jam-full \
/tmp/security-cli-jam-full tools update-db
rsync -a --delete --exclude='.git' --exclude='node_modules' \
--exclude='target' --exclude='.pytest_cache' --exclude='coverage' \
/home/vtopia/git/samples/ /tmp/jam-e2e-samples-v106-toolchain/
for project in /tmp/jam-e2e-samples-v106-toolchain/*; do
PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH" \
NODE_PATH="$(npm root -g)" \
SECURITY_CLI_HOME=/tmp/security-cli-tools-jam-full \
/tmp/jam-toolchain-v106 full "$project" \
--quiet \
--json \
--sarif \
--test-cli /tmp/test-cli-jam-full \
--security-cli /tmp/security-cli-jam-full \
--full-timeout 600s \
--out "/tmp/jam-full-samples-v106-toolchain/$(basename "$project")"
done
2. 종합 결과
| 샘플 | exit | Full | Lite | Test | Test status | Security | Security status |
|---|---|---|---|---|---|---|---|
go-cleanhttp-best | 0 | 83/B | 100 | 62 | ok | 70 | ok |
govwa-stress | 1 | 49/F | 97 | 0 | ok | 0 | failed |
ts-is-best | 1 | 75/C | 99 | 0 | failed | 100 | ok |
js-nodegoat-stress | 1 | 48/F | 96 | 0 | failed | 0 | failed |
py-itsdangerous-best | 1 | 50/D | 99 | 0 | failed | 0 | failed |
py-pygoat-stress | 1 | 49/F | 98 | 0 | ok | 0 | failed |
java-gs-rest-best | 4 | 50/D | 100 | 0 | failed | 0 | error |
java-dvja-stress | 4 | 50/D | 100 | 0 | failed | 0 | error |
cs-stateless-best | 1 | 74/C | 97 | 0 | failed | 100 | ok |
cs-dvcsharp-stress | 1 | 58/D | 98 | 0 | failed | 37 | failed |
rust-hyperfine-best | 0 | 81/B | 96 | 60 | ok | 70 | ok |
rust-rustlings-stress | 0 | 77/C | 94 | 20 | ok | 100 | ok |
c-linenoise-best | 1 | 71/C | 92 | 0 | failed | 100 | ok |
c-how2heap-stress | 1 | 60/D | 97 | 0 | failed | 45 | failed |
cpp-fmt-best | 1 | 74/C | 98 | 0 | failed | 100 | ok |
cpp-fuzzer-test-suite-stress | 1 | 67/C | 98 | 0 | failed | 70 | failed |
해석:
- exit
0: full 측정 성공 및 품질 게이트 통과. - exit
1: 측정은 됐고 test/security 품질 게이트가 실패. - exit
4: 외부 컴포넌트 측정 불가. 이번 라운드에서는 Java 두 샘플의 Maven Central 429만 해당한다.
산출물 생성률:
jam-full.json: 16/16test/report.json: 6/16security/security-report.json: 14/16- exit 분포:
03개,111개,42개
3. Security 결과
| 샘플 | Findings | Policy failures | Critical | High | Medium | Low | Category |
|---|---|---|---|---|---|---|---|
go-cleanhttp-best | 20 | 0 | 0 | 0 | 20 | 0 | sca:20 |
govwa-stress | 126 | 5 | 0 | 5 | 120 | 1 | iac:2, sca:124 |
ts-is-best | 0 | 0 | 0 | 0 | 0 | 0 | - |
js-nodegoat-stress | 262 | 155 | 27 | 128 | 93 | 14 | iac:1, sca:260, secret:1 |
py-itsdangerous-best | 22 | 5 | 0 | 5 | 15 | 2 | sca:22 |
py-pygoat-stress | 195 | 97 | 21 | 76 | 84 | 14 | iac:8, sca:185, secret:2 |
java-gs-rest-best | - | - | - | - | - | - | Maven Central 429 |
java-dvja-stress | - | - | - | - | - | - | Maven Central 429 |
cs-stateless-best | 0 | 0 | 0 | 0 | 0 | 0 | - |
cs-dvcsharp-stress | 12 | 1 | 0 | 1 | 10 | 1 | iac:3, secret:9 |
rust-hyperfine-best | 6 | 0 | 0 | 0 | 6 | 0 | sca:6 |
rust-rustlings-stress | 0 | 0 | 0 | 0 | 0 | 0 | - |
c-linenoise-best | 0 | 0 | 0 | 0 | 0 | 0 | - |
c-how2heap-stress | 7 | 4 | 0 | 4 | 1 | 2 | iac:7 |
cpp-fmt-best | 0 | 0 | 0 | 0 | 0 | 0 | - |
cpp-fuzzer-test-suite-stress | 1 | 1 | 0 | 1 | 0 | 0 | secret:1 |
검증된 점:
govwa-stress,cs-dvcsharp-stress,c-how2heap-stress,cpp-fuzzer-test-suite-stress는 jam-lite에서 A였지만, jam-full security에서 D/F권으로 내려갔다.js-nodegoat-stress,py-pygoat-stress는 jam-lite에서도 SEC critical cap으로 C였고, jam-full에서 SCA/secret finding이 대량 검출되어 F로 내려갔다.ts-is-best,cs-stateless-best,rust-rustlings-stress,c-linenoise-best,cpp-fmt-best는 security component가 clean이었다.go-cleanhttp-best와rust-hyperfine-best는 medium-only SCA가 있으나 policy failure는 0이다. v0.10.5에서 medium/low-only policy-pass 보안 리포트에 70점 하한을 적용해 "주의 필요"와 "게이트 실패"를 분리했다.
4. Test 결과
test-cli는 16개 중 6개에서 report를 생성했다.
| 샘플 | Test status | 요약 |
|---|---|---|
go-cleanhttp-best | ok | Go test 7개, 라인 커버리지 41.1% |
govwa-stress | ok | 테스트 0개, 라인 커버리지 0.0% |
py-pygoat-stress | ok | 테스트 0개, 라인 커버리지 0.0% |
rust-hyperfine-best | ok | 테스트 count는 0으로 집계, 라인 커버리지 92.7% |
rust-rustlings-stress | ok | 테스트 count는 0으로 집계, 라인 커버리지 31.4% |
py-itsdangerous-best | failed | 5개 collection error: project package/dev dependency 미설치 |
| C/C++ 샘플 | failed | test-cli 지원 언어 범위 밖이거나 테스트 artifact 없음 |
ts-is-best | failed | Jest가 현재 소스/transform 설정으로 실행 실패, 산출물 총량 0 |
js-nodegoat-stress | failed | Jest가 test 없는 config 파일을 suite로 수집, 산출물 총량 0 |
| Java 샘플 | failed | 루트가 multi-project 예제 구조라 기본 mvn test jacoco:report로 산출물 없음 |
| C# 샘플 | failed | cs-stateless-best는 루트 solution의 net10 예제가 .NET 8 SDK와 불일치, cs-dvcsharp-stress는 test artifact 없음 |
v0.10.6에서 test-cli가 정상 실행 후 "no test results or coverage were produced"를 반환하는 경우를 infrastructure error가 아니라 test component quality failure로 분류한다. 따라서 C/C++처럼 테스트 증거가 없는 프로젝트도 full 리포트는 완성되고 exit 1로 게이트된다. timeout, 실행 파일 없음, JSON 파싱 실패, security report 미생성은 계속 exit 4 인프라 실패다.
5. Java 보안 오류
Java 두 샘플은 security report를 생성하지 못했다. 원인은 Trivy가 Maven dependency metadata를 가져오는 중 Maven Central 429를 받은 것이다.
대표 오류:
remote Maven repository returned 429 Too Many Requests for https://repo.maven.apache.org/...
The repository blocks all subsequent requests from this IP until the block clears.
To avoid this, populate the local Maven cache before scanning.
이는 JAM 문제가 아니라 외부 SCA 스캐너의 네트워크/레포지토리 제한이다. CI에서는 ~/.m2 캐시를 준비하거나, 보안 스캔을 lockfile/offline DB 중심으로 제한해야 안정적이다.
6. 개선 반영
이번 full E2E로 다음을 반영했다.
jam v0.10.5: full security score 보정.- policy failure가 없고 critical/high도 없는 medium/low-only 보안 리포트는 security component 최소 70점.
- 효과:
go-cleanhttp-best가66/C에서83/B로 회복됐다. jam v0.10.6: test evidence 미생성 분류 보정.test-cli가 "no test results or coverage were produced"를 반환하면 statusfailed, score0, exit1품질 실패로 처리한다.- 효과: C/C++/C#/TS/JS 등 test evidence 부재가 exit
4인프라 실패로 security 결과를 덮지 않는다. jam v0.10.7: 부분 full 실행과 실패 reason 구조화.--component security,--component test,--skip-test,--skip-security지원.- 선택 컴포넌트만 합성할 때 effective weight를 재정규화하고,
reason으로no_test_evidence,remote_rate_limit같은 원인을 기계 판독 가능하게 기록한다. - Toolchain 보강:
- Rust best/stress는
cargo-llvm-cov,cargo-nextest,clippy설치 후 test component가 측정 가능해졌다. - Python best는 최신
pytest설치 후 report는 생성하지만, project dependency 미설치로 collection error가 품질 실패로 남는다.
7. 다음 작업
- Java SCA 안정화: Maven cache 사전 준비 또는
security-cli에 offline/lockfile-only profile 추가. - test-cli 개선: project dependency bootstrap, empty JUnit
<testsuites>처리, Rust nextest JUnit export, C/C++ artifact ingest 경로 보강. - C# full 측정: .NET 10 SDK 설치 경로 또는 solution/test-project 선택 옵션을
test-cliconfig로 제공. - full E2E CI: security-only job과 test-enabled job을 분리하고, toolchain image를 캐시한다.