Reference

06. 출력 명세

report.md / detail.csv / JSON / SARIF / diff 출력 정의, 파일:라인 지목 규칙

Source: docs/06-output-spec.md

06. 출력 명세

jam-lite 스캔 1회 = 기본 출력 3개 (출력 디렉터리 기본값 ./, --out으로 변경):

추가로 --json 옵션 시 jam-result.json(report와 동일 정보의 구조화 버전, CI 연동용)을 생성하고, --sarif 옵션 시 jam.sarif(SARIF 2.1.0, Code Scanning 연동용)을 생성한다.

--set full 또는 jam full은 위 jam-lite 산출물에 더해 다음을 생성한다.

1. jam-detail.csv

1.1 원칙

1.2 컬럼 정의

컬럼타입설명
finding_idstring안정적 ID = metric_id + 파일 + 심볼/원본 메시지/소스 앵커 지문 8자리. 관계없는 라인 이동은 ID를 바꾸지 않음CPLX-01-a3f9c2d1
categoryenumSIZE/CPLX/ARCH/DUP/SEC/RES/HYGCPLX
metric_idstring메트릭 카탈로그의 IDCPLX-01
metric_namestring사람용 이름Cyclomatic Complexity
metric_versionint해당 메트릭의 산출식 버전 (11-metrics-versioning.md)1
severityenuminfo/warning/violation/criticalviolation
confidenceenumhigh/medium/lowhigh
filestring루트 기준 상대 경로, / 구분src/api/handler.ts
lineint1-based 시작 라인142
end_lineint끝 라인(단일 라인이면 동일)198
columnint1-based, 불명확하면 11
symbolstring함수/타입/모듈명 (해당 시)processOrder
valuefloat측정값23
thresholdfloat적용된 임계값15
messagestring설명 (한/영 설정 따름)함수 'processOrder'의 순환 복잡도가 23으로 임계값 15를 초과
relatedstring관련 위치 목록 path:line 세미콜론 구분 (클론 상대편, 사이클 경로)src/util/order.ts:31;src/api/handler.ts:142
debt_minutesfloat부여된 수정 비용(분)30
debt_countedfloat점수 범위·억제·confidence 보정 후 실제 산입된 비용30
rule_refstring근거 참조 (CWE, 문헌 키)CWE-89 / McCabe1976
suppressedbool점수에서 제외된 억제 finding 여부. 억제돼도 severitymessage는 원본을 보존true
suppression_sourceenumjam.yaml / inline; 비억제면 빈 값jam.yaml
suppression_reasonstring억제 사유테스트 fixture
suppression_ownerstring면제 책임자(선택)security-team
suppression_ticketstring추적 ticket/결정 ID(선택)SEC-42
suppression_expiresdate만료일 YYYY-MM-DD(선택). 만료된 규칙은 적용되지 않음2026-12-31
score_exclusionenum/string비억제 finding이 점수 밖인 이유. 빈 값이면 eligible; test/documentation/configuration/generated/unsupported_language/unselected_categorytest

1.3 예시

finding_id,category,metric_id,metric_name,metric_version,severity,confidence,file,line,end_line,column,symbol,value,threshold,message,related,debt_minutes,debt_counted,rule_ref,suppressed,suppression_source,suppression_reason,suppression_owner,suppression_ticket,suppression_expires,score_exclusion
CPLX-01-a3f9c2d1,CPLX,CPLX-01,Cyclomatic Complexity,1,violation,high,src/api/handler.ts,142,198,1,processOrder,23,15,"함수 'processOrder'의 순환 복잡도 23 (임계값 15)",,30,30,McCabe1976,false,,,,,,
SEC-01-f01c44aa,SEC,SEC-01,Hardcoded Secret,2,critical,high,config/db.go,17,17,22,,4.9,4.5,"고엔트로피 문자열이 'password' 변수에 할당됨",,120,0,CWE-798,true,jam.yaml,테스트 fixture,security-team,SEC-42,2026-12-31,
SEC-01-2d81c614,SEC,SEC-01,Hardcoded Secret,2,critical,high,tests/fixture.ts,9,9,1,,4.8,4.5,"테스트 fixture 시크릿",,120,0,CWE-798,false,,,,,,test

1.4 점수 역산용 메타데이터

