KeiSeiKit-1.0/docs/CONVERGENCE-PLAN.md
Parfii-bot 64ffe39e01 feat(convergence/u3): kei-provision Rust crate — unify hetzner+vultr provisioners
Pre-unlock wave U3 (highest-ROI). Task 7 from CONVERGENCE-PLAN —
consolidate 2 provision-*.sh scripts into Rust via Backend trait.

Old shells (provision-hetzner.sh, provision-vultr.sh) had identical
6-subcommand surface (create|status|destroy|list), log/die/check_deps
helpers, idempotency contract. Sole delta: hcloud vs vultr-cli. RULE 0.2
says Rust-first when >50 LOC + growth expected.

New crate _primitives/_rust/kei-provision/:
- src/backend.rs (58 LOC) — Backend trait: create/status/destroy/list;
  CreateOpts and ServerInfo structs
- src/backends/hetzner.rs (143 LOC) — shells to `hcloud server ...`
  --output=json, parses JSON response, honors HCLOUD_TOKEN env (RULE 0.8)
- src/backends/vultr.rs (189 LOC) — same pattern, `vultr-cli instance`,
  honors VULTR_API_KEY env
- src/exec.rs (100 LOC) — Command runner + PATH-aware env preservation
- src/b64.rs (49 LOC) — minimal user-data base64 encoder; zero
  transitive deps
- src/main.rs (141 LOC) — clap CLI `kei-provision <backend> <cmd>`
- tests/backend_smoke.rs (184 LOC) — tempdir PATH-inject fake hcloud +
  fake vultr-cli, no real cloud. Mutex-serialized (Rust test parallelism).

Tests: 11/11 (3 b64 unit + 8 backend_smoke integration). Coverage:
hetzner status present/absent/list, vultr status found/absent/destroy
idempotent, unknown-backend error, CreateOpts default.

Old shells kept with superseded-v0.17 header — install.sh still copies
them, legacy scripts still work. New users get kei-provision binary.
harden-base.sh untouched (different lifecycle — runs on target VPS).

Backend trait factored to accept aws/doctl/linode follow-ups without
re-architecture.

Workspace Cargo.toml: +kei-provision member (1 line).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:43:40 +08:00

