From bbe37c7d0ad58726320527528c98dd224d374820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=84=9D=28Junseok=20Oh=29DevOps?= <52226147+Atom-oh@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:15:44 +0000 Subject: [PATCH 1/3] release: prepare AWSops 0.10.0 for main promotion --- CHANGELOG.md | 10 ++++++++-- README.md | 2 +- docs/runbooks/branch-strategy.md | 26 ++++++++++++++++++++++++++ web/package-lock.json | 4 ++-- web/package.json | 2 +- 5 files changed, 38 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5bbd6c47..bea3b4e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.10.0] - 2026-09-17 + ### Added - VPC connectivity: `/inventory/vpc` adds an on-demand, account/region-scoped peering and TGW attachment viewer with localized guidance, a section shortcut available during inventory loading or failure, and resource-graph navigation. A ReactFlow graph above the record lists draws active source–PCX–peer and source–TGW–peer paths with scoped identities, distinct unknown peers, clickable node/edge details and whole-path limits of 300 nodes/500 edges with visible omission counts. The `/topology/infra?view=vpc` tab loads inventory choices only until Fetch connections; qualified selection links and placement-node raw IDs query only after a unique match in the current inventory scope. The default placement view and its empty-state VPC-graph shortcut remain separate from live connection results, which are not persisted as graph edges. Retains pending and historical records with unknown peer details; labels active peerings and available attachments separately from records whose current connection is unconfirmed, and shows list arrows only for active peerings. TGW lists describe attachment records; only available attachments with associated route tables receive the associated-table label. Shows the VPC owner or unknown and separates shared-VPC/TGW visibility limits from failed or incomplete reads; either prevents a definitive no-connections claim. Operationally complete reads cache for four minutes even with visibility limits; incomplete reads are retried against AWS, with timestamps reflecting read completion. Picker and API share supported regions, disclose excluded choices and the 500-row cap, preserve selection across refreshes, and avoid empty-list claims after failed refreshes. Configuration does not prove reachability. Requires a reviewed saved-plan apply for the region-conditioned `ec2:DescribeVpcPeeringConnections` grant; reuses the existing TGW read permission. @@ -634,7 +636,8 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, - AI routing: Code Interpreter, AgentCore, Steampipe+Bedrock, Bedrock Direct - Bedrock Claude Sonnet/Opus 4.6 integration -[Unreleased]: https://github.com/whchoi98/awsops/compare/v0.9.0...HEAD +[Unreleased]: https://github.com/aws-samples/sample-awsops/compare/v0.10.0...dev +[0.10.0]: https://github.com/aws-samples/sample-awsops/releases/tag/v0.10.0 [0.9.0]: https://github.com/whchoi98/awsops/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/whchoi98/awsops/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/whchoi98/awsops/compare/v0.6.0...v0.7.0 @@ -666,6 +669,8 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, ## [Unreleased] +## [0.10.0] - 2026-09-17 + ### Added - VPC 간 연결: `/inventory/vpc`에 사용자 클릭으로 계정·리전 범위의 피어링과 TGW 어태치먼트를 조회하는 화면, 다국어 안내, 인벤토리 로딩·실패 중에도 사용할 수 있는 섹션 바로가기와 리소스 그래프 이동을 추가합니다. 기록 목록 위의 ReactFlow 그래프는 활성 기준 VPC–PCX–상대 VPC와 기준 VPC–TGW–상대 VPC 경로를 표시하고, 범위가 포함된 식별자·분리된 미확인 상대·노드와 연결선 클릭 상세·노드 300개/엣지 500개의 전체 경로 단위 상한 및 생략 건수를 제공합니다. `/topology/infra?view=vpc` 탭은 연결 조회를 누르기 전까지 인벤토리 선택지만 읽으며, 전체 선택 키 링크와 배치 노드의 원시 VPC ID는 현재 인벤토리 범위에서 하나로 확인된 뒤에만 연결을 조회합니다. 기본 배치 보기와 빈 상태의 VPC 그래프 바로가기는 라이브 연결 결과와 구분하며, 이 결과를 저장 그래프 엣지로 기록하지 않습니다. 상대 정보가 미확인인 대기·과거 기록도 유지하며, 활성 피어링과 사용 가능한 어태치먼트를 현재 연결이 확인되지 않은 기록과 구분하고 목록의 활성 피어링에만 화살표를 표시합니다. TGW 목록은 어태치먼트 기록을 나타내며, 사용 가능한 어태치먼트의 라우트 테이블 연결 상태가 associated인 경우에만 연결된 테이블로 표시합니다. VPC 소유 계정 또는 미확인 상태를 표시하고 공유 VPC·TGW의 가시성 한계를 조회 실패·불완전성과 구분하며, 어느 쪽이든 연결이 없다고 단정하지 않습니다. 조회가 정상 완료되면 가시성 한계가 있어도 4분간 캐시하고, 불완전한 조회를 재시도하면 AWS를 다시 읽으며 조회 시각은 읽기 완료 시점을 나타냅니다. 선택 목록과 API가 지원 리전 기준을 공유하고 제외된 선택지와 500행 상한을 안내하며, 새로고침 시 선택을 유지하고 실패한 새로고침 뒤에는 빈 목록이라고 단정하지 않습니다. 연결 구성만으로 실제 통신 가능 여부를 확정하지 않습니다. 리전 조건이 적용된 `ec2:DescribeVpcPeeringConnections` 권한은 검토된 저장 계획으로 apply해야 하며, 기존 TGW 읽기 권한을 재사용합니다. @@ -1273,7 +1278,8 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, - AI 라우팅: Code Interpreter, AgentCore, Steampipe+Bedrock, Bedrock Direct - Bedrock Claude Sonnet/Opus 4.6 통합 -[Unreleased]: https://github.com/whchoi98/awsops/compare/v0.9.0...HEAD +[Unreleased]: https://github.com/aws-samples/sample-awsops/compare/v0.10.0...dev +[0.10.0]: https://github.com/aws-samples/sample-awsops/releases/tag/v0.10.0 [0.9.0]: https://github.com/whchoi98/awsops/compare/v0.8.0...v0.9.0 [0.8.0]: https://github.com/whchoi98/awsops/compare/v0.7.0...v0.8.0 [0.7.0]: https://github.com/whchoi98/awsops/compare/v0.6.0...v0.7.0 diff --git a/README.md b/README.md index 63dc14868..b40991d63 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![GitHub forks](https://img.shields.io/github/forks/Atom-oh/awsops?style=flat&logo=github)](https://github.com/Atom-oh/awsops/network/members) [![GitHub issues](https://img.shields.io/github/issues/Atom-oh/awsops)](https://github.com/Atom-oh/awsops/issues) [![License](https://img.shields.io/github/license/Atom-oh/awsops)](LICENSE) -[![Version](https://img.shields.io/badge/version-v0.9.0-green.svg)](https://github.com/Atom-oh/awsops/releases) +[![Version](https://img.shields.io/badge/version-v0.10.0-green.svg)](https://github.com/aws-samples/sample-awsops/releases/tag/v0.10.0) [![Last commit](https://img.shields.io/github/last-commit/Atom-oh/awsops)](https://github.com/Atom-oh/awsops/commits/main) [![PR Review](https://github.com/Atom-oh/awsops/actions/workflows/pr-review.yml/badge.svg)](https://github.com/Atom-oh/awsops/actions/workflows/pr-review.yml) diff --git a/docs/runbooks/branch-strategy.md b/docs/runbooks/branch-strategy.md index 84f724075..324e55802 100644 --- a/docs/runbooks/branch-strategy.md +++ b/docs/runbooks/branch-strategy.md @@ -58,6 +58,32 @@ user's branch (or short-lived branches merged into it), then flows up via PR to assumes the deployer role under an S3/KMS-only session policy. Main publication waits for production approval; the separate automatic plan job remains read-only. +## Version and tag on main promotion + +Every `dev → main` release increments the application version in a reviewed PR +into `dev` before the promotion is merged. Keep `web/package.json`, both root +version fields in `web/package-lock.json`, the root README badge, and the first +released English/Korean CHANGELOG headings aligned. Move the existing Unreleased +feature entries under the new dated version and leave an empty Unreleased section; +do not duplicate feature bullets. The sidebar reads CHANGELOG, while migration +release fallback reads `web/package.json`. Existing migration `-- since:` headers +are immutable and must not be retagged for a release bump. + +Choose the next application version from this release line; imported legacy v1 +history and the separate `scripts/v2` tooling package are not its version source. +For this accumulated feature release the application advances from `0.9.0` to +`0.10.0`. Future releases choose their own increment from the actual changes. + +After the promotion's latest HEAD passes complete AI review and required CI, merge +`dev → main` with a merge commit to retain ancestry between the standing branches. +Read the promotion PR's actual merge SHA, fetch it, verify it is reachable from +`samples/main`, and confirm its package and changelog versions. Create an annotated +`v` tag on that merge SHA and push that tag explicitly to `samples`. +Check remote/local tag-name availability first; never force or move an existing +release tag. Do not tag an unmerged dev tip or create the tag while required checks +are blocked. A Git tag is not production deployment approval; the production +workflow/environment gates above still apply. + ## External (fork) PRs / 외부 PR - Anyone may fork and open a PR; the default branch makes `dev` the natural target, diff --git a/web/package-lock.json b/web/package-lock.json index 240e718c5..0aee8adb5 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "awsops-web", - "version": "0.9.0", + "version": "0.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "awsops-web", - "version": "0.9.0", + "version": "0.10.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-sdk/client-bedrock-agentcore": "^3.1061.0", diff --git a/web/package.json b/web/package.json index 6578504a6..fdfeb1e63 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "awsops-web", - "version": "0.9.0", + "version": "0.10.0", "private": true, "scripts": { "dev": "next dev", From 8765d91be4b0825b916267e5a22d81539e86d0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=84=9D=28Junseok=20Oh=29DevOps?= <52226147+Atom-oh@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:29:01 +0000 Subject: [PATCH 2/3] docs: reconcile release cut with immutable migration labels --- CHANGELOG.md | 8 ++++++-- docs/runbooks/branch-strategy.md | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bea3b4e72..e15941b88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.10.0] - 2026-09-17 +**Migration ledger note:** The features grouped in this release include eight migrations retaining checksum-immutable `-- since: 0.9.0` headers: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. Their declared ledger label remains `0.9.0` rather than the application version `0.10.0`; this is not evidence that those features shipped with the historical 0.9.0 release. FinOps retains the separately disclosed `0.8.0` labels. Do not rewrite SQL headers or applied checksums to make the labels match. + ### Added - VPC connectivity: `/inventory/vpc` adds an on-demand, account/region-scoped peering and TGW attachment viewer with localized guidance, a section shortcut available during inventory loading or failure, and resource-graph navigation. A ReactFlow graph above the record lists draws active source–PCX–peer and source–TGW–peer paths with scoped identities, distinct unknown peers, clickable node/edge details and whole-path limits of 300 nodes/500 edges with visible omission counts. The `/topology/infra?view=vpc` tab loads inventory choices only until Fetch connections; qualified selection links and placement-node raw IDs query only after a unique match in the current inventory scope. The default placement view and its empty-state VPC-graph shortcut remain separate from live connection results, which are not persisted as graph edges. Retains pending and historical records with unknown peer details; labels active peerings and available attachments separately from records whose current connection is unconfirmed, and shows list arrows only for active peerings. TGW lists describe attachment records; only available attachments with associated route tables receive the associated-table label. Shows the VPC owner or unknown and separates shared-VPC/TGW visibility limits from failed or incomplete reads; either prevents a definitive no-connections claim. Operationally complete reads cache for four minutes even with visibility limits; incomplete reads are retried against AWS, with timestamps reflecting read completion. Picker and API share supported regions, disclose excluded choices and the 500-row cap, preserve selection across refreshes, and avoid empty-list claims after failed refreshes. Configuration does not prove reachability. Requires a reviewed saved-plan apply for the region-conditioned `ec2:DescribeVpcPeeringConnections` grant; reuses the existing TGW read permission. @@ -83,7 +85,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Datasource detail pages gain a pre-built card dashboard: registering a datasource (and each daily index run) derives an expected card set from the cached schema — the queries each card uses are stored ahead of time (new `datasource_dashboard_cards` table + a deterministic `card_catalog` in the index worker; prometheus/mimir 13 cards covering targets, CPU, memory, disk, load, network, containers, and restarts; loki 2, tempo 2, clickhouse 2) — ready Prometheus/Mimir cards are live-validated against the exact datasource before registration, through the same instant/range tool the page will execute (a conclusive PromQL error — body-derived, never a bare HTTP 4xx — disables only that card and is revalidated on the next daily run; a transient connector failure, a failed re-introspection, or a truncated schema that cannot decide a card requirement all preserve the previous card set; the connector metric-metadata tool also gains a definitive `exists` flag and 3-second upstream deadlines) — card building runs inside the existing datasource-index job, so it is gated on `datasource_diagnosis_enabled` (default false; requires `workers_enabled`/`agentcore_enabled`/`integrations_enabled`) like the diag-signal chips — and the page executes the stored queries live on view through the existing read-only query API (stat/timeseries/table cards; unavailable cards render dimmed with what's missing; a failed card shows an inline error, never a silent zero). - Topology infra page gains two columnar map views — a 5-column infra resource map (External | VPC | Subnet | Compute | NAT) and a per-cluster K8s map (Ingress → Service → Pod → Node; host-account, connected clusters only — the in-cluster read path is host-scoped) — rendered as a real graph (fixed-column ReactFlow with edge lines), with click cross-highlighting, search highlighting, and a color legend. Built on existing inventory/EKS reads plus the pre-existing read-only `/api/tgw` live attachment describe (host-account scope only, first 20 TGWs — degradation is surfaced in the UI); the only server-side addition is the read-only `ingresses` in-cluster kind. -- FinOps baseline-recommendations engine (ADR-020, extends ADR-012, `finops_baseline_enabled`): a daily Fargate batch evaluates a rule catalog (unattached EBS volumes against a published rate card; EC2/RDS rightsizing via Compute Optimizer) against `inventory_resources` + Compute Optimizer — no CUR/Athena cost pipeline in this repo, so amounts come only from a published rate card or Compute Optimizer's own estimate, never invented; Cost Explorer/Cost Optimization Hub/Budgets-based rules are catalogued as future work, not called by this version. The deterministic engine owns status/amount (findings are ordered by amount; there is no separate engine-owned priority field yet); an LLM adds a short Korean explanation only, discarded if it states a different dollar amount. False-positive guards (protected tags, insufficient Compute Optimizer observation window, stale inventory data) demote to `needs_review` rather than hiding a finding. A rule that fails to evaluate no longer looks like a clean run — `finops_runs.status` gains a `partial` state, surfaced by the API/card, and that rule's prior findings are left untouched rather than wiped. Findings are scoped by account/region (not just resource_id), since `inventory_resources` spans every synced account/region. Read-only — no new AWS-mutation path. `ec2_rightsizing`/`rds_rightsizing` call Compute Optimizer only in the worker's host region (a per-region endpoint); each finding's evidence carries an explicit `coverage:"host-region-only"` marker rather than presenting single-region results as account-wide. New `/cost` section (`GET /api/finops/findings`); fixes the ADR-012/terraform drift where `cost-optimization-hub:*` was documented but never granted (the FinOps MCP's Cost Optimization Hub tool has been `AccessDenied` since ADR-012). **Known doc/DB drift (not fixable here):** its three migrations' `-- since: 0.8.0` header is stale — FinOps didn't exist when `[0.8.0]` was cut on 2026-08-19 — but those migrations already merged to main and are checksum-immutable, so the header can't be corrected without breaking `make migrate` for any environment that already applied them. This entry stays in `[Unreleased]` (the truthful release state); `schema_migrations.app_version` for these three rows will read `0.8.0` regardless. +- FinOps baseline-recommendations engine (ADR-020, extends ADR-012, `finops_baseline_enabled`): a daily Fargate batch evaluates a rule catalog (unattached EBS volumes against a published rate card; EC2/RDS rightsizing via Compute Optimizer) against `inventory_resources` + Compute Optimizer — no CUR/Athena cost pipeline in this repo, so amounts come only from a published rate card or Compute Optimizer's own estimate, never invented; Cost Explorer/Cost Optimization Hub/Budgets-based rules are catalogued as future work, not called by this version. The deterministic engine owns status/amount (findings are ordered by amount; there is no separate engine-owned priority field yet); an LLM adds a short Korean explanation only, discarded if it states a different dollar amount. False-positive guards (protected tags, insufficient Compute Optimizer observation window, stale inventory data) demote to `needs_review` rather than hiding a finding. A rule that fails to evaluate no longer looks like a clean run — `finops_runs.status` gains a `partial` state, surfaced by the API/card, and that rule's prior findings are left untouched rather than wiped. Findings are scoped by account/region (not just resource_id), since `inventory_resources` spans every synced account/region. Read-only — no new AWS-mutation path. `ec2_rightsizing`/`rds_rightsizing` call Compute Optimizer only in the worker's host region (a per-region endpoint); each finding's evidence carries an explicit `coverage:"host-region-only"` marker rather than presenting single-region results as account-wide. New `/cost` section (`GET /api/finops/findings`); fixes the ADR-012/terraform drift where `cost-optimization-hub:*` was documented but never granted (the FinOps MCP's Cost Optimization Hub tool has been `AccessDenied` since ADR-012). **Known doc/DB drift (not fixable here):** its three migrations' `-- since: 0.8.0` header is stale — FinOps didn't exist when `[0.8.0]` was cut on 2026-08-19 — but those migrations already merged to main and are checksum-immutable, so the header can't be corrected without breaking `make migrate` for any environment that already applied them. This feature entry is now grouped under `[0.10.0]`; `schema_migrations.app_version` for these three rows will read `0.8.0` regardless. - Add an SG Rules page (`/network/security-groups/rules`, `sg_rule_activity_enabled`, default false) — this is a SEPARATE, additive pipeline from the pre-existing Usage analysis (`[0.8.0]` below); it does not replace it. Rule inventory (rule id/fingerprint/version history) is derived from configured Security Groups; per-rule daily traffic evidence (`observed_compatible`/`overlapping`/`no_observed_evidence`/`unassessable`/`not_configured`) is computed by a Fargate worker (`sg_rule_scan.py`) that resolves ENI-to-SG membership snapshots and matches them against VPC Flow Logs read through Athena — via an isolated broker Lambda (`sg_rule_athena_broker.py`, ADR-019 Role B) that is the ONLY principal allowed to `sts:AssumeRole` into a target account's `AWSopsSgRuleAthenaRole`; the broker resolves account/table config server-side from an opaque `flow_source_id` (never a caller-supplied query/account), re-validates every identifier against strict allowlists, and requires the workgroup to enforce its own `BytesScannedCutoffPerQuery`. A flow can match more than one rule; partition-projection-aware watermarking and per-day SKIPDATA/truncation coverage flags feed the same honest-degrade contract used elsewhere in this app — an incomplete or unattributable day is `unassessable`, never a confident false zero. **SG-reference resolution across a genuinely cross-account or cross-region VPC-peering/RAM-shared reference is a known, disclosed gap, not a working feature today**: this release has no peering/RAM topology data source, so a rule referencing a security group that cannot be found ANYWHERE in the current account/region's own ENI-membership snapshot resolves `unassessable` (never a confident empty match) — that data source can only be populated in a future change. Matching is **day-granular, not per-flow**: `sg_rule_inventory_versions.valid_from`/`valid_to` are *observation* timestamps (the scan run that first/last saw a fingerprint), not the actual rule-change instant, so a day within the actual gap to the previous successful scan of a version boundary is also `unassessable` rather than confidently attributed to either shape (see "Fixed" below). - Add a Network Path Check page (`/network-paths`, top-level nav entry, `network_path_check_enabled`, default false): define a source/destination check (ENI, SG, subnet route, NACL, TGW, peering/VPN/DX boundary, Network Firewall, ALB listener/target-group health, K8s NetworkPolicy/Calico/Cilium/Istio-stub layers, DNS/L7) and run it via a Fargate worker (`network_path.py`, resolve → discover → verify → conclude) that never invents a confident verdict from missing/ambiguous data for any SINGLE layer it evaluates (`unknown`/`conditional` instead of a false `allowed`/`blocked` at that layer). **This is a per-layer guarantee, not yet a full-path one**: every layer is still primarily a source-side check, so a candidate path can still report an overall `allowed` based on less than the full bidirectional policy surface for peering/TGW/VPN/DX-fronted destinations whose own ENI isn't resolved, and for ALB/NLB-fronted targets (the target's own SG is not independently checked past `target-group`) — see `network_path.py`'s own "Known structural gap" docstring section. **`fetch_live_topology` is now real** — best-effort candidate-path discovery from CACHED Aurora topology (`topology_nodes`/`topology_edges`, `class='infra'`), no longer the `NotImplementedError` stub this bullet originally described — but a full LIVE AWS/Kubernetes re-read at run time remains deliberately unimplemented, so starting a NEW run (`POST`) in `web/app/api/network-paths/[id]/runs/route.ts` still 503s (`status: "unimplemented"`) via `networkPathLiveTopologyCapabilityGate()` (`web/lib/network-path-gate.ts`); existing check definitions and prior run history remain fully viewable. `LIVE_TOPOLOGY_IMPLEMENTED` stays `false` until that separate live re-read path exists. Calico, Route 53, and K8s Ingress→Service→EndpointSlice now have REAL evaluators (given already-fetched data); Cilium/Istio remain correctly-stubbed `unknown` (never guessed). `resolve_identities()` still reads Pod/Node/ENI identity from the saved check definition's own fields, but a `pod`/`node` source declaring a `cluster` additionally gets that identity CONFIRMED against a live, read-only K8s/EC2 read (`resolve_live_identity`) rather than trusting the definition's fields as already-verified. A rule inventory row now also surfaces its own `vpc_id`. - Inventory sync: quota-safe collection — Steampipe plugin rate limiter (env-tunable), durable per-type freshness ledger (last_success_at, partial status, unknown_attribute_count disclosure), content-preserving partial runs, and per-type freshness in the inventory MCP tools. An opt-in host guard verifies STS and registry scope, retries transient identity failures within a fixed budget, and exits nonzero after rejected scope without a concurrent restart reviving collection. Unknown attribute coverage recorded as NULL is now degraded rather than healthy; inventory pagination uses a complete key order so tied capture times cannot repeat or omit rows across pages. Exact CloudFront identity lookup returns one disclosed identity-only row; a miss is not evidence of AWS absence. Empty Steampipe scans use the pinned per-account identity table and require exactly one matching account; unverified probes preserve last-good inventory and remain partial. Verified dev profiles support the optional nonsecret `CI_STEAMPIPE_AWS_FILL_RATE_DEV` refill override on full plans; absence preserves tfvars/defaults and Apply replays the reviewed plan. (ADR-021) Sync results, ledger counts and account snapshots use distinct persisted account/region/resource identities, preserving last-row-wins values. Hydrate-fallback unknown-attribute counts use the same persisted identity basis. @@ -671,6 +673,8 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, ## [0.10.0] - 2026-09-17 +**마이그레이션 원장 참고:** 이번 릴리스에 정리한 기능에는 체크섬이 불변인 `-- since: 0.9.0` 헤더를 유지하는 마이그레이션 8건이 포함된다: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. 선언된 원장 표기는 앱 버전 `0.10.0`이 아니라 `0.9.0`으로 유지되며, 해당 기능이 과거 0.9.0 릴리스에 포함됐다는 증거가 아니다. FinOps는 별도로 명시한 `0.8.0` 표기를 유지한다. 표기를 맞추려고 SQL 헤더나 적용된 체크섬을 변경하지 않는다. + ### Added - VPC 간 연결: `/inventory/vpc`에 사용자 클릭으로 계정·리전 범위의 피어링과 TGW 어태치먼트를 조회하는 화면, 다국어 안내, 인벤토리 로딩·실패 중에도 사용할 수 있는 섹션 바로가기와 리소스 그래프 이동을 추가합니다. 기록 목록 위의 ReactFlow 그래프는 활성 기준 VPC–PCX–상대 VPC와 기준 VPC–TGW–상대 VPC 경로를 표시하고, 범위가 포함된 식별자·분리된 미확인 상대·노드와 연결선 클릭 상세·노드 300개/엣지 500개의 전체 경로 단위 상한 및 생략 건수를 제공합니다. `/topology/infra?view=vpc` 탭은 연결 조회를 누르기 전까지 인벤토리 선택지만 읽으며, 전체 선택 키 링크와 배치 노드의 원시 VPC ID는 현재 인벤토리 범위에서 하나로 확인된 뒤에만 연결을 조회합니다. 기본 배치 보기와 빈 상태의 VPC 그래프 바로가기는 라이브 연결 결과와 구분하며, 이 결과를 저장 그래프 엣지로 기록하지 않습니다. 상대 정보가 미확인인 대기·과거 기록도 유지하며, 활성 피어링과 사용 가능한 어태치먼트를 현재 연결이 확인되지 않은 기록과 구분하고 목록의 활성 피어링에만 화살표를 표시합니다. TGW 목록은 어태치먼트 기록을 나타내며, 사용 가능한 어태치먼트의 라우트 테이블 연결 상태가 associated인 경우에만 연결된 테이블로 표시합니다. VPC 소유 계정 또는 미확인 상태를 표시하고 공유 VPC·TGW의 가시성 한계를 조회 실패·불완전성과 구분하며, 어느 쪽이든 연결이 없다고 단정하지 않습니다. 조회가 정상 완료되면 가시성 한계가 있어도 4분간 캐시하고, 불완전한 조회를 재시도하면 AWS를 다시 읽으며 조회 시각은 읽기 완료 시점을 나타냅니다. 선택 목록과 API가 지원 리전 기준을 공유하고 제외된 선택지와 500행 상한을 안내하며, 새로고침 시 선택을 유지하고 실패한 새로고침 뒤에는 빈 목록이라고 단정하지 않습니다. 연결 구성만으로 실제 통신 가능 여부를 확정하지 않습니다. 리전 조건이 적용된 `ec2:DescribeVpcPeeringConnections` 권한은 검토된 저장 계획으로 apply해야 하며, 기존 TGW 읽기 권한을 재사용합니다. @@ -735,7 +739,7 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, - 데이터소스 상세 페이지에 사전 생성 카드 대시보드 추가: 등록 시(및 일일 인덱스 배치마다) 캐시된 스키마로부터 예상 카드 세트를 도출하고 각 카드가 사용할 쿼리를 미리 저장(신규 `datasource_dashboard_cards` 테이블 + 인덱스 워커의 결정론적 `card_catalog` — prometheus/mimir는 타깃·CPU·메모리·디스크·로드·네트워크·컨테이너·재시작을 포괄하는 13종, loki 2종·tempo 2종·clickhouse 2종) — ready Prometheus/Mimir 카드는 등록 전에 해당 데이터소스에서, 페이지가 실제 실행할 instant/range 툴 그대로 라이브 검증하며(확정 PromQL 오류 — 응답 본문 기반, 단순 HTTP 4xx 아님 — 는 해당 카드만 비활성화하고 다음 일일 실행에서 재검증, 일시적 커넥터 장애·재수집 실패·카드 요구 메트릭을 판정할 수 없는 절단 스키마는 모두 기존 카드 세트 보존; 커넥터 metric-metadata 툴에는 확정 `exists` 플래그와 업스트림 3초 제한 추가) — 카드 빌드는 기존 datasource-index 잡 내부에서 실행되므로 diag-signal 칩과 동일하게 `datasource_diagnosis_enabled` 게이트(기본 false; `workers_enabled`/`agentcore_enabled`/`integrations_enabled` 선행) 하에 동작 — 페이지가 저장된 쿼리를 기존 read-only 쿼리 API로 조회 시점에 라이브 실행해 렌더링(stat/시계열/테이블 카드, 미충족 카드는 누락 항목과 함께 비활성 표시, 실패 카드는 조용한 0이 아니라 인라인 에러로 표시). - 토폴로지 인프라 페이지에 컬럼형 맵 뷰 2종 추가 — 5컬럼 인프라 리소스 맵(External | VPC | Subnet | Compute | NAT)과 클러스터별 K8s 맵(Ingress → Service → Pod → Node — in-cluster 조회 경로가 host 스코프라 host 계정의 connected 클러스터만 대상) — 고정 컬럼 ReactFlow에 실제 엣지 연결선을 그리는 그래프로 렌더링되며, 클릭 교차 하이라이트·검색 하이라이트·색상 범례 포함. 기존 인벤토리/EKS 조회와 기존 read-only `/api/tgw` 라이브 어태치먼트 조회(호스트 계정 스코프 한정·최대 20개 — 미조회 시 UI에 표시)를 사용하며, 서버 측 추가는 read-only `ingresses` in-cluster kind 1종뿐. -- FinOps 기본 권장 엔진 추가(ADR-020, ADR-012 확장, `finops_baseline_enabled`): 일별 Fargate 배치가 룰 카탈로그(공개 요율표 기반 미사용 EBS 볼륨; Compute Optimizer 기반 EC2/RDS rightsizing)를 `inventory_resources`/Compute Optimizer에 평가 — 이 저장소엔 CUR/Athena 비용 파이프라인이 없어 금액은 공개 요율표 또는 Compute Optimizer 자체 추정치로만 산출되며 절대 발명되지 않음. Cost Explorer/Cost Optimization Hub/Budgets 기반 룰은 이번 버전에서는 호출되지 않고 카탈로그에 향후 확장으로만 등록됨. 결정론적 엔진이 판정·금액을 소유하며(별도 우선순위 필드는 아직 없고 금액순 정렬만 있음), LLM은 한국어 설명만 덧붙이며 확정 금액과 다른 달러 금액을 말하면 폐기됨. 오탐 가드(보호 태그, Compute Optimizer 관측 기간 부족, 인벤토리 데이터 staleness)는 항목을 숨기지 않고 `needs_review`로 강등. 룰 평가 실패가 더 이상 정상 실행처럼 보이지 않도록 `finops_runs.status`에 `partial` 상태를 추가해 API/카드에 노출하며, 실패한 룰의 기존 finding은 그대로 보존됨. finding은 계정/리전으로 스코프됨(`inventory_resources`가 여러 계정/리전을 아우르므로 resource_id만으로는 식별이 불충분). read-only — 신규 AWS-변경 경로 없음. `ec2_rightsizing`/`rds_rightsizing`은 Compute Optimizer를 워커 호스트 리전(리전별 엔드포인트)에서만 호출 — 각 finding의 evidence에 `coverage:"host-region-only"` 마커를 명시해 단일 리전 결과를 계정 전체로 표기하지 않음. `/cost`에 새 섹션 추가(`GET /api/finops/findings`); ADR-012/terraform 드리프트 수정(`cost-optimization-hub:*`가 문서화됐지만 실제로 부여된 적이 없어 FinOps MCP의 Cost Optimization Hub 툴이 ADR-012 이후 상시 `AccessDenied`였음). **알려진 문서/DB 불일치(여기서 고칠 수 없음):** 관련 마이그레이션 3건의 `-- since: 0.8.0` 헤더는 오래된 값이다 — `[0.8.0]`이 2026-08-19에 컷될 때 FinOps는 아직 존재하지 않았다 — 하지만 그 마이그레이션들은 이미 main에 병합돼 체크섬이 불변이라, 이미 적용한 어떤 환경에서든 `make migrate`를 깨뜨리지 않고는 헤더를 고칠 수 없다. 이 항목은 (사실에 맞게) `[Unreleased]`에 남긴다 — `schema_migrations.app_version`의 해당 3행은 계속 `0.8.0`으로 남는다. +- FinOps 기본 권장 엔진 추가(ADR-020, ADR-012 확장, `finops_baseline_enabled`): 일별 Fargate 배치가 룰 카탈로그(공개 요율표 기반 미사용 EBS 볼륨; Compute Optimizer 기반 EC2/RDS rightsizing)를 `inventory_resources`/Compute Optimizer에 평가 — 이 저장소엔 CUR/Athena 비용 파이프라인이 없어 금액은 공개 요율표 또는 Compute Optimizer 자체 추정치로만 산출되며 절대 발명되지 않음. Cost Explorer/Cost Optimization Hub/Budgets 기반 룰은 이번 버전에서는 호출되지 않고 카탈로그에 향후 확장으로만 등록됨. 결정론적 엔진이 판정·금액을 소유하며(별도 우선순위 필드는 아직 없고 금액순 정렬만 있음), LLM은 한국어 설명만 덧붙이며 확정 금액과 다른 달러 금액을 말하면 폐기됨. 오탐 가드(보호 태그, Compute Optimizer 관측 기간 부족, 인벤토리 데이터 staleness)는 항목을 숨기지 않고 `needs_review`로 강등. 룰 평가 실패가 더 이상 정상 실행처럼 보이지 않도록 `finops_runs.status`에 `partial` 상태를 추가해 API/카드에 노출하며, 실패한 룰의 기존 finding은 그대로 보존됨. finding은 계정/리전으로 스코프됨(`inventory_resources`가 여러 계정/리전을 아우르므로 resource_id만으로는 식별이 불충분). read-only — 신규 AWS-변경 경로 없음. `ec2_rightsizing`/`rds_rightsizing`은 Compute Optimizer를 워커 호스트 리전(리전별 엔드포인트)에서만 호출 — 각 finding의 evidence에 `coverage:"host-region-only"` 마커를 명시해 단일 리전 결과를 계정 전체로 표기하지 않음. `/cost`에 새 섹션 추가(`GET /api/finops/findings`); ADR-012/terraform 드리프트 수정(`cost-optimization-hub:*`가 문서화됐지만 실제로 부여된 적이 없어 FinOps MCP의 Cost Optimization Hub 툴이 ADR-012 이후 상시 `AccessDenied`였음). **알려진 문서/DB 불일치(여기서 고칠 수 없음):** 관련 마이그레이션 3건의 `-- since: 0.8.0` 헤더는 오래된 값이다 — `[0.8.0]`이 2026-08-19에 컷될 때 FinOps는 아직 존재하지 않았다 — 하지만 그 마이그레이션들은 이미 main에 병합돼 체크섬이 불변이라, 이미 적용한 어떤 환경에서든 `make migrate`를 깨뜨리지 않고는 헤더를 고칠 수 없다. 이 기능 항목은 `[0.10.0]` 릴리스에 정리한다 — `schema_migrations.app_version`의 해당 3행은 계속 `0.8.0`으로 남는다. - SG Rules 페이지 추가(`/network/security-groups/rules`, `sg_rule_activity_enabled`, 기본 false) — 기존 Usage 분석(`[0.8.0]` 아래)과는 **별개의, 추가적인** 파이프라인이며 이를 대체하지 않는다. 룰 인벤토리(룰 id/fingerprint/버전 히스토리)는 설정된 보안 그룹에서 도출하고, 룰별 일일 트래픽 근거(`observed_compatible`/`overlapping`/`no_observed_evidence`/`unassessable`/`not_configured`)는 Fargate 워커(`sg_rule_scan.py`)가 ENI-SG 멤버십 스냅샷을 Athena로 조회한 VPC Flow Logs와 매칭해 계산 — 격리된 브로커 Lambda(`sg_rule_athena_broker.py`, ADR-019 Role B)를 통해서만 이루어지며, 이 브로커만이 대상 계정의 `AWSopsSgRuleAthenaRole`에 `sts:AssumeRole`할 수 있다; 브로커는 opaque한 `flow_source_id`로부터 계정/테이블 설정을 서버 측에서 직접 해석(caller가 넘긴 쿼리/계정을 절대 신뢰하지 않음)하고 모든 식별자를 엄격한 allowlist로 재검증하며, workgroup이 자체 `BytesScannedCutoffPerQuery`를 강제하도록 요구한다. 한 플로우가 여러 룰에 매칭될 수 있음; 파티션-projection 인지 워터마킹과 일별 SKIPDATA/절단 커버리지 플래그는 이 앱의 다른 곳과 동일한 정직한 강등 원칙을 따름 — 불완전하거나 귀속 불가능한 날은 확신에 찬 거짓 0이 아니라 `unassessable`로 표시. **계정/리전 간(cross-account/cross-region) VPC 피어링·RAM 공유 참조의 SG-참조 해석은 이번 릴리스에서 실제로 동작하지 않는, 명시적으로 남겨둔 갭이다**: 현재 피어링/RAM 토폴로지 데이터 소스가 없으므로, 현재 계정/리전의 ENI 멤버십 스냅샷 어디에도 없는 참조 SG는 확신에 찬 빈 매칭이 아니라 `unassessable`로 처리된다 — 해당 데이터 소스는 향후 별도 변경에서만 채워질 수 있다. 매칭은 **일(day) 단위이며 개별 플로우 단위가 아니다**: `sg_rule_inventory_versions`의 `valid_from`/`valid_to`는 (룰이 실제로 바뀐 시각이 아니라) 그 fingerprint를 처음/마지막으로 관찰한 스캔 실행 시각이므로, 버전 경계로부터 이전 성공한 스캔까지의 실제 간격 이내에 있는 날은 어느 한쪽 형태로 확신 귀속하지 않고 마찬가지로 `unassessable`로 표시한다(아래 "Fixed" 참조). - Network Path Check 페이지 추가(`/network-paths`, 최상위 nav 항목, `network_path_check_enabled`, 기본 false): 출발지/목적지 체크(ENI, SG, 서브넷 라우트, NACL, TGW, 피어링/VPN/DX 경계, Network Firewall, ALB 리스너/타겟그룹 헬스, K8s NetworkPolicy/Calico/Cilium/Istio-stub 계층, DNS/L7)를 정의하고 Fargate 워커(`network_path.py`, resolve → discover → verify → conclude)로 실행 — 개별 레이어 평가에서는 데이터가 없거나 모호할 때 확신에 찬 거짓 `allowed`/`blocked`를 절대 만들어내지 않고 그 레이어를 `unknown`/`conditional`로 반환한다. **이는 레이어 단위 보장이며, 아직 전체 경로(full-path) 단위의 보장은 아니다**: 모든 레이어가 여전히 주로 source 쪽만 검사하므로, ENI가 확인되지 않는 피어링/TGW/VPN/DX 경유 목적지나 ALB/NLB 대상(대상 자체의 SG는 `target-group` 이후 별도로 검사하지 않음)의 경우 경로 전체 결론이 전체 양방향 정책 표면보다 적은 근거로도 `allowed`로 보고될 수 있다 — `network_path.py`의 "Known structural gap" 문서 참조. **`fetch_live_topology`는 이제 실제 구현이다** — 캐시된 Aurora 토폴로지(`topology_nodes`/`topology_edges`, `class='infra'`)로부터 best-effort 후보 경로를 탐색한다(이 항목이 원래 기술했던 `NotImplementedError` 스텁이 아님), 다만 run 시점의 실시간 AWS/Kubernetes 재조회는 여전히 의도적으로 미구현이라 `web/app/api/network-paths/[id]/runs/route.ts`의 새 run 생성 경로(`POST`)는 여전히 `networkPathLiveTopologyCapabilityGate()`(`web/lib/network-path-gate.ts`)로 게이트되어 503(`status: "unimplemented"`)을 반환한다; 기존 체크 정의와 과거 run 히스토리는 계속 조회 가능하다. `LIVE_TOPOLOGY_IMPLEMENTED`는 그 별도의 실시간 재조회 경로가 실제로 추가될 때까지 `false`로 유지된다. Calico·Route 53·K8s Ingress→Service→EndpointSlice는 이제 (이미 가져온 데이터를 대상으로) 실제 평가기를 갖췄고, Cilium/Istio는 여전히 (추측하지 않고) 정상적으로 `unknown`으로 스텁 처리되어 있다. `resolve_identities()`는 여전히 저장된 체크 정의 자체의 필드에서 Pod/Node/ENI identity를 읽지만, `cluster`를 선언한 `pod`/`node` 소스는 그 identity를 정의의 필드를 이미 검증된 것으로 신뢰하는 대신 라이브 read-only K8s/EC2 조회(`resolve_live_identity`)로 추가 확인한다. 룰 인벤토리 행에도 자신의 `vpc_id`가 노출된다. - 인벤토리 sync: 쿼터 안전 수집 — Steampipe 플러그인 rate limiter(env 조절), 내구성 freshness 원장(last_success_at·partial 상태·unknown_attribute_count 공개), 내용 보존형 partial 런, 인벤토리 MCP 도구의 타입별 freshness 노출. 선택적 호스트 가드는 STS·레지스트리 범위를 검증하고 일시적 식별자 조회 실패를 제한된 횟수로 재시도하며, 범위 거부 시 비정상 종료하고 동시에 진행된 재시작도 수집을 되살리지 못하게 합니다. NULL로 기록된 미확인 속성 범위는 healthy가 아닌 degraded로 공개하며, 수집 시각이 같아도 전체 키로 정렬하여 페이지 간 행 중복·누락을 방지합니다. CloudFront ID 정확 조회는 identity-only 한 행을 명시하여 반환하며, 미발견은 AWS에서의 부재 증거가 아닙니다. 빈 Steampipe 조회는 고정 버전의 계정별 식별 테이블에서 요청 계정과 일치하는 한 행을 확인하며, 확인 실패 시 마지막 정상 인벤토리를 보존하고 partial 상태를 유지합니다. 검증된 dev 프로필은 비밀 값이 아닌 선택적 `CI_STEAMPIPE_AWS_FILL_RATE_DEV` 변수로 전체 Plan의 refill rate만 덮어쓸 수 있으며, 미설정 시 tfvars 또는 기본값을 사용하고 Apply는 검토된 계획을 재사용합니다. (ADR-021) sync 결과·원장·계정 스냅샷의 개수는 실제 저장된 계정·리전·리소스 식별자를 기준으로 하며 마지막 행의 값을 보존합니다. hydrate 폴백의 미확인 속성 개수도 같은 저장 식별자 기준을 사용한다. diff --git a/docs/runbooks/branch-strategy.md b/docs/runbooks/branch-strategy.md index 324e55802..27eb82d28 100644 --- a/docs/runbooks/branch-strategy.md +++ b/docs/runbooks/branch-strategy.md @@ -60,6 +60,9 @@ user's branch (or short-lived branches merged into it), then flows up via PR to ## Version and tag on main promotion +Here `samples` means the Git remote for `aws-samples/sample-awsops`; verify its URL +with `git remote -v` before fetching or pushing. + Every `dev → main` release increments the application version in a reviewed PR into `dev` before the promotion is merged. Keep `web/package.json`, both root version fields in `web/package-lock.json`, the root README badge, and the first @@ -67,7 +70,10 @@ released English/Korean CHANGELOG headings aligned. Move the existing Unreleased feature entries under the new dated version and leave an empty Unreleased section; do not duplicate feature bullets. The sidebar reads CHANGELOG, while migration release fallback reads `web/package.json`. Existing migration `-- since:` headers -are immutable and must not be retagged for a release bump. +are immutable and must not be retagged for a release bump. New migrations should +declare the intended next application release before their first merge. Historical +ledger labels are not an ordered application-release history: disclose mismatched +labels in CHANGELOG rather than changing already-merged SQL or ledger checksums. Choose the next application version from this release line; imported legacy v1 history and the separate `scripts/v2` tooling package are not its version source. From 282d6abef8314c880c04cc7d2eaa56b8bd5c2a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A4=80=EC=84=9D=28Junseok=20Oh=29DevOps?= <52226147+Atom-oh@users.noreply.github.com> Date: Thu, 17 Sep 2026 13:42:36 +0000 Subject: [PATCH 3/3] docs: disclose legacy and apply-time migration version labels --- CHANGELOG.md | 4 ++-- docs/runbooks/branch-strategy.md | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e15941b88..06a0f26ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.10.0] - 2026-09-17 -**Migration ledger note:** The features grouped in this release include eight migrations retaining checksum-immutable `-- since: 0.9.0` headers: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. Their declared ledger label remains `0.9.0` rather than the application version `0.10.0`; this is not evidence that those features shipped with the historical 0.9.0 release. FinOps retains the separately disclosed `0.8.0` labels. Do not rewrite SQL headers or applied checksums to make the labels match. +**Migration ledger note:** The features grouped in this release include eight migrations retaining checksum-immutable `-- since: 0.9.0` headers: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. Their declared ledger label remains `0.9.0` rather than the application version `0.10.0`; this is not evidence that those features shipped with the historical 0.9.0 release. FinOps retains the separately disclosed `0.8.0` labels. This is a focused example list, not an exhaustive migration inventory. Other retained labels include legacy `2.0.0`–`2.4.0` and the literal non-semver `2.x.0`; they are equally not application-release evidence. Header-less migrations use the apply-time `APP_VERSION` override or package-version fallback, so existing and fresh environments can record different labels for identical SQL. Existing ledger rows are not rewritten by a version bump. Audit a release using its Git tag/commit, migration files and checksums, not by filtering the ledger for `app_version = '0.10.0'`. Do not rewrite SQL headers or applied checksums to make the labels match. ### Added @@ -673,7 +673,7 @@ First release of the **v2 line** (versioned independently from the v1 1.x line, ## [0.10.0] - 2026-09-17 -**마이그레이션 원장 참고:** 이번 릴리스에 정리한 기능에는 체크섬이 불변인 `-- since: 0.9.0` 헤더를 유지하는 마이그레이션 8건이 포함된다: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. 선언된 원장 표기는 앱 버전 `0.10.0`이 아니라 `0.9.0`으로 유지되며, 해당 기능이 과거 0.9.0 릴리스에 포함됐다는 증거가 아니다. FinOps는 별도로 명시한 `0.8.0` 표기를 유지한다. 표기를 맞추려고 SQL 헤더나 적용된 체크섬을 변경하지 않는다. +**마이그레이션 원장 참고:** 이번 릴리스에 정리한 기능에는 체크섬이 불변인 `-- since: 0.9.0` 헤더를 유지하는 마이그레이션 8건이 포함된다: `01M279W0J9HNG1QT0MAS60KV8K_topology_graph_collection_state.sql`, `01M27AQXZKQQ5J611R01BEFHPD_worker_jobs_lifecycle_timestamps.sql`, `01M27B0000C6QWJ50NRJ8YAH9D_trace_queue_claim_provenance.sql`, `01M2FV44NER7VC3CTX2ZMT9FZG_topology_inventory_evidence.sql`, `01M2GRW64VTMC9AC8M7T9MZKQ4_graph_attempt_disclosure.sql`, `01M2GTT5VHHH3TZ4PDJS99HWMJ_graph_read_indexes.sql`, `01M2HM8BR5ZC0JZWGQ9ZFV1WT2_graph_projection_parity.sql`, `01M2K0BTQ4P4QHHFHR44ZK1YW6_agent_tool_policy_history.sql`. 선언된 원장 표기는 앱 버전 `0.10.0`이 아니라 `0.9.0`으로 유지되며, 해당 기능이 과거 0.9.0 릴리스에 포함됐다는 증거가 아니다. FinOps는 별도로 명시한 `0.8.0` 표기를 유지한다. 이는 대표 사례 목록이며 전체 마이그레이션 목록이 아니다. 기존 `2.0.0`–`2.4.0` 및 SemVer가 아닌 리터럴 `2.x.0` 표기도 유지되며 앱 릴리스의 증거가 아니다. 헤더가 없는 파일은 적용 시점의 `APP_VERSION` 재정의 또는 패키지 버전을 사용하므로 동일 SQL도 기존 환경과 신규 환경에서 다른 표기로 기록될 수 있다. 버전 변경은 기존 원장 행을 갱신하지 않는다. 릴리스 감사에는 Git 태그·커밋, 마이그레이션 파일과 체크섬을 사용하고 `app_version = '0.10.0'` 필터로 릴리스 소속을 판단하지 않는다. 표기를 맞추려고 SQL 헤더나 적용된 체크섬을 변경하지 않는다. ### Added diff --git a/docs/runbooks/branch-strategy.md b/docs/runbooks/branch-strategy.md index 27eb82d28..4e2792b2e 100644 --- a/docs/runbooks/branch-strategy.md +++ b/docs/runbooks/branch-strategy.md @@ -74,6 +74,10 @@ are immutable and must not be retagged for a release bump. New migrations should declare the intended next application release before their first merge. Historical ledger labels are not an ordered application-release history: disclose mismatched labels in CHANGELOG rather than changing already-merged SQL or ledger checksums. +This includes legacy 2.x-line labels and header-less files: the latter use the +apply-time APP_VERSION override or package fallback, while existing rows remain +unchanged. The release tag/commit and SQL checksums identify the release contents; +an app_version equality filter does not. Choose the next application version from this release line; imported legacy v1 history and the separate `scripts/v2` tooling package are not its version source.