CSV는 순수 finding 데이터만 유지한다. 실행 메타데이터와 비교 가능성은 jam-run.json, 카테고리별 합계(debt, dev cost, TDR, score)는 jam-report.md 부록 A에 기록한다.

1.5 jam-run.json 런 매니페스트

jam-run.json은 옵션이 아니며 모든 scan에서 생성된다. JSON Schema는 docs/schemas/jam-run.schema.json이다.

2. jam-report.md

2.1 구조

# JAM Lite Report — <프로젝트명>
> 스캔: 2026-06-10 14:02 · jam v0.46.0 · metrics v14.5.0 · 대상: /path/to/project
> 파일 1,243개 · 전체 98,402 CLOC · production 1,102개/92,000 CLOC · 언어: TypeScript 61%, Python 30%, 기타 9%

## JAM Lite Score: 62 / 100 (C)
[등급 캡 적용 시: "SEC critical 1건으로 등급 상한 C 적용" 명시]

| 카테고리 | 점수 | 가중치 | 핵심 문제 |
|---|---|---|---|
| Architecture | 55 | 0.22 | 순환 참조 3건 (최대 SCC 크기 4) |
| Complexity   | 70 | 0.20 | CCN>15 함수 12개 |
| ...

## Top Findings (수정 효과 큰 순)
1. **[ARCH-01]** `src/order/` ↔ `src/billing/` 순환 — service.py:3 → core.py:7 → models.py:12 → (back)
2. **[SEC-01]** 하드코딩 시크릿 — config/db.go:17
3. ...상위 10건

## 카테고리별 상세
### Architecture (55/100)
- 순환 의존 3건: (각 사이클 경로를 화살표 체인으로 표기, 파일:라인 링크)
- Fan-out 과다 모듈: ...
[카테고리마다: 무엇이/어디서/왜 문제인지 + 권장 조치 1줄]

## 파일 핫스팟 (debt 상위 10개 파일)
| 파일 | debt(분) | 주요 메트릭 |
|---|---|---|

## AX Readiness: N/4 (점수 미산입)
| 검사 | 항목 | 결과 | 상세 |
|---|---|---|---|
[AX-01 에이전트 지침 / AX-02 기계 검증 게이트 / AX-03 재현 가능 환경 / AX-04 검증 루프 — docs/18 축 C. 코드 품질 점수와 무관한 환경 준비도라 점수·등급·diff에 산입하지 않는다]

## 부록 A. 점수 계산 근거
| 카테고리 | coverage | eligible/production CLOC | findings | dev cost(분) | debt_counted(분) | TDR | score |
[가중 합산식 명시 → 최종 점수 재현 가능]

## 부록 B. 스캔 범위
제외된 파일/디렉터리와 사유(gitignore, 생성 파일, 비UTF-8 등), 파스 실패 파일 목록

## 부록 C. 메트릭 설명과 임계값
[적용된 임계값 표 + 문헌 참조 — 점수의 정당성을 리포트 자체가 설명]

2.2 작성 규칙

3. jam-result.json (옵션)

CI 연동용. 스키마(요약):

{
  "version": "3",
  "metricSet": "jam-lite",
  "tool": {"name": "jam", "version": "v0.46.0"},
  "metricsSpec": {"version": "14.5.0"},
  "scannedAt": "2026-06-10T14:02:11+09:00",
  "target": {"path": "...", "files": 1243, "cloc": 98402, "productionFiles": 1102, "productionCloc": 92000, "languages": {"typescript": 0.61}},
  "score": {"status": "assessed", "total": 62, "grade": "C", "capNote": "SEC critical finding 존재", "notAssessed": [], "devCostMinutes": 331200,
            "categories": {"ARCH": {"score": 55, "debtMinutes": 4320, "tdr": 0.013, "highRiskFiles": 4, "highRiskShare": 0.0036, "productionCloc": 92000, "eligibleCloc": 92000, "productionFiles": 1102, "eligibleFiles": 1102, "coverage": 1.0, "devCostMinutes": 331200}}},
  "findingCounts": {"critical": 1, "violation": 47, "warning": 102, "info": 215, "suppressed": 2, "excluded": 18},
  "policy": {"id": "engineering/portfolio", "version": "3", "hash": "<sha256>"},
  "configuration": {"hash": "<sha256>"},
  "scope": {"metricSet": "jam-lite", "categories": ["SIZE", "CPLX", "ARCH", "DUP", "SEC", "RES", "HYG"], "includeGenerated": false, "excludes": []},
  "integrity": {"status": "complete", "reasons": [], "skippedFiles": [], "parseFailures": [], "analyzerErrors": [], "configWarnings": [], "suppressionIssues": [], "notAssessed": []},
  "comparisonKey": "<sha256>",
  "ax": {"score": 3, "total": 4, "checks": [{"id": "AX-01", "name": "Agent Instructions", "name_ko": "에이전트 지침", "pass": true, "detail": "AGENTS.md — 검증 방법 포함."}]}
}

