Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions architecture/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ architecture/
├── layer-baseline.schema.json
├── lifecycle-contract.json
├── lifecycle-contract.schema.json
├── learning-ledger.json
├── learning-ledger.schema.json
├── deltas/
│ └── <change-id>.json
└── README.md
Expand All @@ -55,6 +57,8 @@ architecture/
- `layer-baseline.schema.json`:layer baseline 的結構 schema。
- `lifecycle-contract.json`:**review-session / endpoint-lease / stage-binding 三個 coordinator 端狀態機的機器定義**(Phase 4):states、observed transitions、forbidden shortcuts、evidence gates、reentry 規則、cross-machine 規則與 readiness evidence binding。
- `lifecycle-contract.schema.json`:lifecycle contract 的結構 schema。
- `learning-ledger.json`:**recurring `$improve-codebase-architecture` findings 的分類帳**(Phase 5):erosion pattern classes、findings(open/refactored/promoted/retired)、machine-verified promotion 記錄(pattern → 真實 invariant gate)。
- `learning-ledger.schema.json`:learning ledger 的結構 schema。

## 3. 第一版硬規則

Expand Down Expand Up @@ -126,6 +130,10 @@ python -m pytest tests/test_layered_architecture.py -q -p no:cacheprovider
# lifecycle contracts(Phase 4)
python scripts/dev/check_lifecycle_contracts.py --repo-root . --strict
python -m pytest tests/test_lifecycle_contracts.py -q -p no:cacheprovider

