feat: implement dcy-007 contract governance and ci gates
This commit is contained in:
@@ -11,6 +11,40 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
- name: Install project dependencies
|
||||||
|
run: uv sync --extra dev
|
||||||
|
- name: Run ruff lint
|
||||||
|
run: uv run ruff check .
|
||||||
|
|
||||||
|
contract-regression:
|
||||||
|
name: contract-regression
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v5
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
- name: Install project dependencies
|
||||||
|
run: uv sync --extra dev
|
||||||
|
- name: Run MCP response contract regression tests
|
||||||
|
run: uv run pytest tests/test_contract_regression.py
|
||||||
|
|
||||||
pytest-regression:
|
pytest-regression:
|
||||||
name: pytest-regression
|
name: pytest-regression
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# BAT-007 Design
|
||||||
|
|
||||||
|
## Contract versioning design
|
||||||
|
|
||||||
|
- Active MCP success response contract version: `1.0`.
|
||||||
|
- Version format: `major.minor`.
|
||||||
|
- Runtime exposure: unexposed. MCP success payloads are not modified to add a common `contract_version` field.
|
||||||
|
- Source of truth: `docs/interface-contract.md`.
|
||||||
|
- Snapshot metadata: `tests/contract_snapshots/response_contract_v1_0.json`.
|
||||||
|
|
||||||
|
## Change classification
|
||||||
|
|
||||||
|
- Breaking / major: required key removal, required key type change, nested object/list shape change that breaks existing consumers.
|
||||||
|
- Additive / minor: optional field additions and documentation/test metadata additions that preserve existing response semantics.
|
||||||
|
- Every response-shape-changing REQ/BAT must record contract impact: none, additive minor, or breaking major.
|
||||||
|
|
||||||
|
## Contract regression design
|
||||||
|
|
||||||
|
Representative first-wave scopes:
|
||||||
|
|
||||||
|
- health/status: `service.health_check`, `service.get_ledger_db_status`
|
||||||
|
- list: `service.list_ledger_accounts`
|
||||||
|
- report: `service.get_monthly_cashflow_report`
|
||||||
|
- mutation success envelope: `service.create_ledger_account`
|
||||||
|
|
||||||
|
The pytest contract checker validates required keys and required types only. Additional keys are allowed, matching the additive policy.
|
||||||
|
|
||||||
|
Synthetic fixture policy:
|
||||||
|
|
||||||
|
- Test DB paths are created under pytest `tmp_path`.
|
||||||
|
- Fixture data uses synthetic names and amounts only.
|
||||||
|
- No operating DB path, source financial filename, password, raw payload, or real account identifier is used.
|
||||||
|
|
||||||
|
## CI design
|
||||||
|
|
||||||
|
Required-check contract job names:
|
||||||
|
|
||||||
|
- `pytest-regression`
|
||||||
|
- `dashboard-app-test`
|
||||||
|
- `dashboard-http-smoke`
|
||||||
|
- `db-safety-smoke`
|
||||||
|
- `contract-regression`
|
||||||
|
- `lint`
|
||||||
|
|
||||||
|
`.github/workflows/ci.yml` adds:
|
||||||
|
|
||||||
|
- `contract-regression`: `uv run pytest tests/test_contract_regression.py`
|
||||||
|
- `lint`: `uv run ruff check .`
|
||||||
|
|
||||||
|
Actual branch protection toggles remain external GitHub repository settings.
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# BAT-007 Implementation
|
||||||
|
|
||||||
|
## Changed files
|
||||||
|
|
||||||
|
- `.github/workflows/ci.yml`
|
||||||
|
- Added stable required-check candidate jobs: `contract-regression`, `lint`.
|
||||||
|
- `pyproject.toml`, `uv.lock`
|
||||||
|
- Added `ruff` to dev dependencies and conservative `E9`/`F` initial lint config.
|
||||||
|
- `tests/test_contract_regression.py`
|
||||||
|
- Added representative MCP response contract regression tests.
|
||||||
|
- `tests/contract_snapshots/response_contract_v1_0.json`
|
||||||
|
- Added contract `1.0` metadata and required-subset schemas for health/status, list, report, and mutation success envelope scopes.
|
||||||
|
- `docs/interface-contract.md`
|
||||||
|
- Added MCP response contract `1.0` governance, breaking/additive classification, runtime exposure policy, version log, and CI required-check contract.
|
||||||
|
- `docs/project-structure.md`, `docs/runtime-flows.md`
|
||||||
|
- Updated baseline structure/flow docs for contract snapshots/tests, `contract-regression`, `lint`, branch-protection boundary, and synthetic-only invariant.
|
||||||
|
- `docs/batches/index.md`
|
||||||
|
- Registered BAT-007.
|
||||||
|
- `docs/batches/bat-007_20260630_mcp-response-contract-ci-versioning/{index,planning,design,implementation,verification}.md`
|
||||||
|
- Added standard batch delivery docs.
|
||||||
|
- `docs/srs/Interface/req-007_mcp-response-contract-versioning-governance.md`
|
||||||
|
- `docs/srs/Testing/req-008_ci-contract-regression-and-lint-required-checks.md`
|
||||||
|
- `docs/srs/index.md`
|
||||||
|
- Updated REQ state to Implemented after verification evidence.
|
||||||
|
|
||||||
|
## Implementation notes
|
||||||
|
|
||||||
|
- Runtime MCP success payloads were not changed.
|
||||||
|
- Contract version `1.0` is documented in `docs/interface-contract.md` and mirrored in `tests/contract_snapshots/response_contract_v1_0.json`.
|
||||||
|
- Contract regression is representative, not exhaustive.
|
||||||
|
- Lint gate uses conservative `ruff` rules: `E9`, `F`.
|
||||||
|
- All new test data uses pytest temp DBs and synthetic names/amounts only.
|
||||||
|
|
||||||
|
## Verification command log
|
||||||
|
|
||||||
|
Executed from `/root/projects/jarca-ledger-mcp`.
|
||||||
|
|
||||||
|
1. `uv sync --extra dev --extra dashboard`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Resolved 72 packages in 1.37s
|
||||||
|
Building jarca-ledger-mcp @ file:///root/projects/jarca-ledger-mcp
|
||||||
|
Downloading ruff (11.0MiB)
|
||||||
|
Downloaded ruff
|
||||||
|
Built jarca-ledger-mcp @ file:///root/projects/jarca-ledger-mcp
|
||||||
|
Prepared 3 packages in 2.94s
|
||||||
|
Uninstalled 2 packages in 18ms
|
||||||
|
Installed 3 packages in 338ms
|
||||||
|
~ jarca-ledger-mcp==0.1.0 (from file:///root/projects/jarca-ledger-mcp)
|
||||||
|
- pytest==9.1.0
|
||||||
|
+ pytest==9.1.1
|
||||||
|
+ ruff==0.15.20
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `uv run pytest tests/test_contract_regression.py`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```text
|
||||||
|
... [100%]
|
||||||
|
3 passed in 0.99s
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `uv run ruff check .`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```text
|
||||||
|
All checks passed!
|
||||||
|
```
|
||||||
|
|
||||||
|
4. `uv run pytest --ignore=tests/test_dashboard_app.py`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```text
|
||||||
|
...............ss...............ss... [100%]
|
||||||
|
33 passed, 4 skipped in 29.37s
|
||||||
|
```
|
||||||
|
|
||||||
|
5. `uv run python scripts/ci_db_safety_smoke.py --timeout 45`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"gate": "db-safety-smoke",
|
||||||
|
"fixture": "synthetic",
|
||||||
|
"fixture_integrity": "ok",
|
||||||
|
"restore_smoke_exit_code": 0,
|
||||||
|
"restored_exists": true,
|
||||||
|
"restored_integrity": "ok",
|
||||||
|
"health_success": true,
|
||||||
|
"operating_db_used": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
6. `uv run python scripts/ci_dashboard_http_smoke.py --host 127.0.0.1 --port 18501 --timeout 45`
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"gate": "dashboard-http-smoke",
|
||||||
|
"fixture": "synthetic",
|
||||||
|
"fixture_integrity": "ok",
|
||||||
|
"http_status": 200,
|
||||||
|
"attempts": 4,
|
||||||
|
"cleanup": "terminated"
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# BAT-007 MCP response contract / CI contract versioning
|
||||||
|
|
||||||
|
- Profile: standard
|
||||||
|
- Status: release-candidate
|
||||||
|
- Included REQ: REQ-007, REQ-008
|
||||||
|
- Discovery: dcy-007
|
||||||
|
- Owner: 렛저장인
|
||||||
|
- Date (KST): 2026-06-30
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
REQ-007/REQ-008에 따라 MCP 성공 응답 contract version `1.0` governance를 확정하고, representative contract regression과 `ruff` lint를 CI required-check 계약에 추가한다. 런타임 성공 응답 shape는 보존하며 `contract_version` 필드는 노출하지 않는다.
|
||||||
|
|
||||||
|
## 산출물
|
||||||
|
|
||||||
|
- `docs/interface-contract.md`의 versioning governance, change classification, version log, required-check contract
|
||||||
|
- `docs/project-structure.md`, `docs/runtime-flows.md` baseline gap 해소 및 CI/branch-protection/synthetic-only 경계 반영
|
||||||
|
- `tests/test_contract_regression.py`와 `tests/contract_snapshots/response_contract_v1_0.json`
|
||||||
|
- `.github/workflows/ci.yml`의 `contract-regression`, `lint` stable job 추가
|
||||||
|
- `pyproject.toml`의 `ruff` dev dependency 및 보수적 초기 lint rule set
|
||||||
|
- BAT-007 planning/design/implementation/verification 문서
|
||||||
|
|
||||||
|
## 범위 제한
|
||||||
|
|
||||||
|
모든 MCP 도구 100% snapshot 커버리지, 런타임 `contract_version` 노출, breaking response migration, 실제 GitHub branch protection setting 토글, browser visual regression CI gate는 포함하지 않는다.
|
||||||
|
|
||||||
|
## 문서
|
||||||
|
|
||||||
|
- `planning.md`: delivery plan, dependencies, risks
|
||||||
|
- `design.md`: contract governance and CI gate design
|
||||||
|
- `implementation.md`: changed files and actual execution evidence
|
||||||
|
- `verification.md`: AC별 검증 결과와 REQ state 판단
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# BAT-007 Planning
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Implement REQ-007: MCP response contract versioning governance.
|
||||||
|
- Implement REQ-008: representative contract regression and lint required checks.
|
||||||
|
- Preserve existing success response shapes; runtime `contract_version` remains unexposed.
|
||||||
|
- Use synthetic/temp data only for tests and evidence.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- dcy-007 confirmed Discovery and approved REQ-007/REQ-008.
|
||||||
|
- Existing BAT-006 CI workflow and synthetic fixture policy.
|
||||||
|
- Existing service-layer functions that accept `db_path` for temp fixture tests.
|
||||||
|
|
||||||
|
## Work plan
|
||||||
|
|
||||||
|
1. Add BAT-007 standard batch docs and index entry.
|
||||||
|
2. Add contract version `1.0` governance and CI required-check contract to baseline docs.
|
||||||
|
3. Add representative contract snapshot metadata and pytest regression for health/status, list, report, and mutation success envelope scopes.
|
||||||
|
4. Add `ruff` dev dependency/config and CI `lint`/`contract-regression` jobs while preserving existing job names.
|
||||||
|
5. Run narrow local verification: contract pytest, ruff, targeted regression that includes the new test, then full pytest if narrow gates pass.
|
||||||
|
6. If evidence satisfies ACs, move REQ-007 and REQ-008 to Implemented.
|
||||||
|
|
||||||
|
## Risks and mitigations
|
||||||
|
|
||||||
|
- Over-strict snapshot failures: mitigate by required-subset/type checks so optional additive fields pass.
|
||||||
|
- Existing code lint churn: mitigate by conservative initial `ruff` rule set (`E9`, `F`).
|
||||||
|
- Branch protection cannot be toggled from code: document as external operator setting and keep stable job names.
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# BAT-007 Verification
|
||||||
|
|
||||||
|
## Verification summary
|
||||||
|
|
||||||
|
Status: PASS
|
||||||
|
|
||||||
|
REQ-007 and REQ-008 have sufficient repository evidence to move from Approved to Implemented.
|
||||||
|
|
||||||
|
Evidence commands executed from `/root/projects/jarca-ledger-mcp`:
|
||||||
|
|
||||||
|
| Command | Result |
|
||||||
|
| --- | --- |
|
||||||
|
| `uv sync --extra dev --extra dashboard` | PASS; `ruff==0.15.20` installed and lockfile updated. |
|
||||||
|
| `uv run pytest tests/test_contract_regression.py` | PASS; `3 passed in 0.99s`. |
|
||||||
|
| `uv run ruff check .` | PASS; `All checks passed!`. |
|
||||||
|
| `uv run pytest --ignore=tests/test_dashboard_app.py` | PASS; `33 passed, 4 skipped in 29.37s`. |
|
||||||
|
| `uv run python scripts/ci_db_safety_smoke.py --timeout 45` | PASS; JSON `success: true`, `fixture: synthetic`, `operating_db_used: false`. |
|
||||||
|
| `uv run python scripts/ci_dashboard_http_smoke.py --host 127.0.0.1 --port 18501 --timeout 45` | PASS; JSON `success: true`, `fixture: synthetic`, `http_status: 200`. |
|
||||||
|
|
||||||
|
## Acceptance criteria mapping
|
||||||
|
|
||||||
|
### REQ-007
|
||||||
|
|
||||||
|
- AC-1: PASS. `docs/interface-contract.md` defines `major.minor` and initial `1.0` contract version.
|
||||||
|
- AC-2: PASS. Breaking vs additive rules cover key removal, type changes, nested structure changes, and optional field additions.
|
||||||
|
- AC-3: PASS. `docs/interface-contract.md` contains source-of-truth rules and a version log linking dcy-007/REQ-007/BAT-007.
|
||||||
|
- AC-4: PASS. Runtime `contract_version` exposure is explicitly unexposed for `1.0`.
|
||||||
|
- AC-5: PASS. Response-shape-changing REQ/BAT work must state contract impact: none, additive minor, or breaking major.
|
||||||
|
- AC-6: PASS. Runtime success payloads were not changed; contract tests assert existing representative shapes.
|
||||||
|
|
||||||
|
### REQ-008
|
||||||
|
|
||||||
|
- AC-1: PASS. `tests/test_contract_regression.py` covers health/status, list, report, and mutation success envelope representative scopes.
|
||||||
|
- AC-2: PASS. The checker fails on missing required keys, required type changes, and required nested structure changes; additive fields are allowed.
|
||||||
|
- AC-3: PASS. Tests use pytest `tmp_path` synthetic SQLite DBs and committed schema metadata only. Smoke evidence also reports `fixture: synthetic` and `operating_db_used: false` where applicable.
|
||||||
|
- AC-4: PASS. `.github/workflows/ci.yml` includes a `lint` job running `uv run ruff check .`; local ruff verification passed.
|
||||||
|
- AC-5: PASS. Required-check job names are documented and existing BAT-006 job names are preserved.
|
||||||
|
- AC-6: PASS. `docs/interface-contract.md`, `docs/project-structure.md`, and `docs/runtime-flows.md` reflect contract-regression/lint/required-check/synthetic-only boundaries.
|
||||||
|
- AC-7: PASS. Baseline docs state actual branch protection toggles are external operator-managed GitHub repository settings.
|
||||||
|
|
||||||
|
## Requirement state decision
|
||||||
|
|
||||||
|
- REQ-007: Implemented.
|
||||||
|
- REQ-008: Implemented.
|
||||||
|
|
||||||
|
## Blockers
|
||||||
|
|
||||||
|
None.
|
||||||
@@ -14,3 +14,4 @@
|
|||||||
| BAT-004 | batch-lite | release-candidate | REQ-004 | dcy-004 | [bat-004_20260629_browser-streamlit-e2e-evidence](bat-004_20260629_browser-streamlit-e2e-evidence/) |
|
| BAT-004 | batch-lite | release-candidate | REQ-004 | dcy-004 | [bat-004_20260629_browser-streamlit-e2e-evidence](bat-004_20260629_browser-streamlit-e2e-evidence/) |
|
||||||
| BAT-005 | standard | release-candidate | REQ-005 | dcy-005 | [bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow](bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow/) |
|
| BAT-005 | standard | release-candidate | REQ-005 | dcy-005 | [bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow](bat-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow/) |
|
||||||
| BAT-006 | standard | release-candidate | REQ-006 | dcy-006 | [bat-006_20260630_pytest-apptest-smoke-ci-automation](bat-006_20260630_pytest-apptest-smoke-ci-automation/) |
|
| BAT-006 | standard | release-candidate | REQ-006 | dcy-006 | [bat-006_20260630_pytest-apptest-smoke-ci-automation](bat-006_20260630_pytest-apptest-smoke-ci-automation/) |
|
||||||
|
| BAT-007 | standard | release-candidate | REQ-007, REQ-008 | dcy-007 | [bat-007_20260630_mcp-response-contract-ci-versioning](bat-007_20260630_mcp-response-contract-ci-versioning/) |
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
# 0. 문서 상태
|
||||||
|
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||||||
|
|
||||||
|
- 상태: confirmed
|
||||||
|
- 문서 ID: dcy-007_20260630_mcp-response-contract-ci-contract-versioning
|
||||||
|
- 이슈명: MCP response contract / CI contract versioning 정립
|
||||||
|
- 작성 시각(KST): 2026-06-30 18:19 KST
|
||||||
|
- 마지막 갱신 시각(KST): 2026-06-30 18:35 KST
|
||||||
|
- 대체됨: 없음
|
||||||
|
- 후속 Discovery 참조: 없음
|
||||||
|
- 생성된 REQ 참조: REQ-007 (`docs/srs/Interface/req-007_mcp-response-contract-versioning-governance.md`), REQ-008 (`docs/srs/Testing/req-008_ci-contract-regression-and-lint-required-checks.md`)
|
||||||
|
|
||||||
|
# 1. 계획 상태 요약
|
||||||
|
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||||||
|
|
||||||
|
- 연계 문서 현황:
|
||||||
|
- 선행 Discovery: `docs/discovery/dcy-006_20260630_pytest-apptest-smoke-ci-automation.md` (CI 검증 자산 자동 실행·게이트화. 본 Discovery는 그 위에 "계약/버저닝 계층"을 올린다.)
|
||||||
|
- baseline 문서: `docs/interface-contract.md`(MCP response contract 정의처), `docs/project-structure.md`, `docs/runtime-flows.md`(CI 게이트/branch protection 경계 기록처)
|
||||||
|
- 해석:
|
||||||
|
- dcy-006/REQ-006/BAT-006으로 `.github/workflows/ci.yml`와 synthetic fixture·smoke wrapper가 도입되어 "검증을 자동 실행·게이트화"하는 문제는 해소됐다. 그러나 `docs/interface-contract.md`는 여전히 "Formal versioning for MCP response contracts is not yet defined."와 "CI contract for tests/lint is not yet present."를 미해결 갭으로 남겨 둔다. 즉, 실행은 있으나 **계약과 버전 규율**이 비어 있다.
|
||||||
|
- 본 Discovery는 (a) MCP 응답 shape의 formal versioning과 회귀 방지 규율, (b) lint를 포함한 CI required-check 계약과 branch protection 경계 명문화라는 두 contract 갭을 한 주기로 정립하는 것을 목표로 한다.
|
||||||
|
|
||||||
|
# 2. 요구사항 판정 결과
|
||||||
|
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||||||
|
|
||||||
|
- 판정: 신규
|
||||||
|
- 근거
|
||||||
|
- dcy-006은 "이미 있는 검증 자산을 CI로 자동 실행·게이트화"하는 실행 계층을 다뤘고, 응답 shape의 **버전 식별·회귀 잠금**이나 **lint 게이트 계약**을 독립 성공 기준으로 다루지 않았다(dcy-006 Out of Scope에 lint/커버리지 고도화 게이트가 후속으로 분리됨). 따라서 본 주제는 dcy-006의 미세 조정이 아니라 별도 contract governance unit이다.
|
||||||
|
- `docs/interface-contract.md`(Status: draft)가 "Formal versioning for MCP response contracts is not yet defined."(L110)와 "CI contract for tests/lint is not yet present."(L112)를 명시적 미해결 갭으로 기록하고, branch protection required-check는 baseline 문서에서 "repository setting outside this codebase"로만 언급되어 형식 계약이 없다. 기존 후보 주기(dcy-004 browser evidence, dcy-005 dashboard ops hub)와도 문제 정의가 겹치지 않는다.
|
||||||
|
|
||||||
|
# 3. 문제점의 요약
|
||||||
|
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||||||
|
|
||||||
|
- 현재 상태
|
||||||
|
- MCP 응답 contract에 formal versioning이 없다: `docs/interface-contract.md`는 "Preserve existing success response shapes unless a migration/REQ explicitly approves a breaking change."(L51)라는 호환성 규칙만 두고, 응답 contract에 버전 식별자·변경 등급(additive/breaking) 분류·버전 changelog 규율이 없다. 동일 문서가 "Formal versioning for MCP response contracts is not yet defined."(L110)로 갭을 자인한다. 결과적으로 호환성 위반을 자동으로 잡는 기준선이 없다.
|
||||||
|
- 응답 shape 회귀를 막는 게이트가 없다: 응답은 `server.py` 시그니처에서 Python dict로 반환되지만(L39-42), 성공 응답 shape를 고정·검증하는 contract/snapshot 회귀 테스트가 없어, 의도치 않은 키 삭제·타입 변경·구조 변경이 조용히 머지될 수 있다. 추가 필드는 additive 권장이지만 이를 강제하는 자동 검사가 없다.
|
||||||
|
- CI 계약 중 tests는 도입됐으나 lint는 비어 있다: dcy-006으로 `.github/workflows/ci.yml`의 필수 게이트 job(`pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`)이 생겼지만, `pyproject.toml`의 dev extra는 `pytest>=8.0.0`뿐이며 ruff/flake8/black/mypy 등 lint·정적분석 도구나 그 CI job이 없다. `docs/interface-contract.md` L112의 "tests/lint" 중 **lint** 부분이 여전히 미해결이다.
|
||||||
|
- branch protection/required-check 경계가 형식 계약이 아니다: `docs/project-structure.md` L139는 "branch protection required-check enforcement is a repository setting outside this codebase.", `docs/runtime-flows.md` L61은 "Stable job names are intended for branch protection configuration outside the repository."로 기록한다. 즉 어떤 job이 required check인지, 누가 어떻게 그 설정을 유지하는지가 코드 밖 설정에 흩어져 있고 문서화된 계약으로 고정돼 있지 않다.
|
||||||
|
- synthetic-only 경계는 흐름 문서에 있으나 contract 차원으로 승격되지 않았다: `docs/runtime-flows.md` L119는 "CI gates must use synthetic/temp DBs only. The default operating DB path ... is forbidden ..."로 기록하고 `scripts/ci_fixture_db.py`가 기본 운영 DB 경로를 거부한다. 그러나 이 synthetic-only 경계가 CI contract의 명시 불변식(invariant)으로 버전 규율과 함께 묶여 있지는 않다.
|
||||||
|
- 기대 상태
|
||||||
|
- MCP 성공 응답 contract에 버전 식별자와 변경 등급(additive vs breaking) 규율, 버전 changelog가 정의되어, 호환성 위반이 리뷰·게이트에서 객관적으로 판별된다.
|
||||||
|
- 대표 MCP 응답 shape에 대한 contract/snapshot 회귀 테스트가 존재해, 키 삭제·타입 변경 같은 비호환 변경이 CI에서 자동 차단된다.
|
||||||
|
- CI required-check 계약이 문서로 고정되어, tests 게이트(dcy-006)에 더해 lint 게이트가 정의되고, 어떤 job이 required check이며 무엇이 blocker/best-effort인지 단일 출처에서 확인된다.
|
||||||
|
- branch protection/required-check 경계와 synthetic-only CI 경계가 `docs/project-structure.md`·`docs/runtime-flows.md`·`docs/interface-contract.md`에 형식 계약으로 명문화되어 후속 REQ/Batch가 참조 가능하다.
|
||||||
|
- 이해관계자
|
||||||
|
- 렛저장인(운영자 겸 작성자) — MCP 응답 contract와 CI required-check 계약을 정의·유지하고 branch protection 설정을 운영하는 주체
|
||||||
|
- MCP 클라이언트/Hermes Agent — 안정적인 응답 shape에 의존하는 소비자. 호환성 회귀의 직접 피해자.
|
||||||
|
- 검수관/리뷰어 — contract 버전·회귀 게이트·required-check 결과를 호환성·QC 판정 근거로 사용하는 주체
|
||||||
|
- StagePilot 후속 SDLC(REQ/Batch) 수행자 — 본 Discovery를 contract/CI 버저닝 요구사항으로 정제
|
||||||
|
- 제약사항
|
||||||
|
- 개인 금융정보를 다루므로 contract 예시·snapshot·CI 로그에 실계좌번호·잔액·`raw_payload`·원본 파일명·비밀번호가 포함되어서는 안 된다. 모든 contract 회귀 검증은 synthetic fixture를 사용한다.
|
||||||
|
- 응답 contract 변경은 기존 호환성 규칙(L51, additive 우선·breaking은 REQ 승인 필요)을 깨지 않아야 하며, 버저닝 도입 자체가 기존 성공 shape를 변경해서는 안 된다(버전 메타는 additive로 도입).
|
||||||
|
- branch protection은 코드 밖 repository setting이므로 본 주기 산출물은 "어떤 job이 required인지"의 계약 명문화와 설정 절차 문서화까지로 하고, 실제 GitHub 설정 토글은 운영자 승인·실행 사항으로 남긴다.
|
||||||
|
|
||||||
|
## 주요 사용자 시나리오
|
||||||
|
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||||||
|
|
||||||
|
- 작성자가 MCP 도구 응답에서 키 이름을 바꾸거나 필드를 제거하면, contract 회귀 테스트가 CI에서 비호환 변경을 감지해 머지를 차단하고, 의도된 변경이라면 버전 등급 상향과 changelog 갱신을 요구한다.
|
||||||
|
- MCP 클라이언트(Hermes Agent)가 특정 응답 shape에 의존해 자동화를 구성할 때, contract 버전과 changelog를 통해 어떤 변경이 호환 가능한 additive인지, 어떤 변경이 breaking인지 예측 가능하게 판단한다.
|
||||||
|
- 작성자가 PR을 올리면 CI가 lint 게이트를 실행해 스타일·정적 오류를 조기에 잡고, required-check 목록에 포함된 job 통과 없이는 `main` 머지가 차단된다.
|
||||||
|
- 검수관이 호환성/QC 판정을 작성할 때, contract 버전·회귀 게이트 통과 로그와 required-check 결과를 1차 근거로 인용하고, 환경 의존 검증(browser evidence)은 dcy-004 로컬 절차로 보강한다.
|
||||||
|
|
||||||
|
# 4. 이번에 정의할 변경
|
||||||
|
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||||||
|
|
||||||
|
- MCP 응답 contract의 formal versioning 규율을 정의한다: 버전 식별자, additive/breaking 변경 등급 분류, 버전 changelog 위치와 갱신 규칙을 `docs/interface-contract.md` 기준으로 정립한다.
|
||||||
|
- 대표 MCP 성공 응답 shape에 대한 contract/snapshot 회귀 테스트를 도입하고, 이를 CI 필수 게이트로 연결해 비호환 응답 변경을 자동 차단한다.
|
||||||
|
- CI contract에 lint/정적분석 게이트를 추가하고(`docs/interface-contract.md` L112의 lint 갭 해소), tests 게이트(dcy-006)와 함께 required-check 계약을 단일 출처에 문서화한다.
|
||||||
|
- branch protection/required-check 경계와 synthetic-only CI 경계를 형식 계약으로 baseline 문서에 반영한다: `docs/project-structure.md`(L139)와 `docs/runtime-flows.md`(L61, L119)의 느슨한 서술을 "required-check 계약 + synthetic-only 불변식"으로 승격하고, `docs/interface-contract.md`의 Current Gaps(L110-L112)를 갱신한다.
|
||||||
|
|
||||||
|
## 영향 범위
|
||||||
|
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||||||
|
|
||||||
|
- 코드 영향:
|
||||||
|
- `tests/` — MCP 대표 응답 shape contract/snapshot 회귀 테스트 신설(synthetic fixture 기반). 기존 테스트 자산은 보존.
|
||||||
|
- `.github/workflows/ci.yml` — lint job 추가 및 contract 회귀 테스트의 필수 게이트 편입. 기존 job 이름(`pytest-regression` 등)은 branch protection 안정성을 위해 보존.
|
||||||
|
- `pyproject.toml` — lint/정적분석 도구(예: ruff)와 그 설정 추가 가능성. dev extra 구성 확장(현재 `dev=["pytest>=8.0.0"]`).
|
||||||
|
- `jarca_ledger_mcp/server.py` 등 — 응답 contract 버전 메타를 additive로 노출할지 여부 검토(노출 시 기존 성공 shape는 보존). 본 주기에서는 contract 정의·회귀 게이트가 우선이며 런타임 버전 필드 노출은 범위 경계에서 결정.
|
||||||
|
- 문서 영향:
|
||||||
|
- `docs/interface-contract.md` — Compatibility Rules에 버전 식별자·변경 등급·changelog 규율 추가, Current Gaps(L110-L112) 갱신.
|
||||||
|
- `docs/project-structure.md` — branch protection required-check 계약 항목 명문화(L139 승격).
|
||||||
|
- `docs/runtime-flows.md` — CI validation gates 흐름에 lint job·contract 회귀 게이트 추가, required-check/synthetic-only 불변식 명시(L61, L119 승격).
|
||||||
|
- `docs/runbooks/` — branch protection required-check 설정/회복 runbook 후보, contract breaking 변경 처리 runbook 후보.
|
||||||
|
- 운영 영향:
|
||||||
|
- 응답 호환성 회귀가 CI에서 조기 차단되어 MCP 클라이언트 신뢰도가 올라간다.
|
||||||
|
- required-check 목록이 문서로 고정되어 branch protection 설정/감사가 재현 가능해진다.
|
||||||
|
- lint 게이트로 스타일·정적 오류가 리뷰 이전에 걸러진다.
|
||||||
|
|
||||||
|
# 5. 요구사항 목록
|
||||||
|
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||||||
|
|
||||||
|
## 기능 요구사항
|
||||||
|
- FR-1: MCP 응답 contract에 formal versioning 규율을 정의해야 한다 — 버전 식별자, additive/breaking 변경 등급 분류 기준, 버전 changelog 위치·갱신 규칙을 `docs/interface-contract.md`에 명문화한다.
|
||||||
|
- FR-2: 대표 MCP 성공 응답 shape(예: health/status, list/계좌, report 계열)에 대한 contract/snapshot 회귀 테스트를 synthetic fixture 기반으로 제공하고, 키 삭제·타입 변경 등 비호환 변경 시 실패하도록 한다.
|
||||||
|
- FR-3: 위 contract 회귀 테스트를 `.github/workflows/ci.yml`의 필수 게이트로 편입하여 push/PR에서 자동 실행·차단되도록 한다.
|
||||||
|
- FR-4: CI contract에 lint/정적분석 게이트를 추가해야 한다 — lint 도구·대상 경로·실패 시 차단 여부를 정의하고 `pyproject.toml`과 CI workflow에 반영한다(`docs/interface-contract.md` L112 lint 갭 해소).
|
||||||
|
- FR-5: CI required-check 계약과 branch protection 경계, synthetic-only CI 불변식을 `docs/project-structure.md`와 `docs/runtime-flows.md`에 반영하고("repository setting outside this codebase" 서술을 형식 계약으로 승격), `docs/interface-contract.md`의 Current Gaps(L110-L112)를 갱신해야 한다.
|
||||||
|
|
||||||
|
## 비기능 요구사항
|
||||||
|
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||||||
|
|
||||||
|
- NFR-1: 버저닝 도입은 후방 호환이어야 한다 — 버전 메타/contract 정의를 추가하더라도 기존 MCP 성공 응답 shape를 변경하지 않는다(additive 원칙, `docs/interface-contract.md` L51 준수).
|
||||||
|
- NFR-2: contract 회귀 테스트와 lint 게이트는 결정적이어야 한다 — synthetic fixture·고정 입력으로 동일 커밋에 동일 결과를 산출하고 flaky 실패를 최소화한다.
|
||||||
|
- NFR-3: contract 예시·snapshot·CI 로그에 실계좌번호·잔액·`raw_payload`·원본 파일명·비밀번호 등 민감정보가 평문 노출되어서는 안 되며, 모든 contract 검증은 synthetic fixture만 사용한다(`docs/runtime-flows.md` L119 불변식 준수).
|
||||||
|
- NFR-4: required-check 계약은 기존 안정 job 이름(`pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`)을 보존해 branch protection 설정을 깨지 않으면서 확장 가능해야 한다.
|
||||||
|
|
||||||
|
## 범위 경계
|
||||||
|
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||||||
|
|
||||||
|
- In Scope
|
||||||
|
- MCP 응답 contract의 formal versioning 규율 정의(버전 식별자·변경 등급·changelog)
|
||||||
|
- 대표 응답 shape contract/snapshot 회귀 테스트 도입 및 CI 필수 게이트 편입
|
||||||
|
- lint/정적분석 CI 게이트 추가와 required-check 계약 문서화
|
||||||
|
- branch protection/required-check 경계와 synthetic-only 불변식의 baseline 문서 명문화 및 interface-contract Current Gaps 갱신
|
||||||
|
- Out of Scope
|
||||||
|
- 모든 MCP 도구에 대한 100% 응답 shape snapshot 커버리지(대표 그룹 우선, 전수 커버리지는 후속). 누락 범위는 문서에 명시.
|
||||||
|
- 응답 contract의 breaking 변경 실제 수행(본 주기는 버저닝·회귀 게이트 정립까지이며 호환성 마이그레이션은 별도 REQ).
|
||||||
|
- 배포/릴리스 자동화(CD), 외부 호스팅 도입(dcy-006 Out of Scope 유지).
|
||||||
|
- 실제 GitHub branch protection 토글 적용(코드 밖 repository setting). 본 주기는 required-check 계약 명문화와 설정 절차 문서화까지로 한정.
|
||||||
|
- 커버리지 목표치 강제·성능 벤치마크 게이트 같은 고도화 품질 게이트(후속 반복).
|
||||||
|
|
||||||
|
# 6. 리스크/가정 목록
|
||||||
|
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||||||
|
|
||||||
|
## 리스크
|
||||||
|
- R-1 (Medium): contract/snapshot 회귀 테스트가 지나치게 엄격하면 정상적인 additive 변경(필드 추가)까지 실패시켜 개발 마찰이 커진다.
|
||||||
|
- 완화 방안: 회귀 기준을 "키 삭제·타입 변경·구조 변경은 실패, additive 필드 추가는 허용"으로 등급화하고, snapshot 갱신 절차와 변경 등급 판정 가이드를 함께 문서화한다.
|
||||||
|
- R-2 (Medium): lint 도구를 required 게이트로 한 번에 강제하면 기존 코드베이스에서 대량 위반이 발생해 도입이 막힐 수 있다.
|
||||||
|
- 완화 방안: lint 도입 시 초기 룰셋을 보수적으로 잡거나 단계적 적용(우선 best-effort → 안정화 후 required)을 선택지로 두고, 사용자 결정으로 차단 여부를 확정한다.
|
||||||
|
- R-3 (Low): branch protection이 코드 밖 설정이라, 문서화한 required-check 계약과 실제 GitHub 설정이 시간이 지나며 어긋날 수 있다(drift).
|
||||||
|
- 완화 방안: required-check 계약을 단일 출처(baseline 문서)에 고정하고, 설정/감사 절차를 runbook 후보로 남겨 drift를 점검 가능하게 한다.
|
||||||
|
|
||||||
|
## 가정
|
||||||
|
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||||||
|
|
||||||
|
- A-1: dcy-006/REQ-006/BAT-006의 CI workflow(`.github/workflows/ci.yml`)와 synthetic fixture builder(`scripts/ci_fixture_db.py`)가 운영 가능한 상태이며, 본 주기는 그 위에 contract 회귀·lint job을 확장한다.
|
||||||
|
- A-2: 대표 MCP 응답 그룹(health/status, list, report 계열)의 성공 shape는 synthetic fixture만으로 결정적으로 재현 가능하다.
|
||||||
|
- A-3: 응답 contract 버저닝은 메타데이터 additive 또는 문서·테스트 차원에서 도입 가능하며, 기존 성공 shape를 깨지 않고 적용할 수 있다.
|
||||||
|
- A-4: branch protection required-check 설정은 운영자가 GitHub repository setting에서 적용 가능하고, 본 주기 산출물(required-check 계약 문서)을 그 설정 근거로 사용할 수 있다.
|
||||||
|
|
||||||
|
## 오픈 질문
|
||||||
|
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||||||
|
|
||||||
|
- OQ-1: MCP 응답 contract 버전 식별 체계를 무엇으로 확정할 것인가(semver 형식 contract 버전 vs 날짜 기반 vs 단조 증가 정수)와, 버전 메타를 런타임 응답에 additive로 노출할지 문서·테스트 차원에만 둘지?
|
||||||
|
- 상태: Answered
|
||||||
|
- 결정: contract 버전 체계는 SemVer 스타일 `major.minor`로 시작하며 초기값은 `1.0`이다. breaking change는 `major`, additive/backward-compatible change는 `minor`를 올린다. 초기 단계에서는 버전 메타를 런타임 응답에 노출하지 않고, `docs/interface-contract.md`와 contract regression test/snapshot metadata에서 우선 관리한다.
|
||||||
|
- 종료 근거: 사용자 승인으로 버전 체계와 노출 범위가 확정되었으며, 이는 FR-1/NFR-1과 REQ acceptance 기준의 기본안으로 사용한다.
|
||||||
|
- OQ-2: lint/정적분석 게이트를 어떤 도구로, 처음부터 required 차단 게이트로 둘지 아니면 단계적(best-effort 후 required)으로 둘지?
|
||||||
|
- 상태: Answered
|
||||||
|
- 결정: lint 도구는 `ruff`를 사용하고, 처음부터 required 차단 게이트로 둔다. 단, 초기 룰셋은 기존 코드베이스를 과도하게 흔들지 않도록 보수적으로 시작한다.
|
||||||
|
- 종료 근거: 사용자 승인으로 lint 도구와 차단 강도가 확정되었으며, 이는 FR-4와 required-check 계약 정의의 기본안으로 사용한다.
|
||||||
|
|
||||||
|
# 7. 초기 성공 기준
|
||||||
|
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||||||
|
|
||||||
|
- S-1: MCP 대표 응답 shape의 키를 삭제하거나 타입을 바꾸는 의도적 비호환 변경을 주입하면 contract 회귀 테스트가 CI에서 실패해 머지를 차단함을 재현 가능하게 확인한다.
|
||||||
|
- S-2: lint 게이트가 CI에서 실행되어 의도적 lint 위반에 대해 정의된 차단 강도대로 동작하고(required면 차단, best-effort면 경고), 로그·아티팩트에 민감정보가 노출되지 않음을 확인한다.
|
||||||
|
- S-3: MCP 응답 contract 버저닝 규율과 required-check/synthetic-only 계약이 `docs/interface-contract.md`(Current Gaps L110-L112 갱신), `docs/project-structure.md`(L139 승격), `docs/runtime-flows.md`(L61/L119 승격)에 반영되어 후속 REQ/Batch에서 참조 가능하다.
|
||||||
|
|
||||||
|
## 측정 방식 및 데이터 출처
|
||||||
|
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||||||
|
|
||||||
|
- S-1 측정 방식: 비호환 변경 주입 시 contract 회귀 테스트 fail 재현 확인 / 데이터 출처: CI workflow 실행 로그, contract/snapshot 테스트 결과, synthetic fixture DB
|
||||||
|
- S-2 측정 방식: lint job 실행 로그와 의도적 위반에 대한 차단/경고 동작 확인, 로그·아티팩트 민감정보 미노출 검수 / 데이터 출처: CI job 로그·아티팩트, `pyproject.toml` lint 설정
|
||||||
|
- S-3 측정 방식: baseline·interface-contract 문서 갱신 여부 리뷰(갭 항목 해소·계약 승격 확인) / 데이터 출처: `docs/interface-contract.md`, `docs/project-structure.md`, `docs/runtime-flows.md`, `docs/runbooks/`
|
||||||
|
|
||||||
|
# 8. REQ로 넘기기 전 확인 체크
|
||||||
|
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||||||
|
|
||||||
|
- [x] 범위(In Scope/Out of Scope) 확정
|
||||||
|
- 기준: 본 주기를 contract 버저닝 정립 + 대표 shape 회귀 게이트 + lint 게이트 + 계약 문서화까지로 한정하고, breaking 마이그레이션·전수 snapshot·실제 branch protection 토글은 제외한다는 점을 사용자가 승인했다.
|
||||||
|
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||||||
|
- 기준: CI 실행 로그·contract 회귀 테스트·lint 로그·baseline 문서 갱신을 acceptance 측정 근거로 사용한다는 점을 승인했다.
|
||||||
|
- [x] High 영향 리스크 우선순위 및 해소 방향 확정
|
||||||
|
- 기준: 본 Discovery에 현재 High 리스크는 없으며, 회귀 게이트 과엄격(R-1)은 additive 허용 규칙과 snapshot 갱신 절차로, lint 도입 마찰(R-2)은 보수적 `ruff` 룰셋으로 완화한다는 방향을 확정했다.
|
||||||
|
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||||||
|
- 기준: OQ-1(버전 체계·노출 범위)과 OQ-2(lint 도구·차단 강도)를 모두 Answered로 정리했다.
|
||||||
|
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||||||
|
- 기준: 사용자(진환 고)가 권장안을 승인하고 confirm 진행을 요청했다.
|
||||||
|
|
||||||
|
# 9. 파일 처리 결과
|
||||||
|
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||||||
|
|
||||||
|
- 처리 결과: 생성
|
||||||
|
- 생성 경로: docs/discovery/dcy-007_20260630_mcp-response-contract-ci-contract-versioning.md
|
||||||
|
- 참조 문서:
|
||||||
|
- docs/discovery/dcy-006_20260630_pytest-apptest-smoke-ci-automation.md
|
||||||
|
- docs/interface-contract.md
|
||||||
|
- docs/project-structure.md
|
||||||
|
- docs/runtime-flows.md
|
||||||
|
- stage-pilot/templates/project-structure.md
|
||||||
|
- stage-pilot/templates/runtime-flows.md
|
||||||
|
|
||||||
|
# 10. 사용자 결정 필요 항목 요약
|
||||||
|
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||||||
|
|
||||||
|
## 확정안
|
||||||
|
- Contract version scheme: SemVer 스타일 `major.minor`를 사용하고 초기값은 `1.0`으로 둔다. breaking change는 `major`, additive/backward-compatible change는 `minor`를 올린다.
|
||||||
|
- Runtime exposure: 초기 단계에서는 버전 메타를 런타임 응답에 노출하지 않고, `docs/interface-contract.md`와 contract regression test/snapshot metadata에서 우선 관리한다.
|
||||||
|
- Lint tool / gate strictness: `ruff`를 사용하고 처음부터 required 차단 게이트로 둔다. 단, 초기 룰셋은 보수적으로 시작한다.
|
||||||
|
- Required check list: `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, `lint`를 required-check 계약 기본안으로 둔다. 실제 GitHub branch protection 토글 적용은 운영자 실행 범위다.
|
||||||
|
- Contract snapshot scope: 1차 범위는 `health/status`, `list`, `report`, `mutation success envelope` 대표 응답군으로 한정하고, 전수 커버리지는 후속 반복으로 미룬다.
|
||||||
|
- Synthetic contract fixture policy: 운영/목록/리포트/변경 결과를 모두 재현하는 synthetic 대표 fixture 세트를 사용하며, 운영 DB·실계좌번호·실잔액·`raw_payload`·원본 파일명·비밀번호는 포함하지 않는다.
|
||||||
|
|
||||||
|
## REQ 단계 입력으로 넘길 항목
|
||||||
|
- `contract-regression` 테스트 대상 응답군의 세부 tool 목록과 snapshot 파일 구조
|
||||||
|
- `ruff` 초기 룰셋의 세부 enable/ignore 목록
|
||||||
|
- representative synthetic fixture 응답 샘플 세트의 파일 경로와 생성 방식
|
||||||
|
|
||||||
|
# 11. Discovery Freeze
|
||||||
|
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||||||
|
|
||||||
|
## 섹션 참조
|
||||||
|
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||||||
|
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||||||
|
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||||||
|
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||||||
|
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||||||
|
|
||||||
|
## Freeze 플래그
|
||||||
|
- Handoff Decision: 승인
|
||||||
|
- Handoff Rationale: 사용자 승인으로 contract version scheme, lint gate, required-check 목록, snapshot 우선 범위, synthetic fixture 정책이 확정되었다. 남은 세부 항목은 REQ 단계 입력으로 정리 가능하며 Discovery 차원의 차단 사유가 아니다.
|
||||||
|
- Ready for REQ Drafting: true
|
||||||
|
- Confirmed By: 진환 고
|
||||||
|
- Confirmed At (KST): 2026-06-30 18:34 KST
|
||||||
@@ -12,3 +12,4 @@
|
|||||||
| dcy-004_20260629_browser-streamlit-e2e-evidence-recovery | 20260629 | Browser/Streamlit E2E evidence 환경 복구 및 QC evidence 기준 강화 | [Discovery](./dcy-004_20260629_browser-streamlit-e2e-evidence-recovery.md) | confirmed |
|
| dcy-004_20260629_browser-streamlit-e2e-evidence-recovery | 20260629 | Browser/Streamlit E2E evidence 환경 복구 및 QC evidence 기준 강화 | [Discovery](./dcy-004_20260629_browser-streamlit-e2e-evidence-recovery.md) | confirmed |
|
||||||
| dcy-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow | 20260629 | Jarca Ledger 대시보드 운영 허브 사양 정립 (이번 달 현금흐름 + 자산 유동성 + 운영 미처리) | [Discovery](./dcy-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow.md) | confirmed |
|
| dcy-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow | 20260629 | Jarca Ledger 대시보드 운영 허브 사양 정립 (이번 달 현금흐름 + 자산 유동성 + 운영 미처리) | [Discovery](./dcy-005_20260629_dashboard-operations-hub-liquidity-monthly-cashflow.md) | confirmed |
|
||||||
| dcy-006_20260630_pytest-apptest-smoke-ci-automation | 20260630 | pytest/AppTest/Smoke CI 자동화 | [Discovery](./dcy-006_20260630_pytest-apptest-smoke-ci-automation.md) | confirmed |
|
| dcy-006_20260630_pytest-apptest-smoke-ci-automation | 20260630 | pytest/AppTest/Smoke CI 자동화 | [Discovery](./dcy-006_20260630_pytest-apptest-smoke-ci-automation.md) | confirmed |
|
||||||
|
| dcy-007_20260630_mcp-response-contract-ci-contract-versioning | 20260630 | MCP response contract / CI contract versioning 정립 | [Discovery](./dcy-007_20260630_mcp-response-contract-ci-contract-versioning.md) | confirmed |
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
- Baseline Mode: observed
|
- Baseline Mode: observed
|
||||||
- Baseline Seed: `.stage-pilot/bootstrap/baseline.yaml`
|
- Baseline Seed: `.stage-pilot/bootstrap/baseline.yaml`
|
||||||
- Status: draft
|
- Status: governed
|
||||||
- Owner: Jarca / project maintainer
|
- Owner: Jarca / project maintainer
|
||||||
- Last Updated (KST): 2026-06-29
|
- Last Updated (KST): 2026-06-30
|
||||||
- Source Discovery / Batch: bootstrap-baseline
|
- Source Discovery / Batch: bootstrap-baseline, dcy-007, BAT-007
|
||||||
|
|
||||||
## Project Summary
|
## Project Summary
|
||||||
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
|
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
- Python dictionary payloads containing success data, counts, records, previews, reports, or error details.
|
- Python dictionary payloads containing success data, counts, records, previews, reports, or error details.
|
||||||
- Backup/status tools return metadata-only DB evidence: path, size, mtime, sha256, integrity check, table row counts, and aggregate totals.
|
- Backup/status tools return metadata-only DB evidence: path, size, mtime, sha256, integrity check, table row counts, and aggregate totals.
|
||||||
- Existing success shape compatibility is preferred; new fields should be additive where possible.
|
- Existing success shape compatibility is preferred; new fields should be additive where possible.
|
||||||
|
- Contract version metadata is not exposed in runtime success payloads for version `1.0`; version governance is tracked in this document and contract regression snapshot metadata.
|
||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
- Validation errors for invalid parameters or missing IDs.
|
- Validation errors for invalid parameters or missing IDs.
|
||||||
@@ -49,10 +50,36 @@
|
|||||||
|
|
||||||
### Compatibility Rules
|
### Compatibility Rules
|
||||||
- Preserve existing success response shapes unless a migration/REQ explicitly approves a breaking change.
|
- Preserve existing success response shapes unless a migration/REQ explicitly approves a breaking change.
|
||||||
|
- MCP success response contract version uses `major.minor`; the initial governed contract version is `1.0`.
|
||||||
|
- Increment `major` for breaking changes, including required key removal, existing key type change, or nested object/list structure changes that break existing consumers.
|
||||||
|
- Increment `minor` for backward-compatible additive changes, including optional field additions or documentation/test metadata that preserves existing payload semantics.
|
||||||
|
- Ambiguous response-shape changes must identify contract impact in the owning REQ/BAT and be reviewed against this document before implementation.
|
||||||
- Additive backup/status/list/hash tools are allowed; restore execution must not be added as an MCP tool.
|
- Additive backup/status/list/hash tools are allowed; restore execution must not be added as an MCP tool.
|
||||||
- Do not write secrets, raw passwords, raw_payload, full merchant/counterparty dumps, or full account identifiers into persistent output, skill files, or committed documentation.
|
- Do not write secrets, raw passwords, raw_payload, full merchant/counterparty dumps, or full account identifiers into persistent output, skill files, or committed documentation.
|
||||||
- Source evidence should remain auditable through raw fields and import batch records.
|
- Source evidence should remain auditable through raw fields and import batch records.
|
||||||
|
|
||||||
|
### MCP Response Contract Version Governance
|
||||||
|
- Source of truth: this `docs/interface-contract.md` section defines the active contract version, change classification rules, and runtime exposure policy.
|
||||||
|
- Snapshot metadata: representative contract regression fixtures live under `tests/contract_snapshots/` and must carry the same contract version as this document.
|
||||||
|
- Runtime exposure policy: `contract_version` remains unexposed in MCP success responses for `1.0`; adding it to runtime payloads requires a future approved REQ/migration because it changes every success envelope, even if additive.
|
||||||
|
- Required first-wave regression scopes: health/status, list, report, and mutation success envelope responses.
|
||||||
|
- Additive policy: contract tests intentionally assert a required subset and required types so newly added optional fields do not fail the gate.
|
||||||
|
- Breaking-change policy: removing a required key, changing a required key type, or changing a required nested shape must fail contract regression unless an approved REQ/BAT updates the contract version and snapshot metadata.
|
||||||
|
- Every REQ/BAT that changes MCP success response shapes must state one of: `contract impact: none`, `contract impact: additive minor`, or `contract impact: breaking major`.
|
||||||
|
|
||||||
|
### MCP Response Contract Version Log
|
||||||
|
| Version | Date (KST) | Change Type | Source | Notes |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| 1.0 | 2026-06-30 | Initial governed baseline | dcy-007 / REQ-007 / BAT-007 | Formalizes compatibility governance without exposing runtime version fields or changing success payload shapes. |
|
||||||
|
|
||||||
|
## Interface: CI Required-Check Contract
|
||||||
|
- Required-check source of truth in code: `.github/workflows/ci.yml` job names are stable and must be mirrored by repository branch protection settings.
|
||||||
|
- Required-check default set for PR/push merge blocking: `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, `lint`.
|
||||||
|
- `contract-regression` runs `pytest tests/test_contract_regression.py` against synthetic temp fixtures and representative response snapshot metadata.
|
||||||
|
- `lint` runs `ruff check .` with the conservative BAT-007 initial rule set (`E9`, `F`) as a required blocking gate.
|
||||||
|
- Actual GitHub branch protection toggles are repository settings outside this codebase; this document and the workflow define the intended contract for the operator to apply and audit.
|
||||||
|
- CI contract tests, AppTest, HTTP smoke, and DB safety smoke must use synthetic/temp DBs only and must not read the default operating DB path.
|
||||||
|
|
||||||
## Interface: Streamlit Dashboard
|
## Interface: Streamlit Dashboard
|
||||||
- Type: web-app
|
- Type: web-app
|
||||||
- Actors: Local operator
|
- Actors: Local operator
|
||||||
@@ -107,9 +134,9 @@
|
|||||||
- Financial data and passwords are sensitive runtime inputs and should not be committed.
|
- Financial data and passwords are sensitive runtime inputs and should not be committed.
|
||||||
|
|
||||||
## Current Gaps / Planned Changes
|
## Current Gaps / Planned Changes
|
||||||
- Formal versioning for MCP response contracts is not yet defined.
|
- Formal MCP response contract versioning is defined for version `1.0`; full tool-surface snapshot coverage remains a future expansion beyond the representative BAT-007 scope.
|
||||||
- Browser-level UI evidence criteria are captured in REQ-004; broader CI automation is still not present.
|
- Browser-level UI evidence criteria are captured in REQ-004 and remain local evidence rather than a required CI gate unless a future REQ changes that policy.
|
||||||
- CI contract for tests/lint is not yet present.
|
- CI tests/lint required-check contract is defined for the six stable jobs listed above; actual branch protection enforcement remains an operator-managed repository setting.
|
||||||
|
|
||||||
## Change Log
|
## Change Log
|
||||||
- 2026-06-26 22:24 KST: Created by `bootstrap-baseline` from repository observation.
|
- 2026-06-26 22:24 KST: Created by `bootstrap-baseline` from repository observation.
|
||||||
@@ -117,3 +144,4 @@
|
|||||||
- 2026-06-29 KST: REQ-003/BAT-003 기준으로 account statement status update와 balance reconciliation report contract 추가.
|
- 2026-06-29 KST: REQ-003/BAT-003 기준으로 account statement status update와 balance reconciliation report contract 추가.
|
||||||
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 Streamlit browser evidence 산출물과 UI evidence gate 규칙 추가.
|
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 Streamlit browser evidence 산출물과 UI evidence gate 규칙 추가.
|
||||||
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 account `liquidity_bucket`, `get_asset_liquidity_report`, dashboard operations hub contract 추가.
|
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 account `liquidity_bucket`, `get_asset_liquidity_report`, dashboard operations hub contract 추가.
|
||||||
|
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 MCP response contract `1.0` governance, representative contract-regression scope, `ruff` lint, six-job required-check contract를 추가.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
- Status: draft
|
- Status: draft
|
||||||
- Owner: Jarca / project maintainer
|
- Owner: Jarca / project maintainer
|
||||||
- Last Updated (KST): 2026-06-30
|
- Last Updated (KST): 2026-06-30
|
||||||
- Source Discovery / Batch: bootstrap-baseline, BAT-006
|
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007
|
||||||
|
|
||||||
## Project Summary
|
## Project Summary
|
||||||
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
|
- Jarca 가계부 전용 MCP 서버와 Streamlit 대시보드로 계좌/카드 거래, 예산, 분류 규칙, import, 현금흐름 분석을 SQLite에 저장하고 운영한다.
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
- pandas / plotly dashboard dependencies
|
- pandas / plotly dashboard dependencies
|
||||||
- PyMuPDF, openpyxl, msoffcrypto-tool
|
- PyMuPDF, openpyxl, msoffcrypto-tool
|
||||||
- pytest
|
- pytest
|
||||||
|
- ruff
|
||||||
|
|
||||||
## Primary Runtime
|
## Primary Runtime
|
||||||
- Type: mixed
|
- Type: mixed
|
||||||
@@ -60,6 +61,9 @@ jarca-ledger-mcp/
|
|||||||
│ ├── toss_securities_pdf.py
|
│ ├── toss_securities_pdf.py
|
||||||
│ └── seoulpay_xlsx.py
|
│ └── seoulpay_xlsx.py
|
||||||
├── tests/
|
├── tests/
|
||||||
|
│ ├── contract_snapshots/
|
||||||
|
│ │ └── response_contract_v1_0.json
|
||||||
|
│ ├── test_contract_regression.py
|
||||||
│ ├── test_service.py
|
│ ├── test_service.py
|
||||||
│ ├── test_categorization.py
|
│ ├── test_categorization.py
|
||||||
│ ├── test_category_rule_filters.py
|
│ ├── test_category_rule_filters.py
|
||||||
@@ -106,9 +110,11 @@ jarca-ledger-mcp/
|
|||||||
- `dashboard/app.py`
|
- `dashboard/app.py`
|
||||||
- Streamlit operator UI for the operations hub summary, transaction filtering, uncategorized triage, rule management, review queues, categories, accounts/liquidity bucket editing, and import operations.
|
- Streamlit operator UI for the operations hub summary, transaction filtering, uncategorized triage, rule management, review queues, categories, accounts/liquidity bucket editing, and import operations.
|
||||||
- `tests/`
|
- `tests/`
|
||||||
- Regression tests for service behavior, parser behavior, categorization, category rules, cross-account transfer matching, ledger category semantics, and Streamlit AppTest dashboard smoke.
|
- Regression tests for service behavior, parser behavior, categorization, category rules, cross-account transfer matching, ledger category semantics, MCP response contract compatibility, and Streamlit AppTest dashboard smoke.
|
||||||
|
- `tests/contract_snapshots/`
|
||||||
|
- Representative MCP response contract metadata/schemas. Version `1.0` is used by `tests/test_contract_regression.py` and intentionally asserts required subset/types so optional additive fields remain compatible.
|
||||||
- `.github/workflows/ci.yml`
|
- `.github/workflows/ci.yml`
|
||||||
- GitHub Actions workflow for `pull_request`, `push` to `main`, and `workflow_dispatch`; stable required gate jobs are `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, and `db-safety-smoke`.
|
- GitHub Actions workflow for `pull_request`, `push` to `main`, and `workflow_dispatch`; stable required gate jobs are `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, and `lint`.
|
||||||
- `scripts/ci_fixture_db.py`
|
- `scripts/ci_fixture_db.py`
|
||||||
- Synthetic fixture DB builder for CI/AppTest/smoke checks. Refuses the default operating DB path and seeds only synthetic ledger data.
|
- Synthetic fixture DB builder for CI/AppTest/smoke checks. Refuses the default operating DB path and seeds only synthetic ledger data.
|
||||||
- `scripts/ci_dashboard_http_smoke.py`
|
- `scripts/ci_dashboard_http_smoke.py`
|
||||||
@@ -133,10 +139,12 @@ jarca-ledger-mcp/
|
|||||||
- Manual reconciliation is represented through memo tags and narrow transaction updates, not broad rewrites.
|
- Manual reconciliation is represented through memo tags and narrow transaction updates, not broad rewrites.
|
||||||
- Browser evidence artifacts are generated from synthetic/validation DBs and should not embed real financial identifiers or raw source payloads.
|
- Browser evidence artifacts are generated from synthetic/validation DBs and should not embed real financial identifiers or raw source payloads.
|
||||||
- CI gate fixtures are generated at runtime under temp space by `scripts/ci_fixture_db.py`; CI and AppTest must not read the default operating DB path.
|
- CI gate fixtures are generated at runtime under temp space by `scripts/ci_fixture_db.py`; CI and AppTest must not read the default operating DB path.
|
||||||
|
- MCP contract regression fixtures use pytest `tmp_path` synthetic DBs and committed schema metadata only; they must not read the operating DB or expose runtime `contract_version` fields.
|
||||||
|
- Branch protection required-check contract is documented in `docs/interface-contract.md` and represented by stable workflow job names. Applying branch protection remains an external GitHub repository setting.
|
||||||
|
|
||||||
## Current Gaps / Planned Changes
|
## Current Gaps / Planned Changes
|
||||||
- Local browser evidence capture exists for core Streamlit pages; browser screenshot/visual regression remains local evidence rather than a required CI gate.
|
- Local browser evidence capture exists for core Streamlit pages; browser screenshot/visual regression remains local evidence rather than a required CI gate.
|
||||||
- CI workflow now covers pytest/AppTest/HTTP smoke/DB safety smoke; branch protection required-check enforcement is a repository setting outside this codebase.
|
- CI workflow now covers pytest/AppTest/HTTP smoke/DB safety smoke plus BAT-007 contract-regression/lint required-check jobs; branch protection enforcement remains a repository setting outside this codebase.
|
||||||
- Long-term policy for accounts without downloadable statements remains partially operational/documentation work.
|
- Long-term policy for accounts without downloadable statements remains partially operational/documentation work.
|
||||||
- Separate automatic asset snapshots/manual asset tables are not present; BAT-005 uses account rows as the manual asset/liability source.
|
- Separate automatic asset snapshots/manual asset tables are not present; BAT-005 uses account rows as the manual asset/liability source.
|
||||||
|
|
||||||
@@ -150,3 +158,4 @@ jarca-ledger-mcp/
|
|||||||
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 `scripts/streamlit_browser_evidence.py` 및 browser evidence artifact 구조 추가.
|
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 `scripts/streamlit_browser_evidence.py` 및 browser evidence artifact 구조 추가.
|
||||||
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub와 service asset liquidity aggregation 책임 추가.
|
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub와 service asset liquidity aggregation 책임 추가.
|
||||||
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI workflow, synthetic fixture DB builder, AppTest/HTTP/DB safety smoke wrappers, CI-vs-local-browser evidence 경계 추가.
|
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI workflow, synthetic fixture DB builder, AppTest/HTTP/DB safety smoke wrappers, CI-vs-local-browser evidence 경계 추가.
|
||||||
|
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 MCP response contract snapshot/test 위치와 `contract-regression`/`lint` required-check job 계약 추가.
|
||||||
|
|||||||
+13
-7
@@ -5,7 +5,7 @@
|
|||||||
- Status: draft
|
- Status: draft
|
||||||
- Owner: Jarca / project maintainer
|
- Owner: Jarca / project maintainer
|
||||||
- Last Updated (KST): 2026-06-30
|
- Last Updated (KST): 2026-06-30
|
||||||
- Source Discovery / Batch: bootstrap-baseline, BAT-006
|
- Source Discovery / Batch: bootstrap-baseline, BAT-006, BAT-007
|
||||||
- Primary Runtime: mixed
|
- Primary Runtime: mixed
|
||||||
|
|
||||||
## Project Summary
|
## Project Summary
|
||||||
@@ -54,12 +54,14 @@
|
|||||||
|
|
||||||
## Flow: `CI validation gates`
|
## Flow: `CI validation gates`
|
||||||
1. GitHub Actions starts `.github/workflows/ci.yml` on `pull_request`, `push` to `main`, or `workflow_dispatch`.
|
1. GitHub Actions starts `.github/workflows/ci.yml` on `pull_request`, `push` to `main`, or `workflow_dispatch`.
|
||||||
2. `pytest-regression` installs the project with dev/dashboard extras and runs `pytest --ignore=tests/test_dashboard_app.py` so service/parser/regression failures fail the workflow.
|
2. `lint` installs dev dependencies and runs `ruff check .` with the conservative BAT-007 required rule set.
|
||||||
3. `dashboard-app-test` runs `pytest tests/test_dashboard_app.py`; the AppTest test creates a synthetic fixture DB through `scripts/ci_fixture_db.py`, sets `JARCA_LEDGER_DB_PATH`, and asserts core dashboard pages render without Streamlit exceptions.
|
3. `contract-regression` installs dev dependencies and runs `pytest tests/test_contract_regression.py`; the test uses synthetic pytest temp DBs and `tests/contract_snapshots/response_contract_v1_0.json` metadata for health/status, list, report, and mutation success envelope scopes.
|
||||||
4. `dashboard-http-smoke` runs `scripts/ci_dashboard_http_smoke.py`; the wrapper creates a temp fixture DB, starts Streamlit on loopback, polls for HTTP 200, then terminates the process.
|
4. `pytest-regression` installs the project with dev/dashboard extras and runs `pytest --ignore=tests/test_dashboard_app.py` so service/parser/regression failures fail the workflow.
|
||||||
5. `db-safety-smoke` runs `scripts/ci_db_safety_smoke.py`; the wrapper creates temp source/backup/target DB files and invokes `scripts/ledger_restore_smoke.py` without touching the operating DB path.
|
5. `dashboard-app-test` runs `pytest tests/test_dashboard_app.py`; the AppTest test creates a synthetic fixture DB through `scripts/ci_fixture_db.py`, sets `JARCA_LEDGER_DB_PATH`, and asserts core dashboard pages render without Streamlit exceptions.
|
||||||
6. Any job failure fails the workflow. Stable job names are intended for branch protection configuration outside the repository.
|
6. `dashboard-http-smoke` runs `scripts/ci_dashboard_http_smoke.py`; the wrapper creates a temp fixture DB, starts Streamlit on loopback, polls for HTTP 200, then terminates the process.
|
||||||
7. CI logs are status-oriented and synthetic-data-only; they must not contain real account identifiers, raw payloads, statement filenames, balances from the operating DB, or passwords.
|
7. `db-safety-smoke` runs `scripts/ci_db_safety_smoke.py`; the wrapper creates temp source/backup/target DB files and invokes `scripts/ledger_restore_smoke.py` without touching the operating DB path.
|
||||||
|
8. Any required job failure fails the workflow. Stable required-check job names are `pytest-regression`, `dashboard-app-test`, `dashboard-http-smoke`, `db-safety-smoke`, `contract-regression`, and `lint`; actual branch protection toggle enforcement is configured outside the repository.
|
||||||
|
9. CI logs are status-oriented and synthetic-data-only; they must not contain real account identifiers, raw payloads, statement filenames, balances from the operating DB, or passwords.
|
||||||
|
|
||||||
## Flow: `Import preview and import`
|
## Flow: `Import preview and import`
|
||||||
1. Operator chooses or supplies a supported source file: KB Bank PDF, Toss Bank PDF, Toss Securities PDF, SeoulPay XLSX, or CSV.
|
1. Operator chooses or supplies a supported source file: KB Bank PDF, Toss Bank PDF, Toss Securities PDF, SeoulPay XLSX, or CSV.
|
||||||
@@ -117,11 +119,14 @@
|
|||||||
- Manual balance reconciliation must remain preview/report-first unless a future REQ approves persistent review routing and confirmation mutation.
|
- Manual balance reconciliation must remain preview/report-first unless a future REQ approves persistent review routing and confirmation mutation.
|
||||||
- Streamlit browser evidence must use synthetic/validation DBs only and must not screenshot/log real account numbers, balances, source financial filenames, passwords, full merchant/counterparty dumps, or raw payloads.
|
- Streamlit browser evidence must use synthetic/validation DBs only and must not screenshot/log real account numbers, balances, source financial filenames, passwords, full merchant/counterparty dumps, or raw payloads.
|
||||||
- CI gates must use synthetic/temp DBs only. The default operating DB path (`~/.local/share/jarca/ledger.sqlite3`) is forbidden for AppTest, HTTP smoke, and DB safety smoke wrappers.
|
- CI gates must use synthetic/temp DBs only. The default operating DB path (`~/.local/share/jarca/ledger.sqlite3`) is forbidden for AppTest, HTTP smoke, and DB safety smoke wrappers.
|
||||||
|
- Contract regression must use synthetic/temp DBs only and must not expose runtime contract-version fields in MCP success payloads unless a future approved REQ changes the policy.
|
||||||
|
- Branch protection required-check drift should be audited against the six stable workflow job names documented in `docs/interface-contract.md`; changing a job name requires an explicit REQ/BAT note because it can break external repository settings.
|
||||||
- Dashboard asset liquidity calculations must use `accounts.liquidity_bucket` and `accounts.last_balance`; `loan` is excluded from the asset composition denominator and reflected as debt/net-worth only.
|
- Dashboard asset liquidity calculations must use `accounts.liquidity_bucket` and `accounts.last_balance`; `loan` is excluded from the asset composition denominator and reflected as debt/net-worth only.
|
||||||
- Sensitive passwords are runtime inputs and must not be committed.
|
- Sensitive passwords are runtime inputs and must not be committed.
|
||||||
|
|
||||||
## Current Gaps / Planned Changes
|
## Current Gaps / Planned Changes
|
||||||
- Browser-level evidence is available locally through `scripts/streamlit_browser_evidence.py`; it remains outside required CI gates by design.
|
- Browser-level evidence is available locally through `scripts/streamlit_browser_evidence.py`; it remains outside required CI gates by design.
|
||||||
|
- Contract-regression and lint gates are now part of the required-check contract for CI; full MCP tool-surface snapshot coverage remains deferred beyond the representative health/status/list/report/mutation scope.
|
||||||
- Dedicated reconciliation workbench and persistent review-decision queue are deferred beyond REQ-003.
|
- Dedicated reconciliation workbench and persistent review-decision queue are deferred beyond REQ-003.
|
||||||
- Separate asset snapshot/manual asset tables and automatic market/real-estate valuation are deferred beyond REQ-005.
|
- Separate asset snapshot/manual asset tables and automatic market/real-estate valuation are deferred beyond REQ-005.
|
||||||
|
|
||||||
@@ -137,3 +142,4 @@
|
|||||||
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 Streamlit browser evidence capture flow 및 UI evidence gate 기준 추가.
|
- 2026-06-29 KST: REQ-004/BAT-004 기준으로 Streamlit browser evidence capture flow 및 UI evidence gate 기준 추가.
|
||||||
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub 및 `liquidity_bucket`/`last_balance` 자산 유동성 flow 추가.
|
- 2026-06-29 KST: REQ-005/BAT-005 기준으로 dashboard operations hub 및 `liquidity_bucket`/`last_balance` 자산 유동성 flow 추가.
|
||||||
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI validation gates, synthetic fixture DB boundary, AppTest/HTTP/DB safety smoke flow, local browser evidence boundary 추가.
|
- 2026-06-30 KST: REQ-006/BAT-006 기준으로 GitHub Actions CI validation gates, synthetic fixture DB boundary, AppTest/HTTP/DB safety smoke flow, local browser evidence boundary 추가.
|
||||||
|
- 2026-06-30 KST: REQ-007/REQ-008/BAT-007 기준으로 `contract-regression`/`lint` CI required-check flow, contract snapshot scope, branch protection drift boundary 추가.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# REQ-005 Dashboard operations hub/liquidity/monthly cashflow
|
# REQ-005 Dashboard operations hub/liquidity/monthly cashflow
|
||||||
|
|
||||||
- Status: Approved
|
- Status: Implemented
|
||||||
- Type: Interface
|
- Type: Interface
|
||||||
- Priority: High
|
- Priority: High
|
||||||
- Owner: 렛저장인
|
- Owner: 렛저장인
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
- Batch: BAT-005
|
- Batch: BAT-005
|
||||||
- Created At (KST): 2026-06-29
|
- Created At (KST): 2026-06-29
|
||||||
- Approved At (KST): 2026-06-29 21:08 KST
|
- Approved At (KST): 2026-06-29 21:08 KST
|
||||||
|
- Implemented At (KST): 2026-06-30 16:48 KST
|
||||||
|
|
||||||
## 1. 배경
|
## 1. 배경
|
||||||
|
|
||||||
@@ -90,4 +91,4 @@
|
|||||||
- `liquidity_bucket`의 권위 소스는 계좌 단위로 고정하며 거래/카테고리 override는 이번 범위에서 제외한다.
|
- `liquidity_bucket`의 권위 소스는 계좌 단위로 고정하며 거래/카테고리 override는 이번 범위에서 제외한다.
|
||||||
- 운영 DB, 원본 금융 파일, 민감정보를 사용하지 않고 synthetic/validation DB에서 대시보드 evidence를 생성한다.
|
- 운영 DB, 원본 금융 파일, 민감정보를 사용하지 않고 synthetic/validation DB에서 대시보드 evidence를 생성한다.
|
||||||
- dcy-004 QC 결과에 따라 UI 영향 batch는 browser screenshot/snapshot evidence를 필수로 한다.
|
- dcy-004 QC 결과에 따라 UI 영향 batch는 browser screenshot/snapshot evidence를 필수로 한다.
|
||||||
- 이 REQ는 구현 기준으로 승인되었으며, 후속 batch에서 구현·검증을 진행한다.
|
- 이 REQ는 BAT-005 `release-candidate` 상태, `verification.md`의 AC mapping, `pytest -q` PASS, synthetic DB 기반 Streamlit screenshot/DOM evidence를 근거로 Implemented로 전환했다. 남은 blocker는 없다.
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# REQ-007 MCP response contract versioning governance
|
||||||
|
|
||||||
|
- Status: Implemented
|
||||||
|
- Type: Interface
|
||||||
|
- Priority: High
|
||||||
|
- Owner: 렛저장인
|
||||||
|
- Source Discovery: `docs/discovery/dcy-007_20260630_mcp-response-contract-ci-contract-versioning.md`
|
||||||
|
- Batch: BAT-007
|
||||||
|
- Created At (KST): 2026-06-30 18:35 KST
|
||||||
|
- Approved At (KST): 2026-06-30 18:56 KST
|
||||||
|
- Implemented At (KST): 2026-06-30
|
||||||
|
|
||||||
|
## 1. 배경
|
||||||
|
|
||||||
|
현재 저장소는 MCP 도구의 성공 응답 shape에 대해 "기존 success response shape를 breaking change 없이 보존한다"는 호환성 원칙만 문서로 두고 있으나, formal versioning 규율과 변경 등급 체계가 없다. 그 결과 응답 키 삭제, 타입 변경, 중첩 구조 변경 같은 비호환 변경이 발생했을 때 이를 문서·리뷰·검증에서 어떤 버전 규칙으로 다뤄야 하는지 일관된 기준이 없다. dcy-007 confirmed Discovery에 따라 MCP response contract에 대한 버전 체계, 변경 등급 규칙, changelog/location 규율을 정의해 후속 회귀 게이트와 batch 검증의 기준선으로 고정한다.
|
||||||
|
|
||||||
|
## 2. Intent
|
||||||
|
|
||||||
|
MCP 성공 응답 contract에 대해 사람이 해석하는 호환성 원칙을 넘어서, 저장소 문서와 후속 QC/CI가 공통으로 참조할 수 있는 버전 governance 기준을 확정한다. 이 REQ의 목적은 모든 응답 payload를 즉시 바꾸는 것이 아니라, 어떤 변경이 additive인지 breaking인지, 어떤 문서가 source of truth인지, 버전 이력이 어디에서 관리되는지를 명확히 하여 후속 contract regression과 implementation batch가 흔들리지 않게 하는 것이다.
|
||||||
|
|
||||||
|
## 3. 범위
|
||||||
|
|
||||||
|
### In Scope
|
||||||
|
- MCP 성공 응답 contract의 버전 체계를 `major.minor` 형식으로 정의하고 초기값을 `1.0`으로 설정한다.
|
||||||
|
- additive/backward-compatible change와 breaking change의 판정 기준을 문서로 명시한다.
|
||||||
|
- contract 버전과 변경 이력을 어떤 문서/metadata에서 관리할지 규정한다.
|
||||||
|
- `docs/interface-contract.md`에 contract versioning governance와 compatibility rule을 반영한다.
|
||||||
|
- 런타임 응답에 버전 메타를 즉시 노출하지 않는 초기 운영 방침을 명시한다.
|
||||||
|
|
||||||
|
### Out of Scope
|
||||||
|
- 기존 MCP 성공 응답 shape를 실제로 breaking change 하는 작업.
|
||||||
|
- 런타임 응답 payload에 공통 `contract_version` 필드를 즉시 추가하는 작업.
|
||||||
|
- 모든 MCP 도구 응답을 한 번에 재설계하는 작업.
|
||||||
|
- CI workflow 구성, lint 게이트, branch protection 실제 설정 적용.
|
||||||
|
|
||||||
|
## 4. Requirement
|
||||||
|
|
||||||
|
### FR-1. Contract version scheme
|
||||||
|
- MCP 성공 응답 contract는 `major.minor` 체계를 사용해야 한다.
|
||||||
|
- 초기 contract version은 `1.0`이어야 한다.
|
||||||
|
- breaking change는 `major`를 증가시키고, additive/backward-compatible change는 `minor`를 증가시켜야 한다.
|
||||||
|
|
||||||
|
### FR-2. Change classification rule
|
||||||
|
- 다음 변경은 최소한 breaking change로 분류해야 한다.
|
||||||
|
- 기존 key 삭제
|
||||||
|
- 기존 key 타입 변경
|
||||||
|
- 기존 중첩 구조/배열/객체 shape 변경으로 소비자 파싱이 깨지는 경우
|
||||||
|
- 다음 변경은 additive/backward-compatible change로 분류할 수 있어야 한다.
|
||||||
|
- 기존 소비자를 깨지 않는 신규 optional field 추가
|
||||||
|
- 기존 의미를 유지한 상태에서 설명/metadata를 문서에 보강하는 경우
|
||||||
|
- ambiguous case를 reviewer/QC가 판정할 수 있도록 예시를 남겨야 한다.
|
||||||
|
|
||||||
|
### FR-3. Governance source of truth
|
||||||
|
- contract version과 compatibility rule의 source of truth는 `docs/interface-contract.md`여야 한다.
|
||||||
|
- contract change log 또는 동등한 버전 이력 섹션이 존재해야 하며, 버전 증가 이유와 관련 REQ/BAT를 추적할 수 있어야 한다.
|
||||||
|
- REQ 또는 batch가 MCP 응답 shape에 영향을 주는 경우, 해당 문서가 contract version 영향 여부를 명시해야 한다.
|
||||||
|
|
||||||
|
### FR-4. Initial runtime exposure policy
|
||||||
|
- 초기 단계에서는 contract version 메타를 런타임 응답에 노출하지 않아야 한다.
|
||||||
|
- 대신 contract regression test/snapshot metadata와 `docs/interface-contract.md`를 통해 버전을 관리해야 한다.
|
||||||
|
- 런타임 노출이 필요해지면 별도 REQ 또는 승인된 migration으로 다뤄야 한다.
|
||||||
|
|
||||||
|
### FR-5. Baseline documentation alignment
|
||||||
|
- `docs/interface-contract.md`는 formal versioning 부재와 CI contract gap에 대한 Current Gaps 서술을 갱신해야 한다.
|
||||||
|
- 필요 시 `docs/project-structure.md`, `docs/runtime-flows.md`에 contract governance가 CI 회귀 게이트와 어떻게 연결되는지 참조 수준으로 반영해야 한다.
|
||||||
|
|
||||||
|
## 5. Acceptance Criteria
|
||||||
|
|
||||||
|
- AC-1: `docs/interface-contract.md`에 `major.minor` contract version 체계와 초기값 `1.0`이 명시된다.
|
||||||
|
- AC-2: breaking vs additive change 판정 기준이 최소 key 삭제, 타입 변경, 구조 변경, optional field 추가 예시와 함께 문서화된다.
|
||||||
|
- AC-3: contract change log 또는 동등한 버전 이력 규칙이 정의되고, 관련 REQ/BAT 추적 방식이 명시된다.
|
||||||
|
- AC-4: 초기 단계에서 contract version 메타를 런타임 응답에 노출하지 않는다는 운영 방침이 문서에 명시된다.
|
||||||
|
- AC-5: 응답 shape 영향 변경이 발생할 때 contract version 영향 여부를 검토해야 한다는 규칙이 문서에 반영된다.
|
||||||
|
- AC-6: 기존 성공 응답 shape를 즉시 변경하지 않는다는 backward-compatible 도입 원칙이 유지된다.
|
||||||
|
|
||||||
|
## 6. Impacted Area
|
||||||
|
|
||||||
|
- `docs/interface-contract.md`
|
||||||
|
- 필요 시 `docs/project-structure.md`
|
||||||
|
- 필요 시 `docs/runtime-flows.md`
|
||||||
|
- 후속 batch planning/QC에서 참조할 contract versioning 기준
|
||||||
|
|
||||||
|
## 7. Notes
|
||||||
|
|
||||||
|
- 이 REQ는 문서와 인터페이스 governance를 다루므로 `Interface` 타입으로 관리한다.
|
||||||
|
- contract version 자체는 CI 회귀 테스트 및 snapshot metadata와 연결되어야 하지만, 테스트/게이트 구체 구현은 `REQ-008`에서 다룬다.
|
||||||
|
- runtime payload version field는 이번 범위에서 제외하며, 후속 필요 시 별도 REQ로 분리한다.
|
||||||
|
|
||||||
|
## 8. Change Log
|
||||||
|
|
||||||
|
- 2026-06-30 18:35 KST: dcy-007 confirmed 범위를 바탕으로 Proposed 초안을 생성.
|
||||||
|
- 2026-06-30 18:56 KST: Discovery 결정사항과 승인 게이트를 재검토해 Approved로 전환.
|
||||||
|
- 2026-06-30 KST: BAT-007 implementation/verification evidence 기준으로 Implemented로 전환.
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# REQ-008 CI contract regression and lint required checks
|
||||||
|
|
||||||
|
- Status: Implemented
|
||||||
|
- Type: Testing
|
||||||
|
- Priority: High
|
||||||
|
- Owner: 렛저장인
|
||||||
|
- Source Discovery: `docs/discovery/dcy-007_20260630_mcp-response-contract-ci-contract-versioning.md`
|
||||||
|
- Batch: BAT-007
|
||||||
|
- Created At (KST): 2026-06-30 18:35 KST
|
||||||
|
- Approved At (KST): 2026-06-30 18:56 KST
|
||||||
|
- Implemented At (KST): 2026-06-30
|
||||||
|
|
||||||
|
## 1. 배경
|
||||||
|
|
||||||
|
REQ-006으로 GitHub Actions 기반 `pytest`/`AppTest`/HTTP smoke/DB safety smoke 게이트는 도입되었지만, MCP 성공 응답 shape 회귀를 자동 차단하는 contract regression gate와 lint required-check 계약은 아직 없다. 또한 branch protection required-check 목록과 synthetic-only CI 불변식이 코드 밖 운영 설정 또는 느슨한 baseline 문장에 머물러 있어, 어떤 게이트가 머지 차단 기준인지 단일 출처가 부족하다. dcy-007 confirmed Discovery에 따라 contract regression과 lint를 CI required-check 계약으로 편입하고, representative synthetic fixture 기반 회귀 검증 및 baseline 문서 정렬을 정의한다.
|
||||||
|
|
||||||
|
## 2. Intent
|
||||||
|
|
||||||
|
MCP 응답 shape 회귀와 lint 누락을 사람 리뷰에 의존하지 않고 CI required-check 계약으로 고정한다. 이 REQ의 목적은 representative contract regression과 `ruff` lint를 기존 안정 게이트 위에 추가하여, branch protection이 어떤 job 통과를 머지 조건으로 삼아야 하는지와 synthetic-only 검증 원칙을 단일 delivery 입력으로 만드는 것이다.
|
||||||
|
|
||||||
|
## 3. 범위
|
||||||
|
|
||||||
|
### In Scope
|
||||||
|
- representative MCP 성공 응답군에 대한 contract/snapshot 회귀 테스트를 도입한다.
|
||||||
|
- contract regression을 GitHub Actions required-check 후보 job으로 추가한다.
|
||||||
|
- `ruff` 기반 lint gate를 도입하고 required-check 기본안으로 고정한다.
|
||||||
|
- required-check 목록을 기존 4개 안정 job + `contract-regression` + `lint`로 문서화한다.
|
||||||
|
- synthetic-only CI 불변식과 branch protection/required-check 경계를 baseline 문서에 명시한다.
|
||||||
|
|
||||||
|
### Out of Scope
|
||||||
|
- 모든 MCP 도구 응답군 100% 전수 snapshot 커버리지.
|
||||||
|
- browser-screenshot evidence를 CI 필수 차단 게이트로 승격하는 작업.
|
||||||
|
- 실제 GitHub branch protection 토글 적용 자체.
|
||||||
|
- coverage percent gate, benchmark gate, flaky quarantine 같은 추가 품질 체계.
|
||||||
|
|
||||||
|
## 4. Requirement
|
||||||
|
|
||||||
|
### FR-1. Contract regression representative scope
|
||||||
|
- contract regression은 1차로 다음 대표 응답군을 다뤄야 한다.
|
||||||
|
- `health/status`
|
||||||
|
- `list`
|
||||||
|
- `report`
|
||||||
|
- `mutation success envelope`
|
||||||
|
- 각 그룹은 synthetic fixture 기반 고정 입력으로 결정적으로 재현 가능해야 한다.
|
||||||
|
- key 삭제, 타입 변경, 구조 변경 같은 비호환 변경이 발생하면 테스트가 실패해야 한다.
|
||||||
|
- additive field 추가는 정의된 정책에 따라 허용 가능해야 한다.
|
||||||
|
|
||||||
|
### FR-2. Synthetic-only contract fixture policy
|
||||||
|
- contract regression 테스트는 synthetic representative fixture만 사용해야 한다.
|
||||||
|
- fixture는 운영/목록/리포트/변경 결과를 모두 재현할 수 있어야 한다.
|
||||||
|
- fixture와 로그에는 운영 DB, 실계좌번호, 실잔액, `raw_payload`, 원본 파일명, 비밀번호가 포함되어서는 안 된다.
|
||||||
|
|
||||||
|
### FR-3. Lint gate
|
||||||
|
- 저장소는 `ruff`를 lint/정적분석 도구로 사용해야 한다.
|
||||||
|
- lint는 CI에서 required gate로 실행되어야 한다.
|
||||||
|
- 초기 룰셋은 보수적으로 시작하되, 명백한 스타일/정적 오류 회귀를 막을 수 있어야 한다.
|
||||||
|
|
||||||
|
### FR-4. Required-check contract
|
||||||
|
- CI required-check 기본안은 다음 6개 job이어야 한다.
|
||||||
|
- `pytest-regression`
|
||||||
|
- `dashboard-app-test`
|
||||||
|
- `dashboard-http-smoke`
|
||||||
|
- `db-safety-smoke`
|
||||||
|
- `contract-regression`
|
||||||
|
- `lint`
|
||||||
|
- 위 job 이름은 branch protection 안정성을 위해 의도 없이 변경되면 안 된다.
|
||||||
|
- 실제 GitHub branch protection 설정 적용은 운영자 실행 범위임을 문서에 명시해야 한다.
|
||||||
|
|
||||||
|
### FR-5. Baseline documentation alignment
|
||||||
|
- `docs/interface-contract.md`는 CI contract/tests-lint gap이 해소되는 방향으로 갱신되어야 한다.
|
||||||
|
- `docs/project-structure.md`는 required-check contract와 lint/contract-regression job 책임을 반영해야 한다.
|
||||||
|
- `docs/runtime-flows.md`는 PR/push → required-check → merge-blocking 흐름과 synthetic-only invariant를 반영해야 한다.
|
||||||
|
- 필요 시 runbook은 branch protection setting drift나 local reproduction 절차를 포함해야 한다.
|
||||||
|
|
||||||
|
## 5. Acceptance Criteria
|
||||||
|
|
||||||
|
- AC-1: representative MCP 응답군(`health/status`, `list`, `report`, `mutation success envelope`)에 대한 contract regression 테스트가 추가된다.
|
||||||
|
- AC-2: 비호환 응답 변경(key 삭제, 타입 변경, 구조 변경)을 주입했을 때 contract regression이 실패하는 검증 경로가 존재한다.
|
||||||
|
- AC-3: contract regression은 synthetic fixture만 사용하며 민감정보를 로그/fixture에 포함하지 않는다.
|
||||||
|
- AC-4: `ruff` 기반 lint gate가 CI에 추가되고 required gate로 동작한다.
|
||||||
|
- AC-5: CI required-check 기본안 6개 job이 문서에 명시되고, 기존 4개 안정 job 이름이 유지된다.
|
||||||
|
- AC-6: `docs/interface-contract.md`, `docs/project-structure.md`, `docs/runtime-flows.md`가 contract-regression/lint/required-check/synthetic-only 경계를 반영한다.
|
||||||
|
- AC-7: 실제 branch protection 토글 적용은 운영자 실행 범위임이 문서에 명시된다.
|
||||||
|
|
||||||
|
## 6. Impacted Area
|
||||||
|
|
||||||
|
- `.github/workflows/ci.yml`
|
||||||
|
- `tests/` 및 contract snapshot/fixture 자산
|
||||||
|
- `pyproject.toml`
|
||||||
|
- `docs/interface-contract.md`
|
||||||
|
- `docs/project-structure.md`
|
||||||
|
- `docs/runtime-flows.md`
|
||||||
|
- 필요 시 `docs/runbooks/`
|
||||||
|
|
||||||
|
## 7. Notes
|
||||||
|
|
||||||
|
- 이 REQ는 테스트/게이트/검증 계약을 다루므로 `Testing` 타입으로 관리한다.
|
||||||
|
- contract version scheme과 breaking/additive 분류 규칙 자체는 `REQ-007`에서 관리하고, 이 REQ는 그것을 회귀 게이트로 강제하는 경로를 다룬다.
|
||||||
|
- representative scope를 우선 도입하고, 전수 응답군 snapshot 확대는 후속 반복으로 남긴다.
|
||||||
|
|
||||||
|
## 8. Change Log
|
||||||
|
|
||||||
|
- 2026-06-30 18:35 KST: dcy-007 confirmed 범위를 바탕으로 Proposed 초안을 생성.
|
||||||
|
- 2026-06-30 18:56 KST: Discovery 결정사항과 승인 게이트를 재검토해 Approved로 전환.
|
||||||
|
- 2026-06-30 KST: BAT-007 implementation/verification evidence 기준으로 Implemented로 전환.
|
||||||
+8
-2
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
## Next Requirement ID
|
## Next Requirement ID
|
||||||
|
|
||||||
- Current: `REQ-007`
|
- Current: `REQ-009`
|
||||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||||
|
|
||||||
## Requirement Register
|
## Requirement Register
|
||||||
@@ -44,8 +44,10 @@
|
|||||||
| REQ-002 | 운영 안전 백업·복구·검증 DB 워크플로우 | Non-Functional | Implemented | High | 렛저장인 | [req-002_operational-safety-backup-restore-validation-db.md](Non-Functional/req-002_operational-safety-backup-restore-validation-db.md) |
|
| REQ-002 | 운영 안전 백업·복구·검증 DB 워크플로우 | Non-Functional | Implemented | High | 렛저장인 | [req-002_operational-safety-backup-restore-validation-db.md](Non-Functional/req-002_operational-safety-backup-restore-validation-db.md) |
|
||||||
| REQ-003 | Statement-unavailable 계좌 수동 증빙 및 잔액대조 | Interface | Implemented | High | 렛저장인 | [req-003_statement-unavailable-manual-evidence-balance-reconciliation.md](Interface/req-003_statement-unavailable-manual-evidence-balance-reconciliation.md) |
|
| REQ-003 | Statement-unavailable 계좌 수동 증빙 및 잔액대조 | Interface | Implemented | High | 렛저장인 | [req-003_statement-unavailable-manual-evidence-balance-reconciliation.md](Interface/req-003_statement-unavailable-manual-evidence-balance-reconciliation.md) |
|
||||||
| REQ-004 | Browser/Streamlit E2E evidence 환경 복구 | Testing | Implemented | High | 렛저장인 | [req-004_browser-streamlit-e2e-evidence-recovery.md](Testing/req-004_browser-streamlit-e2e-evidence-recovery.md) |
|
| REQ-004 | Browser/Streamlit E2E evidence 환경 복구 | Testing | Implemented | High | 렛저장인 | [req-004_browser-streamlit-e2e-evidence-recovery.md](Testing/req-004_browser-streamlit-e2e-evidence-recovery.md) |
|
||||||
| REQ-005 | Dashboard operations hub/liquidity/monthly cashflow | Interface | Approved | High | 렛저장인 | [req-005_dashboard-operations-hub-liquidity-monthly-cashflow.md](Interface/req-005_dashboard-operations-hub-liquidity-monthly-cashflow.md) |
|
| REQ-005 | Dashboard operations hub/liquidity/monthly cashflow | Interface | Implemented | High | 렛저장인 | [req-005_dashboard-operations-hub-liquidity-monthly-cashflow.md](Interface/req-005_dashboard-operations-hub-liquidity-monthly-cashflow.md) |
|
||||||
| REQ-006 | pytest/AppTest/Smoke CI 자동화 | Testing | Implemented | High | 렛저장인 | [req-006_pytest-apptest-smoke-ci-automation.md](Testing/req-006_pytest-apptest-smoke-ci-automation.md) |
|
| REQ-006 | pytest/AppTest/Smoke CI 자동화 | Testing | Implemented | High | 렛저장인 | [req-006_pytest-apptest-smoke-ci-automation.md](Testing/req-006_pytest-apptest-smoke-ci-automation.md) |
|
||||||
|
| REQ-007 | MCP response contract versioning governance | Interface | Implemented | High | 렛저장인 | [req-007_mcp-response-contract-versioning-governance.md](Interface/req-007_mcp-response-contract-versioning-governance.md) |
|
||||||
|
| REQ-008 | CI contract regression and lint required checks | Testing | Implemented | High | 렛저장인 | [req-008_ci-contract-regression-and-lint-required-checks.md](Testing/req-008_ci-contract-regression-and-lint-required-checks.md) |
|
||||||
|
|
||||||
## Recent Change Log Summary
|
## Recent Change Log Summary
|
||||||
|
|
||||||
@@ -57,6 +59,10 @@
|
|||||||
- 2026-06-29 KST: dcy-004 confirmed 범위로 REQ-004를 생성하고 BAT-004 browser evidence 구현·검증 대상으로 등록.
|
- 2026-06-29 KST: dcy-004 confirmed 범위로 REQ-004를 생성하고 BAT-004 browser evidence 구현·검증 대상으로 등록.
|
||||||
- 2026-06-29 20:03 KST: dcy-005 confirmed 범위로 REQ-005를 Proposed 상태로 생성하고 dashboard operations hub/liquidity/monthly cashflow 요구사항으로 등록.
|
- 2026-06-29 20:03 KST: dcy-005 confirmed 범위로 REQ-005를 Proposed 상태로 생성하고 dashboard operations hub/liquidity/monthly cashflow 요구사항으로 등록.
|
||||||
- 2026-06-29 21:08 KST: REQ-005를 Approved로 전환하고 dcy-005 구현 입력으로 확정.
|
- 2026-06-29 21:08 KST: REQ-005를 Approved로 전환하고 dcy-005 구현 입력으로 확정.
|
||||||
|
- 2026-06-30 16:48 KST: BAT-005 구현·검증 evidence와 `pytest -q` PASS, synthetic Streamlit browser evidence를 근거로 REQ-005를 Implemented로 전환.
|
||||||
- 2026-06-30 14:42 KST: dcy-006 confirmed 범위로 REQ-006을 Proposed 상태로 생성하고 GitHub Actions 기반 pytest/AppTest/HTTP smoke/DB safety smoke CI 요구사항으로 등록.
|
- 2026-06-30 14:42 KST: dcy-006 confirmed 범위로 REQ-006을 Proposed 상태로 생성하고 GitHub Actions 기반 pytest/AppTest/HTTP smoke/DB safety smoke CI 요구사항으로 등록.
|
||||||
- 2026-06-30 14:50 KST: REQ-006을 Approved로 전환하고 BAT-006 standard batch 입력으로 확정.
|
- 2026-06-30 14:50 KST: REQ-006을 Approved로 전환하고 BAT-006 standard batch 입력으로 확정.
|
||||||
- 2026-06-30 15:29 KST: BAT-006 구현과 jarcaledgerqc PASS_WITH_NOTES 검증을 근거로 REQ-006을 Implemented로 전환.
|
- 2026-06-30 15:29 KST: BAT-006 구현과 jarcaledgerqc PASS_WITH_NOTES 검증을 근거로 REQ-006을 Implemented로 전환.
|
||||||
|
- 2026-06-30 18:35 KST: dcy-007 confirmed 범위를 `REQ-007`(MCP response contract versioning governance)과 `REQ-008`(CI contract regression and lint required checks) Proposed 상태로 생성했다.
|
||||||
|
- 2026-06-30 18:56 KST: `REQ-007`, `REQ-008`을 승인 게이트 재검토 후 Approved로 전환했다.
|
||||||
|
- 2026-06-30 KST: BAT-007 구현과 contract regression/lint/smoke 검증 evidence를 근거로 `REQ-007`, `REQ-008`을 Implemented로 전환했다.
|
||||||
|
|||||||
+10
-1
@@ -17,7 +17,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = ["pytest>=8.0.0"]
|
dev = ["pytest>=8.0.0", "ruff>=0.6.0"]
|
||||||
dashboard = [
|
dashboard = [
|
||||||
"pandas>=2.0.0",
|
"pandas>=2.0.0",
|
||||||
"plotly>=5.0.0",
|
"plotly>=5.0.0",
|
||||||
@@ -31,6 +31,15 @@ jarca-ledger-mcp = "jarca_ledger_mcp.server:main"
|
|||||||
addopts = "-q"
|
addopts = "-q"
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
target-version = "py311"
|
||||||
|
line-length = 240
|
||||||
|
|
||||||
|
[tool.ruff.lint]
|
||||||
|
# Conservative initial required gate for BAT-007: block syntax/undefined-name
|
||||||
|
# regressions without reformatting the existing codebase in this delivery.
|
||||||
|
select = ["E9", "F"]
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
include = ["jarca_ledger_mcp*"]
|
include = ["jarca_ledger_mcp*"]
|
||||||
exclude = ["dashboard*"]
|
exclude = ["dashboard*"]
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"contract_version": "1.0",
|
||||||
|
"runtime_exposure": "unexposed",
|
||||||
|
"policy": {
|
||||||
|
"breaking_changes": ["remove_required_key", "change_required_type", "change_required_structure"],
|
||||||
|
"additive_changes": ["add_optional_field"]
|
||||||
|
},
|
||||||
|
"scopes": {
|
||||||
|
"health": {
|
||||||
|
"required": {
|
||||||
|
"success": "bool",
|
||||||
|
"server": "str",
|
||||||
|
"db_path": "str",
|
||||||
|
"schema_version": "str",
|
||||||
|
"active_transactions": "int"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"required": {
|
||||||
|
"success": "bool",
|
||||||
|
"db_path": "str",
|
||||||
|
"exists": "bool",
|
||||||
|
"size_bytes": "int",
|
||||||
|
"mtime": "str",
|
||||||
|
"table_counts": {"type": "dict", "required": {"accounts": "int", "transactions": "int"}},
|
||||||
|
"aggregate_totals": {"type": "dict", "required": {"month": "str", "by_cashflow_kind": "dict"}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"required": {
|
||||||
|
"success": "bool",
|
||||||
|
"accounts": "list",
|
||||||
|
"count": "int"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"accounts": {
|
||||||
|
"required": {
|
||||||
|
"id": "str",
|
||||||
|
"name": "str",
|
||||||
|
"type": "str",
|
||||||
|
"institution": "str",
|
||||||
|
"currency": "str",
|
||||||
|
"active": "bool",
|
||||||
|
"statement_status": "str",
|
||||||
|
"liquidity_bucket": "str"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"required": {
|
||||||
|
"success": "bool",
|
||||||
|
"month": "str",
|
||||||
|
"income": "int",
|
||||||
|
"expenses": "int",
|
||||||
|
"net_cashflow": "int",
|
||||||
|
"savings_rate": ["float", "none"],
|
||||||
|
"by_type": "dict",
|
||||||
|
"by_cashflow_kind": "dict"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mutation_success_envelope": {
|
||||||
|
"required": {
|
||||||
|
"success": "bool",
|
||||||
|
"account": {"type": "dict", "required": {"id": "str", "name": "str", "type": "str", "active": "bool"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from jarca_ledger_mcp import service
|
||||||
|
|
||||||
|
SNAPSHOT_PATH = Path(__file__).parent / "contract_snapshots" / "response_contract_v1_0.json"
|
||||||
|
TYPE_MAP = {
|
||||||
|
"bool": bool,
|
||||||
|
"dict": dict,
|
||||||
|
"float": float,
|
||||||
|
"int": int,
|
||||||
|
"list": list,
|
||||||
|
"none": type(None),
|
||||||
|
"str": str,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _load_contract() -> dict[str, Any]:
|
||||||
|
with SNAPSHOT_PATH.open(encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_type(value: Any, spec: str | list[str], path: str) -> None:
|
||||||
|
names = spec if isinstance(spec, list) else [spec]
|
||||||
|
expected = tuple(TYPE_MAP[name] for name in names)
|
||||||
|
# bool is a subclass of int; contract int should not silently accept bool.
|
||||||
|
if "int" in names and isinstance(value, bool):
|
||||||
|
pytest.fail(f"{path} expected int-compatible contract type, got bool")
|
||||||
|
assert isinstance(value, expected), f"{path} expected {names}, got {type(value).__name__}"
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_subset_schema(payload: dict[str, Any], schema: dict[str, Any], path: str) -> None:
|
||||||
|
for key, spec in schema.get("required", {}).items():
|
||||||
|
assert key in payload, f"{path}.{key} missing from response contract"
|
||||||
|
value = payload[key]
|
||||||
|
if isinstance(spec, dict):
|
||||||
|
_assert_type(value, spec["type"], f"{path}.{key}")
|
||||||
|
if spec.get("type") == "dict":
|
||||||
|
_assert_subset_schema(value, spec, f"{path}.{key}")
|
||||||
|
else:
|
||||||
|
_assert_type(value, spec, f"{path}.{key}")
|
||||||
|
|
||||||
|
for list_key, item_schema in schema.get("items", {}).items():
|
||||||
|
items = payload.get(list_key, [])
|
||||||
|
assert isinstance(items, list), f"{path}.{list_key} expected list for item schema"
|
||||||
|
for index, item in enumerate(items):
|
||||||
|
assert isinstance(item, dict), f"{path}.{list_key}[{index}] expected dict"
|
||||||
|
_assert_subset_schema(item, item_schema, f"{path}.{list_key}[{index}]")
|
||||||
|
|
||||||
|
|
||||||
|
def _build_contract_fixture(db_path: str) -> None:
|
||||||
|
account = service.create_ledger_account(
|
||||||
|
"Synthetic Checking",
|
||||||
|
type="bank",
|
||||||
|
institution="Synthetic Bank",
|
||||||
|
account_number_masked="SYN-0001",
|
||||||
|
last_balance=1_000_000,
|
||||||
|
last_synced_at="2026-06-30T12:00:00+09:00",
|
||||||
|
statement_status="manual_only",
|
||||||
|
liquidity_bucket="cash",
|
||||||
|
db_path=db_path,
|
||||||
|
)
|
||||||
|
assert account["success"] is True
|
||||||
|
account_id = account["account"]["id"]
|
||||||
|
assert service.create_ledger_transaction(
|
||||||
|
"2026-06-01",
|
||||||
|
"income",
|
||||||
|
3_000_000,
|
||||||
|
category="수입",
|
||||||
|
merchant="Synthetic Employer",
|
||||||
|
account_id=account_id,
|
||||||
|
db_path=db_path,
|
||||||
|
)["success"] is True
|
||||||
|
assert service.create_ledger_transaction(
|
||||||
|
"2026-06-02",
|
||||||
|
"expense",
|
||||||
|
50_000,
|
||||||
|
category="식비",
|
||||||
|
merchant="Synthetic Market",
|
||||||
|
account_id=account_id,
|
||||||
|
db_path=db_path,
|
||||||
|
)["success"] is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_response_contract_metadata_versioning_policy() -> None:
|
||||||
|
contract = _load_contract()
|
||||||
|
assert contract["contract_version"] == "1.0"
|
||||||
|
assert contract["runtime_exposure"] == "unexposed"
|
||||||
|
assert "remove_required_key" in contract["policy"]["breaking_changes"]
|
||||||
|
assert "add_optional_field" in contract["policy"]["additive_changes"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_health_and_status_success_response_contract(tmp_path: Path) -> None:
|
||||||
|
contract = _load_contract()["scopes"]
|
||||||
|
db_path = str(tmp_path / "contract-fixture.sqlite3")
|
||||||
|
_build_contract_fixture(db_path)
|
||||||
|
|
||||||
|
health = service.health_check(db_path)
|
||||||
|
assert health["success"] is True
|
||||||
|
_assert_subset_schema(health, contract["health"], "health")
|
||||||
|
|
||||||
|
status = service.get_ledger_db_status(db_path, include_hash=False, include_integrity=False)
|
||||||
|
assert status["success"] is True
|
||||||
|
_assert_subset_schema(status, contract["status"], "status")
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_report_and_mutation_success_envelope_contract(tmp_path: Path) -> None:
|
||||||
|
contract = _load_contract()["scopes"]
|
||||||
|
db_path = str(tmp_path / "contract-fixture.sqlite3")
|
||||||
|
_build_contract_fixture(db_path)
|
||||||
|
|
||||||
|
mutation = service.create_ledger_account("Synthetic Savings", type="bank", liquidity_bucket="deposit", db_path=db_path)
|
||||||
|
assert mutation["success"] is True
|
||||||
|
_assert_subset_schema(mutation, contract["mutation_success_envelope"], "mutation_success_envelope")
|
||||||
|
|
||||||
|
accounts = service.list_ledger_accounts(db_path=db_path)
|
||||||
|
assert accounts["success"] is True
|
||||||
|
assert accounts["count"] >= 2
|
||||||
|
_assert_subset_schema(accounts, contract["list"], "list")
|
||||||
|
|
||||||
|
report = service.get_monthly_cashflow_report("2026-06", db_path=db_path)
|
||||||
|
assert report["success"] is True
|
||||||
|
_assert_subset_schema(report, contract["report"], "report")
|
||||||
@@ -487,6 +487,7 @@ dashboard = [
|
|||||||
]
|
]
|
||||||
dev = [
|
dev = [
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
|
{ name = "ruff" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
@@ -499,6 +500,7 @@ requires-dist = [
|
|||||||
{ name = "pymupdf", specifier = ">=1.24.0" },
|
{ name = "pymupdf", specifier = ">=1.24.0" },
|
||||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" },
|
||||||
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
{ name = "python-dotenv", specifier = ">=1.0.0" },
|
||||||
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.6.0" },
|
||||||
{ name = "streamlit", marker = "extra == 'dashboard'", specifier = ">=1.30.0" },
|
{ name = "streamlit", marker = "extra == 'dashboard'", specifier = ">=1.30.0" },
|
||||||
]
|
]
|
||||||
provides-extras = ["dev", "dashboard"]
|
provides-extras = ["dev", "dashboard"]
|
||||||
@@ -1432,6 +1434,31 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ea/ea/e7b0251441da9adfeaebcf29601d10f2a1455fcf0772fae9e7e19032bd96/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049", size = 586326, upload-time = "2026-05-28T12:02:11.47Z" },
|
{ url = "https://files.pythonhosted.org/packages/ea/ea/e7b0251441da9adfeaebcf29601d10f2a1455fcf0772fae9e7e19032bd96/rpds_py-2026.5.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8c43a8a973270fd173bf48cdf80bbe66312421cba68d40845034f174f2389049", size = 586326, upload-time = "2026-05-28T12:02:11.47Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.15.20"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user