@softspark/ai-toolkit 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,917 @@
1
+ ---
2
+ title: "Plan: Enterprise Config Inheritance — Multi-Repo Governance with extends"
3
+ category: planning
4
+ service: ai-toolkit
5
+ tags:
6
+ - enterprise
7
+ - multi-repo
8
+ - config-inheritance
9
+ - extends
10
+ - governance
11
+ - team-management
12
+ - monorepo
13
+ doc_type: plan
14
+ status: proposed
15
+ created: "2026-04-10"
16
+ last_updated: "2026-04-10"
17
+ completion: "0%"
18
+ description: "Configuration inheritance system for ai-toolkit. Enables organizations to define a shared base config (agents, rules, hooks, profiles, constitution overrides) published as an npm package or local path, which individual projects extend via an `extends` field. Changes to the base config propagate automatically on `ai-toolkit update`. Targets enterprises managing 10-100+ repositories with uniform AI governance."
19
+ ---
20
+
21
+ # Plan: Enterprise Config Inheritance — Multi-Repo Governance with `extends`
22
+
23
+ **Status:** Proposed
24
+ **Completion:** 0%
25
+ **Created:** 2026-04-10
26
+ **Origin:** Organizations adopting ai-toolkit across 10-100+ repositories face a config synchronization problem — updating a rule or policy requires touching every repository individually. The `extends` pattern (popularized by ESLint, TypeScript, Prettier) solves this by establishing a single source of truth that projects inherit from.
27
+ **Estimated Effort:** 5-7 weeks (1 person) — MVP (core engine + install integration) shippable in ~3.5 weeks
28
+
29
+ ---
30
+
31
+ ## 1. Objective
32
+
33
+ Create a configuration inheritance system where projects can extend a shared base config published as an npm package, a Git URL, or a local path. The base config defines organizational defaults (which agents to enable, which rules to enforce, which hooks to require, persona presets, and constitution amendments). Individual projects can override or supplement the base, creating a layered governance model.
34
+
35
+ **Key design principles:**
36
+ - **Familiar pattern** — mirrors ESLint's `extends`, TypeScript's `extends`, and Prettier's shared configs
37
+ - **npm-first distribution** — base configs are regular npm packages (e.g., `@mycompany/ai-toolkit-config`). Resolver shells out to `npm pack` CLI (respects `.npmrc` auth) — no hand-rolled npm client, preserves stdlib-only constraint
38
+ - **Single extends in v1** — `"extends": "string"` only. Multi-base merge (`"extends": [...]`) deferred to v2 to avoid merge-ordering complexity (ESLint's multi-extends is a known source of confusion)
39
+ - **Layered merge** — base → project, with explicit override semantics (`override: true` required for safety-critical overrides)
40
+ - **Constitution immutable** — base constitution articles cannot be modified by projects, period. Projects can only ADD new articles (article 6+). No weakening detection heuristics — absolute immutability is simpler and safer
41
+ - **Offline-capable** — resolved at `install`/`update` time, not at runtime
42
+ - **Backward-compatible** — projects without `extends` work exactly as today (no breaking changes)
43
+ - **Audit trail** — `state.json` records which base config was resolved and what was overridden
44
+
45
+ ---
46
+
47
+ ## 1a. Functional Requirements
48
+
49
+ | ID | Requirement | Priority | Success Metric |
50
+ |----|-------------|----------|----------------|
51
+ | FR1 | Resolve `extends` from npm package, git URL, local path | Must | 4 source types work |
52
+ | FR2 | Deep merge base → project config with layered semantics | Must | Merge engine handles dict, list, scalar types |
53
+ | FR3 | Constitution immutability — Articles I-V cannot be modified | Must | 100% block rate on modification attempts |
54
+ | FR4 | Override validation with `override: true` + `justification` | Must | Missing justification → error |
55
+ | FR5 | `enforce` block constraints (minHookProfile, requiredPlugins, forbidOverride, requiredAgents) | Must | All 4 constraint types enforced |
56
+ | FR6 | Install/update integration — resolve extends during install | Must | `install --local` detects `.ai-toolkit.json` |
57
+ | FR7 | `config diff` command — show project vs base differences | Must | All merge layers visible |
58
+ | FR8 | `config validate` command — schema + enforcement validation | Must | Exit 0/1 for pass/fail |
59
+ | FR9 | `config init` — interactive project config setup | Should | Guided flow produces valid `.ai-toolkit.json` |
60
+ | FR10 | `config create-base` — scaffold npm base config package | Should | Ready-to-publish package with `package.json` |
61
+ | FR11 | Lock file for reproducible installs | Should | Identical resolved config across team members |
62
+ | FR12 | Audit trail in `state.json` | Should | Resolved version + overrides recorded |
63
+ | FR13 | CI enforcement command (`config check`) | Could | Exit 0/1 for governance compliance |
64
+ | FR14 | Multi-base extends (`"extends": [...]`) | Won't (v2) | Deferred — merge ordering complexity |
65
+
66
+ ---
67
+
68
+ ## 2. Architecture Overview
69
+
70
+ ```
71
+ Organization Level (published once, consumed by all repos):
72
+ ═══════════════════════════════════════════════════════════
73
+
74
+ @mycompany/ai-toolkit-config (npm package)
75
+ ├── ai-toolkit.config.json ← base configuration
76
+ ├── rules/
77
+ │ ├── code-review-policy.md ← company-specific rules
78
+ │ └── deployment-checklist.md
79
+ ├── agents/
80
+ │ └── compliance-auditor.md ← company-specific agent
81
+ └── package.json
82
+
83
+ Project Level (per-repository):
84
+ ══════════════════════════════
85
+
86
+ my-service/
87
+ ├── .ai-toolkit.json ← project config with "extends"
88
+ ├── .claude/
89
+ │ ├── CLAUDE.md ← generated (base + project merged)
90
+ │ └── settings.json ← generated (base hooks + project hooks merged)
91
+ └── ...
92
+
93
+ Merge Pipeline:
94
+ ══════════════
95
+
96
+ @mycompany/ai-toolkit-config ← Layer 0: organizational defaults
97
+
98
+
99
+ ai-toolkit defaults (manifest.json) ← Layer 1: toolkit defaults
100
+
101
+
102
+ .ai-toolkit.json ← Layer 2: project overrides
103
+
104
+
105
+ Resolved Configuration ← Final: CLAUDE.md, settings.json, etc.
106
+ ```
107
+
108
+ ### Config Resolution Order
109
+
110
+ ```
111
+ 1. Load base config from "extends" (npm package, git URL, or local path)
112
+ 2. Merge with ai-toolkit defaults (manifest.json profiles)
113
+ 3. Apply project-level overrides from .ai-toolkit.json
114
+ 4. Validate merged config (constitution immutability, schema validation)
115
+ 5. Generate output files (CLAUDE.md, settings.json, agent symlinks, etc.)
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 3. Progress Tracking
121
+
122
+ | # | Feature | Priority | Status | Est. Time | Notes |
123
+ |---|---------|----------|--------|-----------|-------|
124
+ | 1.1 | `.ai-toolkit.json` schema definition | P0 | Proposed | 1d | JSON Schema with `extends` field |
125
+ | 1.2 | Config resolver (npm, git, local path) | P0 | Proposed | 3d | Fetch + cache + validate base configs |
126
+ | 1.3 | Merge engine (layered merge with override semantics) | P0 | Proposed | 3d | Deep merge with `override: true` gates |
127
+ | 1.4 | Constitution immutability guard | P0 | Proposed | 1d | Block weakening of safety articles |
128
+ | 2.1 | Install/update integration | P0 | Proposed | 2d | Resolve extends during install/update |
129
+ | 2.2 | `ai-toolkit config diff` command | P0 | Proposed | 1.5d | Show project vs base differences — primary debugging tool |
130
+ | 2.3 | `ai-toolkit config validate` command | P0 | Proposed | 1d | Validate .ai-toolkit.json schema + extends resolution |
131
+ | 2.4 | `ai-toolkit config init` command | P1 | Proposed | 1.5d | Interactive project config setup |
132
+ | 2.5 | `ai-toolkit config create-base` command | P1 | Proposed | 2d | Scaffold base config package |
133
+ | 3.1 | Audit trail in state.json | P1 | Proposed | 1d | Record resolved config provenance |
134
+ | 3.2 | Lock file (`.ai-toolkit.lock.json`) | P1 | Proposed | 1.5d | Pin resolved versions for reproducibility |
135
+ | 3.3 | Base config scaffolder (npm package template) | P1 | Proposed | 1.5d | Ready-to-publish template |
136
+ | 3.4 | CI enforcement (`ai-toolkit config check`) | P2 | Proposed | 1d | Verify project adheres to base + no unapproved overrides |
137
+ | 4.1 | Tests | P1 | Proposed | 3d | Unit: resolution, merge, immutability, override, CLI commands. Integration: `install --local` with `.ai-toolkit.json` containing `extends`, verify resolved `CLAUDE.md` has base + project rules merged end-to-end |
138
+ | 4.2 | Documentation | P1 | Proposed | 3d | Enterprise setup guide + all 9 docs per CLAUDE.md: README, CLAUDE.md, ARCHITECTURE.md, package.json, llms.txt, llms-full.txt, AGENTS.md, skills-catalog.md, architecture-overview.md |
139
+
140
+ **Phasing (MVP-first):**
141
+ - **MVP Phase 1 (week 1-2):** Core engine — schema (1.1), resolver (1.2), merge engine (1.3), constitution guard (1.4)
142
+ - **MVP Phase 2 (week 2-3):** Integration + diff — install integration (2.1), `config diff` (2.2), `config validate` (2.3), tests for above (~3.5 weeks = shippable MVP)
143
+ - **Phase 3 (week 4-5):** CLI polish — `config init` (2.4), `config create-base` (2.5), scaffolder (3.3)
144
+ - **Phase 4 (week 5-6):** Enterprise — audit trail (3.1), lock file (3.2), CI enforcement (3.4) (**gate behind real enterprise feedback**)
145
+ - **Phase 5 (week 6-7):** Tests + documentation (4.1, 4.2) (3d docs — all 9 docs per CLAUDE.md rules)
146
+
147
+ > **Demand validation gate:** Ship MVP (Phases 1-2), announce, measure adoption. Only build Phase 4 (lock file, CI enforcement, audit trail) in response to confirmed enterprise demand.
148
+
149
+ ---
150
+
151
+ ## 4. Dependency Graph
152
+
153
+ ```
154
+ MVP Phase 1: Core Engine (week 1-2)
155
+ ====================================
156
+ Schema definition (1.1) ──────┐
157
+ ├──► Merge engine (1.3)
158
+ Config resolver (1.2) ────────┤
159
+ └──► Constitution guard (1.4)
160
+
161
+ MVP Phase 2: Integration + Diff (week 2-3)
162
+ ============================================
163
+ Install integration (2.1) ──┐
164
+ ├──► config diff (2.2)
165
+ └──► config validate (2.3)
166
+ └──► MVP tests → SHIP
167
+
168
+ ═══ DEMAND VALIDATION GATE ═══
169
+
170
+ Phase 3: CLI Polish (week 4-5)
171
+ ===============================
172
+ ├──► config init (2.4)
173
+ └──► create-base (2.5) + scaffolder (3.3)
174
+
175
+ Phase 4: Enterprise (week 5-6)
176
+ ================================
177
+ Audit trail (3.1) ──┐
178
+ ├──► Lock file (3.2)
179
+ └──► CI enforcement (3.4)
180
+
181
+ Phase 5: Polish (week 6-7)
182
+ ===========================
183
+ └──► Full tests + docs (4.1, 4.2)
184
+ ```
185
+
186
+ ---
187
+
188
+ ## 5. Detailed Implementation
189
+
190
+ ### Phase 1: Core Engine (week 1-2)
191
+
192
+ #### 1.1 Configuration Schema (`.ai-toolkit.json`)
193
+
194
+ > **v1 scope:** The full schema below shows the target state. v1 implements only: `extends`, `profile`, `agents`, `rules`, `constitution`, and `enforce`. See section 6a for the v1/v2 field breakdown.
195
+
196
+ **Project-level config file:**
197
+
198
+ ```json
199
+ {
200
+ "$schema": "https://softspark.github.io/ai-toolkit/schemas/ai-toolkit-config.json",
201
+
202
+ "extends": "@mycompany/ai-toolkit-config",
203
+
204
+ "profile": "standard",
205
+ "persona": "backend-lead",
206
+ "hookProfile": "strict",
207
+
208
+ "agents": {
209
+ "enabled": ["backend-specialist", "test-engineer", "debugger"],
210
+ "disabled": ["game-developer", "mobile-developer"],
211
+ "custom": ["./agents/compliance-auditor.md"]
212
+ },
213
+
214
+ "skills": {
215
+ "disabled": ["/deploy", "/rollback"],
216
+ "custom": ["./skills/internal-deploy/"]
217
+ },
218
+
219
+ "rules": {
220
+ "inject": ["./rules/code-review-policy.md"],
221
+ "remove": []
222
+ },
223
+
224
+ "plugins": {
225
+ "required": ["security-pack", "memory-pack"],
226
+ "forbidden": []
227
+ },
228
+
229
+ "languages": ["typescript", "python"],
230
+
231
+ "editors": ["cursor", "windsurf", "copilot"],
232
+
233
+ "constitution": {
234
+ "amendments": [
235
+ {
236
+ "article": 6,
237
+ "title": "Data Sovereignty",
238
+ "text": "All code generation must comply with GDPR. No personal data in prompts. No PII in generated code comments."
239
+ }
240
+ ]
241
+ },
242
+
243
+ "overrides": {
244
+ "hooks": {
245
+ "quality-check": {
246
+ "override": true,
247
+ "justification": "Company uses custom lint pipeline via Jenkins",
248
+ "replacement": "skip"
249
+ }
250
+ }
251
+ }
252
+ }
253
+ ```
254
+
255
+ **Base config (`ai-toolkit.config.json` in npm package):**
256
+
257
+ ```json
258
+ {
259
+ "$schema": "https://softspark.github.io/ai-toolkit/schemas/ai-toolkit-base-config.json",
260
+ "name": "@mycompany/ai-toolkit-config",
261
+ "version": "2.1.0",
262
+ "description": "MyCompany standard AI coding config",
263
+
264
+ "extends": null,
265
+
266
+ "profile": "strict",
267
+ "persona": "backend-lead",
268
+ "hookProfile": "strict",
269
+
270
+ "agents": {
271
+ "enabled": ["backend-specialist", "test-engineer", "code-reviewer", "security-auditor", "debugger", "documenter"],
272
+ "disabled": ["game-developer"],
273
+ "custom": ["./agents/compliance-auditor.md"]
274
+ },
275
+
276
+ "rules": {
277
+ "inject": [
278
+ "./rules/code-review-policy.md",
279
+ "./rules/deployment-checklist.md",
280
+ "./rules/data-handling-policy.md"
281
+ ]
282
+ },
283
+
284
+ "plugins": {
285
+ "required": ["security-pack"]
286
+ },
287
+
288
+ "languages": ["typescript"],
289
+
290
+ "constitution": {
291
+ "amendments": [
292
+ {
293
+ "article": 6,
294
+ "title": "Data Sovereignty",
295
+ "text": "All code generation must comply with GDPR. No personal data in prompts."
296
+ },
297
+ {
298
+ "article": 7,
299
+ "title": "Audit Compliance",
300
+ "text": "All AI-generated code changes must be logged to the company audit system. The governance-capture hook must remain enabled."
301
+ }
302
+ ]
303
+ },
304
+
305
+ "enforce": {
306
+ "minHookProfile": "standard",
307
+ "requiredPlugins": ["security-pack"],
308
+ "forbidOverride": ["constitution", "guard-destructive", "guard-path"],
309
+ "requiredAgents": ["security-auditor"]
310
+ }
311
+ }
312
+ ```
313
+
314
+ **`enforce` section:** Base configs can define non-overridable constraints:
315
+ - `minHookProfile` — projects cannot go below this profile
316
+ - `requiredPlugins` — must be installed in all projects
317
+ - `forbidOverride` — these components cannot be overridden
318
+ - `requiredAgents` — must be enabled in all projects
319
+
320
+ ---
321
+
322
+ #### 1.2 Config Resolver
323
+
324
+ **Resolution sources:**
325
+
326
+ | Source | Syntax | Resolution |
327
+ |--------|--------|------------|
328
+ | npm package | `"extends": "@mycompany/ai-toolkit-config"` | `npm pack --pack-destination /tmp` + extract |
329
+ | npm with version | `"extends": "@mycompany/ai-toolkit-config@^2.0.0"` | Version resolution via npm |
330
+ | Git URL | `"extends": "git+https://github.com/myco/ai-config.git"` | `git clone --depth 1` to cache |
331
+ | Local path | `"extends": "../shared-config"` | Resolve relative to project root |
332
+ | ~~Multiple bases~~ | ~~`"extends": ["@mycompany/base", "@mycompany/typescript-extra"]`~~ | Deferred to v2 — multi-base merge ordering is a complexity trap |
333
+
334
+ **Cache directory:** `~/.ai-toolkit/config-cache/`
335
+ ```
336
+ ~/.ai-toolkit/config-cache/
337
+ @mycompany/
338
+ ai-toolkit-config/
339
+ 2.1.0/
340
+ ai-toolkit.config.json
341
+ rules/
342
+ agents/
343
+ ```
344
+
345
+ **Resolution algorithm:**
346
+ ```python
347
+ def resolve_extends(extends_value: str, project_root: str) -> list[BaseConfig]:
348
+ """Resolve extends chain into ordered list of base configs.
349
+
350
+ v1: single string only. Multi-base (list) deferred to v2.
351
+ """
352
+ configs = []
353
+ for source in [extends_value]: # v2: support list[str]
354
+ if source.startswith('@') or source.startswith('npm:'):
355
+ config = resolve_npm(source)
356
+ elif source.startswith('git+'):
357
+ config = resolve_git(source)
358
+ elif source.startswith('.') or source.startswith('/'):
359
+ config = resolve_local(source, project_root)
360
+ else:
361
+ raise ConfigError(f"Unknown extends source: {source}")
362
+
363
+ # Recursive: base config may also have "extends"
364
+ if config.extends:
365
+ parent_configs = resolve_extends(config.extends, config.root)
366
+ configs.extend(parent_configs)
367
+
368
+ configs.append(config)
369
+
370
+ return configs
371
+
372
+
373
+ def resolve_extends(extends_value: str, project_root: str,
374
+ _visited: set[str] | None = None) -> list[BaseConfig]:
375
+ """Full signature with cycle detection via visited set."""
376
+ if _visited is None:
377
+ _visited = set()
378
+ if extends_value in _visited:
379
+ raise ConfigError(
380
+ f"Circular extends detected: {extends_value} already in chain "
381
+ f"{' → '.join(_visited)}. Check your base config's 'extends' field."
382
+ )
383
+ if len(_visited) >= 5:
384
+ raise ConfigError(
385
+ f"Extends chain too deep (max 5 levels). Chain: {' → '.join(_visited)}"
386
+ )
387
+ _visited.add(extends_value)
388
+ # ... resolution logic as above, passing _visited to recursive calls
389
+ ```
390
+
391
+ **Max recursion depth:** 5 levels (prevent circular extends). Circular detection via visited set.
392
+
393
+ **Offline handling:** If the npm/git source is unavailable:
394
+ 1. Check cache (`~/.ai-toolkit/config-cache/`)
395
+ 2. If cached version found → use with warning: "Using cached config v2.1.0 (offline)"
396
+ 3. If not cached → error with instructions: "Run `ai-toolkit config update` when online"
397
+
398
+ ---
399
+
400
+ #### 1.3 Merge Engine
401
+
402
+ **Layered deep merge with explicit override semantics:**
403
+
404
+ ```python
405
+ def merge_configs(base: dict, project: dict) -> dict:
406
+ """Merge project config over base config with rules."""
407
+ merged = {}
408
+
409
+ for key in set(base.keys()) | set(project.keys()):
410
+ base_val = base.get(key)
411
+ proj_val = project.get(key)
412
+
413
+ if proj_val is None:
414
+ merged[key] = base_val
415
+ elif base_val is None:
416
+ merged[key] = proj_val
417
+ elif key == 'constitution':
418
+ merged[key] = merge_constitution(base_val, proj_val)
419
+ elif key == 'agents':
420
+ merged[key] = merge_agents(base_val, proj_val)
421
+ elif key == 'rules':
422
+ merged[key] = merge_rules(base_val, proj_val)
423
+ elif key == 'overrides':
424
+ merged[key] = validate_overrides(base, proj_val)
425
+ elif isinstance(base_val, dict) and isinstance(proj_val, dict):
426
+ merged[key] = merge_configs(base_val, proj_val)
427
+ elif isinstance(base_val, list) and isinstance(proj_val, list):
428
+ merged[key] = list(set(base_val + proj_val)) # union
429
+ else:
430
+ merged[key] = proj_val # project wins for scalars
431
+
432
+ return merged
433
+ ```
434
+
435
+ **Agent merge rules:**
436
+ ```python
437
+ def merge_agents(base: dict, project: dict) -> dict:
438
+ """Merge agent configs — project can enable/disable but not remove base-required."""
439
+ merged_enabled = set(base.get('enabled', []))
440
+
441
+ # Project can add agents
442
+ merged_enabled.update(project.get('enabled', []))
443
+
444
+ # Project can disable agents (unless base enforces them)
445
+ for agent in project.get('disabled', []):
446
+ if agent in base.get('enforce', {}).get('requiredAgents', []):
447
+ raise ConfigError(
448
+ f"Cannot disable '{agent}' — required by base config '{base['name']}'. "
449
+ f"Contact your team lead to request an exemption."
450
+ )
451
+ merged_enabled.discard(agent)
452
+
453
+ return {
454
+ 'enabled': sorted(merged_enabled),
455
+ 'custom': base.get('custom', []) + project.get('custom', [])
456
+ }
457
+ ```
458
+
459
+ **Override validation:**
460
+ ```python
461
+ def validate_overrides(base: dict, overrides: dict) -> dict:
462
+ """Validate project overrides against base enforcement rules."""
463
+ forbidden = set(base.get('enforce', {}).get('forbidOverride', []))
464
+
465
+ for key, override in overrides.items():
466
+ if key in forbidden:
467
+ raise ConfigError(
468
+ f"Cannot override '{key}' — forbidden by base config '{base['name']}'.\n"
469
+ f"Forbidden overrides: {', '.join(sorted(forbidden))}\n"
470
+ f"Contact your team lead to request an exemption."
471
+ )
472
+ if not override.get('override'):
473
+ raise ConfigError(
474
+ f"Override for '{key}' requires explicit 'override: true' + 'justification' field.\n"
475
+ f"This ensures intentional deviation from organizational defaults."
476
+ )
477
+ if not override.get('justification'):
478
+ raise ConfigError(
479
+ f"Override for '{key}' requires a 'justification' field explaining why.\n"
480
+ f"Example: \"Company uses custom lint pipeline via Jenkins\""
481
+ )
482
+
483
+ return overrides
484
+ ```
485
+
486
+ ---
487
+
488
+ #### 1.4 Constitution Immutability Guard
489
+
490
+ **Core rule:** Base constitution articles are absolutely immutable. Projects can only ADD new articles.
491
+
492
+ No weakening-detection heuristic (character count, semantic analysis) — these produce false positives and are gameable. Instead, the rule is simple and absolute: if an article number exists in the base, it cannot be modified by the project.
493
+
494
+ ```python
495
+ def merge_constitution(base: dict, project: dict) -> dict:
496
+ """Merge constitution — additions only, no modifications."""
497
+ base_amendments = {a['article']: a for a in base.get('amendments', [])}
498
+ proj_amendments = {a['article']: a for a in project.get('amendments', [])}
499
+
500
+ # Toolkit articles I-V are always immutable
501
+ IMMUTABLE_ARTICLES = {1, 2, 3, 4, 5}
502
+
503
+ merged = dict(base_amendments)
504
+
505
+ for article_num, amendment in proj_amendments.items():
506
+ if article_num in IMMUTABLE_ARTICLES:
507
+ raise ConfigError(
508
+ f"Cannot modify Constitution Article {article_num} — immutable.\n"
509
+ f"Articles I-V are defined by ai-toolkit and cannot be overridden.\n"
510
+ f"You can ADD new articles (article 6+)."
511
+ )
512
+ if article_num in base_amendments:
513
+ # Base articles are immutable — projects cannot modify them
514
+ raise ConfigError(
515
+ f"Cannot modify Constitution Article {article_num} — "
516
+ f"defined by base config '{base.get('name', 'unknown')}'.\n"
517
+ f"Base articles are immutable. You can ADD new articles "
518
+ f"with a higher article number."
519
+ )
520
+ merged[article_num] = amendment
521
+
522
+ return {'amendments': list(merged.values())}
523
+ ```
524
+
525
+ ---
526
+
527
+ ### MVP Phase 2: Integration + Diff (week 2-3)
528
+
529
+ #### 2.1 Install/Update Integration
530
+
531
+ **Modified `install.py` flow:**
532
+
533
+ ```python
534
+ # During install --local:
535
+ # 1. Check for .ai-toolkit.json in project root
536
+ # 2. If found and has "extends":
537
+ # a. Resolve base config(s)
538
+ # b. Merge base → project
539
+ # c. Validate merged config
540
+ # d. Generate files from merged config
541
+ # 3. If not found: proceed with current behavior (backwards compatible)
542
+ ```
543
+
544
+ **CLI flags:**
545
+ ```bash
546
+ ai-toolkit install --local # auto-detect .ai-toolkit.json
547
+ ai-toolkit install --local --config ./custom.json # explicit config file
548
+ ai-toolkit update --local # re-resolve extends + update
549
+ ai-toolkit update --local --refresh-base # force re-fetch base config
550
+ ```
551
+
552
+ ---
553
+
554
+ #### 2.2 `ai-toolkit config diff`
555
+
556
+ **Show differences between project config and base:**
557
+
558
+ ```bash
559
+ ai-toolkit config diff
560
+
561
+ # Output:
562
+ # Base: @mycompany/ai-toolkit-config@2.1.0
563
+ #
564
+ # Profile: strict (base) → standard (project) ⚠ OVERRIDE
565
+ # Persona: backend-lead (base) → frontend-lead (project)
566
+ # Hook Profile: strict (base) → strict (inherited)
567
+ #
568
+ # Agents:
569
+ # + frontend-specialist (project adds)
570
+ # - game-developer (base disables)
571
+ # = security-auditor (base requires, cannot disable)
572
+ #
573
+ # Rules:
574
+ # + ./rules/api-standards.md (project adds)
575
+ # = code-review-policy.md (inherited from base)
576
+ #
577
+ # Constitution:
578
+ # = Articles I-V (immutable)
579
+ # = Article 6: Data Sovereignty (inherited from base)
580
+ # + Article 8: API Standards (project adds)
581
+ #
582
+ # Overrides:
583
+ # quality-check: SKIP (justification: "Custom Jenkins pipeline")
584
+ ```
585
+
586
+ ---
587
+
588
+ #### 2.3 `ai-toolkit config validate`
589
+
590
+ ```bash
591
+ ai-toolkit config validate
592
+
593
+ # Checks:
594
+ # ✓ .ai-toolkit.json schema valid
595
+ # ✓ extends: @mycompany/ai-toolkit-config@2.1.0 resolved
596
+ # ✓ No forbidden overrides
597
+ # ✓ Required plugins installed: security-pack
598
+ # ✓ Required agents enabled: security-auditor
599
+ # ✓ Constitution articles I-V intact
600
+ # ✓ Hook profile meets minimum: standard ≥ standard
601
+ # ✓ All custom rule files exist
602
+ # ✓ All custom agent files exist
603
+ ```
604
+
605
+ ---
606
+
607
+ ### Phase 3: CLI Polish (week 4-5)
608
+
609
+ #### 2.4 `ai-toolkit config init`
610
+
611
+ **Interactive project config setup:**
612
+
613
+ ```bash
614
+ ai-toolkit config init
615
+
616
+ # Flow:
617
+ # 1. "Does your organization have a shared ai-toolkit config? [y/n]"
618
+ # → y: "npm package name or git URL:" → resolves + validates
619
+ # → n: creates minimal .ai-toolkit.json without extends
620
+ # 2. "Which profile? [minimal/standard/strict]" → default from base or standard
621
+ # 3. "Which persona? [none/backend-lead/frontend-lead/devops-eng/junior-dev]"
622
+ # 4. Auto-detect languages from project
623
+ # 5. Auto-detect editors from project files
624
+ # 6. Write .ai-toolkit.json
625
+ # 7. Run ai-toolkit install --local
626
+ ```
627
+
628
+ ---
629
+
630
+ #### 2.5 `ai-toolkit config create-base`
631
+
632
+ **Scaffold a base config package:**
633
+
634
+ ```bash
635
+ ai-toolkit config create-base @mycompany/ai-toolkit-config
636
+
637
+ # Creates:
638
+ # @mycompany-ai-toolkit-config/
639
+ # ├── package.json (name, version, files, peerDependencies)
640
+ # ├── ai-toolkit.config.json (base config with sane defaults)
641
+ # ├── rules/ (empty, ready for company rules)
642
+ # ├── agents/ (empty, ready for company agents)
643
+ # └── README.md (setup instructions)
644
+ ```
645
+
646
+ **Generated `package.json`:**
647
+ ```json
648
+ {
649
+ "name": "@mycompany/ai-toolkit-config",
650
+ "version": "1.0.0",
651
+ "description": "Shared ai-toolkit configuration for MyCompany",
652
+ "main": "ai-toolkit.config.json",
653
+ "files": ["ai-toolkit.config.json", "rules/", "agents/"],
654
+ "peerDependencies": {
655
+ "@softspark/ai-toolkit": ">=1.5.0"
656
+ },
657
+ "keywords": ["ai-toolkit", "config", "shared"]
658
+ }
659
+ ```
660
+
661
+ ---
662
+
663
+ ### Phase 4: Enterprise Features (week 5-6)
664
+
665
+ #### 3.1 Audit Trail
666
+
667
+ **`state.json` additions:**
668
+ ```json
669
+ {
670
+ "extends": {
671
+ "source": "@mycompany/ai-toolkit-config",
672
+ "version": "2.1.0",
673
+ "resolved_at": "2026-04-10T10:30:00Z",
674
+ "hash": "sha256:abc123...",
675
+ "overrides_applied": [
676
+ {
677
+ "key": "hooks.quality-check",
678
+ "action": "skip",
679
+ "justification": "Custom Jenkins pipeline"
680
+ }
681
+ ]
682
+ }
683
+ }
684
+ ```
685
+
686
+ ---
687
+
688
+ #### 3.2 Lock File (`.ai-toolkit.lock.json`)
689
+
690
+ **Purpose:** Pin the exact resolved version of base configs for reproducible installs across team members and CI.
691
+
692
+ ```json
693
+ {
694
+ "lockfileVersion": 1,
695
+ "resolved": {
696
+ "@mycompany/ai-toolkit-config": {
697
+ "version": "2.1.0",
698
+ "resolved": "https://registry.npmjs.org/@mycompany/ai-toolkit-config/-/ai-toolkit-config-2.1.0.tgz",
699
+ "integrity": "sha512-abc123...",
700
+ "cached": "~/.ai-toolkit/config-cache/@mycompany/ai-toolkit-config/2.1.0/"
701
+ }
702
+ },
703
+ "generated_at": "2026-04-10T10:30:00Z",
704
+ "ai_toolkit_version": "1.5.1"
705
+ }
706
+ ```
707
+
708
+ **Behavior:**
709
+ - `ai-toolkit install --local` → uses lock file if present (like `npm ci`)
710
+ - `ai-toolkit update --local` → re-resolves and updates lock file (like `npm install`)
711
+ - `ai-toolkit update --local --refresh-base` → force re-fetch ignoring cache
712
+ - `.ai-toolkit.lock.json` should be committed to git (team synchronization)
713
+
714
+ ---
715
+
716
+ #### 3.4 CI Enforcement
717
+
718
+ **`ai-toolkit config check` — for CI pipelines:**
719
+
720
+ ```bash
721
+ ai-toolkit config check
722
+
723
+ # Exit codes:
724
+ # 0 — project complies with base config
725
+ # 1 — violations found (missing required plugins, forbidden overrides, etc.)
726
+ # 2 — .ai-toolkit.json not found
727
+ ```
728
+
729
+ **GitHub Actions example:**
730
+ ```yaml
731
+ - name: AI Toolkit Governance Check
732
+ run: |
733
+ npx @softspark/ai-toolkit config check
734
+ npx @softspark/ai-toolkit config validate --strict
735
+ ```
736
+
737
+ **What it checks:**
738
+ 1. Required plugins are installed
739
+ 2. Required agents are enabled
740
+ 3. No forbidden overrides applied without exemption
741
+ 4. Hook profile meets minimum
742
+ 5. Constitution articles intact
743
+ 6. Lock file up-to-date (warn if stale)
744
+
745
+ ---
746
+
747
+ ## 6. File Summary
748
+
749
+ | File | Action | LOC (est.) | Description |
750
+ |------|--------|------------|-------------|
751
+ | `scripts/config_resolver.py` | CREATE | ~400 | Resolve extends (npm, git, local path) |
752
+ | `scripts/config_merger.py` | CREATE | ~350 | Layered merge engine |
753
+ | `scripts/config_validator.py` | CREATE | ~200 | Schema + enforcement validation |
754
+ | `scripts/config_scaffold.py` | CREATE | ~250 | create-base scaffolder |
755
+ | `scripts/config_diff.py` | CREATE | ~200 | Diff viewer |
756
+ | `scripts/config_check.py` | CREATE | ~150 | CI enforcement checker |
757
+ | `scripts/install.py` | EDIT | +80 | Integrate extends resolution |
758
+ | `bin/ai-toolkit.js` | EDIT | +40 | Register config subcommands |
759
+ | `manifest.json` | EDIT | +10 | Schema references |
760
+ | `kb/reference/enterprise-config-guide.md` | CREATE | ~300 | Enterprise setup guide |
761
+ | `kb/reference/base-config-template/` | CREATE | ~200 | Scaffolded base config files |
762
+ | `tests/test_config_resolver.bats` | CREATE | ~150 | Resolution tests |
763
+ | `tests/test_config_merger.bats` | CREATE | ~200 | Merge + override tests |
764
+ | `tests/test_config_immutability.bats` | CREATE | ~100 | Constitution guard tests |
765
+ | `tests/test_config_cli.bats` | CREATE | ~150 | CLI command tests |
766
+ | **Total** | | **~2780** | |
767
+
768
+ ---
769
+
770
+ ## 6a. Schema Scope (v1 vs v2)
771
+
772
+ v1 ships with a minimal schema. Each additional field adds merge logic, validation, diff output, and test surface. Expand based on real usage, not speculation.
773
+
774
+ | Field | v1 | v2 | Rationale |
775
+ |-------|----|----|-----------|
776
+ | `extends` | single string | array (multi-base) | Multi-base merge ordering is complex |
777
+ | `profile` | yes | — | Core governance knob |
778
+ | `agents` | yes | — | Most common customization |
779
+ | `rules` | yes | — | Rule injection is existing feature |
780
+ | `constitution` | yes | — | Key differentiator |
781
+ | `enforce` | yes | — | Non-overridable constraints |
782
+ | `skills` | — | yes | Less commonly customized at org level |
783
+ | `plugins` | — | yes | Depends on plugin maturity |
784
+ | `languages` | — | yes | Auto-detected, rarely org-level |
785
+ | `editors` | — | yes | Auto-detected, rarely org-level |
786
+ | `overrides` | — | yes | Complex, needs real-world feedback |
787
+ | `hookProfile` / `persona` | — | yes | Low demand signal |
788
+
789
+ ---
790
+
791
+ ## 6b. Non-Functional Requirements
792
+
793
+ | Category | Requirement |
794
+ |----------|-------------|
795
+ | **Performance** | `install --local` with extends resolution < 5s (cached), < 15s (first fetch). Config merge < 100ms. |
796
+ | **Offline** | Cached configs used when registry unavailable, with clear warning. |
797
+ | **Security** | No secret exposure in config files or audit trail. npm auth via `.npmrc` (user-managed). `execFile` for npm CLI (no shell injection). |
798
+ | **Error messages** | Every validation error includes: what failed, which config layer caused it, and what to do (e.g., "Contact your team lead to request an exemption"). |
799
+ | **Backward compatibility** | 100% — projects without `.ai-toolkit.json` work exactly as today. Zero behavioral changes for existing users. |
800
+ | **Maintainability** | Each new schema field requires: merge logic, validation, diff output, test. Budget 0.5d per new field. |
801
+ | **Quality gates** | `ruff check scripts/config_*.py` (0 errors), `mypy --strict scripts/config_*.py` (0 errors). Run before every commit. |
802
+ | **Type safety** | 100% public API type hints (all function signatures). >60% internal. Use `TypedDict` for config schemas, `dataclass` for resolved configs. |
803
+
804
+ ---
805
+
806
+ ## 7. Success Criteria (Overall)
807
+
808
+ | Metric | Target |
809
+ |--------|--------|
810
+ | Extends sources (v1) | 4 (npm, npm+version, git URL, local path) — single string only |
811
+ | Merge depth | 5 levels max (recursive extends) |
812
+ | Config schema | JSON Schema validated |
813
+ | Constitution protection | 100% (Articles I-V immutable) |
814
+ | Override justification | Required for all overrides |
815
+ | Enforce constraints | 4 types (minHookProfile, requiredPlugins, forbidOverride, requiredAgents) |
816
+ | Backward compatibility | 100% (projects without .ai-toolkit.json work as today) |
817
+ | CI enforcement | Exit code 0/1 for governance compliance |
818
+ | Lock file | Reproducible installs across team members |
819
+ | Scaffold command | Ready-to-publish npm package template |
820
+ | Tests | 30+ |
821
+ | Offline resolution | Cached configs with warning |
822
+
823
+ ---
824
+
825
+ ## 8. Risks and Mitigation
826
+
827
+ | Risk | Probability | Impact | Mitigation |
828
+ |------|-------------|--------|------------|
829
+ | npm registry unavailable during install | Low | Medium | Cache + offline fallback with warning |
830
+ | Circular extends chain | Low | High | Max depth 5 + visited set for cycle detection |
831
+ | Base config breaks project | Medium | High | Lock file pins exact version; `ai-toolkit config diff` shows changes before update |
832
+ | Override abuse (teams bypass governance) | Medium | Medium | `enforce.forbidOverride` + CI check + justification requirement |
833
+ | Config schema too restrictive | Medium | Medium | Start with minimal enforcement, expand based on enterprise feedback |
834
+ | Multiple base configs conflict | — | — | Deferred to v2 (single extends only in v1) |
835
+ | Private npm registry authentication | Medium | Low | Use existing npm auth (`.npmrc`), document setup |
836
+ | Git URL resolution slow | Low | Low | `--depth 1` clone, cache aggressively |
837
+
838
+ ---
839
+
840
+ ## 9. Pre-Mortem
841
+
842
+ 1. **"Config file fatigue"** — developers already have `.eslintrc`, `tsconfig.json`, `.prettierrc`. Another `.ai-toolkit.json` may feel like bloat. Mitigation: file is optional, all features work without it. The DX gain (organizational governance without per-repo updates) justifies the file.
843
+ 2. **"Base config never gets updated"** — team lead creates base config, nobody maintains it. Mitigation: `ai-toolkit config check` in CI catches drift; lock file staleness warnings.
844
+ 3. **"Override justification is annoying"** — developers will write "needed" as justification. Mitigation: CI check can enforce minimum justification length (>20 chars); code review culture catches low-effort justifications.
845
+ 4. **"Merge semantics are confusing"** — "does project override or extend the base agent list?" Mitigation: explicit semantics documented in schema; `ai-toolkit config diff` shows exactly what happened.
846
+ 5. **"Enterprise teams want RBAC on overrides"** — who can approve overrides? Mitigation: v1 uses justification text + code review; v2 could integrate with GitHub CODEOWNERS for override approval.
847
+
848
+ ---
849
+
850
+ ## 10. Market Positioning
851
+
852
+ **Target users:**
853
+ 1. **Engineering managers** — enforce AI coding standards across 20+ repos without touching each one
854
+ 2. **Security teams** — ensure constitution + security-auditor agent is always enabled
855
+ 3. **Platform teams** — distribute company-specific agents, rules, and plugins via npm
856
+ 4. **Compliance officers** — audit trail of what AI governance rules are active in each project
857
+
858
+ **Competitive advantage:** No existing AI coding toolkit supports configuration inheritance. This is a unique enterprise feature that transforms ai-toolkit from a developer tool into an organizational governance platform.
859
+
860
+ **Revenue potential:** Enterprise teams are the primary audience for paid support/consulting around ai-toolkit. Config inheritance is the feature that makes enterprise adoption manageable.
861
+
862
+ ---
863
+
864
+ ## 11. Next Actions
865
+
866
+ **MVP (ship first, ~3.5 weeks):**
867
+ 1. [ ] Approve plan
868
+ 2. [ ] Define `.ai-toolkit.json` JSON Schema — v1 scope only (1.1)
869
+ 3. [ ] Implement config resolver (npm, git, local) with caching (1.2)
870
+ 4. [ ] Implement merge engine with override validation (1.3)
871
+ 5. [ ] Implement constitution immutability guard (1.4)
872
+ 6. [ ] Integrate into install.py flow (2.1)
873
+ 7. [ ] Create `config diff` viewer (2.2) — primary debugging tool
874
+ 8. [ ] Create `config validate` checker (2.3)
875
+ 9. [ ] Tests for above (4.1 partial)
876
+ 10. [ ] **Ship MVP → announce → measure adoption**
877
+
878
+ **Post-MVP (if demand validated):**
879
+ 11. [ ] Create `config init` interactive command (2.4)
880
+ 12. [ ] Create `config create-base` scaffolder (2.5)
881
+ 13. [ ] Add audit trail to state.json (3.1)
882
+ 14. [ ] Implement lock file generation + resolution (3.2)
883
+ 15. [ ] Create base config npm package template (3.3)
884
+ 16. [ ] Create CI enforcement command `config check` (3.4)
885
+ 17. [ ] Full tests + documentation — all 9 docs per CLAUDE.md (4.1, 4.2)
886
+
887
+ ---
888
+
889
+ ## 12. Future (v2)
890
+
891
+ | Feature | Rationale |
892
+ |---------|-----------|
893
+ | Multi-base extends (`"extends": [...]`) | Needs real-world feedback on merge ordering UX |
894
+ | v1 deferred schema fields (skills, plugins, languages, editors, overrides, hookProfile, persona) | Expand based on actual enterprise requests |
895
+ | RBAC on overrides (GitHub CODEOWNERS integration) | v1 uses justification + code review |
896
+ | Semantic constitution analysis | Character-count heuristics removed in v1; revisit only if absolute immutability proves too restrictive |
897
+ | `ai-toolkit config audit` (full governance report) | Depends on audit trail maturity |
898
+
899
+ ---
900
+
901
+ ## 13. Cross-Plan Dependencies
902
+
903
+ This plan shares modification targets with two other proposed plans:
904
+
905
+ | Shared File | This Plan | Local Dashboard Plan | Offline SLM Plan |
906
+ |-------------|-----------|---------------------|-----------------|
907
+ | `scripts/install.py` | +80 LOC (extends resolution) | — | +30 LOC (offline-slm profile) |
908
+ | `manifest.json` | +10 LOC (schema refs) | — | +5 LOC (offline-slm profile) |
909
+ | `bin/ai-toolkit.js` | +40 LOC (config subcommands) | +15 LOC (ui command) | +10 LOC (compile-slm command) |
910
+
911
+ **If implementing in parallel:** coordinate merge order for shared files. Recommended sequence: Offline SLM (smallest changes) → Enterprise Config → Dashboard (no install.py changes).
912
+
913
+ **Dashboard integration note:** If this plan ships before the Dashboard plan, the Dashboard's Config page (2.6) should display `.ai-toolkit.json` / `extends` status and the `config diff` output.
914
+
915
+ ---
916
+
917
+ **Last Updated:** 2026-04-10