# learning ledger + quality grade(Phase 5)
python scripts/dev/report_architecture_quality.py --repo-root . --strict
python -m pytest tests/test_architecture_learning.py -q -p no:cacheprovider
```

`--report-only` 產出的 report 是 **可重生的本機產物**(`artifacts/architecture/` 已 gitignore),同一份 source tree 在 Windows 與 Linux 會得到 byte-identical 輸出。入庫的權威是 `observed-baseline.json`。
Expand Down Expand Up @@ -196,12 +204,13 @@ finding

## 7. 尚未宣稱完成的能力

以下仍是後續 phase,不應被目前文件或 PR 誤報為已完成:
以下不在目前 gates 的宣稱範圍,不應被文件或 PR 誤報為已完成:

- architecture quality grade 與定期 architecture garbage collection(Phase 5)。
- 跨 service 的 module-level layer 比對;目前只在 service 內部判定。
- 動態 import 與執行期才決定的 module 名稱;靜態掃描看不到,因此不宣稱涵蓋。
- lifecycle contract 對 transition **行為**的執行期驗證;Phase 4 的 gate 只驗 contract 一致性與 state 集同步,行為由各 service 自己的 runtime 與測試持有(見 Phase 4 界線)。
- learning ledger 的 C2–F5 開放項的逐項修復/promotion(Phase 5 交付的是分類帳+promotion 機制+graded 快照;backlog 由後續輪次逐項關閉)。
- quality report 的歷史 trend:報告是當前快照,shallow CI clone 無法重建時間序列;「trend」以 attributed debt inventory 表述。定期 architecture garbage collection 仍屬人工節奏。

### Phase 2 的已知偏離與界線(誠實揭露)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
"contract": "lifecycle contract gate",
"change_type": "additive",
"description": "Adds architecture/lifecycle-contract.json plus a standard-library checker that validates machine well-formedness, keeps declared state sets synchronized with the owning TypeScript unions, and cross-checks the readiness binding against the review-session-ready policy. Declaration of existing runtime behavior only; no product API, event contract, or runtime behavior changes."
},
{
"contract": "architecture learning ledger",
"change_type": "additive",
"description": "Adds architecture/learning-ledger.json classifying recurring improve-codebase-architecture findings into erosion patterns with a machine-verified promotion record, plus a read-only quality report aggregating every architecture gate into a graded snapshot. Inventory and reporting only; nothing is auto-repaired and no product contract changes."
}
],
"data_ownership_changes": [],
Expand Down
152 changes: 152 additions & 0 deletions architecture/learning-ledger.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
{
"$schema": "./learning-ledger.schema.json",
"schema_version": "ai-bim-learning-ledger/v1",
"purpose": "Machine-readable classification of recurring $improve-codebase-architecture findings (Phase 5). Findings are grouped into named erosion patterns; a pattern that recurs can be promoted to an invariant, a validator, or a structural test, and that promotion is recorded here with its mechanism. The ledger is an inventory and a promotion record - it never triggers automatic repairs.",
"pattern_classes": [
{
"id": "duplicate-capability-implementations",
"title": "Same truth implemented more than once",
"description": "One capability or constant is implemented in several places and the copies drift apart (duplicate readiness computations, repeated redaction helpers, forked polling defaults). The erosion is invisible until the copies disagree in production."
},
{
"id": "god-file-accretion",
"title": "Orchestration accretes into one oversized file",
"description": "Route handling, orchestration, and domain authority accumulate in a single large file instead of a deep module with a small interface, making every later change a wide edit with unrelated blast radius."
},
{
"id": "unregistered-literal-fanout",
"title": "Literals declared in many places with no single source",
"description": "Route keys, status unions, and similar literals are hand-written at many sites; adding or renaming one requires finding every copy, and a missed copy fails silently."
},
{
"id": "test-fixture-monolith",
"title": "Shared test fixtures fused into one file",
"description": "Unrelated test concerns share one fixture monolith, so fixture edits ripple across suites and honesty markers get lost in the fusion."
},
{
"id": "deep-module-consolidation",
"title": "Deep-module consolidation (positive template)",
"description": "The proven repair shape for the patterns above: one deep module with a small typed surface absorbs the scattered implementations (usePolledResource, useRuleRun, a1Machine, ifcReadyConversionPipeline, minioWatchSurface are landed examples)."
},
{
"id": "structural-erosion-classes",
"title": "Repository-level structural erosion",
"description": "Cross-cutting erosion the per-file patterns roll up into: new undeclared dependency edges, dependency cycles, cross-layer reaches, and state-machine drift between contract and code. These are the classes the executable architecture gates were built from."
}
],
"promoted_patterns": [
{
"pattern": "structural-erosion-classes",
"promoted_to": "ARCH-GRAPH-001",
"mechanism": "observed-graph ratchet (scripts/lib/observed_architecture.py): no new statically detectable dependency edges or cycles beyond the approved baseline",
"evidence": "Phase 2 delivery (tasks.md) - recurring post-hoc cycle and edge findings became a fail-closed canonical gate"
},
{
"pattern": "structural-erosion-classes",
"promoted_to": "ARCH-LAYER-001",
"mechanism": "layer-boundary ratchet (scripts/lib/layered_architecture.py): intra-service cross-layer reaches are rejected beyond the grandfathered baseline",
"evidence": "Phase 3 delivery (tasks.md) - recurring layering erosion became a fail-closed canonical gate"
},
{
"pattern": "structural-erosion-classes",
"promoted_to": "ARCH-LIFECYCLE-001",
"mechanism": "lifecycle contract gate (scripts/lib/lifecycle_contracts.py): machine well-formedness plus TS-union state-set synchronization keeps declared lifecycles equal to runtime unions",
"evidence": "Phase 4 delivery (tasks.md) - state-machine drift between docs and code became a fail-closed canonical gate"
}
],
"findings": [
{
"id": "c1-minio-watch-surface",
"recorded_on": "2026-07-30",
"pattern": "deep-module-consolidation",
"area": "bim-review-coordinator MinIO watch/browse/trigger surface",
"summary": "Watcher loop, toggle, status, browse, manual trigger, and SSE were scattered across app.ts and a raw client; consolidated into minioWatchSurface + an ObjectStorePort adapter, root-causing the watcher-observation flaky class.",
"status": "refactored",
"resolution": {
"kind": "refactor",
"reference": "PR #442 + PR #444 (2026-07-30 round)"
}
},
{
"id": "c2-edge-artifact-health",
"recorded_on": "2026-07-30",
"pattern": "god-file-accretion",
"area": "bim-review-coordinator artifact health orchestration",
"summary": "As recorded in the 2026-07-30 round: ~275 lines of orphan orchestration in app.ts with all_required_ready computed by two implementations (also duplicate-capability-implementations).",
"status": "open",
"resolution": null
},
{
"id": "c3-governance-egress-redaction",
"recorded_on": "2026-07-30",
"pattern": "duplicate-capability-implementations",
"area": "bim-review-coordinator governance egress",
"summary": "As recorded in the 2026-07-30 round: redactServerPaths existed as three copies and the diffs route bypassed the governed egress workflow.",
"status": "open",
"resolution": null
},
{
"id": "c4-session-artifact-authority",
"recorded_on": "2026-07-30",
"pattern": "god-file-accretion",
"area": "bim-review-coordinator A4 session-artifact authority",
"summary": "As recorded in the 2026-07-30 round: the A4 authority logic was spread across app.ts with all-optional route dependencies; largest blast radius of the candidate set.",
"status": "open",
"resolution": null
},
{
"id": "c5-job-projection-fusion",
"recorded_on": "2026-07-30",
"pattern": "duplicate-capability-implementations",
"area": "bim-review-coordinator job projections",
"summary": "As recorded in the 2026-07-30 round: five small job-projection files carrying near-identical shapes, candidates for one projection module.",
"status": "open",
"resolution": null
},
{
"id": "f1-page-registry",
"recorded_on": "2026-07-30",
"pattern": "unregistered-literal-fanout",
"area": "web-viewer-sample console routing",
"summary": "As recorded in the 2026-07-30 round: route keys declared at five sites; pages.tsx changed 43 times in six weeks.",
"status": "open",
"resolution": null
},
{
"id": "f2-session-view-read-model",
"recorded_on": "2026-07-30",
"pattern": "unregistered-literal-fanout",
"area": "web-viewer-sample session view",
"summary": "As recorded in the 2026-07-30 round: seventeen files hand-wrote RuntimeStatus literals instead of a typed read model with typed errors.",
"status": "open",
"resolution": null
},
{
"id": "f3-rule-run-triplication",
"recorded_on": "2026-07-30",
"pattern": "duplicate-capability-implementations",
"area": "web-viewer-sample rule-run flow",
"summary": "As recorded in the 2026-07-30 round: the rule-run flow existed as three copies; extracting the non-React core of useRuleRun was assessed lowest-risk.",
"status": "open",
"resolution": null
},
{
"id": "f4-viewer-lease-fork",
"recorded_on": "2026-07-30",
"pattern": "duplicate-capability-implementations",
"area": "web-viewer-sample viewer lease client",
"summary": "As recorded in the 2026-07-30 round: two lease client implementations diverged (heartbeat floor 1000 vs 5000); assessed highest-risk of the frontend set. The server-side lease lifecycle is now pinned by ARCH-LIFECYCLE-001; the client fork remains open.",
"status": "open",
"resolution": null
},
{
"id": "f5-unified-fixtures",
"recorded_on": "2026-07-30",
"pattern": "test-fixture-monolith",
"area": "web-viewer-sample unified console fixtures",
"summary": "As recorded in the 2026-07-30 round: fixture monolith needing a split, with the PaneSource provenance honesty marker to be restored explicitly (a product decision that must stay visible).",
"status": "open",
"resolution": null
}
]
}
168 changes: 168 additions & 0 deletions architecture/learning-ledger.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "urn:ai-bim-governance:learning-ledger:v1",
"title": "AI-BIM architecture learning ledger",
"type": "object",
"additionalProperties": false,
"required": [
"$schema",
"schema_version",
"purpose",
"pattern_classes",
"promoted_patterns",
"findings"
],
"properties": {
"$schema": {
"type": "string",
"minLength": 1
},
"schema_version": {
"const": "ai-bim-learning-ledger/v1"
},
"purpose": {
"$ref": "#/definitions/nonEmptyString"
},
"pattern_classes": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/patternClass"
}
},
"promoted_patterns": {
"type": "array",
"items": {
"$ref": "#/definitions/promotedPattern"
}
},
"findings": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/definitions/finding"
}
}
},
"definitions": {
"identifier": {
"type": "string",
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
},
"nonEmptyString": {
"type": "string",
"minLength": 1,
"pattern": "\\S"
},
"patternClass": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"title",
"description"
],
"properties": {
"id": {
"$ref": "#/definitions/identifier"
},
"title": {
"$ref": "#/definitions/nonEmptyString"
},
"description": {
"$ref": "#/definitions/nonEmptyString"
}
}
},
"promotedPattern": {
"type": "object",
"additionalProperties": false,
"required": [
"pattern",
"promoted_to",
"mechanism",
"evidence"
],
"properties": {
"pattern": {
"$ref": "#/definitions/identifier"
},
"promoted_to": {
"type": "string",
"pattern": "^ARCH-[A-Z]+-[0-9]{3}$"
},
"mechanism": {
"$ref": "#/definitions/nonEmptyString"
},
"evidence": {
"$ref": "#/definitions/nonEmptyString"
}
}
},
"finding": {
"type": "object",
"additionalProperties": false,
"required": [
"id",
"recorded_on",
"pattern",
"area",
"summary",
"status",
"resolution"
],
"properties": {
"id": {
"$ref": "#/definitions/identifier"
},
"recorded_on": {
"type": "string",
"format": "date"
},
"pattern": {
"$ref": "#/definitions/identifier"
},
"area": {
"$ref": "#/definitions/nonEmptyString"
},
"summary": {
"$ref": "#/definitions/nonEmptyString"
},
"status": {
"enum": [
"open",
"refactored",
"promoted",
"retired"
]
},
"resolution": {
"$ref": "#/definitions/resolution"
}
}
},
"resolution": {
"type": [
"object",
"null"
],
"additionalProperties": false,
"required": [
"kind",
"reference"
],
"properties": {
"kind": {
"enum": [
"refactor",
"promotion",
"retirement"
]
},
"reference": {
"$ref": "#/definitions/nonEmptyString"
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,4 @@ Mitigation:只接入 `verification-manifest.json` 與 root pytest,不新增
2. Phase 2(2026-07-30 完成):deterministic static desired-vs-observed graph report + no-new-edge / no-new-cycle ratchet。GitNexus 改列 advisory,理由見上。
3. Phase 3(2026-08-03 完成):~~TypeScript dependency-cruiser + Python Import Linter~~ → 純標準函式庫的 layer boundary ratchet(`scripts/lib/layered_architecture.py`)。**Phase 3 更正:** 原文指名的兩個第三方工具未採用——canonical root-contract CI job 只裝 `pytest`/`jsonschema`,`apps/kit-manager-web` 沒有 lockfile 可釘版本,且兩者都不保證本 repo 要求的 Windows/Linux byte-identical 輸出。改為重用 Phase 2 已對抗硬化的 module graph extractor,沿用同一套 baseline ratchet。任務產出(可執行的分層邊界契約)不變,工具不同;偏離記於 `architecture/layer-contract.json` 的 `tooling_deviation` 並由測試斷言,只能 supersede 不能刪除。
4. Phase 4(2026-08-05 完成):review-session / endpoint-lease / stage-binding executable state machines(`architecture/lifecycle-contract.json` + `scripts/lib/lifecycle_contracts.py`)。**Phase 4 定調:** contract 描述 current runtime truth 而非 target intent——`failed` 記為 declared-only(零寫入路徑)、`created→active` 不虛構 runtime 轉移;gate 驗 machine well-formedness、TS union state 集同步(fail-closed 純字面 union 掃描)與 readiness binding 一致性,transition 行為仍由各 service 測試持有。
5. Phase 5:將 recurring `$improve-codebase-architecture` findings 編譯成 permanent rules 與 quality grade。
5. Phase 5(2026-08-10 完成):`architecture/learning-ledger.json`(findings 分類+machine-verified promotion 記錄)+ `scripts/dev/report_architecture_quality.py`(四 gate 彙總、graded 快照 A/B/C、attributed debt inventory)。**Phase 5 定調:** ledger 是 inventory 與 promotion 帳,不觸發自動修復;quality report 唯讀且只宣稱當前快照(不重建歷史 trend);canonical 現況 grade B(全 gate 綠+16 筆 attributed debt)
Loading
Loading