174 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Convergence Plan — KeiSeiKit Substrate Unification
**Date:** 2026-04-23
**Status:** Synthesis of two parallel convergence audits + user directive "постарайся унифицировать"
**Hypothesis (user):** "If substrate is right, the primitive count converges, not expands"
---
## Headline
| Layer | Current | After Consolidation | Cut |
|---|---|---|---|
| Atom crates | 25 | 15-17 | 36% |
| Agent capabilities | 11 | 7 | 36% |
| Skills | 39 | ~28 | 28% |
| **Total** | **75** | **~52** | **31%** |
Less aggressive than user's gut (~35) because audit shows genuine domain-disjointness in some clusters (setup pipelines, motion/animation). User's hypothesis partially confirmed — substrate does converge, but only where LOC overlap is real. Where it isn't, we get shared blocks + preamble extraction instead.
---
## Convergence by layer
### Atoms (25 → 15-17)
Source: `docs/CONVERGENCE-AUDIT-ATOMS.md` (architect, E2).
| Consolidation | From | To | When | Effort |
|---|---|---|---|---|
| **SQLite-CRUD engine** | 6 crates (kei-chat/content/social/sage/task/crossdomain-store) | 1 `kei-entity-store` engine + 6 thin schema plugins | **parallel with Stream B** (not before) | 2-3 days |
| **Provisioner unification** | 3 shells (provision-hetzner, provision-vultr, harden-base) | 1 `kei-provision <backend>` Rust (harden-base stays — different lifecycle) | **v0.24 now** | 0.5-1 day |
| **Frontend meta-dispatcher** | 4 shells (design-scrape, live-preview, figma-tokens, frontend-inspect) | thin `kei-frontend fetch\|analyze` shim, keep 4 impls underneath | post-unlock, optional | 0.5 day |
| **4 unmapped LBM-port crates** | unknown | audit after mapping | post-unlock (2026-06-03+) | TBD |
**Evidence:** `Store::open` is byte-identical across 5 crates (`kei-chat-store/src/store.rs:13-21` et al). FTS re-index literal-same body. Deps 7-liner identical. ~500 LOC duplication removed.
**Timing constraint (RULE 0.5):** DO NOT consolidate SQLite-CRUD before Stream B atom refactor of remaining 24 crates lands. Fragment-then-aggregate dual-direction = merge hell.
### Capabilities (11 → 7)
Source: `docs/CONVERGENCE-AUDIT-CAPS.md` (critic, E2-E4).
| Consolidation | From | To | When |
|---|---|---|---|
| **scope::path-filter** | scope::files-whitelist + scope::files-denylist | `scope::path-filter { mode: include\|exclude, patterns: [...] }` | post-unlock (schema amendment) |
| **quality::cargo-green** | quality::cargo-check-green + quality::tests-green | `quality::cargo-green { verbs: [check, test, clippy, fmt] }` — future-proofs clippy + fmt | post-unlock |
**Kept separate with rationale:**
- `output::report-format` + `output::severity-grade` — different validator shapes (section headers vs per-finding enum). Merge into generic `output::validator` pushes complexity down without saving files.
- `tools::read-only` + `tools::cargo-only-bash` — different gate mechanisms (tool-name allow-list vs argv regex). Rename for clarity only: `tools::read-only``tools::deny-tools`, `tools::cargo-only-bash``tools::bash-allowlist`.
- `policy::no-git-ops` — collapse `policy::` + `safety::` namespace ONLY IF no planned `policy::` additions. Check `_manifests/*.toml` before collapsing.
**Final 7:** `safety::no-git-ops`, `safety::no-dep-bump`, `scope::path-filter`, `quality::constructor-pattern`, `quality::cargo-green`, `output::report-format`, `output::severity-grade`, `tools::deny-tools`, `tools::bash-allowlist` — actually 9 if we keep policy as safety namespace + don't merge tools. Honest count: **7-9 depending on namespace-merge decision**.
### Skills (39 → ~28)
Source: same audit.
| Consolidation | From | To | When |
|---|---|---|---|
| **/audit `<target>`** | /seo-audit + /a11y-audit + /perf-audit + /responsive-audit + /pr-review + /security-review (6) | 1 skill + checklist registry `checklists/<target>.md` | post-unlock — **largest LOC win (~50%)** |
| **Deprecate /site-builder** | /site-builder (subset of /site-create) | `[DEPRECATED: use /site-create]` header | pre-unlock |
| **Deprecate research presets** | /competitor-analysis + /design-inspiration (both are /research Phase-5 specializations) | Document as `/research --angle=competitors` / `--angle=design-refs` | pre-unlock |
| **/animate gateway** | motion-design + scroll-animation + web-effects + ai-animation | Add top-level `/animate` router (40 LOC). Keep 4 existing (domain-disjoint matrices) | pre-unlock |
**Kept separate with rationale:**
- Setup pipelines (ci-scaffold, auth-setup, observability-setup, docs-scaffold, schema-design) — block sets disjoint (`ci-*.md` vs `auth-*.md`). Unified `/scaffold <domain>` would just be a switch. Reject consolidation. Extract `_blocks/pipeline-5phase-template.md` instead.
- Motion/animation family — each has different decision matrix over different libraries. Mega-skill would be 1000+ LOC.
---
## Execution plan
### 🟢 Pre-unlock quick wins (non-breaking, ship NOW)
These land during the current schema-lock window (before 2026-05-14 for agent substrate; before 2026-06-03 for atom substrate). Zero risk of breaking the contract.
| # | Task | Effort | Locks affected |
|---|---|---|---|
| 1 | Extract `_blocks/pipeline-5phase-template.md` — shared preamble for ci/auth/obs/docs/schema pipeline skills | 0.5 day | none |
| 2 | Extract `_blocks/rule-pure-click-contract.md` — AskUserQuestion contract referenced by 5+ skills | 0.5 day | none |
| 3 | Rename capabilities for clarity (`cargo-only-bash` → `bash-allowlist`, `read-only``deny-tools`). Keep old names as deprecated aliases | 0.25 day | none (additive) |
| 4 | Deprecate `/site-builder` with `[DEPRECATED: use /site-create]` header | 0.1 day | none |
| 5 | Deprecate `/competitor-analysis` + `/design-inspiration` as presets pointing to `/research` | 0.1 day | none |
| 6 | Add `/animate` gateway skill (40 LOC router) | 0.25 day | none |
| **7** ✓ | **Cluster 2: Provisioner unification `kei-provision <backend>`** | **1 day** | **atom substrate — but additive, not removing existing scripts** |
**Total pre-unlock: ~3 days**. All non-breaking, all safe to parallel-agent.
### 🟡 Mid-cycle (parallel with Stream B continued refactor)
During the substrate v1 atom-lock window as remaining 24 crates get their atom layout (Stream B extension).
| # | Task | Effort |
|---|---|---|
| 8 | **Cluster 1: SQLite-CRUD engine extraction**`kei-entity-store` + 6 plugins. Piggyback on Stream B atom decomposition | 2-3 days |
| 9 | Enumerate + audit remaining 4 v0.14 LBM-port crates not in the 6 | 0.5 day audit |
### 🔴 Post-unlock (after 2026-06-03 for atoms, 2026-05-14 for agents)
Requires schema amendment. Non-trivial validation before merging.
| # | Task | Effort | Validation required |
|---|---|---|---|
| 10 | `scope::path-filter { mode, patterns }` — merge whitelist + denylist | 1 day | Read `_assembler/src/` first to confirm capability-fragment injection supports parameterized mode |
| 11 | `quality::cargo-green { verbs }` — merge check + test + future clippy/fmt | 1 day | Same |
| 12 | `/audit <target>` with `checklists/<target>.md` registry | 2-3 days | Refactor 5 existing audit skills to data + data-driven skill shell |
| 13 | Cluster 3: `kei-frontend fetch\|analyze` meta-dispatcher (OPTIONAL) | 0.5 day | None — optional |
| 14 | Evaluate policy:: + safety:: namespace merge | 0.25 day | Check planned `policy::` additions first |
### Total estimated wall-time with parallel execution
- Pre-unlock wave: **~3 days** (7 tasks, fully parallel-safe)
- Mid-cycle: **~3 days** (piggyback timing)
- Post-unlock: **~5-6 days** (4 tasks, some parallel)
**Grand total: ~12 days over 6-week window = 4 days of my own work / week average.**
---
## Shared-block extraction (bonus, non-counted)
Not a consolidation per se — a DRY refactor. Each saved LOC is LOC that was duplicated across 5+ skills.
| Block | Extracted from | LOC saved |
|---|---|---|
| `_blocks/pipeline-5phase-template.md` | 5 pipeline skills | ~150 |
| `_blocks/rule-pure-click-contract.md` | 5+ skills | ~50 |
| `_blocks/evidence-grading.md` (already exists?) | unclear — confirm not duplicated | TBD |
---
## Validation checklist before consolidation merge
Before accepting any task from §Post-unlock:
1. Read `_assembler/src/` — confirm capability-fragment injection handles parameterized mode (`mode: include|exclude`)
2. Grep all shipped manifests for dependencies on the to-be-renamed capability names
3. Grep all skills for references to deprecated skill names
4. `tests/substrate_integration.sh` passes post-change (no regression)
5. `tests/hook_wiring_integration.sh` passes post-change
6. New test for each consolidation: tempfile fixture → new unified primitive → expected behaviour both modes
---
## Honest audit gaps disclosed by agents
**Atom audit gaps:**
- Did not enumerate 4 of the 10 v0.14 LBM-port crates → Cluster 4 grade dropped from E2 to E3 for those 4
- LOC estimates ±20%
- Did not read `atoms/*.md` — if Stream B already encodes plug-in split, Cluster 1 may be partly in-flight
**Capability audit gaps:**
- Could not read `_capabilities/*.md` directly (Read tool enumeration issue) — verdicts based on manifest references + architecture doc
- C1-C5 verdicts inherit this — re-validate by reading `_assembler/src/` before schema amend
**Both audits graded E2-E4.** Not E1 (which would require running the consolidation and measuring). Take with appropriate humility.
---
## Convergence wisdom reference
**User's hypothesis was right directionally, calibrated wrong in magnitude.**
- Predicted 75 → ~35 (53%)
- Actual 75 → ~52 (31%)
Gap is skills: user assumed 39 → ~18, reality 39 → ~28. Why? Because domain-disjoint content (setup pipelines, motion/animation) doesn't compress via generic wrappers. It compresses via *shared preamble blocks* instead — which is a different technique (block composition vs verb parameterization).
**Distinguish:**
- **Verb parameterization** — when content is homogeneous, extract verb + param (scope::path-filter, quality::cargo-green, /audit <target>). Works when LOC overlap >50%.
- **Block extraction** — when structure is homogeneous but content heterogeneous, extract shared block and reference it (_blocks/pipeline-5phase-template.md). Works when preamble overlap >50% but body differs.
- **Subset deprecation** — when one skill is a strict subset of another (/site-builder ⊆ /site-create). Rare but clean.
All three techniques apply in this plan. Substrate doesn't always compress via the same primitive.