{
  "schema_version": 1,
  "observed_at": "2026-09-21T16:17:49.564298+00:00",
  "environment": {
    "os": "Linux",
    "python": "3.10.12",
    "nginx": "nginx version: nginx/1.18.0 (Ubuntu)"
  },
  "method": "고유 임시 경로·루프백 포트의 Nginx. 후보 검사와 교체, 해당 master에만 HUP, 순차 GET 요청. 운영 설정·서비스·외부 API를 사용하지 않았다.",
  "limits": "단일 실행. 무중단성·동시 접속·잘못된 설정의 실제 reload·CDN·브라우저 라우터·검색엔진의 soft 404 판정은 시험하지 않았다.",
  "observations": {
    "invalid": {
      "exit": 1,
      "stderr": "nginx: [alert] could not open error log file: open() \"/var/log/nginx/error.log\" failed (13: Permission denied)\n2026/09/21 16:17:49 [emerg] 2531605#2531605: unknown directive \"not_a_real_nginx_directive\" in LAB_ROOT/invalid.conf:22\nnginx: configuration file LAB_ROOT/invalid.conf test failed\n"
    },
    "valid": {
      "exit": 0,
      "stderr": "nginx: [alert] could not open error log file: open() \"/var/log/nginx/error.log\" failed (13: Permission denied)\nnginx: the configuration file LAB_ROOT/candidate.conf syntax is ok\nnginx: configuration file LAB_ROOT/candidate.conf test is successful\n"
    },
    "before": {
      "status": 200,
      "body": "config-v1",
      "returns_home": false
    },
    "after_invalid": {
      "status": 200,
      "body": "config-v1",
      "returns_home": false
    },
    "before_apply": {
      "status": 200,
      "body": "config-v1",
      "returns_home": false
    },
    "after_apply": {
      "status": 200,
      "body": "config-v2",
      "returns_home": false
    },
    "routes": {
      "/article.html": {
        "status": 200,
        "body": "article-v1\n",
        "returns_home": false
      },
      "/missing-post/": {
        "status": 404,
        "body": "Nginx 404 page",
        "returns_home": false
      },
      "/assets/missing.css": {
        "status": 404,
        "body": "Nginx 404 page",
        "returns_home": false
      },
      "/fallback/missing-post/": {
        "status": 200,
        "body": "home-v1\n",
        "returns_home": true
      },
      "/fallback/assets/missing.css": {
        "status": 200,
        "body": "home-v1\n",
        "returns_home": true
      }
    },
    "own_master_exit": 0
  },
  "tables": {
    "candidate": {
      "headers": [
        "단계",
        "검사 종료 코드",
        "/version 응답"
      ],
      "rows": [
        [
          "잘못된 후보만 검사",
          1,
          "config-v1"
        ],
        [
          "수정한 후보만 검사",
          0,
          "config-v1"
        ],
        [
          "검증된 후보를 교체하고 로컬 master에 HUP",
          "검사 후 적용",
          "config-v2"
        ]
      ]
    },
    "routes": {
      "headers": [
        "요청 경로",
        "HTTP 상태",
        "홈 본문과 일치"
      ],
      "rows": [
        [
          "/article.html",
          200,
          "아님"
        ],
        [
          "/missing-post/",
          404,
          "아님"
        ],
        [
          "/assets/missing.css",
          404,
          "아님"
        ],
        [
          "/fallback/missing-post/",
          200,
          "일치"
        ],
        [
          "/fallback/assets/missing.css",
          200,
          "일치"
        ]
      ]
    }
  }
}
