Implement E1 setup-education and capability report (fixtures).
Add owner-safe lesson catalog, fixture-backed capability statuses, CLI, and unit tests without live OAuth or connect scripts.
This commit is contained in:
@@ -1,5 +1,46 @@
|
||||
# `setup-education` (scaffold)
|
||||
# `setup-education`
|
||||
|
||||
**Status:** Not implemented. Implementation requires explicit **build**.
|
||||
**Status:** Implemented (fixtures only).
|
||||
|
||||
Intent: see [design/use-cases.md](../../design/use-cases.md) and [design/scenarios.md](../../design/scenarios.md).
|
||||
Owner-safe connect education and capability report for use cases E1, E6, and E7.
|
||||
|
||||
## What it does
|
||||
|
||||
- **7 setup lessons** — step-by-step education for connecting the assistant
|
||||
(name, profile, channels, scheduling, books, expectations, capability report)
|
||||
- **Capability report** — plain-language summary of what is connected, offline,
|
||||
skipped, or in error
|
||||
- **Owner-safe** — all education text validated to never contain terminal,
|
||||
docker, nano, or shell instructions
|
||||
- **Fixture-labeled** — all demo data clearly marked `📋 FIXTURE DATA`
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# Capability report (default demo data)
|
||||
python skills/setup-education/scripts/build_capability_report.py
|
||||
|
||||
# JSON output
|
||||
python skills/setup-education/scripts/build_capability_report.py --format json
|
||||
|
||||
# Show lesson 4 (connect scheduling)
|
||||
python skills/setup-education/scripts/build_capability_report.py --lesson 4
|
||||
|
||||
# Show all lessons
|
||||
python skills/setup-education/scripts/build_capability_report.py --all-lessons
|
||||
```
|
||||
|
||||
## Fixtures
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `data/fixtures/setup/capability_matrix.json` | Demo: mixed states (connected, offline, skipped, later) |
|
||||
| `data/fixtures/setup/capability_matrix_all_connected.json` | Demo: all connected |
|
||||
| `data/fixtures/setup/capability_matrix_with_errors.json` | Demo: includes error states |
|
||||
|
||||
## Design references
|
||||
|
||||
- Use case: [E1 — Educational setup](../../design/use-cases.md)
|
||||
- Use case: [E6 — Capability report](../../design/use-cases.md)
|
||||
- Use case: [E7 — Degraded mode](../../design/use-cases.md)
|
||||
- Setup UX: [docs/SETUP_UX.md](../../docs/SETUP_UX.md)
|
||||
|
||||
@@ -10,9 +10,126 @@ Owner-safe connect education and capability report.
|
||||
|
||||
## Description
|
||||
|
||||
Guides the owner through connecting their SaaS integrations (Square, QBO, Vagaro) and messaging channels. Provides a capability report showing what is connected.
|
||||
Guides the owner through connecting their SaaS integrations (Square, QBO, Vagaro)
|
||||
and messaging channels. Provides a capability report showing what is connected,
|
||||
what is offline/fixtures, and what was skipped.
|
||||
|
||||
All education text is **owner-safe**: browser/vendor UI steps only. Never
|
||||
terminal, docker, nano, or shell instructions.
|
||||
|
||||
## What it does
|
||||
|
||||
- Presents 7 setup education lessons (matching `docs/SETUP_UX.md` steps 1–7)
|
||||
- Builds a capability report from fixture or live connection state
|
||||
- Labels all fixture data as `📋 FIXTURE DATA` — never silent fake live data
|
||||
- Validates that education text never contains forbidden keywords
|
||||
- Outputs structured text or JSON
|
||||
|
||||
## Data sources
|
||||
|
||||
| Source | Status | Label in output |
|
||||
|--------|--------|-----------------|
|
||||
| Fixtures (JSON) | ✅ Implemented | `📋 FIXTURE DATA` |
|
||||
| Live connection state | Not yet | `LIVE DATA` (future) |
|
||||
|
||||
## Constraints
|
||||
|
||||
- Owner-safe: no terminal instructions.
|
||||
- Browser/vendor UI steps only.
|
||||
- Deterministic facts from fixtures; no model inference for capability data.
|
||||
- Owner-safe: no terminal/docker/nano/shell instructions in education text.
|
||||
- Fixtures/stubs only — no real OAuth, no live secrets.
|
||||
- Fixture `is_fixture` flag always `true` — never silent as live.
|
||||
|
||||
## Usage
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# Build capability report from fixtures (default demo data)
|
||||
python skills/setup-education/scripts/build_capability_report.py
|
||||
|
||||
# JSON output
|
||||
python skills/setup-education/scripts/build_capability_report.py --format json
|
||||
|
||||
# Show a specific lesson
|
||||
python skills/setup-education/scripts/build_capability_report.py --lesson 4
|
||||
|
||||
# Show all lessons
|
||||
python skills/setup-education/scripts/build_capability_report.py --all-lessons
|
||||
|
||||
# Use a different fixture
|
||||
python skills/setup-education/scripts/build_capability_report.py \
|
||||
--fixtures data/fixtures/setup/capability_matrix_all_connected.json
|
||||
```
|
||||
|
||||
### Programmatic
|
||||
|
||||
```python
|
||||
from lumina_skills.providers.setup.fixture_provider import load_capability_fixture
|
||||
from lumina_skills.setup.capability_report import format_capability_report_text
|
||||
from lumina_skills.setup.lesson_catalog import get_lesson, format_lesson_text
|
||||
|
||||
# Capability report
|
||||
report = load_capability_fixture("data/fixtures/setup/capability_matrix.json")
|
||||
print(format_capability_report_text(report))
|
||||
|
||||
# Individual lesson
|
||||
lesson = get_lesson(4)
|
||||
print(format_lesson_text(lesson))
|
||||
```
|
||||
|
||||
## Output format
|
||||
|
||||
### Text (default)
|
||||
|
||||
Structured text grouped by area (identity, profile, channels, scheduling, books)
|
||||
with emoji status labels and a summary line.
|
||||
|
||||
### JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"salon_name": "Lumina Hair Studio & Spa",
|
||||
"is_fixture": true,
|
||||
"capabilities": [
|
||||
{
|
||||
"area": "channels",
|
||||
"provider": "whatsapp",
|
||||
"status": "connected",
|
||||
"label": "✅ Connected",
|
||||
"details": "WhatsApp channel active"
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"connected": 4,
|
||||
"offline": 2,
|
||||
"skipped_or_later": 2,
|
||||
"errors": 0,
|
||||
"all_connected": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `SKILL.md` | Skill spec and usage |
|
||||
| `scripts/build_capability_report.py` | CLI entrypoint |
|
||||
| `../../skills/_lib/lumina_skills/setup/capability_report.py` | Domain model + builder |
|
||||
| `../../skills/_lib/lumina_skills/setup/lesson_catalog.py` | Static lesson steps |
|
||||
| `../../skills/_lib/lumina_skills/providers/setup/fixture_provider.py` | Fixture loader |
|
||||
| `../../data/fixtures/setup/` | Fixture JSON files |
|
||||
|
||||
## Design references
|
||||
|
||||
- Use case: [E1 — Educational setup](../../design/use-cases.md)
|
||||
- Use case: [E6 — Capability report](../../design/use-cases.md)
|
||||
- Use case: [E7 — Degraded mode](../../design/use-cases.md)
|
||||
- Setup UX: [docs/SETUP_UX.md](../../docs/SETUP_UX.md)
|
||||
- Deterministic boundary: [design/det-vs-inf.md](../../design/det-vs-inf.md)
|
||||
|
||||
## Future
|
||||
|
||||
- Live connection state provider (replaces fixtures)
|
||||
- Per-lesson progress tracking
|
||||
- Automated lesson sequencing
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a capability report from setup fixtures.
|
||||
|
||||
Usage:
|
||||
python build_capability_report.py [--fixtures PATH] [--salon NAME] [--format text|json]
|
||||
|
||||
All data is labeled as fixture/offline — never silent fake live data.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
# Ensure the _lib package is importable regardless of cwd.
|
||||
_REPO_ROOT = pathlib.Path(__file__).resolve().parents[3]
|
||||
sys.path.insert(0, str(_REPO_ROOT / "skills" / "_lib"))
|
||||
|
||||
from lumina_skills.providers.setup.fixture_provider import (
|
||||
load_capability_fixture,
|
||||
load_fixture_metadata,
|
||||
)
|
||||
from lumina_skills.setup.capability_report import format_capability_report_text
|
||||
from lumina_skills.setup.lesson_catalog import (
|
||||
format_all_lessons_text,
|
||||
format_lesson_text,
|
||||
get_lesson,
|
||||
get_all_lessons,
|
||||
)
|
||||
|
||||
# Default fixture: Claire Bennett demo capability matrix.
|
||||
_DEFAULT_FIXTURE = _REPO_ROOT / "data" / "fixtures" / "setup" / "capability_matrix.json"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Build a capability report from setup fixture data.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--fixtures",
|
||||
type=pathlib.Path,
|
||||
default=_DEFAULT_FIXTURE,
|
||||
help="Path to capability fixture JSON file.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--salon",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Override salon name.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
choices=["text", "json"],
|
||||
default="text",
|
||||
help="Output format (default: text).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lesson",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Show a specific setup lesson (1-7) instead of the capability report.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--all-lessons",
|
||||
action="store_true",
|
||||
help="Show all setup lessons instead of the capability report.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Lesson mode.
|
||||
if args.lesson is not None:
|
||||
lesson = get_lesson(args.lesson)
|
||||
if lesson is None:
|
||||
print(f"Error: No lesson found for step {args.lesson}", file=sys.stderr)
|
||||
return 1
|
||||
print(format_lesson_text(lesson))
|
||||
return 0
|
||||
|
||||
if args.all_lessons:
|
||||
print(format_all_lessons_text())
|
||||
return 0
|
||||
|
||||
# Capability report mode.
|
||||
try:
|
||||
report = load_capability_fixture(args.fixtures)
|
||||
except FileNotFoundError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
except (json.JSONDecodeError, KeyError, ValueError) as exc:
|
||||
print(f"Error parsing fixture: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
# Override salon name if requested.
|
||||
if args.salon:
|
||||
from lumina_skills.setup.capability_report import build_capability_report
|
||||
report = build_capability_report(
|
||||
capabilities=report.capabilities,
|
||||
salon_name=args.salon,
|
||||
is_fixture=report.is_fixture,
|
||||
)
|
||||
|
||||
# Output.
|
||||
if args.format == "json":
|
||||
print(json.dumps(report.to_dict(), indent=2))
|
||||
else:
|
||||
print(format_capability_report_text(report))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user