ax는 v0.46.0에 추가된 선택적 additive 필드다(docs/18 축 C의 Agent-Experience readiness). 점수·등급·comparisonKey·jam diff 어디에도 산입되지 않는 순수 보고 데이터이며, 구버전 소비자는 무시해도 안전하다.

finding 전체는 JSON에 중복 포함하지 않는다(CSV가 원본). findingCounts의 네 severity 건수는 score-eligible finding만 세며, suppressedexcluded는 이들과 겹치지 않는 별도 건수다. 따라서 여섯 값의 합이 CSV 데이터 행 수와 같다. production code가 없어 평가할 수 없으면 score.status="not_assessed", grade="N/A", categories={}다. JSON Schema는 docs/schemas/jam-result.schema.json이다.

3.5 jam-full.json

full 모드의 합성 결과는 jam-full.json에 기록한다. jam-lite의 finding 원본은 계속 jam-detail.csv이며, test/security/spec의 원본은 각 외부 CLI JSON이다. JSON Schema는 docs/schemas/jam-full.schema.json에 둔다.

{
  "version": "2",
  "metricSet": "jam-full",
  "tool": {"name": "jam", "version": "v0.46.0"},
  "score": {
    "total": 88,
    "grade": "B",
    "status": "final",
    "usable": true,
    "weights": {"jam-lite": 0.5, "test": 0.25, "security": 0.25}
  },
  "decision": {"status": "pass", "reasons": []},
  "components": [
    {"id": "jam-lite", "score": 82, "status": "ok", "reportPath": "jam-report.md"},
    {
      "id": "test",
      "score": 94,
      "status": "ok",
      "reportPath": "test/report.json",
      "metrics": [
        {"id": "TEST-PASS", "score": 100, "weight": 0.25, "status": "ok"},
        {"id": "TEST-LINE-COV", "score": 90, "weight": 0.20, "status": "ok"},
        {"id": "TEST-BRANCH-COV", "score": 0, "weight": 0.12, "status": "not_assessed"},
        {"id": "TEST-EVIDENCE", "score": 85, "weight": 0.10, "status": "ok"},
        {"id": "TEST-COV-TAIL", "score": 88, "weight": 0.08, "status": "ok"},
        {"id": "TEST-DENSITY", "score": 92, "weight": 0.12, "status": "ok"},
        {"id": "TEST-SOURCE-COV", "score": 100, "weight": 0.10, "status": "ok"},
        {"id": "TEST-SKIP-RISK", "score": 100, "weight": 0.02, "status": "ok"},
        {"id": "TEST-DURATION-TAIL", "score": 100, "weight": 0.01, "status": "ok"},
        {"id": "TEST-QUALITY", "score": 94, "weight": 0.00, "status": "ok"}
      ]
    },
    {
      "id": "security",
      "score": 95,
      "status": "ok",
      "reportPath": "security/security-report.json",
      "metrics": [
        {"id": "SEC-SEVERITY", "score": 100, "weight": 0.25, "status": "ok"},
        {"id": "SEC-POLICY", "score": 100, "weight": 0.18, "status": "ok"},
        {"id": "SEC-CATEGORY", "score": 100, "weight": 0.12, "status": "ok"},
        {"id": "SEC-FIXABILITY", "score": 100, "weight": 0.10, "status": "ok"},
        {"id": "SEC-SUPPRESSION", "score": 100, "weight": 0.05, "status": "ok"},
        {"id": "SEC-TOOLCHAIN", "score": 0, "weight": 0.12, "status": "not_assessed"},
        {"id": "SEC-CVSS", "score": 0, "weight": 0.10, "status": "not_assessed"},
        {"id": "SEC-CWE", "score": 0, "weight": 0.08, "status": "not_assessed"},
        {"id": "SEC-COVERAGE", "score": 100, "weight": 0.00, "status": "ok"}
      ]
    }
  ]
}

