chore(ci): bump actions/setup-python from 5 to 6#84
Conversation
- Root configs: package.json, pnpm-workspace.yaml, turbo.json - Env: .env.example with per-app DB URLs + app/migrator role separation - Infra: docker-compose (pgvector/pg16 + redis:7-alpine) + multi-db init script - Git hygiene: .gitignore, .gitattributes (LF normalization), .nvmrc (Node 20) - License: MIT - Docs: 13-part design bible in docs/design/ covering Boardly + Knowlex - Overview, monorepo structure, requirements, ER schemas (Prisma) - OpenAPI specs, Week 3 daily task breakdown - ADR 0001-0022, STRIDE threat model, Runbook, rate limits, retention - RAG prompt registry + golden QA eval pipeline - Interview Q&A 30 + portfolio LP + demo storyboards - Self-review critical/high fix log
apps/ - collab (Boardly): Next.js 16 + Turbopack + TailwindCSS + TS, port 3000 - knowledge (Knowlex): Next.js 16 + Turbopack + TailwindCSS + TS, port 3001 packages/ - @craftstack/ui: shared React components entry - @craftstack/auth: Auth.js v5 wrapper entry - @craftstack/db: Prisma client + withTenant helper entry - @craftstack/logger: pino + Sentry entry - @craftstack/config: shared tsconfig.base.json + prettier.config.js - @craftstack/api-client: OpenAPI-generated types entry Workspace install verified (pnpm 9.15, Node 20, 355 packages). Collab production build passes.
Shared packages (ui/auth/db/logger/api-client) had lint/typecheck scripts pointing to tools that were not installed (eslint/tsc configs missing). Turbo invoked them and failed with exit code 2. Scripts will be re-added per package when actual source + configs land. Apps (collab/knowledge) retain their Next.js-provided lint/typecheck.
- prisma + @prisma/client 7.7.0, dotenv, tsx dev deps - prisma.config.ts: Prisma 7 native config (migrator URL resolution) - .gitignore: /src/generated/prisma - dotenv/config imported for env var loading - pg_trgm extension declared for future full-text search
…ership, Invitation) Schema follows docs/design/05_prisma_schemas.md: Auth.js v5 adapter tables: - Account (provider+providerAccountId unique) - Session (sessionToken unique) - VerificationToken (identifier+token unique) Core domain (Boardly): - User: email-unique identity + locale/theme preferences - Workspace: slug-unique tenant with owner + soft-delete - Membership: user x workspace with Role enum (OWNER/ADMIN/EDITOR/VIEWER) - Invitation: email invites with tokenHash + expiresAt + optional inviter (SetNull) Design decisions applied: - onDelete cascades follow docs/design/12_critical_fixes.md C-1 - Invitation.inviterId nullable + SetNull so invites survive user deletion - Indexes: email, slug, owner, deletedAt, workspace+role, workspace+email Generator: Prisma Client 7.7 with fullTextSearchPostgres + postgresqlExtensions. prisma format and prisma generate both pass.
…ity log
Schema additions (docs/design/05_prisma_schemas.md):
Enums:
- ActivityAction (24 variants) for AuditLog-style append-only tracking
- NotificationType (MENTION/ASSIGNED/DUE_SOON/INVITED/COMMENT_ON_CARD)
Kanban domain:
- Board: workspace-scoped with color/icon/archived/position, soft delete
- List: LexoRank position, optional WIP limit
- Card: optimistic locking via version column (ADR-0007),
title/description/dueDate/position, Cascade from List
Labels (many-to-many via CardLabel):
- Label: workspace-scoped name+color, unique per workspace
- CardLabel: composite PK (cardId, labelId)
- CardAssignee: composite PK (cardId, userId) with assignedAt
Comments & attachments:
- Comment: self-referential parentId for threads, soft delete
- Mention: (commentId, userId) unique for per-user notification dispatch
- Attachment: R2-backed (r2Key unique), uploader reference, MIME metadata
Observability:
- ActivityLog: workspace-scoped audit trail with JSON payload,
actor SetNull on user delete (ADR-0010 C-1)
- Notification + NotificationSubscription: Web Push VAPID endpoints
prisma format/validate/generate all pass.
Full monorepo lint/typecheck/build verified locally.
src/lib/db.ts exports a global-cached PrismaClient so HMR in Next.js does not spawn a new connection pool on every module reload. - dev: log query/error/warn - prod: log error only (avoid PII leakage, ADR-0009 observability plan) - globalThis-based caching gated on NODE_ENV !== production
src/lib/lexorank.ts wraps the `lexorank` npm package (Jira-compatible) per ADR-0006 + ADR-0021. API: - first(): rank placed before everything - last(): rank placed after everything - between(prev?, next?): insert between two neighbors - compare(a, b): stable comparator for Array.sort Tests (src/lib/lexorank.test.ts, all passing): 1. first() < last() 2. between(first, last) strictly between 3. between(null, null) yields a valid rank 4. between(prev, undefined) places after prev 5. between(undefined, next) places before next 6. repeated insertions remain strictly ordered 7. compare is antisymmetric vitest.config.ts: node env, v8 coverage over src/lib/.
…r-pg)
Prisma 7 constructor requires either an `adapter` or `accelerateUrl`;
plain DATABASE_URL no longer auto-configures the client.
- @prisma/adapter-pg + pg + @types/pg added
- src/lib/db.ts instantiates PrismaClient({ adapter, log })
- same HMR-safe globalThis caching preserved
- production can later swap the adapter for Neon HTTP driver without
touching call sites
Also: .gitignore the harness-local .claude/ so it does not pollute the repo.
Auth.js v5 (next-auth@5.0.0-beta.31) + @auth/prisma-adapter setup per
ADR-0003 (database session strategy for server-side revocation).
src/auth/
- config.ts : NextAuthConfig with PrismaAdapter + Google/GitHub
providers + signin page override, session callback
exposes internal user.id
- index.ts : re-exports { handlers, signIn, signOut, auth }
- handlers.ts : thin shim for the app route handler
- rbac.ts : roleAtLeast / hasRole / requireRole (throws RoleError)
- rbac.test.ts: exhaustive 4x4 hierarchy matrix (16 Vitest cases)
- types.d.ts : augment Session with internal user.id
vitest.config.ts updated to resolve @/ path alias and widen coverage
to src/**/*.ts (excluding app/, generated/, and *.test.ts).
…guard src/app/api/auth/[...nextauth]/route.ts - Re-exports GET/POST from the Auth.js handlers src/app/signin/page.tsx - Server component with async action handlers for Google/GitHub signIn - Inline SVG brand icons (no extra dependency) - Redirects to /dashboard (or callbackUrl) if already authenticated - Dark neutral-950 aesthetic matching the Boardly brand src/proxy.ts - Next.js 16 renamed `middleware` to `proxy` - Default-export auth() so the proxy runtime recognizes the function - Matcher excludes /signin, /api/auth, and Next static assets Verified with preview: /signin renders 200, /dashboard redirects 307 through the proxy to /signin as expected (no dashboard route yet).
- ApiError base class with status/code/message/details - UnauthorizedError (401), ForbiddenError (403), NotFoundError (404), ConflictError (409), BadRequestError (400) - Shape matches OpenAPI Error schema (docs/design/06_openapi_specs.md) - handle(): Route Handler wrapper that turns thrown ApiErrors into JSON 4xx responses and unexpected errors into logged 500s
Returns every non-deleted workspace the authenticated user belongs to,
ordered by join date desc. Unauthenticated requests get a 401 JSON
payload (handled by the new errors/handle wrapper).
src/server/workspace.ts
- listWorkspacesForUser(userId): joins Membership + Workspace,
filters out soft-deleted workspaces, returns { id, name, slug,
color, iconUrl, role }
src/app/api/workspaces/route.ts
- GET handler wired through handle() so errors become proper JSON
- auth() checked first; throws UnauthorizedError if missing session
src/app/page.tsx
- Root '/' now acts as a session gate: authenticated -> /dashboard,
unauthenticated -> /signin. Removes the scaffold landing content.
src/app/dashboard/page.tsx
- Server component rendering header with user email + sign-out form
- Workspace grid (1/2/3 columns responsive) with color swatch,
name, /slug path, and role badge (OWNER/ADMIN/EDITOR/VIEWER)
- Empty-state CTA when the user has no memberships
- 'New workspace' button placeholder pointing at /workspaces/new
src/proxy.ts
- Matcher now excludes /api/* so API routes handle their own auth
and return 401 JSON instead of 307 redirects to /signin.
Confirmed: GET /api/workspaces without session returns
{ code: 'UNAUTHORIZED', message: 'Authentication required' }
with HTTP 401. Page routes still redirect as expected.
Uniform LF, double quotes, trailing commas 'all' applied across existing source files after wiring up prettier config.
src/lib/validation.ts
- parseCreateWorkspaceInput(raw): trims, lowercases slug, enforces
slug regex ^[a-z0-9-]{3,32}$, 80-char name cap, #RRGGBB color
- Aggregates every field error into BadRequestError.details.fieldErrors
so the client can highlight individual fields
src/lib/validation.test.ts
- 8 cases: valid body, valid color, non-object body, bad slug,
missing name, overlong name, malformed color, slug normalization
src/server/workspace.ts
- createWorkspace(userId, input): transactional create with owner
Membership row; throws ConflictError('SLUG_TAKEN') on collision
src/app/api/workspaces/route.ts
- POST handler: auth check, body parse, create, 201 with the row
- Validation errors surface as 400 fieldErrors; slug conflict as 409
src/app/workspaces/new/page.tsx
- Server component form with Tailwind dark-mode styling
- Async server action that catches ApiError, rehydrates field values
via query string, and re-renders with the error banner
- Native HTML pattern='[a-z0-9-]{3,32}' mirrors server-side regex
Verified: POST /api/workspaces unauthenticated returns 401 JSON;
/workspaces/new unauthenticated redirects to /signin with callbackUrl.
apps/collab/next.config.ts
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: camera/microphone/geolocation/payment denied
- Strict-Transport-Security: 2 years + includeSubDomains + preload
- poweredByHeader: false
- reactStrictMode: true
- CSP intentionally deferred until external origins are finalized
docs/architecture/system-overview.md
- Mermaid graph of Edge, Vercel, Fly.io, Data, External APIs,
Observability layers with all relationships drawn
- Per-app resource isolation table
- Two concrete request paths (card edit, RAG question) walking
through the full round trip
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 6. - [Release notes](https://github.com/pnpm/action-setup/releases) - [Commits](pnpm/action-setup@v4...v6) --- updated-dependencies: - dependency-name: pnpm/action-setup dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](actions/setup-node@v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the react group with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). Updates `react` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react) Updates `react-dom` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-dom) --- updated-dependencies: - dependency-name: react dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
/api/health - Cheap liveness check (no DB hit) for UptimeRobot 4-minute pings - Keeps the Neon free tier warm (ADR-0016) - Cache-Control: no-store so the poll always reaches the app not-found.tsx - Branded 404 with Back to dashboard CTA error.tsx - Global error boundary (client component) - Shows error.digest for Sentry correlation once wired - Try again + Go home buttons - Uses next/link per no-html-link-for-pages lint rule
Same X-Content-Type-Options / X-Frame-Options / Referrer-Policy / Permissions-Policy / HSTS plus poweredByHeader=false, reactStrictMode=true.
prisma/seed.ts - Uses the Prisma PG driver adapter + DIRECT_DATABASE_URL (migrator) - Upserts 2 users, 1 workspace with 2 memberships + 3 labels - Creates a Welcome board with 3 LexoRank-ordered lists and 3 cards - Idempotent via upsert + skipDuplicates for rerun safety pnpm --filter collab db:seed once Docker/Neon is reachable.
src/server/workspace-detail.ts
- loadWorkspaceForMember(userId, slug): returns null for non-members
so the caller can 404 without leaking existence
- One round trip via nested include: boards + memberships + user
- Skips soft-deleted workspaces and boards
src/app/w/[slug]/page.tsx
- Header with workspace color swatch + slug crumb + back link
- Boards grid with 'New board' CTA gated by role (OWNER/ADMIN/EDITOR)
- Empty state when no boards exist
- Members list with role badges and avatar initials
- Self role displayed inline next to member count
- Consistent RoleBadge component (also used in dashboard)
src/server/board.ts - createBoard: enforces role >= EDITOR via roleAtLeast() before INSERT - New boards are placed at the bottom of the workspace via LexoRank last() - Throws NotFoundError if workspace missing, ForbiddenError otherwise src/app/w/[slug]/boards/new/page.tsx - Server-action form with Tailwind dark styling - Rehydrates prior values + error banner via query string - Redirects to /w/[slug]/b/[boardId] on success src/app/w/[slug]/b/[boardId]/page.tsx - Workspace-scoped board query: 404 for non-members or missing board - Static kanban layout (lists + cards); realtime editing arrives in Week 6 - Per-list card count with optional WIP limit display - Subtle board-color tint on the header gradient
* feat(data-analytics-demo): T-03 data generation + T-12 Python CI infra Phase 1 of the data-analytics-demo bolt-on. Ships the synthetic data generator (T-03) alongside the Python CI infrastructure (T-12) so the new Python code is verified by CI from the first commit. T-03 — Data generation (AC-1.1 to 1.5 + AC-γ.1 + AC-δ.2): - src/data_analytics_demo/data/schemas.py — Pydantic models for the 4 SaaS tables (Customer / Subscription / Event / Invoice) - src/data_analytics_demo/data/generate.py — Faker + numpy synthesis, deterministic via DEMO_RANDOM_SEED (default 42). Writes a DuckDB file at warehouse/analytics.duckdb. Engineered signal: trailing-30d event drop-off biases churn probability; free-tier customers using premium-feature events bias upsell probability (both observable through SQL, no leak from the generator into the ML feature surface). - tests/test_data_generate.py — 7 pytest cases covering each AC. - Makefile + cli.py — `make data` and `data-analytics-demo data` now do real work instead of exit-1 TODO placeholders. T-12 — Python CI infrastructure: - .github/workflows/python-test.yml — Python 3.11, install editable + dev, run ruff + mypy --strict + pytest (with the 80% coverage gate set in pyproject.toml). - .github/workflows/python-audit.yml — pip-audit --strict against OSV. - .github/dependabot.yml — pip ecosystem on /packages/data-analytics-demo, grouped by dbt / ml / duckdb / dev for review readability. Design note: ADR-0070 mentioned the DuckDB tpcds extension as a synthetic-data source. tpcds is a retail benchmark and does not fit the 4-table SaaS schema this package commits to. Reverted to pure Faker + numpy synthesis; ADR-0070 will be amended in T-13 polish phase to record the deviation. Local verify: - python -m compileall on src/ + tests/ → OK - node scripts/check-doc-drift.mjs → 0 failure(s), 0 warning(s) - node scripts/check-adr-claims.mjs → 77/77 PASS - HIVE-token sweep on new files → 0 hits (D-HIVE-OPACITY) * fix(data-analytics-demo): ruff lint — 4 errors in generate.py CI feedback from #83 (python-test workflow). All 4 are clean style/typing nits: - F401: drop unused `from collections.abc import Sequence` (TYPE_CHECKING block); the `Sequence` was never referenced. - UP017: `timezone.utc` -> `UTC` (Python 3.11+ alias). - T201: `_emit()` is the deliberate single exception to the print-suppression rule for this package — annotated with `noqa: T201` plus a docstring note so the exception is auditable in code review. - E501: split the timestamps list comprehension at 121 cols into 3 lines. Verify: python -m compileall src/ OK. * fix(data-analytics-demo): mypy — PEP 561 typed marker + 3rd-party overrides CI feedback from #83 (mypy step). 3 errors clear: - Add `src/data_analytics_demo/py.typed` (PEP 561 marker). Resolves the two `import-untyped` errors on cli.py importing `data_analytics_demo` and `data_analytics_demo.data` — the package now declares inline type info. - Register the marker in [tool.setuptools.package-data] so it ships in the installed wheel. - Add a [[tool.mypy.overrides]] block for pandas / duckdb / faker / shap / xgboost / sklearn — none of these publish type stubs that match the current Python 3.11 + pandas 3.x stack. pandas-stubs exists but lags pandas releases, so ignore_missing_imports is the pragmatic floor. * chore: trigger CI re-run on latest HEAD (d87e787) * fix(data-analytics-demo): mypy src-layout dual-path conflict CI feedback: `mypy src` walked the file as both `src.data_analytics_demo.…` and `data_analytics_demo.…` because the package is editable-installed AND src is on the filesystem path. - pyproject.toml: add `mypy_path = "src"` so mypy resolves the package unambiguously through its installed name. - python-test.yml + Makefile: invoke mypy as `mypy -p data_analytics_demo` (installed-package mode) instead of `mypy src` (filesystem walk). Same coverage, no path collision. * fix(data-analytics-demo): drop EmailStr dependency CI feedback: pydantic.EmailStr requires the optional `email-validator` package, which is not in our dependency set. No AC requires email-format validation; the field stores a Faker company_email() string and downstream consumers (dbt staging, ML features) read it as a string anyway. Dropping EmailStr removes the runtime dep without any functional change. --------- Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
LabelsThe following labels could not be found: Please fix the above issues or remove invalid values from |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…#86) Phase 2 of the data-analytics-demo bolt-on. Ships the dbt layer that transforms the 4 synthetic SaaS source tables into 4 mart tables ready for the ML and dashboard layers. T-04 — dbt scaffold + staging + intermediate (AC-2.5): - dbt_project.yml + profiles.yml (DuckDB target reads ../warehouse/analytics.duckdb) - models/staging/_sources.yml declares the 4 raw tables - models/staging/stg_{customers,subscriptions,events,invoices}.sql (views, thin pass-through + dtype casting) - models/intermediate/int_customer_features.sql (one row per customer combining latest subscription + lifetime event stats + invoice totals) - models/intermediate/int_event_aggregates.sql (per-customer × event_type rollup powering both churn and upsell marts) T-05 — marts + schema tests (AC-2.1, AC-2.2, AC-2.3, AC-2.4, AC-2.6): - marts/rfm_segments.sql (R/F/M quintile scoring + 5-bucket label; self-anchored to max(event_at) so it's reproducible per seed) - marts/churn_features.sql (one row per customer; trailing-30d vs lifetime daily-avg ratio is the engineered churn signal label) - marts/upsell_opportunities.sql (one row per free/pro customer; premium / advanced event counts as engineered signal; upgraded label) - marts/cohort_retention.sql (monthly cohort × months-since-signup grid) - marts/schema.yml (not_null + unique + accepted_values tests, new dbt generic-test argument syntax) Makefile: `dbt` target now runs `dbt run && dbt test` from dbt_project/ with DBT_PROFILES_DIR=. so the project ships its own profile. .gitignore: add dbt_project/.user.yml (dbt's per-developer anonymous tracking UUID; not portfolio-relevant). Local verify on seeded DuckDB (n_customers=1000): - dbt parse → OK (deprecation warnings cleared) - dbt run → 10/10 OK (4 staging views + 2 int views + 4 mart tables) - dbt test → 20/20 PASS (not_null + unique + accepted_values) - Mart rowcounts → rfm 1000, churn 1000, upsell 898, cohort 319 - Sanity numbers → churn rate 26.2% (matches synth ~25% canceled), upsell rate 35.5%, RFM segments distributed across 5 buckets - Python verify → ruff OK / mypy OK / pytest 8 PASS (cov 83.92%) - doc-drift + adr-claims → 0 failure / 77/77 PASS Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
Phase 3 of the data-analytics-demo bolt-on. Trains two propensity models on the dbt marts shipped in #86 and saves the resulting model artifacts + SHAP summary that the narrative layer (T-08) consumes next. T-06 — Churn pipeline (AC-3.1〜3.5): - ml/churn.py — fits a LogisticRegression baseline AND an XGBoost classifier on `churn_features`, picks the higher hold-out ROC-AUC, and saves model.pkl + metadata.json + shap_summary.json. - ml/explain.py — SHAP wrapper used by both the churn and (later) narrative paths. TreeExplainer first, falls back to model-agnostic. - ml/_io.py — shared mart loader, fails with clear errors when the warehouse / mart is missing (AC-3.4). T-07 — Upsell propensity (AC-3.6〜3.7): - ml/upsell.py — fits a LogisticRegression propensity model on `upsell_opportunities`, measures hold-out ROC-AUC and lift @ top-10%, raises if the lift falls below the 1.5× floor. Data-generator amendment: the churn signal in `data/generate.py` was under-engineered (best ROC-AUC was 0.6972, just below the AC-3.2 0.70 floor). Reworked the event generator so churned customers (a) get 4× lower event weight and (b) have their timestamps biased into the older half of the history window. The mart's `recent_to_lifetime_ratio` feature now correlates cleanly with the cancel label, pushing churn ROC-AUC to 0.7448 on a seed=42 / n_customers=1000 run. Local verify (Python 3.12 venv, deterministic seed=42): - `make data` + `make dbt` + `make ml` end-to-end OK - Churn ROC-AUC = 0.7448 (LR wins; XGBoost 0.7196), AC-3.2 PASS - Upsell lift @ top-10% = 2.81× (vs 1.5× floor), AC-3.7 PASS - ruff OK / mypy OK / pytest 15 PASS, coverage 86.75% - doc-drift 0 fail / adr-claims 77/77 Test infra: switched from `subprocess.run(["dbt", ...])` to `dbt.cli.main.dbtRunner` so the fixtures work on Windows without venv Scripts being on PATH. DuckDB rw-mode for both dbt + ml avoids the "different configuration" connection error when both run in-process. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…#88) Phase 4 of the data-analytics-demo bolt-on. Reads the SHAP summary that the ML layer (#87) writes, sends a templated prompt to a local Ollama daemon, and saves an executive-facing markdown narrative — never touching a cloud LLM API. What lands: - narrative/ollama_client.py — env-var-gated host/model resolution (defaults: localhost:11434 + llama3.1:8b-instruct-q4_K_M), AC-4.3 assertion that no cloud-LLM credentials are present at invocation, AC-4.2 remediation hint when Ollama is unreachable. - narrative/prompts.py — the executive-brief prompt template; SHAP-summary rendering is the only call point. - narrative/generate.py — orchestration. Reads shap_summary.json, builds the prompt, calls Ollama, wraps the body with provenance metadata (model id, SHAP source path, timestamp, "external calls: 0" assertion-enforced advertisement) — satisfies AC-4.1, AC-4.4, AC-4.5. - tests/test_narrative.py — 7 cases covering AC-4.1〜4.5 plus missing-data and prompt-builder paths. Uses monkeypatch to stub `ollama.Client` so no network is required in CI. - Makefile narrative target + cli.py narrative subcommand wire-up. AC coverage (mock-Ollama tests + real-Ollama smoke locally): - AC-4.1 produces output.md PASS - AC-4.2 unreachable Ollama PASS (clear RuntimeError with "ollama serve" hint) - AC-4.3 external API guard PASS (raises before any client call) - AC-4.4 cites shap_summary.json PASS - AC-4.5 model identifier in output PASS Local verify: - ruff OK / mypy OK (14 source files) / pytest 22 PASS / coverage 87.20% - Real smoke vs Ollama (gemma3:4b, env-var override): 3-paragraph executive narrative produced end-to-end, all metadata fields present. Design note: the literal AC-4.5 default model name is preserved as the package default; deployments running a different quantized variant can override via the OLLAMA_MODEL env var without code changes. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…Plotly (Evidence pivot) (#89) Phase 5 of the data-analytics-demo bolt-on. Ships a static-HTML dashboard generator that reads the dbt marts and emits 4 pages of charts + tables to `dashboard/build/`. Pivot from Evidence: the original Tradeoff 4 (ADR-0070) chose Evidence as the BI tool. Local installation hit four+ chained peer-dependency failures under pnpm 10 isolated layout and npm strict resolution (@sveltejs/kit, vite, @evidence-dev/tailwind, ...). The fix path was unbounded and would have introduced a second package manager into an otherwise-Python sub-tree for a build-time-only artifact. ADR-0070 is amended in this PR with the revised Tradeoff 4 and the rationale. What lands: - src/data_analytics_demo/dashboard/{__init__,render,queries,charts}.py Pure Python generator — reads from analytics.duckdb via duckdb-py, builds Plotly figures, renders Jinja2 templates. - src/data_analytics_demo/dashboard/templates/{base,index,rfm,churn,kpi}.html.j2 Layout (header / nav / metric strip / cards / footer) + 4 page templates. - tests/test_dashboard.py — 4 cases covering AC-5.1〜5.4 (renders all pages; index contains all 3 required sections; uses the supplied DuckDB; missing-warehouse raises a clear error). - Makefile + cli.py — `make dashboard` / `data-analytics-demo dashboard` now invoke the Python generator. - pyproject.toml — adds jinja2 (BSD, Pallets) + plotly (MIT, Plotly Inc.) to dependencies; both already in the audited stack space. - ADR-0070 amendment with the revised Tradeoff 4 and a "Why this is the better fit" narrative. Local verify (Python 3.12 venv, seed=42): - `make data` + `make dbt` + `make dashboard` end-to-end OK - 4 pages produced: index.html (28KB), rfm.html (39KB), churn.html (12KB), kpi.html (38KB) — Plotly via CDN keeps per-page size small. - ruff OK / mypy OK (19 source files) / pytest 26 PASS / coverage 88.31% - doc-drift 0 fail / adr-claims 77/77 PASS Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
Phase 6 of the data-analytics-demo bolt-on. Ships the single source of truth for KPI definitions and a validator that enforces the MetricFlow schema invariants the rest of the package depends on. What lands: - semantic/kpi.yml — 3 semantic models (customers, subscriptions, invoices) and 4 metrics (customers, active_subscriptions, monthly_recurring_revenue, paid_invoice_volume). Each metric carries ≥ 1 dimension and ≥ 1 measure per AC-6.2. - src/data_analytics_demo/semantic/validator.py — pure-Python validator. Parses the YAML, walks semantic_models[] / metrics[], and enforces: required keys, non-empty dims + measures, metric.measure references a declared measure, metric.dimensions all reference declared dimensions. - tests/test_semantic.py — 5 cases covering AC-6.1 + AC-6.2 + negative paths (missing file, empty dim list, unknown measure ref) + canonical metric inventory. - Makefile semantic-validate target + cli.py semantic subcommand wired up. - types-PyYAML added to dev deps so mypy --strict sees the yaml stubs. Local verify: - `make semantic-validate` → "OK — 3 semantic models / 4 metrics: …" - ruff OK / mypy OK (21 source files) / pytest 32 PASS / coverage 87.20% - check-doc-drift / check-adr-claims unchanged (0 fail / 77/77) Design note: the validator is independent of the MetricFlow CLI by design — it enforces the structural invariants the package relies on without shelling out to `mf validate-configs`, so the test suite has no CLI dependency and runs cleanly under pytest. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…ners (#91) Phase 7 of the data-analytics-demo bolt-on. Makefile: `make demo` now prints a banner per stage (1/6 .. 6/6 + done) so the stage boundaries are visible in long logs. Sub-makes run sequentially inside a single recipe, so any non-zero exit halts the chain (AC-α.2). tests/test_e2e_demo.py — 4 cases covering AC-α.1〜3 + AC-δ.1: - Happy-path full pipeline: data → dbt → ml → narrative → dashboard → semantic-validate. Ollama is mocked at the client boundary so the test runs offline in CI. Asserts every expected artifact exists. - Three negative-path cases enforce AC-α.2 at the Python-API surface: missing warehouse halts ml, missing shap_summary halts narrative, missing warehouse halts dashboard. Each raises a `FileNotFoundError` with a remediation hint. The e2e test goes through the package's Python entry points rather than shelling out to `make`, so the same coverage works on Windows + Linux without depending on the shell. Local verify: - `make demo` end-to-end OK with banners visible at each stage - `ruff` + `mypy --strict` clean (21 source files) - `pytest` 36 passed (was 32; +4 e2e); coverage **87.20%** - `check-doc-drift.mjs` 0 fail / `check-adr-claims.mjs` 77/77 Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…4 close-out) (#92) Final phase of the data-analytics-demo bolt-on. Closes Stage 4 of the Spec-Driven workflow (T-01〜T-14 — 14 / 14 tasks done across 8 PRs). T-13 — Documentation: - packages/data-analytics-demo/README.md: full rewrite. Quickstart in ≤ 5 commands (AC-ε.1), per-layer one-line architecture summary, layout table, constraints (load-bearing), engineered ML signals explanation. - packages/data-analytics-demo/docs/architecture.md: mermaid pipeline diagram covering all 6 layers plus per-layer detail tables and the list of files produced by a full `make demo` run. T-14 — Changelog + handoff: - CHANGELOG.md (root): adds an Unreleased entry recording the data-analytics-demo 0.1.0 ship — six layers, CI infrastructure, security mitigations, test surface, all 8 PRs referenced. The package becomes the seventh `packages/*` entry and the monorepo's first Python sub-tree. - HANDOFF.md (root): "Current" block flipped from "planning phase" to "shipped"; the verified-prior-art table superseded by the ADR-0070 reference (it now lives in the design ADR, not in the ephemeral handoff). Local verify: - ruff OK / mypy OK (21 source files) / pytest 36 PASS / coverage 87.20% - check-doc-drift 0 fail / check-adr-claims 77/77 PASS Stage 4 done. The package can be developed and demoed end-to-end with a single `make demo` invocation; recruiters can clone the repo and read all three deliverables (SQL marts, ML + SHAP, dashboard + narrative) in under five minutes. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…ar contract (#93) Closes the AC-γ.3 drift surfaced by 2026-05-18 objective review: - ADR-0070 L62 previously stated `.env.example ships placeholders only (deferred to T-03)`, but no `.env.example` was ever shipped (Security Master Layer 6 blocks the `.env*` filename pattern by design). - The package's env-var surface (DEMO_* seed + row counts, OLLAMA_HOST / MODEL, prohibited cloud-LLM credentials) is genuinely documented; it just had no single source of truth. This commit: - Adds an "Environment variables" table to packages/data-analytics-demo/README.md (defaults match in-code constants; prohibited cloud-LLM credential list mirrors `ollama_client.EXTERNAL_API_ENV_VARS` exactly). - Rewrites ADR-0070 security-mitigations bullet to reference the README section and enumerate the AC-4.3 fail-stop credential list explicitly. Verify: - ruff + mypy --strict: clean - pytest: 36 PASS, coverage 87.20% - doc-drift: 0 failure - adr-claims: 77/77 PASS Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
…enforced floors (#94) Documents the two CI-enforced floors (Churn ROC-AUC ≥ 0.70 via AC-3.2, Upsell lift @ top-10% ≥ 1.5× via AC-3.7) with literal links to the asserting test files and explanation of the seed-based reproduction contract. External-facing portfolio copy can now cite this section instead of presenting unsourced specific numbers. Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
#95) The Cron health step in `Live smoke` has been failing on every run since the 2026-04-29 Gemini Free tier account-level revocation incident (ADR-0067). EMERGENCY_STOP is the intended containment per ADR-0046's kill-switch design — its existence is the structural defense, and the smoke badge should reflect that the state is intentional, not a regression. Adds an ingest-endpoint probe before the threshold compare. If the probe returns `{"code":"EMERGENCY_STOP"}` (the literal response from apps/knowledge/src/lib/emergency-stop.ts when the env flag is set), the gate downgrades from error to warning and exits 0, referencing ADR-0067 in the workflow log. Under normal operation the gate stays unchanged — `daysSinceLastGreenRun > 7` still fails the workflow. Net effect: - `Live smoke` returns to green while EMERGENCY_STOP is engaged - `RAG eval` cron continues to fail as operator signal (the underlying problem — Gemini Free tier loss — is unchanged) - When EMERGENCY_STOP lifts (post LLM-provider migration ratchet), the gate immediately re-engages without further code change Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
42e8716 to
8c585fb
Compare
The nightly RAG eval has been failing every run since the 2026-04-29
Gemini Free tier account-level revocation incident (ADR-0067), because
/api/kb/ingest returns 503 with {"code":"EMERGENCY_STOP"} under the
operator-active kill switch. The failure is the intended containment,
not a regression — same logic as PR #95 applied at the eval gate.
Adds an ingest-endpoint probe as the first step. If the probe returns
EMERGENCY_STOP, the remaining steps are skipped via step-level `if:`
conditions and the job exits green with a clear warning that references
ADR-0067. When EMERGENCY_STOP lifts (post LLM-provider migration
ratchet), the probe falls through and eval resumes its normal
regression-detection role with no further code change.
Net effect:
- `RAG eval` cron returns to green while EMERGENCY_STOP is engaged
- Eval log carries an explicit warning so any operator reading the run
understands the deferred state and can find ADR-0067 in two clicks
- Eval still detects real regressions the moment the kill switch lifts
Co-authored-by: leagames0221-sys <leagames0221@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](actions/setup-python@v5...v6) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
8c585fb to
1d5fecf
Compare
|
OK, I won't notify you again about this release, but will get in touch when a new version is available. If you'd rather skip all updates until the next major or minor version, let me know by commenting If you change your mind, just re-open this PR and I'll resolve any conflicts on it. |
Bumps actions/setup-python from 5 to 6.
Release notes
Sourced from actions/setup-python's releases.
... (truncated)
Commits
a309ff8Bump urllib3 from 2.6.0 to 2.6.3 in /tests/data (#1264)bfe8cc5Upgrade@actionsdependencies to Node 24 compatible versions (#1259)4f41a90Bump urllib3 from 2.5.0 to 2.6.0 in /tests/data (#1253)83679a8Bump@types/nodefrom 24.1.0 to 24.9.1 and update macos-13 to macos-15-intel ...bfc4944Bump prettier from 3.5.3 to 3.6.2 (#1234)97aeb3eBump requests from 2.32.2 to 2.32.4 in /tests/data (#1130)443da59Bump actions/publish-action from 0.3.0 to 0.4.0 & Documentation update for pi...cfd55cagraalpy: add graalpy early-access and windows builds (#880)bba65e5Bump typescript from 5.4.2 to 5.9.3 and update docs/advanced-usage.md (#1094)18566f8Improve wording and "fix example" (remove 3.13) on testing against pre-releas...