metrics는 jam-full이 외부 CLI JSON을 어떻게 점수화·검증했는지 보여주는 하위 지표 배열이다. status=not_assessed인 하위 지표는 해당 외부 도구가 값을 제공하지 않은 경우이며, 컴포넌트 내부 가중치 합산에서 제외한다. TEST-QUALITY, SEC-COVERAGE, SPEC-TRACE-GATEweight=0인 계약 신호다. 첫째는 test 점수 상한, 나머지는 measurement/trace gate에 쓰이며 기존 evidence를 양의 가중치로 이중 계상하지 않는다.

schema v2의 decisionpass|fail|incomplete 3상태다. 모든 요청 컴포넌트와 jam-lite 무결성이 완전한 경우에만 score.status=final, score.usable=true다. 측정은 완전하지만 품질/보안 게이트가 실패하면 decision=fail이며 점수는 여전히 비교 가능한 최종값이다. jam-lite 무결성 불완전, 외부 컴포넌트 error, 또는 요청 컴포넌트 not_assessed가 하나라도 있으면 decision=incomplete, score.status=provisional, score.usable=false이고 reasons가 원인을 기록한다. 이 잠정 숫자는 일부 성공한 측정의 진단에는 쓸 수 있지만 포트폴리오 순위·릴리스 승인에는 사용할 수 없다.

컴포넌트가 실패하면 reason이 추가될 수 있다.

{"id": "test", "score": 0, "status": "failed", "reason": "no_test_evidence"}

부분 full(--component security, --skip-test)에서는 선택된 컴포넌트만 componentsartifacts에 포함하고, score.weights는 effective weight를 기록한다.

실제로 생성된 companion 산출물만 artifacts에 기록된다. spec v0.2 컴포넌트를 선택하면 생성 여부에 따라 specReport, specTrace, specSummary가 기록된다. security v0.2 coverage가 불완전하면 component는 status=error, reason=coverage_incomplete이며 full 실행은 exit 4다.

4. jam.sarif (옵션)

--sarif 사용 시 SARIF 2.1.0 JSON을 jam.sarif로 생성한다. SARIF는 GitHub Code Scanning 같은 도구에 위치 정보를 전달하기 위한 투영이며, 점수 산출과 감사의 원본은 계속 jam-detail.csv다.

4.5 assurance 출력

jam assurance verdictsecurity-verdict.json/.md, jam assurance evidencecontrol-evidence.json/.md를 생성한다. 이 출력은 jam-lite/jam-full 점수와 독립이다.

5. jam diff 출력

jam diff OLD.csv NEW.csv는 두 jam-detail.csvfinding_id 기준으로 비교한다. 각 CSV와 같은 디렉터리의 jam-run.json을 먼저 읽어 두 측정이 모두 complete이고 comparisonKey가 같을 때만 기본 비교를 허용한다.

jam diff: added 2 · fixed 1 · unchanged 31 · old 32 · new 33 · debt +40 min

Added findings:
  - [SEC-01 critical] config/db.go:17 — 고엔트로피 문자열이 'password' 변수에 할당됨 (debt 120)

Fixed findings:
  - [CPLX-01 violation] api/handler.go:142 — 함수 'processOrder'의 순환 복잡도 23 (임계값 15) (debt 30)

6. 종료 코드

code의미
0스캔 성공 (게이트 통과 또는 게이트 미설정)
1품질 게이트 또는 assurance 보안/통제 게이트 실패
2명령 자체 실패 (스캔 대상 없음, 권한 오류, CSV 파싱 오류 등)
3scan --strict 불완전 측정 또는 assurance 불완전 판정/증거. 리포트는 생성
4full 모드 외부 컴포넌트 측정 불가

scan --strictscan --fail-under가 동시에 실패하면 측정 불완전성 신호를 우선해 exit 3을 반환한다. 리포트/CSV/런 매니페스트/JSON/SARIF는 가능한 범위에서 모두 작성한 뒤 종료한다.