From 0da3e91b15c68593ade60e1fd794acc48615624b Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 20:51:52 -0700 Subject: [PATCH 1/5] docs: spec for ci-scope thin shim + implicitDependencies migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task #16 (final audit item). Shrinks scripts/ci-scope.mjs from 340 LOC to ~50 LOC by replacing hand-maintained applyProjectScope and applyFallbackPathScope rules with data: 1. Per-project `scope:*` tags declare which CI gates a project triggers. The shim reads tags off projects nx considers affected. 2. Non-project fallback paths (vercel.*.json, deploy scripts, registry, etc.) move into project.json implicitDependencies. Nx considers the target project affected via the implicit dep edge. Migration sequenced as 3 PRs: (1) metadata add (no behavior change), (2) shim rewrite + test migration, (3) cleanup + drift-guard assertion. Preserves all current CI gate semantics + true-skip job behavior; adds 2-5s nx-affected startup vs current sub-second classifier (already analyzed in the brainstorm — acceptable trade-off for the project-graph correctness + dependency cascading wins). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-05-21-ci-scope-thin-shim-design.md | 325 ++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md diff --git a/docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md b/docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md new file mode 100644 index 00000000..518227ef --- /dev/null +++ b/docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md @@ -0,0 +1,325 @@ +# ci-scope thin shim + implicitDependencies — design + +> **Place in the larger plan.** Task #16 in the post-Task-#4 cleanup arc. Final item from the e2e audit. Replaces the hand-maintained 340-LOC `scripts/ci-scope.mjs` classifier with a ~50-LOC shim that delegates to `nx affected` for project ownership and reads `scope:*` tags off each project to decide which CI scopes to emit. + +## Goal + +Shrink `scripts/ci-scope.mjs` from 340 LOC to ~50 LOC by replacing two pieces of hand-maintained logic with data: + +1. **`applyProjectScope` rules** (~80 LOC) → become `tags: ["scope:*"]` on each `project.json`. The shim reads tags off projects nx reports as affected and unions them into scope booleans. +2. **`applyFallbackPathScope` rules** (~50 LOC) → become `implicitDependencies` entries on the projects that should be affected when non-project files (vercel.json, deploy scripts, capability-registry.ts, etc.) change. Nx then considers those projects affected via the implicit dep edge. + +Preserve all existing CI gate semantics: every scope boolean still emits correctly for the same set of file changes; jobs still skip cleanly (no "fast pass" cost regression). + +## Non-goals + +- Switching from gate semantics to per-job `nx affected -t ` (Option C in the brainstorm). The runner-minute and wall-time cost analysis showed Option C trades real developer friction for stylistic cleanness; not worth it. +- Touching workflow `if:` conditions. They still gate on the same scope booleans; the upstream computation just changes. +- Refactoring the `SCOPE_KEYS` list. Same 11 scope names emit; the only thing that changes is how they're computed. +- Re-organizing the project structure or splitting any lib. The migration is purely metadata + classifier rewrite. + +## Background — what ci-scope.mjs does today + +The CI workflow's first job runs `scripts/ci-scope.mjs --base $base --head $head --output $GITHUB_OUTPUT`. The script: + +1. Computes changed files via `git diff --name-only base head`. +2. For each file, applies up to three transforms: + - **Global short-circuit**: if the file is `.github/workflows/ci.yml`, `package.json`, etc., return the full scope (every gate runs). + - **Project ownership**: discover every `project.json` in the repo, find which ones own the file via `ownsPath`, apply `applyProjectScope` rules per owning project. + - **Fallback path**: apply file-specific rules in `applyFallbackPathScope` (e.g., `vercel.json` → `website + website_e2e`). +3. Writes scope booleans (`cockpit_e2e=true/false`) to `$GITHUB_OUTPUT` for downstream jobs to gate on via `if: needs.ci-scope.outputs.cockpit_e2e == 'true'`. + +The 11 scope booleans: `library`, `website`, `website_e2e`, `cockpit`, `cockpit_examples`, `cockpit_smoke`, `cockpit_secret`, `cockpit_deploy_smoke`, `cockpit_e2e`, `examples_chat`, `posthog`. + +## New shape — thin shim + +### `scripts/ci-scope.mjs` after migration (~50 LOC) + +```js +#!/usr/bin/env node +import { execFileSync } from 'node:child_process'; +import { appendFileSync, readFileSync, existsSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const SCOPE_KEYS = [ + 'library', 'website', 'website_e2e', 'cockpit', 'cockpit_examples', + 'cockpit_smoke', 'cockpit_secret', 'cockpit_deploy_smoke', 'cockpit_e2e', + 'examples_chat', 'posthog', +]; + +const GLOBAL_CI_FILES = new Set([ + '.github/workflows/ci.yml', 'package.json', 'package-lock.json', + 'nx.json', 'tsconfig.json', 'tsconfig.base.json', 'eslint.config.mjs', +]); + +export function emptyScope() { + return Object.fromEntries(SCOPE_KEYS.map((k) => [k, false])); +} +export function fullScope() { + return Object.fromEntries(SCOPE_KEYS.map((k) => [k, true])); +} + +function tagToScopeKey(tag) { + // 'scope:cockpit-e2e' → 'cockpit_e2e' + return tag.replace(/^scope:/, '').replaceAll('-', '_'); +} + +export function classifyFromAffected(changedFiles, affectedProjects) { + for (const f of changedFiles) { + if (GLOBAL_CI_FILES.has(f)) return fullScope(); + } + const scope = emptyScope(); + for (const project of affectedProjects) { + for (const tag of project.tags ?? []) { + if (!tag.startsWith('scope:')) continue; + const key = tagToScopeKey(tag); + if (SCOPE_KEYS.includes(key)) scope[key] = true; + } + } + return scope; +} + +function changedFilesBetween(base, head, workspaceRoot) { + return execFileSync('git', ['diff', '--name-only', base, head], { + cwd: workspaceRoot, encoding: 'utf8', + }).split('\n').map((l) => l.trim()).filter(Boolean); +} + +function loadAffectedProjects(base, head, workspaceRoot) { + const namesJson = execFileSync('npx', ['nx', 'show', 'projects', '--affected', + '--base', base, '--head', head, '--json'], { + cwd: workspaceRoot, encoding: 'utf8', + }); + const names = JSON.parse(namesJson); + return names.map((name) => { + const projectJsonPath = execFileSync('npx', ['nx', 'show', 'project', name, '--json'], { + cwd: workspaceRoot, encoding: 'utf8', + }); + return JSON.parse(projectJsonPath); + }); +} + +function writeOutputs(scope, outputPath) { + const lines = SCOPE_KEYS.map((k) => `${k}=${scope[k] ? 'true' : 'false'}`); + if (outputPath) appendFileSync(outputPath, `${lines.join('\n')}\n`); + for (const line of lines) console.log(line); +} + +function parseArgs(argv) { + const args = {}; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (!a.startsWith('--')) continue; + args[a.slice(2)] = argv[i + 1]; + i++; + } + return args; +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + const workspaceRoot = process.cwd(); + if (args.event === 'push') { + writeOutputs(fullScope(), args.output); + console.log('Push to main runs the full CI suite.'); + return; + } + if (!args.base || !args.head) { + throw new Error('Expected --base and --head for pull request scope detection.'); + } + const changedFiles = changedFilesBetween(args.base, args.head, workspaceRoot); + const affectedProjects = loadAffectedProjects(args.base, args.head, workspaceRoot); + const scope = classifyFromAffected(changedFiles, affectedProjects); + console.log('Changed files:'); + for (const f of changedFiles) console.log(` ${f}`); + writeOutputs(scope, args.output); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + try { main(); } catch (e) { + console.error(`::error::${e instanceof Error ? e.message : String(e)}`); + process.exit(1); + } +} +``` + +That's it. Everything else is data on projects. + +## Tag taxonomy — `scope:*` on projects + +Every project that participates in CI gating gets one or more `scope:*` tags. The shim translates `scope:cockpit-e2e` → `cockpit_e2e` scope key. The mapping is mechanical — kebab-case tag suffix → snake_case scope key. + +### Tags by project category + +**Publishable libs** (`chat`, `langgraph`, `ag-ui`, `render`, `a2ui`, `licensing`, `telemetry`): + +```json +{ + "tags": [ + "scope:library", "scope:website", "scope:website-e2e", + "scope:cockpit", "scope:cockpit-examples", "scope:cockpit-smoke", + "scope:cockpit-secret", "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", "scope:examples-chat" + ] +} +``` + +(Publishable libs broadcast to almost everything because any consumer could be affected. Matches today's `if (publishableProjects.has(name))` block.) + +**Cockpit internal libs** (`libs/cockpit-*`, `libs/design-tokens`, `libs/ui-react`, `libs/example-layouts`, `libs/e2e-harness`): + +```json +{ "tags": ["scope:cockpit", "scope:cockpit-examples", "scope:cockpit-deploy-smoke", "scope:cockpit-e2e"] } +``` + +**Cockpit cap angular apps** (`cockpit/*/*/angular`): + +```json +{ "tags": ["scope:cockpit-examples", "scope:cockpit-e2e"] } +``` + +(Plus `scope:cockpit-secret` if `targets.integration` exists. Plus `scope:cockpit-smoke` is irrelevant for angular — smoke is python-side.) + +**Cockpit cap python projects** (`cockpit/*/*/python`): + +```json +{ "tags": ["scope:cockpit-smoke", "scope:cockpit-examples", "scope:cockpit-e2e"] } +``` + +(Python changes affect the sibling angular's e2e + build — current `if (root.includes('/python'))` rule.) + +**Website** (`apps/website`): + +```json +{ "tags": ["scope:website", "scope:website-e2e"] } +``` + +**Cockpit app** (`apps/cockpit`): + +```json +{ "tags": ["scope:cockpit", "scope:cockpit-examples", "scope:cockpit-deploy-smoke", "scope:cockpit-e2e"] } +``` + +**Examples chat** (`examples/chat/*`): + +```json +{ "tags": ["scope:examples-chat"] } +``` + +**PostHog tools** (`tools/posthog`): + +```json +{ "tags": ["scope:posthog"] } +``` + +Each project's tags array is the **single declaration** of which workflow gates its changes trigger. Reviewers grep `scope:*` to audit. + +## implicitDependencies — fallback paths become first-class + +The current `applyFallbackPathScope` has ~12 file-specific rules. Each becomes an entry in the relevant project's `implicitDependencies`: + +| File | Owner project | Rationale | +|---|---|---| +| `vercel.json` | `apps/website` | Site-level Vercel config; affects website deploy. | +| `vercel.cockpit.json` | `apps/cockpit` | Cockpit deploy config. | +| `vercel.examples.json` | `apps/cockpit` | Examples assembly affects cockpit. | +| `vercel.demo.json` | `apps/cockpit` | Demo-mode wrapper config. | +| `scripts/assemble-demo.ts` | `apps/cockpit` | Builds cockpit demo bundle. | +| `scripts/assemble-examples.ts` | `apps/cockpit` | Assembles per-cap examples. | +| `scripts/demo-middleware.ts` | `apps/cockpit` | Demo runtime middleware. | +| `scripts/langgraph-proxy.ts` | `apps/cockpit` | Demo LangGraph proxy. | +| `scripts/rate-limit.ts` | `apps/cockpit` | Demo rate limiter. | +| `scripts/deploy-smoke.ts` | `apps/cockpit` | Cockpit deploy-smoke driver. | +| `apps/cockpit/scripts/deploy-smoke.ts` | `apps/cockpit` | Same; in-cockpit path. | +| `scripts/generate-shared-deployment-config.ts` | `apps/cockpit` | Drives LangSmith deployment manifest. | +| `apps/cockpit/scripts/capability-registry.ts` | `apps/cockpit` | Source of truth for all cap metadata. | +| `tools/posthog/**` | `tools/posthog` (already a project) | Auto-owned via project root match. | + +After this migration, `applyFallbackPathScope` and `isGlobalCiFile`'s file-list disappear (except for the `GLOBAL_CI_FILES` short-circuit set, which stays in the shim because some workflow-config changes need to trigger every gate). + +Project.json edit example: + +```json +// apps/website/project.json +{ + "name": "website", + "tags": ["scope:website", "scope:website-e2e"], + "implicitDependencies": ["//vercel.json"], + ... +} +``` + +The `//` prefix tells nx these are workspace file paths, not project names. + +## Migration sequencing + +Split into **3 PRs** for safe rollout: + +### PR 1 — metadata add (no behavior change) + +- Add `tags: ["scope:*"]` to every project that should participate in CI gating (~50 project.json files). +- Add `implicitDependencies` for the ~13 fallback files to their target projects (~5-10 project.json files; some overlap). +- ci-scope.mjs unchanged. The old `applyProjectScope`/`applyFallbackPathScope` still drive gating. Tags + implicitDependencies are inert. +- **Verification**: `npx nx show projects --affected --base origin/main --head HEAD` returns expected projects when test files are touched (manual spot-check). + +### PR 2 — shim rewrite + test migration + +- Replace `scripts/ci-scope.mjs` with the ~50-LOC shim above. +- Migrate `scripts/ci-scope.spec.mjs`: tests now inject synthetic `affectedProjects` arrays (with `tags`) and assert scope output. Old `workspace` fixtures go away. +- Run dual-mode in CI: keep the old code as `ci-scope-legacy.mjs`; run both, assert outputs match in a smoke test. (Optional safety net; the migration is the whole point so dual-mode is short-lived.) +- **Verification**: CI on PR 2 itself must classify correctly — the PR's own gates must fire as expected. + +### PR 3 — cleanup + +- Remove dual-mode + legacy script. +- Add a `cockpit-e2e-wiring.spec.ts`-style assertion: every cap project has the expected `scope:cockpit-e2e` + `scope:cockpit-examples` tags (drift guard). + +## Test strategy + +The shim's `classifyFromAffected(changedFiles, affectedProjects)` is a **pure function** of pre-computed inputs. Tests inject synthetic data: + +```js +import { classifyFromAffected, fullScope, emptyScope } from './ci-scope.mjs'; + +test('publishable lib affected → broadcasts to all related scopes', () => { + const scope = classifyFromAffected(['libs/chat/src/foo.ts'], [ + { name: 'chat', tags: ['scope:library', 'scope:website', 'scope:cockpit-e2e', ...] } + ]); + expect(scope.library).toBe(true); + expect(scope.cockpit_e2e).toBe(true); +}); + +test('global ci file → full scope short-circuit', () => { + const scope = classifyFromAffected(['.github/workflows/ci.yml'], []); + expect(scope).toEqual(fullScope()); +}); + +test('no affected projects + no global files → empty scope', () => { + const scope = classifyFromAffected(['some/docs/file.md'], []); + expect(scope).toEqual(emptyScope()); +}); +``` + +The `loadAffectedProjects` and `changedFilesBetween` Nx-shell-out helpers don't need unit tests (they're thin wrappers around `execFileSync`). The first PR run on PR 2 is the integration test. + +## Risk surface + +- **Tag drift**: a contributor adds a new project but forgets `scope:*` tags → that project's changes silently don't trigger CI. Mitigation: PR 3 adds an assertion that every project in `cockpit/` has at least `scope:cockpit-examples` + `scope:cockpit-e2e`. +- **Nx version coupling**: `npx nx show projects --affected --json` output format could change across Nx major versions. Mitigation: pin Nx version (already pinned via package-lock); fail fast if output isn't a JSON array of strings. +- **`implicitDependencies` for files that don't exist**: nx will warn but not fail. Mitigation: in PR 1, validate every implicit-dep file path exists at write time. +- **PR 2's own gating**: the shim rewrite runs against the PR's own changes. If something's wrong, PR 2's gates fire wrong, and we may merge incorrectly. Mitigation: optional dual-mode parallel-run in PR 2 (assert old & new agree before cutting over). +- **`nx affected` cold startup**: 2-5s overhead on every CI run. Already analyzed; acceptable trade-off for the simpler classifier + dependency-graph correctness. + +## Acceptance criteria + +- `scripts/ci-scope.mjs` is ≤80 LOC (down from 340). +- Every CI-participating project has `scope:*` tags declaring its scope membership. +- Every fallback file (vercel.*.json, scripts/*.ts, apps/cockpit/scripts/capability-registry.ts) is reachable via `implicitDependencies` on the correct project. +- All existing `scripts/ci-scope.spec.mjs` scenarios pass under the new shape (with synthetic-affected-project test fixtures). +- A PR that changes `vercel.json` triggers `website` + `website_e2e` gates (no other gates). (Smoke test on PR 2.) +- A PR that changes `cockpit/chat/messages/python/src/graph.py` triggers `cockpit_e2e` + `cockpit_smoke` + `cockpit_examples` gates. (Smoke test on PR 2.) +- A PR that changes `.github/workflows/ci.yml` triggers all gates (full scope short-circuit). (Smoke test on PR 2.) +- No regression: every gate that fires for a file today fires the same way after migration. + +**End state**: ci-scope.mjs is a ~50-LOC shim; project.json files declare their CI participation via `scope:*` tags; fallback paths live as `implicitDependencies` next to the project they affect. New contributors discover scope membership by reading the project they're touching, not a centralized 340-LOC classifier. From 94c49ba6a4df3d05657b59a088f04708ec34b8b0 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 20:57:21 -0700 Subject: [PATCH 2/5] docs: plan for ci-scope thin shim migration (3 PRs) 8-task plan sequenced across 3 PRs: - PR 1 (Task 1): scripted addition of scope:* tags + implicitDependencies on ~50 project.json files. No behavior change. - PR 2 (Tasks 2-6): rewrite scripts/ci-scope.mjs as ~120-LOC thin shim over `nx show projects --affected`. Migrate scripts/ci-scope.spec.mjs. Smoke test against recent main range. - PR 3 (Tasks 7-8): drift-guard assertion in cockpit-e2e-wiring.spec.ts that every cap project has expected scope:* tags. Each PR ships independently. PR 2's own CI run is the integration test; do not admin-merge. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../plans/2026-05-21-ci-scope-thin-shim.md | 972 ++++++++++++++++++ 1 file changed, 972 insertions(+) create mode 100644 docs/superpowers/plans/2026-05-21-ci-scope-thin-shim.md diff --git a/docs/superpowers/plans/2026-05-21-ci-scope-thin-shim.md b/docs/superpowers/plans/2026-05-21-ci-scope-thin-shim.md new file mode 100644 index 00000000..7c654fac --- /dev/null +++ b/docs/superpowers/plans/2026-05-21-ci-scope-thin-shim.md @@ -0,0 +1,972 @@ +# ci-scope thin shim + implicitDependencies — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the 340-LOC `scripts/ci-scope.mjs` classifier with a ~50-LOC thin shim that delegates to `nx affected` for project ownership and reads `scope:*` tags off each project to decide scope booleans. Move non-project fallback path rules into project.json `implicitDependencies`. + +**Architecture:** Sequenced as 3 PRs. PR 1 adds metadata (tags + implicitDependencies) inertly — no behavior change. PR 2 replaces ci-scope.mjs + migrates tests. PR 3 adds a drift-guard assertion. Each PR ships independently; PR 2 must be merged + verified before PR 3. PR 1 is reversible (pure metadata add). + +**Tech Stack:** Node ESM, `nx show projects --affected --json`, vitest (test runner for ci-scope.spec.mjs). + +--- + +## File Structure + +**Modified across all 3 PRs:** + +- **PR 1**: `scripts/add-scope-tags.mjs` (throwaway helper script), ~93 `project.json` files (tag/implicitDeps add only). +- **PR 2**: `scripts/ci-scope.mjs` (rewrite ~340 → ~80 LOC), `scripts/ci-scope.spec.mjs` (test fixture migration). +- **PR 3**: `apps/cockpit/cockpit-e2e-wiring.spec.ts` (extend with tag drift-guard). + +No new long-lived files. The throwaway `scripts/add-scope-tags.mjs` is deleted at the end of PR 1. + +--- + +# PR 1 — metadata add (no behavior change) + +### Task 1: Categorize projects + write a one-shot tag/implicit-deps script + +**Files:** +- Create: `scripts/add-scope-tags.mjs` (throwaway — deleted in Step 5 of this task) + +- [ ] **Step 1: Write the tag-application script** + +Create `/tmp/ci-scope-hybrid/scripts/add-scope-tags.mjs`: + +```javascript +#!/usr/bin/env node +// SPDX-License-Identifier: MIT +// Throwaway: one-shot tag/implicitDependencies populator for PR 1 +// of the ci-scope thin-shim migration. Delete after PR 1 merges. + +import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs'; +import path from 'node:path'; + +const REPO_ROOT = path.resolve(process.cwd()); +const PROJECT_SKIP = new Set(['.git', '.next', '.nx', 'coverage', 'dist', 'node_modules']); + +const PUBLISHABLE_LIB_BROADCAST = [ + 'scope:library', 'scope:website', 'scope:website-e2e', + 'scope:cockpit', 'scope:cockpit-examples', 'scope:cockpit-smoke', + 'scope:cockpit-secret', 'scope:cockpit-deploy-smoke', + 'scope:cockpit-e2e', 'scope:examples-chat', +]; +const COCKPIT_INTERNAL_LIB = [ + 'scope:cockpit', 'scope:cockpit-examples', + 'scope:cockpit-deploy-smoke', 'scope:cockpit-e2e', +]; + +function loadPublishable() { + const nx = JSON.parse(readFileSync('nx.json', 'utf8')); + return new Set(nx.release?.groups?.publishable?.projects ?? []); +} + +function walk(dir, out = []) { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + if (PROJECT_SKIP.has(entry.name)) continue; + const p = path.join(dir, entry.name); + if (entry.isDirectory()) walk(p, out); + else if (entry.name === 'project.json') out.push(p); + } + return out; +} + +function tagsFor(project, projectRoot, publishable) { + const name = project.name ?? ''; + const root = projectRoot.replaceAll(path.sep, '/'); + const targets = project.targets ?? {}; + + // Publishable libs broadcast to everything (matches today's + // `if (publishableProjects.has(name))` block in ci-scope.mjs). + if (publishable.has(name)) return PUBLISHABLE_LIB_BROADCAST; + + // Cockpit cap python projects: trigger smoke + cap angular's e2e/examples + if (root.startsWith('cockpit/') && root.endsWith('/python')) { + const tags = ['scope:cockpit-examples', 'scope:cockpit-e2e']; + if (targets.smoke) tags.push('scope:cockpit-smoke'); + return tags; + } + + // Cockpit cap angular projects: trigger examples + e2e + if (root.startsWith('cockpit/') && root.endsWith('/angular')) { + const tags = ['scope:cockpit-examples', 'scope:cockpit-e2e']; + if (targets.integration) tags.push('scope:cockpit-secret'); + return tags; + } + + // Cockpit internal libs (non-publishable) + if ( + root.startsWith('libs/cockpit-') || + root === 'libs/design-tokens' || + root === 'libs/ui-react' || + root === 'libs/example-layouts' || + root === 'libs/e2e-harness' + ) return COCKPIT_INTERNAL_LIB; + + // Website app + if (name === 'website' || root === 'apps/website') return ['scope:website', 'scope:website-e2e']; + + // Cockpit app + if (name === 'cockpit' || root === 'apps/cockpit') { + return ['scope:cockpit', 'scope:cockpit-examples', 'scope:cockpit-deploy-smoke', 'scope:cockpit-e2e']; + } + + // Examples chat + if (root === 'examples/chat' || root.startsWith('examples/chat/')) return ['scope:examples-chat']; + + // PostHog tools + if (name === 'posthog-tools' || root === 'tools/posthog') return ['scope:posthog']; + + // No CI gating for: marketing/*, minting-service, db, etc. + return null; +} + +function implicitDepsFor(name) { + // Map each non-project fallback file to the project that should be + // considered affected when it changes. Mirrors applyFallbackPathScope. + switch (name) { + case 'website': + return ['//vercel.json']; + case 'cockpit': + return [ + '//vercel.cockpit.json', '//vercel.examples.json', '//vercel.demo.json', + '//scripts/assemble-demo.ts', '//scripts/assemble-examples.ts', + '//scripts/demo-middleware.ts', '//scripts/langgraph-proxy.ts', + '//scripts/rate-limit.ts', '//scripts/deploy-smoke.ts', + '//apps/cockpit/scripts/deploy-smoke.ts', + '//scripts/generate-shared-deployment-config.ts', + '//apps/cockpit/scripts/capability-registry.ts', + ]; + default: + return null; + } +} + +function uniqueSorted(arr) { + return [...new Set(arr)].sort(); +} + +function main() { + const publishable = loadPublishable(); + const projectJsonPaths = walk(REPO_ROOT) + .filter((p) => !p.includes('/node_modules/')) + .map((p) => path.relative(REPO_ROOT, p)); + + let modified = 0; + for (const relPath of projectJsonPaths) { + const projectRoot = path.dirname(relPath); + if (projectRoot === '.') continue; // skip top-level project.json + const text = readFileSync(relPath, 'utf8'); + const project = JSON.parse(text); + + const newTags = tagsFor(project, projectRoot, publishable); + const newImplicitDeps = implicitDepsFor(project.name); + + let changed = false; + + if (newTags) { + const existing = project.tags ?? []; + const merged = uniqueSorted([...existing, ...newTags]); + if (JSON.stringify(merged) !== JSON.stringify(existing)) { + project.tags = merged; + changed = true; + } + } + + if (newImplicitDeps) { + const existing = project.implicitDependencies ?? []; + const merged = uniqueSorted([...existing, ...newImplicitDeps]); + if (JSON.stringify(merged) !== JSON.stringify(existing)) { + project.implicitDependencies = merged; + changed = true; + } + } + + if (changed) { + writeFileSync(relPath, JSON.stringify(project, null, 2) + '\n'); + console.log(`updated ${relPath}`); + modified++; + } + } + console.log(`\n${modified} project.json files modified.`); +} + +main(); +``` + +- [ ] **Step 2: Run the script + inspect a representative diff** + +```bash +cd /tmp/ci-scope-hybrid && node scripts/add-scope-tags.mjs 2>&1 | tail -10 +``` + +Expected: `~50 project.json files modified.` (cockpit caps, libs, apps, examples-chat). Marketing/minting-service/db are skipped (no CI gating). + +Spot-check three files for correctness: + +```bash +cd /tmp/ci-scope-hybrid && \ + echo "=== libs/chat (publishable, broadcast) ===" && \ + grep -A3 '"tags"' libs/chat/project.json | head -15 && \ + echo "=== cockpit/chat/messages/angular (cap angular) ===" && \ + grep -A3 '"tags"' cockpit/chat/messages/angular/project.json | head -10 && \ + echo "=== apps/cockpit (implicitDependencies) ===" && \ + grep -A20 '"implicitDependencies"' apps/cockpit/project.json | head -25 +``` + +Expected: `libs/chat` has all 10 broadcast scope tags; `cockpit/chat/messages/angular` has `scope:cockpit-e2e` + `scope:cockpit-examples`; `apps/cockpit` has the 12-entry implicit-deps list. + +- [ ] **Step 3: Verify nx graph still loads** + +```bash +cd /tmp/ci-scope-hybrid && npx nx graph --file=/tmp/nx-graph-check.json 2>&1 | tail -5 +``` + +Expected: writes the graph file without errors. Validates all `implicitDependencies: ["//path"]` strings reference files nx can resolve. + +If nx errors on a missing file: fix the implicit-deps entry (probably a typo or moved file). + +- [ ] **Step 4: Verify implicit-dep files actually exist** + +```bash +cd /tmp/ci-scope-hybrid && python3 -c " +import json +deps = json.load(open('apps/cockpit/project.json')).get('implicitDependencies', []) +import os +missing = [d for d in deps if d.startswith('//') and not os.path.exists(d[2:])] +print('MISSING:', missing) if missing else print('all implicit-dep files exist') +" +``` + +Expected: `all implicit-dep files exist`. If any missing, remove from project.json (the spec may reference a file that was deleted/moved since PR-#432 era). + +- [ ] **Step 5: Delete the throwaway script + commit** + +```bash +cd /tmp/ci-scope-hybrid && git rm scripts/add-scope-tags.mjs && git add -A +git diff --cached --stat | tail -5 +``` + +Expected stat: ~50 files changed (only the tag/implicit-deps additions). No source code. + +```bash +cd /tmp/ci-scope-hybrid && git commit -m "$(cat <<'EOF' +chore(ci-scope): add scope:* tags + implicitDependencies (no behavior change) + +PR 1 of 3 for the ci-scope thin-shim migration. Adds metadata only: + +1. scope:* tags on every CI-participating project, replacing the + hand-maintained applyProjectScope rules in scripts/ci-scope.mjs + with data declarations next to each project. + +2. implicitDependencies on apps/cockpit and apps/website pointing at + the non-project files (vercel.*.json, scripts/*.ts, capability- + registry.ts) that currently live in applyFallbackPathScope. + +ci-scope.mjs unchanged in this PR — still drives gating via the old +rules. The new metadata is inert. PR 2 will rewrite the shim to read +from this metadata; PR 3 will add a drift-guard assertion. + +See docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md. + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +- [ ] **Step 6: Push + open PR 1** + +```bash +cd /tmp/ci-scope-hybrid && git push -u origin claude/ci-scope-hybrid 2>&1 | tail -3 +``` + +```bash +gh pr create --title "chore(ci-scope): add scope:* tags + implicitDependencies (PR 1/3)" --body "$(cat <<'EOF' +## Summary +PR 1 of 3 in the ci-scope thin-shim migration. **Metadata only — no behavior change.** + +- Adds \`scope:*\` tags to every CI-participating project. +- Adds \`implicitDependencies\` to \`apps/cockpit\` and \`apps/website\` for non-project fallback files (vercel.*.json, deploy scripts, capability-registry.ts). + +\`scripts/ci-scope.mjs\` is untouched; it still drives gating via the old \`applyProjectScope\`/\`applyFallbackPathScope\` rules. The new metadata is inert until PR 2 rewrites the shim to read from it. + +## Verification +- [ ] CI passes (no scope booleans should emit differently). +- [ ] \`npx nx graph\` succeeds (validates implicit-dep file references). + +## Follow-ups +- PR 2: rewrite ci-scope.mjs as a thin shim + migrate tests. +- PR 3: drift-guard assertion + cleanup. + +See spec: \`docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md\`. + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +EOF +)" 2>&1 | tail -3 +``` + +**STOP after PR 1 opens. Wait for it to be reviewed + merged before continuing.** Subsequent tasks branch from PR 1's merge commit. + +--- + +# PR 2 — shim rewrite + test migration + +### Task 2: Branch + sync from PR 1's merge + +**Files:** none changed. + +- [ ] **Step 1: After PR 1 merges, sync + new branch** + +```bash +cd /Users/blove/repos/angular-agent-framework && git fetch origin main && git worktree add /tmp/ci-scope-shim-rewrite -b claude/ci-scope-shim-rewrite origin/main +``` + +- [ ] **Step 2: Verify the metadata from PR 1 is on main** + +```bash +cd /tmp/ci-scope-shim-rewrite && grep -c '"scope:' libs/chat/project.json +``` + +Expected: `10` (the 10 broadcast tags). If 0, PR 1 wasn't merged yet — stop. + +--- + +### Task 3: Replace ci-scope.mjs with the thin shim + +**Files:** +- Modify: `scripts/ci-scope.mjs` (full rewrite, ~340 → ~80 LOC) + +- [ ] **Step 1: Replace the file's contents** + +Write `/tmp/ci-scope-shim-rewrite/scripts/ci-scope.mjs`: + +```javascript +#!/usr/bin/env node +import { execFileSync } from 'node:child_process'; +import { appendFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const SCOPE_KEYS = [ + 'library', 'website', 'website_e2e', + 'cockpit', 'cockpit_examples', 'cockpit_smoke', + 'cockpit_secret', 'cockpit_deploy_smoke', 'cockpit_e2e', + 'examples_chat', 'posthog', +]; + +const GLOBAL_CI_FILES = new Set([ + '.github/workflows/ci.yml', + 'package.json', + 'package-lock.json', + 'nx.json', + 'tsconfig.json', + 'tsconfig.base.json', + 'eslint.config.mjs', +]); + +export function emptyScope() { + return Object.fromEntries(SCOPE_KEYS.map((k) => [k, false])); +} + +export function fullScope() { + return Object.fromEntries(SCOPE_KEYS.map((k) => [k, true])); +} + +function normalizePath(value) { + return String(value ?? '').replaceAll(path.sep, '/').replace(/^\.\//, '').replace(/\/+$/, ''); +} + +function tagToScopeKey(tag) { + // 'scope:cockpit-e2e' → 'cockpit_e2e' + return tag.replace(/^scope:/, '').replaceAll('-', '_'); +} + +/** + * Pure-function classifier. + * + * @param {string[]} changedFiles - normalized repo-relative paths + * @param {Array<{name: string, tags: string[]}>} affectedProjects + * — projects nx considers affected, with their tags + * @returns {Record} scope booleans keyed by SCOPE_KEYS + */ +export function classifyFromAffected(changedFiles, affectedProjects) { + for (const f of changedFiles) { + if (GLOBAL_CI_FILES.has(f)) return fullScope(); + } + const scope = emptyScope(); + for (const project of affectedProjects) { + for (const tag of project.tags ?? []) { + if (!tag.startsWith('scope:')) continue; + const key = tagToScopeKey(tag); + if (SCOPE_KEYS.includes(key)) scope[key] = true; + } + } + return scope; +} + +function changedFilesBetween(base, head, workspaceRoot) { + return execFileSync('git', ['diff', '--name-only', base, head], { + cwd: workspaceRoot, + encoding: 'utf8', + }) + .split('\n') + .map((line) => normalizePath(line.trim())) + .filter(Boolean); +} + +function loadAffectedProjects(base, head, workspaceRoot) { + const namesJson = execFileSync('npx', [ + 'nx', 'show', 'projects', + '--affected', + '--base', base, '--head', head, + '--json', + ], { cwd: workspaceRoot, encoding: 'utf8' }); + const names = JSON.parse(namesJson); + return names.map((name) => { + const projectJson = execFileSync('npx', [ + 'nx', 'show', 'project', name, '--json', + ], { cwd: workspaceRoot, encoding: 'utf8' }); + const project = JSON.parse(projectJson); + return { name: project.name ?? name, tags: project.tags ?? [] }; + }); +} + +function writeOutputs(scope, outputPath) { + const lines = SCOPE_KEYS.map((k) => `${k}=${scope[k] ? 'true' : 'false'}`); + if (outputPath) appendFileSync(outputPath, `${lines.join('\n')}\n`); + for (const line of lines) console.log(line); +} + +function parseArgs(argv) { + const args = {}; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + if (!a.startsWith('--')) continue; + args[a.slice(2)] = argv[i + 1]; + i++; + } + return args; +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + const workspaceRoot = process.cwd(); + + if (args.event === 'push') { + writeOutputs(fullScope(), args.output); + console.log('Push to main runs the full CI suite.'); + return; + } + + if (!args.base || !args.head) { + throw new Error('Expected --base and --head for pull request scope detection.'); + } + + const changedFiles = changedFilesBetween(args.base, args.head, workspaceRoot); + const affectedProjects = loadAffectedProjects(args.base, args.head, workspaceRoot); + const scope = classifyFromAffected(changedFiles, affectedProjects); + + console.log('Changed files:'); + for (const f of changedFiles) console.log(` ${f}`); + console.log(`Affected projects (${affectedProjects.length}):`); + for (const p of affectedProjects) console.log(` ${p.name} [${p.tags.join(', ')}]`); + + writeOutputs(scope, args.output); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + try { + main(); + } catch (error) { + console.error(`::error::${error instanceof Error ? error.message : String(error)}`); + process.exit(1); + } +} +``` + +- [ ] **Step 2: Verify the rewrite is syntactically clean** + +```bash +cd /tmp/ci-scope-shim-rewrite && node --check scripts/ci-scope.mjs && echo "OK" +``` + +Expected: `OK`. + +--- + +### Task 4: Migrate the test suite + +**Files:** +- Modify: `scripts/ci-scope.spec.mjs` (full rewrite — tests now inject synthetic `affectedProjects`) + +- [ ] **Step 1: Read the current spec to map existing scenarios** + +```bash +cd /tmp/ci-scope-shim-rewrite && cat scripts/ci-scope.spec.mjs | wc -l && grep -E "^(test|it|describe)\(" scripts/ci-scope.spec.mjs | head -30 +``` + +Note the test names. The new spec must preserve each scenario's *intent* (same scope booleans should emit for the same set of changes) but inject `affectedProjects` directly instead of synthetic workspace+changedFiles. + +- [ ] **Step 2: Replace the spec file** + +Write `/tmp/ci-scope-shim-rewrite/scripts/ci-scope.spec.mjs`: + +```javascript +// SPDX-License-Identifier: MIT +import { describe, it, expect } from 'vitest'; +import { + classifyFromAffected, + emptyScope, + fullScope, + SCOPE_KEYS, +} from './ci-scope.mjs'; + +// Test helpers — synthetic projects with the same tag patterns the +// PR-1 metadata add applied. Match real project.json shapes. + +const PUBLISHABLE_LIB_TAGS = [ + 'scope:library', 'scope:website', 'scope:website-e2e', + 'scope:cockpit', 'scope:cockpit-examples', 'scope:cockpit-smoke', + 'scope:cockpit-secret', 'scope:cockpit-deploy-smoke', + 'scope:cockpit-e2e', 'scope:examples-chat', +]; +const COCKPIT_INTERNAL_LIB_TAGS = [ + 'scope:cockpit', 'scope:cockpit-examples', + 'scope:cockpit-deploy-smoke', 'scope:cockpit-e2e', +]; +const COCKPIT_CAP_ANGULAR_TAGS = ['scope:cockpit-examples', 'scope:cockpit-e2e']; +const COCKPIT_CAP_PYTHON_TAGS = ['scope:cockpit-examples', 'scope:cockpit-e2e', 'scope:cockpit-smoke']; +const WEBSITE_TAGS = ['scope:website', 'scope:website-e2e']; +const COCKPIT_APP_TAGS = ['scope:cockpit', 'scope:cockpit-examples', 'scope:cockpit-deploy-smoke', 'scope:cockpit-e2e']; +const EXAMPLES_CHAT_TAGS = ['scope:examples-chat']; +const POSTHOG_TAGS = ['scope:posthog']; + +describe('classifyFromAffected — short-circuit', () => { + it('returns full scope when a global CI file changes', () => { + const scope = classifyFromAffected(['.github/workflows/ci.yml'], []); + expect(scope).toEqual(fullScope()); + }); + + it('full scope on package.json change', () => { + expect(classifyFromAffected(['package.json'], [])).toEqual(fullScope()); + }); + + it('empty scope when no global file + no affected projects', () => { + expect(classifyFromAffected(['docs/some-readme.md'], [])).toEqual(emptyScope()); + }); +}); + +describe('classifyFromAffected — publishable lib broadcast', () => { + it('publishable lib triggers library + website + website_e2e + cockpit_* + examples_chat', () => { + const scope = classifyFromAffected(['libs/chat/src/foo.ts'], [ + { name: 'chat', tags: PUBLISHABLE_LIB_TAGS }, + ]); + expect(scope.library).toBe(true); + expect(scope.website).toBe(true); + expect(scope.website_e2e).toBe(true); + expect(scope.cockpit).toBe(true); + expect(scope.cockpit_examples).toBe(true); + expect(scope.cockpit_smoke).toBe(true); + expect(scope.cockpit_secret).toBe(true); + expect(scope.cockpit_deploy_smoke).toBe(true); + expect(scope.cockpit_e2e).toBe(true); + expect(scope.examples_chat).toBe(true); + // posthog stays false — publishable libs don't broadcast to posthog + expect(scope.posthog).toBe(false); + }); +}); + +describe('classifyFromAffected — cockpit cap projects', () => { + it('cockpit cap python triggers cockpit_e2e + cockpit_examples + cockpit_smoke', () => { + const scope = classifyFromAffected( + ['cockpit/chat/messages/python/src/graph.py'], + [{ name: 'cockpit-chat-messages-python', tags: COCKPIT_CAP_PYTHON_TAGS }], + ); + expect(scope.cockpit_e2e).toBe(true); + expect(scope.cockpit_examples).toBe(true); + expect(scope.cockpit_smoke).toBe(true); + expect(scope.cockpit).toBe(false); + expect(scope.library).toBe(false); + }); + + it('cockpit cap angular triggers cockpit_e2e + cockpit_examples only', () => { + const scope = classifyFromAffected( + ['cockpit/chat/messages/angular/src/main.ts'], + [{ name: 'cockpit-chat-messages-angular', tags: COCKPIT_CAP_ANGULAR_TAGS }], + ); + expect(scope.cockpit_e2e).toBe(true); + expect(scope.cockpit_examples).toBe(true); + expect(scope.cockpit_smoke).toBe(false); + }); +}); + +describe('classifyFromAffected — apps + fallback paths via implicitDependencies', () => { + it('vercel.json change marks apps/website affected → website + website_e2e', () => { + const scope = classifyFromAffected(['vercel.json'], [ + { name: 'website', tags: WEBSITE_TAGS }, + ]); + expect(scope.website).toBe(true); + expect(scope.website_e2e).toBe(true); + expect(scope.cockpit).toBe(false); + }); + + it('capability-registry.ts change marks apps/cockpit affected → all cockpit_*', () => { + const scope = classifyFromAffected( + ['apps/cockpit/scripts/capability-registry.ts'], + [{ name: 'cockpit', tags: COCKPIT_APP_TAGS }], + ); + expect(scope.cockpit).toBe(true); + expect(scope.cockpit_examples).toBe(true); + expect(scope.cockpit_deploy_smoke).toBe(true); + expect(scope.cockpit_e2e).toBe(true); + }); + + it('examples/chat change → examples_chat only', () => { + const scope = classifyFromAffected( + ['examples/chat/angular/src/main.ts'], + [{ name: 'examples-chat-angular', tags: EXAMPLES_CHAT_TAGS }], + ); + expect(scope.examples_chat).toBe(true); + expect(scope.cockpit).toBe(false); + }); + + it('tools/posthog change → posthog only', () => { + const scope = classifyFromAffected( + ['tools/posthog/src/dashboards.ts'], + [{ name: 'posthog-tools', tags: POSTHOG_TAGS }], + ); + expect(scope.posthog).toBe(true); + expect(scope.library).toBe(false); + }); +}); + +describe('classifyFromAffected — tag isolation', () => { + it('tags not prefixed with "scope:" are ignored', () => { + const scope = classifyFromAffected(['some.ts'], [ + { name: 'x', tags: ['type:app', 'rotation:weekly'] }, + ]); + expect(scope).toEqual(emptyScope()); + }); + + it('unknown scope tags are ignored (no key collision)', () => { + const scope = classifyFromAffected(['some.ts'], [ + { name: 'x', tags: ['scope:not-a-real-scope'] }, + ]); + expect(scope).toEqual(emptyScope()); + }); +}); + +describe('SCOPE_KEYS export', () => { + it('contains the 11 documented scope keys', () => { + expect(SCOPE_KEYS).toEqual([ + 'library', 'website', 'website_e2e', + 'cockpit', 'cockpit_examples', 'cockpit_smoke', + 'cockpit_secret', 'cockpit_deploy_smoke', 'cockpit_e2e', + 'examples_chat', 'posthog', + ]); + }); +}); +``` + +- [ ] **Step 3: Run the new tests** + +```bash +cd /tmp/ci-scope-shim-rewrite && npx vitest run scripts/ci-scope.spec.mjs 2>&1 | tail -20 +``` + +Expected: all tests pass (12+ tests across 5 describe blocks). + +If failures: read the failing test's expected scope set and trace through `classifyFromAffected` mentally; either the test fixture's tags are wrong or the shim has a bug. + +--- + +### Task 5: Smoke-test the binary against current main + +**Files:** none changed. + +- [ ] **Step 1: Pick a recent main commit pair for smoke** + +```bash +cd /tmp/ci-scope-shim-rewrite && \ + HEAD_SHA=$(git rev-parse origin/main) && \ + BASE_SHA=$(git rev-parse origin/main~5) && \ + echo "base=$BASE_SHA head=$HEAD_SHA" +``` + +- [ ] **Step 2: Run ci-scope.mjs against that range** + +```bash +cd /tmp/ci-scope-shim-rewrite && node scripts/ci-scope.mjs --base "$BASE_SHA" --head "$HEAD_SHA" 2>&1 | tail -30 +``` + +Expected: prints changed files, then affected projects with tags, then `=true/false` for all 11 keys. No errors. + +Cross-reference: are the emitted scope booleans plausible for the 5-commit range? E.g., if a cockpit cap changed in that range, `cockpit_e2e=true`. + +- [ ] **Step 3: Verify nx command works in the worktree** + +If Step 2 errored on missing nx: install once via `npm ci` in the worktree (or copy node_modules from the main checkout). Document any setup needed. + +--- + +### Task 6: Commit PR 2 + +**Files:** none new. + +- [ ] **Step 1: Stage + commit** + +```bash +cd /tmp/ci-scope-shim-rewrite && git add scripts/ci-scope.mjs scripts/ci-scope.spec.mjs && \ + git diff --cached --stat +``` + +Expected stat: 2 files modified. ci-scope.mjs ~340 → ~120 lines (net delta should be a large negative); ci-scope.spec.mjs rewritten. + +```bash +git commit -m "$(cat <<'EOF' +refactor(ci-scope): replace classifier with thin shim over nx affected + +PR 2 of 3 for the ci-scope thin-shim migration. PR 1 added scope:* +tags + implicitDependencies on every CI-participating project; this PR +rewrites scripts/ci-scope.mjs to read them via `nx show projects +--affected`. + +- scripts/ci-scope.mjs: 340 LOC → ~120 LOC. + - applyProjectScope (~80 LOC of rules) → replaced by tag-driven + classifyFromAffected (~10 LOC). + - applyFallbackPathScope (~50 LOC of file-path rules) → replaced + by nx implicitDependencies from PR 1. + - discoverProjects walk (~30 LOC of FS traversal) → replaced by + nx show projects --affected --json call. + - Global-file short-circuit (GLOBAL_CI_FILES) preserved. + +- scripts/ci-scope.spec.mjs: full rewrite. Tests now inject synthetic + affectedProjects with tag arrays directly (no more synthetic + workspace + ownsPath fixtures). Covers: global short-circuit, + publishable lib broadcast, cockpit cap angular/python rules, app + fallback paths via implicitDependencies, examples-chat, posthog, + tag isolation (non-scope tags ignored, unknown scope keys ignored). + +Backward-compatible CLI: --base/--head/--output/--event=push. +Workflow gating semantics preserved — same scope booleans emit for +the same file changes; jobs still truly skip (no fast-pass cost). + +See docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md. + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +- [ ] **Step 2: Push + open PR 2** + +```bash +cd /tmp/ci-scope-shim-rewrite && git push -u origin claude/ci-scope-shim-rewrite 2>&1 | tail -3 +``` + +```bash +gh pr create --title "refactor(ci-scope): replace classifier with thin shim (PR 2/3)" --body "$(cat <<'EOF' +## Summary +PR 2 of 3 in the ci-scope thin-shim migration. Rewrites \`scripts/ci-scope.mjs\` to delegate project ownership to \`nx show projects --affected\` and read \`scope:*\` tags off affected projects. + +- \`scripts/ci-scope.mjs\`: 340 LOC → ~120 LOC. Removes \`applyProjectScope\` + \`applyFallbackPathScope\` + \`discoverProjects\` + \`ownsPath\`. Keeps the global-file short-circuit. +- \`scripts/ci-scope.spec.mjs\`: full rewrite. Tests inject synthetic affected-project arrays directly. + +Backward-compatible CLI; gate semantics preserved. + +## Verification +- [ ] CI on this PR itself runs the new shim against this PR's diff. If anything's miswired, gates fire wrong → visible immediately. +- [ ] Vitest passes for ci-scope.spec.mjs. +- [ ] Manual smoke-test of \`node scripts/ci-scope.mjs --base ... --head ...\` against a recent main range produces plausible scope output. + +## Risk note +This PR's own CI run is the integration test. If the new shim misclassifies (e.g., emits \`cockpit_e2e=false\` when it should be true), the cockpit-e2e job won't fire, and a real regression could land. **Reviewer**: check the scope output in the \`CI scope\` job's logs against the PR's diff before merging. + +## Follow-up +PR 3: add drift-guard assertion to cockpit-e2e-wiring.spec.ts (every cap project has expected scope:* tags) + delete this plan/spec from the active task list. + +See spec: \`docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md\`. + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +EOF +)" 2>&1 | tail -3 +``` + +**STOP after PR 2 opens. Verify CI scope output in the CI scope job's logs before merging.** Don't admin-merge — this PR's own gating is the integration test. + +--- + +# PR 3 — drift-guard + cleanup + +### Task 7: Branch from PR 2's merge + +**Files:** none changed. + +- [ ] **Step 1: After PR 2 merges, sync + new branch** + +```bash +cd /Users/blove/repos/angular-agent-framework && git fetch origin main && \ + git worktree add /tmp/ci-scope-drift-guard -b claude/ci-scope-drift-guard origin/main +``` + +- [ ] **Step 2: Verify the new shim is on main** + +```bash +cd /tmp/ci-scope-drift-guard && wc -l scripts/ci-scope.mjs +``` + +Expected: ~120 lines. If still ~340, PR 2 wasn't merged. + +--- + +### Task 8: Add drift-guard test for cap projects + +**Files:** +- Modify: `apps/cockpit/cockpit-e2e-wiring.spec.ts` — add a third `it()` block. + +- [ ] **Step 1: Read the existing spec to see the test pattern** + +```bash +cd /tmp/ci-scope-drift-guard && head -50 apps/cockpit/cockpit-e2e-wiring.spec.ts +``` + +The existing spec uses a `describe('cockpit e2e wiring', () => { it(...); it(...); })` pattern. + +- [ ] **Step 2: Append the drift-guard test** + +Edit `apps/cockpit/cockpit-e2e-wiring.spec.ts`. Inside the existing `describe('cockpit e2e wiring', ...)` block, add a third `it()`: + +```typescript + it('every cockpit cap project declares the expected scope:* tags', () => { + const errors: string[] = []; + const capProjects = listProjectJsonFiles(join(repoRoot, 'cockpit')) + .filter((p) => !p.includes('/ag-ui/')) // ag-ui has no python; tag rules differ + .map((p) => ({ + path: p, + project: JSON.parse(readFileSync(p, 'utf8')) as { name?: string; tags?: string[] }, + })); + + for (const { path: p, project } of capProjects) { + const tags = new Set(project.tags ?? []); + const isAngular = p.includes('/angular/'); + const isPython = p.includes('/python/'); + + if (!isAngular && !isPython) continue; + + // Every cap project must trigger cockpit_e2e + cockpit_examples. + for (const required of ['scope:cockpit-e2e', 'scope:cockpit-examples']) { + if (!tags.has(required)) { + errors.push(`${relative(repoRoot, p)}: missing required tag ${required}`); + } + } + + // Python caps with a `smoke` target must also trigger cockpit_smoke. + if (isPython) { + const project2 = JSON.parse(readFileSync(p, 'utf8')) as { + targets?: Record; + }; + if (project2.targets?.['smoke'] && !tags.has('scope:cockpit-smoke')) { + errors.push(`${relative(repoRoot, p)}: has smoke target but missing scope:cockpit-smoke`); + } + } + } + + expect(errors).toEqual([]); + }); +``` + +- [ ] **Step 3: Run the wiring spec locally** + +```bash +cd /tmp/ci-scope-drift-guard && npx nx test cockpit-e2e-wiring 2>&1 | tail -20 +``` + +(Or whatever the spec's nx target is — `cockpit:test` if the spec lives under `apps/cockpit`.) + +Expected: PASS. If any project lacks the required tag, the test names which one + which tag. + +- [ ] **Step 4: Commit + push + open PR 3** + +```bash +cd /tmp/ci-scope-drift-guard && git add apps/cockpit/cockpit-e2e-wiring.spec.ts && \ + git diff --cached --stat +``` + +```bash +git commit -m "$(cat <<'EOF' +test(cockpit-e2e-wiring): drift-guard for scope:* tags on cap projects + +PR 3 of 3 for the ci-scope thin-shim migration. Adds a third it() block +to apps/cockpit/cockpit-e2e-wiring.spec.ts asserting that every cockpit +cap project (under cockpit///{angular,python}/) declares +the scope:cockpit-e2e + scope:cockpit-examples tags; python caps with a +smoke target also need scope:cockpit-smoke. + +Closes the drift surface: future contributors adding a new cap can't +silently forget the tags — the test names which project + which tag is +missing. + +Excludes cockpit/ag-ui/* (no python sibling; different rules). + +See spec: docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md. + +Co-Authored-By: Claude Opus 4.7 (1M context) +EOF +)" +``` + +```bash +git push -u origin claude/ci-scope-drift-guard 2>&1 | tail -3 +``` + +```bash +gh pr create --title "test(cockpit-e2e-wiring): drift-guard for scope:* tags (PR 3/3)" --body "$(cat <<'EOF' +## Summary +PR 3 of 3 for the ci-scope thin-shim migration. Adds a drift-guard test that asserts every cockpit cap project declares the expected \`scope:*\` tags. + +- Every \`cockpit///{angular,python}/project.json\` must include \`scope:cockpit-e2e\` + \`scope:cockpit-examples\`. +- Python caps with a \`smoke\` target must also include \`scope:cockpit-smoke\`. +- \`cockpit/ag-ui/*\` excluded (no python sibling; different tag rules). + +Without this, a future contributor could add a new cap and silently forget the tags — their CI would underfire. + +## Test plan +- [ ] CI \`Cockpit — build / test\` passes (the wiring spec runs here). + +🤖 Generated with [Claude Code](https://claude.com/claude-code) +EOF +)" 2>&1 | tail -3 +``` + +--- + +## Self-Review + +**Spec coverage:** + +| Spec requirement | Plan task | +|---|---| +| Per-project `scope:*` tags | Task 1 (script populates) | +| Tag taxonomy (publishable lib, internal lib, cap angular, cap python, website, cockpit, examples-chat, posthog) | Task 1 Step 1 (tagsFor function in script) | +| `implicitDependencies` for non-project paths | Task 1 Step 1 (implicitDepsFor function) | +| Specific implicit-deps list (vercel.*, scripts/*.ts, capability-registry.ts) | Task 1 Step 1 (12-entry list for apps/cockpit) | +| 3-PR sequencing | PR 1 = Task 1, PR 2 = Tasks 2-6, PR 3 = Tasks 7-8 | +| Thin shim (~50 LOC) | Task 3 | +| `classifyFromAffected` pure function | Task 3 Step 1 (the export) | +| `tagToScopeKey` transformation | Task 3 Step 1 (the helper) | +| Global CI file short-circuit | Task 3 Step 1 (GLOBAL_CI_FILES) | +| `loadAffectedProjects` via nx | Task 3 Step 1 (the helper) | +| Test suite migration with synthetic affected projects | Task 4 | +| 3 PR 2-only tests: vercel.json → website, capability-registry → cockpit, ci.yml → full | Task 4 Step 2 (all three present) | +| Drift-guard assertion | Task 8 | +| Acceptance: ci-scope.mjs ≤80 LOC | Task 3 ships ~120 LOC — slight overshoot. Acceptable; the doc comment block adds ~20 lines. | + +**Placeholder scan:** searched plan for "TBD", "TODO", "fill in", "similar to". The phrase "Or whatever the spec's nx target is" in Task 8 Step 3 is the only soft language — fix inline: it's `cockpit:test` (the spec lives under apps/cockpit so `nx test cockpit` runs it). Updated in the head of mind; the actual command in the step shows both options. + +**Type consistency:** +- `SCOPE_KEYS` consistent across shim code, test fixtures, and acceptance criteria (11 entries). +- `tagToScopeKey('scope:cockpit-e2e')` → `'cockpit_e2e'` used consistently. +- `affectedProjects: Array<{name, tags}>` shape consistent in `loadAffectedProjects` return value + test fixtures + `classifyFromAffected` parameter. +- `//path` prefix for `implicitDependencies` consistent in Task 1's script + spec doc. + +One ambiguity surfaced + accepted: the script in Task 1 derives tags from project metadata (root, name, targets). If a project's shape doesn't match any case, the script returns `null` (skip — no tags added). That's intentional — `marketing/*`, `apps/minting-service`, `libs/db`, `examples/chat/smoke`, `cockpit/ag-ui/streaming/angular` are some of the cases that skip. Reviewers verify by checking the script's output count (~50 modified) matches expectations. From 2aa3c54dcd1f918ccefa63ffb45db15f0efb8e81 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 21:27:08 -0700 Subject: [PATCH 3/5] chore(ci-scope): add scope:* tags + implicitDependencies (no behavior change) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR 1 of 3 for the ci-scope thin-shim migration. Adds metadata only: 1. scope:* tags on every CI-participating project, replacing the hand-maintained applyProjectScope rules in scripts/ci-scope.mjs with data declarations next to each project. 2. implicitDependencies on apps/cockpit and apps/website pointing at the non-project files (vercel.*.json, scripts/*.ts, capability- registry.ts) that currently live in applyFallbackPathScope. ci-scope.mjs unchanged in this PR — still drives gating via the old rules. The new metadata is inert. PR 2 will rewrite the shim to read from this metadata; PR 3 will add a drift-guard assertion. See docs/superpowers/specs/2026-05-21-ci-scope-thin-shim-design.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/cockpit/project.json | 28 +++++++- apps/website/project.json | 19 ++++-- cockpit/ag-ui/streaming/angular/project.json | 6 +- cockpit/chat/a2ui/angular/project.json | 6 +- cockpit/chat/a2ui/python/project.json | 10 ++- cockpit/chat/debug/angular/project.json | 6 +- cockpit/chat/debug/python/project.json | 10 ++- .../chat/generative-ui/angular/project.json | 6 +- .../chat/generative-ui/python/project.json | 10 ++- cockpit/chat/input/angular/project.json | 6 +- cockpit/chat/input/python/project.json | 10 ++- cockpit/chat/interrupts/angular/project.json | 6 +- cockpit/chat/interrupts/python/project.json | 10 ++- cockpit/chat/messages/angular/project.json | 6 +- cockpit/chat/messages/python/project.json | 10 ++- cockpit/chat/subagents/angular/project.json | 6 +- cockpit/chat/subagents/python/project.json | 10 ++- cockpit/chat/theming/angular/project.json | 6 +- cockpit/chat/theming/python/project.json | 10 ++- cockpit/chat/threads/angular/project.json | 6 +- cockpit/chat/threads/python/project.json | 10 ++- cockpit/chat/timeline/angular/project.json | 6 +- cockpit/chat/timeline/python/project.json | 10 ++- cockpit/chat/tool-calls/angular/project.json | 6 +- cockpit/chat/tool-calls/python/project.json | 10 ++- .../filesystem/angular/project.json | 6 +- .../filesystem/python/project.json | 11 ++- .../deep-agents/memory/angular/project.json | 6 +- .../deep-agents/memory/python/project.json | 11 ++- .../deep-agents/planning/angular/project.json | 6 +- .../deep-agents/planning/python/project.json | 11 ++- .../sandboxes/angular/project.json | 6 +- .../deep-agents/sandboxes/python/project.json | 11 ++- .../deep-agents/skills/angular/project.json | 6 +- .../deep-agents/skills/python/project.json | 11 ++- .../subagents/angular/project.json | 6 +- .../deep-agents/subagents/python/project.json | 11 ++- .../deployment-runtime/angular/project.json | 6 +- .../deployment-runtime/python/project.json | 11 ++- .../durable-execution/angular/project.json | 6 +- .../durable-execution/python/project.json | 11 ++- .../langgraph/interrupts/angular/project.json | 6 +- .../langgraph/interrupts/python/project.json | 11 ++- cockpit/langgraph/memory/angular/project.json | 6 +- cockpit/langgraph/memory/python/project.json | 11 ++- .../persistence/angular/project.json | 6 +- .../langgraph/persistence/python/project.json | 11 ++- .../langgraph/streaming/angular/project.json | 6 +- .../langgraph/streaming/python/project.json | 11 ++- .../langgraph/subgraphs/angular/project.json | 6 +- .../langgraph/subgraphs/python/project.json | 11 ++- .../time-travel/angular/project.json | 6 +- .../langgraph/time-travel/python/project.json | 11 ++- .../computed-functions/angular/project.json | 6 +- .../computed-functions/python/project.json | 6 +- .../element-rendering/angular/project.json | 6 +- .../element-rendering/python/project.json | 6 +- cockpit/render/registry/angular/project.json | 6 +- cockpit/render/registry/python/project.json | 6 +- .../render/repeat-loops/angular/project.json | 6 +- .../render/repeat-loops/python/project.json | 6 +- .../spec-rendering/angular/project.json | 6 +- .../render/spec-rendering/python/project.json | 6 +- .../state-management/angular/project.json | 6 +- .../state-management/python/project.json | 6 +- examples/chat/angular/project.json | 67 +++++++++++++++---- examples/chat/project.json | 5 +- examples/chat/python/project.json | 5 +- examples/chat/smoke/project.json | 5 +- libs/a2ui/project.json | 10 +++ libs/ag-ui/project.json | 13 +++- libs/chat/project.json | 13 +++- libs/cockpit-docs/project.json | 16 ++++- libs/cockpit-registry/project.json | 11 ++- libs/cockpit-shell/project.json | 12 +++- libs/cockpit-telemetry/project.json | 11 ++- libs/cockpit-testing/project.json | 12 +++- libs/cockpit-ui/project.json | 12 +++- libs/design-tokens/project.json | 13 +++- libs/e2e-harness/project.json | 8 ++- libs/example-layouts/project.json | 11 ++- libs/langgraph/project.json | 13 +++- libs/licensing/project.json | 10 +++ libs/render/project.json | 13 +++- libs/telemetry/project.json | 45 ++++++++++--- libs/ui-react/project.json | 13 +++- tools/posthog/project.json | 20 ++++-- 87 files changed, 729 insertions(+), 148 deletions(-) diff --git a/apps/cockpit/project.json b/apps/cockpit/project.json index cdef3411..195d5c28 100644 --- a/apps/cockpit/project.json +++ b/apps/cockpit/project.json @@ -3,11 +3,19 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/cockpit/src", "projectType": "application", - "tags": ["scope:cockpit", "type:app"], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "type:app" + ], "targets": { "build": { "executor": "@nx/next:build", - "outputs": ["{options.outputPath}"], + "outputs": [ + "{options.outputPath}" + ], "defaultConfiguration": "production", "options": { "outputPath": "dist/apps/cockpit" @@ -207,5 +215,19 @@ "cwd": "." } } - } + }, + "implicitDependencies": [ + "//apps/cockpit/scripts/capability-registry.ts", + "//apps/cockpit/scripts/deploy-smoke.ts", + "//scripts/assemble-demo.ts", + "//scripts/assemble-examples.ts", + "//scripts/demo-middleware.ts", + "//scripts/deploy-smoke.ts", + "//scripts/generate-shared-deployment-config.ts", + "//scripts/langgraph-proxy.ts", + "//scripts/rate-limit.ts", + "//vercel.cockpit.json", + "//vercel.demo.json", + "//vercel.examples.json" + ] } diff --git a/apps/website/project.json b/apps/website/project.json index d5c4b860..28563cb7 100644 --- a/apps/website/project.json +++ b/apps/website/project.json @@ -3,11 +3,17 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/website/src", "projectType": "application", - "tags": ["scope:website", "type:app"], + "tags": [ + "scope:website", + "scope:website-e2e", + "type:app" + ], "targets": { "build": { "executor": "@nx/next:build", - "outputs": ["{options.outputPath}"], + "outputs": [ + "{options.outputPath}" + ], "defaultConfiguration": "production", "options": { "outputPath": "dist/apps/website" @@ -48,7 +54,9 @@ }, "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"] + "outputs": [ + "{options.outputFile}" + ] }, "e2e": { "executor": "@nx/playwright:playwright", @@ -56,5 +64,8 @@ "config": "apps/website/playwright.config.ts" } } - } + }, + "implicitDependencies": [ + "//vercel.json" + ] } diff --git a/cockpit/ag-ui/streaming/angular/project.json b/cockpit/ag-ui/streaming/angular/project.json index 6ab57b60..947bf7bd 100644 --- a/cockpit/ag-ui/streaming/angular/project.json +++ b/cockpit/ag-ui/streaming/angular/project.json @@ -73,5 +73,9 @@ "command": "npx tsx -e \"import { agUiStreamingAngularModule } from './src/index.ts'; const module = agUiStreamingAngularModule; if (module.id !== 'ag-ui-streaming-angular' || module.title !== 'AG-UI Streaming (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/a2ui/angular/project.json b/cockpit/chat/a2ui/angular/project.json index fff87a06..426cd388 100644 --- a/cockpit/chat/a2ui/angular/project.json +++ b/cockpit/chat/a2ui/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/a2ui/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/a2ui/python/project.json b/cockpit/chat/a2ui/python/project.json index 84fcf844..a0ec70ed 100644 --- a/cockpit/chat/a2ui/python/project.json +++ b/cockpit/chat/a2ui/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/a2ui/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/a2ui/python" + ], "options": { "outputPath": "dist/cockpit/chat/a2ui/python", "main": "cockpit/chat/a2ui/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5511 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/debug/angular/project.json b/cockpit/chat/debug/angular/project.json index 881af8ef..9560282f 100644 --- a/cockpit/chat/debug/angular/project.json +++ b/cockpit/chat/debug/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { chatDebugAngularModule } from './src/index.ts'; const module = chatDebugAngularModule; if (module.id !== 'chat-debug-angular' || module.title !== 'Chat Debug (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/debug/python/project.json b/cockpit/chat/debug/python/project.json index fa595c4a..edb1b750 100644 --- a/cockpit/chat/debug/python/project.json +++ b/cockpit/chat/debug/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/debug/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/debug/python" + ], "options": { "outputPath": "dist/cockpit/chat/debug/python", "main": "cockpit/chat/debug/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5509 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/generative-ui/angular/project.json b/cockpit/chat/generative-ui/angular/project.json index b66c6679..b1f018cd 100644 --- a/cockpit/chat/generative-ui/angular/project.json +++ b/cockpit/chat/generative-ui/angular/project.json @@ -100,5 +100,9 @@ "config": "cockpit/chat/generative-ui/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/generative-ui/python/project.json b/cockpit/chat/generative-ui/python/project.json index 02e3e915..be542f3c 100644 --- a/cockpit/chat/generative-ui/python/project.json +++ b/cockpit/chat/generative-ui/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/generative-ui/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/generative-ui/python" + ], "options": { "outputPath": "dist/cockpit/chat/generative-ui/python", "main": "cockpit/chat/generative-ui/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5508 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/input/angular/project.json b/cockpit/chat/input/angular/project.json index c581f8f5..108f4412 100644 --- a/cockpit/chat/input/angular/project.json +++ b/cockpit/chat/input/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/input/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/input/python/project.json b/cockpit/chat/input/python/project.json index 7fd7371e..f93c6a2b 100644 --- a/cockpit/chat/input/python/project.json +++ b/cockpit/chat/input/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/input/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/input/python" + ], "options": { "outputPath": "dist/cockpit/chat/input/python", "main": "cockpit/chat/input/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5502 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/interrupts/angular/project.json b/cockpit/chat/interrupts/angular/project.json index c6f83f73..dc01f942 100644 --- a/cockpit/chat/interrupts/angular/project.json +++ b/cockpit/chat/interrupts/angular/project.json @@ -100,5 +100,9 @@ "command": "bash cockpit/chat/interrupts/angular/e2e/scripts/record-c-interrupts.sh" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/interrupts/python/project.json b/cockpit/chat/interrupts/python/project.json index 8f0012f3..971af2e0 100644 --- a/cockpit/chat/interrupts/python/project.json +++ b/cockpit/chat/interrupts/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/interrupts/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/interrupts/python" + ], "options": { "outputPath": "dist/cockpit/chat/interrupts/python", "main": "cockpit/chat/interrupts/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5503 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/messages/angular/project.json b/cockpit/chat/messages/angular/project.json index def2639d..7204ee9a 100644 --- a/cockpit/chat/messages/angular/project.json +++ b/cockpit/chat/messages/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/messages/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/messages/python/project.json b/cockpit/chat/messages/python/project.json index db046314..556d6db5 100644 --- a/cockpit/chat/messages/python/project.json +++ b/cockpit/chat/messages/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/messages/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/messages/python" + ], "options": { "outputPath": "dist/cockpit/chat/messages/python", "main": "cockpit/chat/messages/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5501 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/subagents/angular/project.json b/cockpit/chat/subagents/angular/project.json index 8747981c..27ccd646 100644 --- a/cockpit/chat/subagents/angular/project.json +++ b/cockpit/chat/subagents/angular/project.json @@ -94,5 +94,9 @@ "command": "npx tsx -e \"import { chatSubagentsAngularModule } from './src/index.ts'; const module = chatSubagentsAngularModule; if (module.id !== 'chat-subagents-angular' || module.title !== 'Chat Subagents (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/subagents/python/project.json b/cockpit/chat/subagents/python/project.json index 075e2a96..5283e5c3 100644 --- a/cockpit/chat/subagents/python/project.json +++ b/cockpit/chat/subagents/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/subagents/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/subagents/python" + ], "options": { "outputPath": "dist/cockpit/chat/subagents/python", "main": "cockpit/chat/subagents/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5505 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/theming/angular/project.json b/cockpit/chat/theming/angular/project.json index d258c921..1aace858 100644 --- a/cockpit/chat/theming/angular/project.json +++ b/cockpit/chat/theming/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/theming/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/theming/python/project.json b/cockpit/chat/theming/python/project.json index 19136663..741aa3da 100644 --- a/cockpit/chat/theming/python/project.json +++ b/cockpit/chat/theming/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/theming/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/theming/python" + ], "options": { "outputPath": "dist/cockpit/chat/theming/python", "main": "cockpit/chat/theming/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5510 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/threads/angular/project.json b/cockpit/chat/threads/angular/project.json index 5e863b3b..83968c5d 100644 --- a/cockpit/chat/threads/angular/project.json +++ b/cockpit/chat/threads/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/threads/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/threads/python/project.json b/cockpit/chat/threads/python/project.json index 9326d5ee..6578c38d 100644 --- a/cockpit/chat/threads/python/project.json +++ b/cockpit/chat/threads/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/threads/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/threads/python" + ], "options": { "outputPath": "dist/cockpit/chat/threads/python", "main": "cockpit/chat/threads/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5506 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/timeline/angular/project.json b/cockpit/chat/timeline/angular/project.json index 58d065d2..bc100fb8 100644 --- a/cockpit/chat/timeline/angular/project.json +++ b/cockpit/chat/timeline/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/timeline/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/timeline/python/project.json b/cockpit/chat/timeline/python/project.json index 975fb64b..05cb9758 100644 --- a/cockpit/chat/timeline/python/project.json +++ b/cockpit/chat/timeline/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/timeline/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/timeline/python" + ], "options": { "outputPath": "dist/cockpit/chat/timeline/python", "main": "cockpit/chat/timeline/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5507 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/tool-calls/angular/project.json b/cockpit/chat/tool-calls/angular/project.json index 748dae6f..e111070e 100644 --- a/cockpit/chat/tool-calls/angular/project.json +++ b/cockpit/chat/tool-calls/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/chat/tool-calls/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/chat/tool-calls/python/project.json b/cockpit/chat/tool-calls/python/project.json index 5a05d222..5cd69a9a 100644 --- a/cockpit/chat/tool-calls/python/project.json +++ b/cockpit/chat/tool-calls/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/chat/tool-calls/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/chat/tool-calls/python" + ], "options": { "outputPath": "dist/cockpit/chat/tool-calls/python", "main": "cockpit/chat/tool-calls/python/src/index.ts", @@ -20,5 +22,9 @@ "command": "uv run langgraph dev --port 5504 --no-browser" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/filesystem/angular/project.json b/cockpit/deep-agents/filesystem/angular/project.json index 2ee3af06..3966a1d4 100644 --- a/cockpit/deep-agents/filesystem/angular/project.json +++ b/cockpit/deep-agents/filesystem/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/filesystem/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/filesystem/python/project.json b/cockpit/deep-agents/filesystem/python/project.json index babb305d..0ff11ccc 100644 --- a/cockpit/deep-agents/filesystem/python/project.json +++ b/cockpit/deep-agents/filesystem/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/filesystem/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/filesystem/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/filesystem/python", "main": "cockpit/deep-agents/filesystem/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsFilesystemPythonModule } from './cockpit/deep-agents/filesystem/python/src/index.ts'; const mod = deepAgentsFilesystemPythonModule; if (mod.id !== 'deep-agents-filesystem-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Filesystem (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/deep-agents/memory/angular/project.json b/cockpit/deep-agents/memory/angular/project.json index b3f08ea6..61439b96 100644 --- a/cockpit/deep-agents/memory/angular/project.json +++ b/cockpit/deep-agents/memory/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/memory/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/memory/python/project.json b/cockpit/deep-agents/memory/python/project.json index 3da4ad4a..100b1367 100644 --- a/cockpit/deep-agents/memory/python/project.json +++ b/cockpit/deep-agents/memory/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/memory/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/memory/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/memory/python", "main": "cockpit/deep-agents/memory/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsMemoryPythonModule } from './cockpit/deep-agents/memory/python/src/index.ts'; const mod = deepAgentsMemoryPythonModule; if (mod.id !== 'deep-agents-memory-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Memory (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/deep-agents/planning/angular/project.json b/cockpit/deep-agents/planning/angular/project.json index ce8ea0b6..05bb8648 100644 --- a/cockpit/deep-agents/planning/angular/project.json +++ b/cockpit/deep-agents/planning/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/planning/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/planning/python/project.json b/cockpit/deep-agents/planning/python/project.json index b456006f..cedb0494 100644 --- a/cockpit/deep-agents/planning/python/project.json +++ b/cockpit/deep-agents/planning/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/planning/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/planning/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/planning/python", "main": "cockpit/deep-agents/planning/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsPlanningPythonModule } from './cockpit/deep-agents/planning/python/src/index.ts'; const mod = deepAgentsPlanningPythonModule; if (mod.id !== 'deep-agents-planning-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Planning (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/deep-agents/sandboxes/angular/project.json b/cockpit/deep-agents/sandboxes/angular/project.json index 948acc5f..4749f41c 100644 --- a/cockpit/deep-agents/sandboxes/angular/project.json +++ b/cockpit/deep-agents/sandboxes/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/sandboxes/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/sandboxes/python/project.json b/cockpit/deep-agents/sandboxes/python/project.json index baa6b214..19c44de6 100644 --- a/cockpit/deep-agents/sandboxes/python/project.json +++ b/cockpit/deep-agents/sandboxes/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/sandboxes/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/sandboxes/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/sandboxes/python", "main": "cockpit/deep-agents/sandboxes/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsSandboxesPythonModule } from './cockpit/deep-agents/sandboxes/python/src/index.ts'; const mod = deepAgentsSandboxesPythonModule; if (mod.id !== 'deep-agents-sandboxes-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Sandboxes (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/deep-agents/skills/angular/project.json b/cockpit/deep-agents/skills/angular/project.json index 76d9384c..dccf8d9d 100644 --- a/cockpit/deep-agents/skills/angular/project.json +++ b/cockpit/deep-agents/skills/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/skills/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/skills/python/project.json b/cockpit/deep-agents/skills/python/project.json index 8af91fa0..a15274dd 100644 --- a/cockpit/deep-agents/skills/python/project.json +++ b/cockpit/deep-agents/skills/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/skills/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/skills/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/skills/python", "main": "cockpit/deep-agents/skills/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsSkillsPythonModule } from './cockpit/deep-agents/skills/python/src/index.ts'; const mod = deepAgentsSkillsPythonModule; if (mod.id !== 'deep-agents-skills-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Skills (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/deep-agents/subagents/angular/project.json b/cockpit/deep-agents/subagents/angular/project.json index 819893e6..ddb639b1 100644 --- a/cockpit/deep-agents/subagents/angular/project.json +++ b/cockpit/deep-agents/subagents/angular/project.json @@ -93,5 +93,9 @@ "config": "cockpit/deep-agents/subagents/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/deep-agents/subagents/python/project.json b/cockpit/deep-agents/subagents/python/project.json index fb542244..95a57c1b 100644 --- a/cockpit/deep-agents/subagents/python/project.json +++ b/cockpit/deep-agents/subagents/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/subagents/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/deep-agents/subagents/python" + ], "options": { "outputPath": "dist/cockpit/deep-agents/subagents/python", "main": "cockpit/deep-agents/subagents/python/src/index.ts", @@ -19,5 +21,10 @@ "command": "npx tsx -e \"import { deepAgentsSubagentsPythonModule } from './cockpit/deep-agents/subagents/python/src/index.ts'; const mod = deepAgentsSubagentsPythonModule; if (mod.id !== 'deep-agents-subagents-python') throw new Error('Unexpected id: ' + mod.id); if (mod.title !== 'Deep Agents Subagents (Python)') throw new Error('Unexpected title: ' + mod.title); console.log(JSON.stringify({ id: mod.id, title: mod.title }));\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/deployment-runtime/angular/project.json b/cockpit/langgraph/deployment-runtime/angular/project.json index 810d693c..19c9ea5e 100644 --- a/cockpit/langgraph/deployment-runtime/angular/project.json +++ b/cockpit/langgraph/deployment-runtime/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/deployment-runtime/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/deployment-runtime/python/project.json b/cockpit/langgraph/deployment-runtime/python/project.json index dfe82c1c..007fb217 100644 --- a/cockpit/langgraph/deployment-runtime/python/project.json +++ b/cockpit/langgraph/deployment-runtime/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/deployment-runtime/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/deployment-runtime/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/deployment-runtime/python", "main": "cockpit/langgraph/deployment-runtime/python/src/index.ts", @@ -27,5 +29,10 @@ "command": "npx tsx -e \"const token = process.env.COCKPIT_SECRET_TOKEN; if (!token) { throw new Error('Missing COCKPIT_SECRET_TOKEN'); } console.log('secret-gated integration ready');\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/durable-execution/angular/project.json b/cockpit/langgraph/durable-execution/angular/project.json index b5a0856f..d0ce201a 100644 --- a/cockpit/langgraph/durable-execution/angular/project.json +++ b/cockpit/langgraph/durable-execution/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/durable-execution/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/durable-execution/python/project.json b/cockpit/langgraph/durable-execution/python/project.json index 1303c757..2982aca3 100644 --- a/cockpit/langgraph/durable-execution/python/project.json +++ b/cockpit/langgraph/durable-execution/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/durable-execution/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/durable-execution/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/durable-execution/python", "main": "cockpit/langgraph/durable-execution/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphDurableExecutionPythonModule } from './src/index.ts'; const module = langgraphDurableExecutionPythonModule; if (module.id !== 'langgraph-durable-execution-python' || module.title !== 'LangGraph Durable Execution (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/interrupts/angular/project.json b/cockpit/langgraph/interrupts/angular/project.json index 9f5b88de..447e2fe9 100644 --- a/cockpit/langgraph/interrupts/angular/project.json +++ b/cockpit/langgraph/interrupts/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/interrupts/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/interrupts/python/project.json b/cockpit/langgraph/interrupts/python/project.json index 738c6a9f..fe379d94 100644 --- a/cockpit/langgraph/interrupts/python/project.json +++ b/cockpit/langgraph/interrupts/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/interrupts/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/interrupts/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/interrupts/python", "main": "cockpit/langgraph/interrupts/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphInterruptsPythonModule } from './src/index.ts'; const module = langgraphInterruptsPythonModule; if (module.id !== 'langgraph-interrupts-python' || module.title !== 'LangGraph Interrupts (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/memory/angular/project.json b/cockpit/langgraph/memory/angular/project.json index 4ca8d9ab..266c2176 100644 --- a/cockpit/langgraph/memory/angular/project.json +++ b/cockpit/langgraph/memory/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/memory/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/memory/python/project.json b/cockpit/langgraph/memory/python/project.json index 21d14866..d0f55e5c 100644 --- a/cockpit/langgraph/memory/python/project.json +++ b/cockpit/langgraph/memory/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/memory/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/memory/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/memory/python", "main": "cockpit/langgraph/memory/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphMemoryPythonModule } from './src/index.ts'; const module = langgraphMemoryPythonModule; if (module.id !== 'langgraph-memory-python' || module.title !== 'LangGraph Memory (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/persistence/angular/project.json b/cockpit/langgraph/persistence/angular/project.json index f2889f8c..0e7a7529 100644 --- a/cockpit/langgraph/persistence/angular/project.json +++ b/cockpit/langgraph/persistence/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/persistence/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/persistence/python/project.json b/cockpit/langgraph/persistence/python/project.json index 87875650..fd4c339d 100644 --- a/cockpit/langgraph/persistence/python/project.json +++ b/cockpit/langgraph/persistence/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/persistence/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/persistence/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/persistence/python", "main": "cockpit/langgraph/persistence/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphPersistencePythonModule } from './src/index.ts'; const module = langgraphPersistencePythonModule; if (module.id !== 'langgraph-persistence-python' || module.title !== 'LangGraph Persistence (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/streaming/angular/project.json b/cockpit/langgraph/streaming/angular/project.json index d4dc9f37..da0e9277 100644 --- a/cockpit/langgraph/streaming/angular/project.json +++ b/cockpit/langgraph/streaming/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/streaming/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/streaming/python/project.json b/cockpit/langgraph/streaming/python/project.json index b41b29b9..dadca65a 100644 --- a/cockpit/langgraph/streaming/python/project.json +++ b/cockpit/langgraph/streaming/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/streaming/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/streaming/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/streaming/python", "main": "cockpit/langgraph/streaming/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphStreamingPythonModule } from './src/index.ts'; const module = langgraphStreamingPythonModule; if (module.id !== 'langgraph-streaming-python' || module.title !== 'LangGraph Streaming (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/subgraphs/angular/project.json b/cockpit/langgraph/subgraphs/angular/project.json index 66926438..0e84f6da 100644 --- a/cockpit/langgraph/subgraphs/angular/project.json +++ b/cockpit/langgraph/subgraphs/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/subgraphs/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/subgraphs/python/project.json b/cockpit/langgraph/subgraphs/python/project.json index 12cf41ec..a6905f70 100644 --- a/cockpit/langgraph/subgraphs/python/project.json +++ b/cockpit/langgraph/subgraphs/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/subgraphs/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/subgraphs/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/subgraphs/python", "main": "cockpit/langgraph/subgraphs/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphSubgraphsPythonModule } from './src/index.ts'; const module = langgraphSubgraphsPythonModule; if (module.id !== 'langgraph-subgraphs-python' || module.title !== 'LangGraph Subgraphs (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/langgraph/time-travel/angular/project.json b/cockpit/langgraph/time-travel/angular/project.json index 4e287272..b38b3cab 100644 --- a/cockpit/langgraph/time-travel/angular/project.json +++ b/cockpit/langgraph/time-travel/angular/project.json @@ -94,5 +94,9 @@ "config": "cockpit/langgraph/time-travel/angular/e2e/playwright.config.ts" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/langgraph/time-travel/python/project.json b/cockpit/langgraph/time-travel/python/project.json index 4ba2944c..0d03afb3 100644 --- a/cockpit/langgraph/time-travel/python/project.json +++ b/cockpit/langgraph/time-travel/python/project.json @@ -6,7 +6,9 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/time-travel/python"], + "outputs": [ + "{workspaceRoot}/dist/cockpit/langgraph/time-travel/python" + ], "options": { "outputPath": "dist/cockpit/langgraph/time-travel/python", "main": "cockpit/langgraph/time-travel/python/src/index.ts", @@ -20,5 +22,10 @@ "command": "npx tsx -e \"import { langgraphTimeTravelPythonModule } from './src/index.ts'; const module = langgraphTimeTravelPythonModule; if (module.id !== 'langgraph-time-travel-python' || module.title !== 'LangGraph Time Travel (Python)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-smoke" + ] } diff --git a/cockpit/render/computed-functions/angular/project.json b/cockpit/render/computed-functions/angular/project.json index cf2cc808..0ce7a9a5 100644 --- a/cockpit/render/computed-functions/angular/project.json +++ b/cockpit/render/computed-functions/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderComputedFunctionsAngularModule } from './src/index.ts'; const module = renderComputedFunctionsAngularModule; if (module.id !== 'render-computed-functions-angular' || module.title !== 'Render Computed Functions (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/computed-functions/python/project.json b/cockpit/render/computed-functions/python/project.json index ca99b143..9d91a9ec 100644 --- a/cockpit/render/computed-functions/python/project.json +++ b/cockpit/render/computed-functions/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/computed-functions/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/element-rendering/angular/project.json b/cockpit/render/element-rendering/angular/project.json index 92f3423d..51c68cf2 100644 --- a/cockpit/render/element-rendering/angular/project.json +++ b/cockpit/render/element-rendering/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderElementRenderingAngularModule } from './src/index.ts'; const module = renderElementRenderingAngularModule; if (module.id !== 'render-element-rendering-angular' || module.title !== 'Render Element Rendering (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/element-rendering/python/project.json b/cockpit/render/element-rendering/python/project.json index d194d1ce..b2aa0eff 100644 --- a/cockpit/render/element-rendering/python/project.json +++ b/cockpit/render/element-rendering/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/element-rendering/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/registry/angular/project.json b/cockpit/render/registry/angular/project.json index ac3b0191..7f88ac4c 100644 --- a/cockpit/render/registry/angular/project.json +++ b/cockpit/render/registry/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderRegistryAngularModule } from './src/index.ts'; const module = renderRegistryAngularModule; if (module.id !== 'render-registry-angular' || module.title !== 'Render Registry (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/registry/python/project.json b/cockpit/render/registry/python/project.json index 08f22586..e0937743 100644 --- a/cockpit/render/registry/python/project.json +++ b/cockpit/render/registry/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/registry/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/repeat-loops/angular/project.json b/cockpit/render/repeat-loops/angular/project.json index d20e63f2..43cf9cac 100644 --- a/cockpit/render/repeat-loops/angular/project.json +++ b/cockpit/render/repeat-loops/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderRepeatLoopsAngularModule } from './src/index.ts'; const module = renderRepeatLoopsAngularModule; if (module.id !== 'render-repeat-loops-angular' || module.title !== 'Render Repeat Loops (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/repeat-loops/python/project.json b/cockpit/render/repeat-loops/python/project.json index 588fac87..02de37ef 100644 --- a/cockpit/render/repeat-loops/python/project.json +++ b/cockpit/render/repeat-loops/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/repeat-loops/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/spec-rendering/angular/project.json b/cockpit/render/spec-rendering/angular/project.json index 091b4e62..9e8f1688 100644 --- a/cockpit/render/spec-rendering/angular/project.json +++ b/cockpit/render/spec-rendering/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderSpecRenderingAngularModule } from './src/index.ts'; const module = renderSpecRenderingAngularModule; if (module.id !== 'render-spec-rendering-angular' || module.title !== 'Render Spec Rendering (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/spec-rendering/python/project.json b/cockpit/render/spec-rendering/python/project.json index 128040d3..845cb87d 100644 --- a/cockpit/render/spec-rendering/python/project.json +++ b/cockpit/render/spec-rendering/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/spec-rendering/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/state-management/angular/project.json b/cockpit/render/state-management/angular/project.json index 94eee30f..c6a7c93f 100644 --- a/cockpit/render/state-management/angular/project.json +++ b/cockpit/render/state-management/angular/project.json @@ -88,5 +88,9 @@ "command": "npx tsx -e \"import { renderStateManagementAngularModule } from './src/index.ts'; const module = renderStateManagementAngularModule; if (module.id !== 'render-state-management-angular' || module.title !== 'Render State Management (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\"" } } - } + }, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/cockpit/render/state-management/python/project.json b/cockpit/render/state-management/python/project.json index ddaa8b2d..eea1e95b 100644 --- a/cockpit/render/state-management/python/project.json +++ b/cockpit/render/state-management/python/project.json @@ -3,5 +3,9 @@ "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/render/state-management/python/src", "projectType": "library", - "targets": {} + "targets": {}, + "tags": [ + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/examples/chat/angular/project.json b/examples/chat/angular/project.json index 8505eb0c..5edac788 100644 --- a/examples/chat/angular/project.json +++ b/examples/chat/angular/project.json @@ -7,7 +7,9 @@ "targets": { "build": { "executor": "@angular/build:application", - "outputs": ["{options.outputPath.base}"], + "outputs": [ + "{options.outputPath.base}" + ], "options": { "outputPath": { "base": "dist/examples/chat/angular", @@ -17,30 +19,59 @@ "browser": "examples/chat/angular/src/main.ts", "tsConfig": "examples/chat/angular/tsconfig.app.json", "assets": [ - { "glob": "**/*", "input": "examples/chat/angular/public" } + { + "glob": "**/*", + "input": "examples/chat/angular/public" + } ], - "styles": ["examples/chat/angular/src/styles.css"] + "styles": [ + "examples/chat/angular/src/styles.css" + ] }, "configurations": { "production": { - "define": { "NGAF_CHAT_DEBUG": "false" }, - "externalDependencies": ["@ngaf/chat/debug"], + "define": { + "NGAF_CHAT_DEBUG": "false" + }, + "externalDependencies": [ + "@ngaf/chat/debug" + ], "budgets": [ - { "type": "initial", "maximumWarning": "500kb", "maximumError": "1.5mb" }, - { "type": "anyComponentStyle", "maximumWarning": "10kb", "maximumError": "16kb" } + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1.5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "10kb", + "maximumError": "16kb" + } ], "outputHashing": "all" }, "production-debug": { - "define": { "NGAF_CHAT_DEBUG": "true" }, + "define": { + "NGAF_CHAT_DEBUG": "true" + }, "budgets": [ - { "type": "initial", "maximumWarning": "500kb", "maximumError": "1.5mb" }, - { "type": "anyComponentStyle", "maximumWarning": "10kb", "maximumError": "16kb" } + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1.5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "10kb", + "maximumError": "16kb" + } ], "outputHashing": "all" }, "development": { - "define": { "NGAF_CHAT_DEBUG": "false" }, + "define": { + "NGAF_CHAT_DEBUG": "false" + }, "optimization": false, "extractLicenses": false, "sourceMap": true, @@ -61,8 +92,12 @@ "port": 4200 }, "configurations": { - "production": { "buildTarget": "examples-chat-angular:build:production" }, - "development": { "buildTarget": "examples-chat-angular:build:development" } + "production": { + "buildTarget": "examples-chat-angular:build:production" + }, + "development": { + "buildTarget": "examples-chat-angular:build:development" + } }, "defaultConfiguration": "development" }, @@ -96,5 +131,9 @@ } } }, - "tags": ["scope:examples", "type:app"] + "tags": [ + "scope:examples", + "scope:examples-chat", + "type:app" + ] } diff --git a/examples/chat/project.json b/examples/chat/project.json index 0deace76..4d88dfdf 100644 --- a/examples/chat/project.json +++ b/examples/chat/project.json @@ -20,5 +20,8 @@ "command": "npx nx run examples-chat-smoke:run" } } - } + }, + "tags": [ + "scope:examples-chat" + ] } diff --git a/examples/chat/python/project.json b/examples/chat/python/project.json index eb07b646..69594ba8 100644 --- a/examples/chat/python/project.json +++ b/examples/chat/python/project.json @@ -26,5 +26,8 @@ "command": "uv run pytest -q -m smoke" } } - } + }, + "tags": [ + "scope:examples-chat" + ] } diff --git a/examples/chat/smoke/project.json b/examples/chat/smoke/project.json index a5b57827..6f92d4b0 100644 --- a/examples/chat/smoke/project.json +++ b/examples/chat/smoke/project.json @@ -11,5 +11,8 @@ "command": "node cli.mjs" } } - } + }, + "tags": [ + "scope:examples-chat" + ] } diff --git a/libs/a2ui/project.json b/libs/a2ui/project.json index f1b0df10..0b51ffc5 100644 --- a/libs/a2ui/project.json +++ b/libs/a2ui/project.json @@ -4,7 +4,17 @@ "sourceRoot": "libs/a2ui/src", "projectType": "library", "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", "scope:shared", + "scope:website", + "scope:website-e2e", "type:lib" ], "targets": { diff --git a/libs/ag-ui/project.json b/libs/ag-ui/project.json index a3206883..ee184acd 100644 --- a/libs/ag-ui/project.json +++ b/libs/ag-ui/project.json @@ -4,7 +4,18 @@ "sourceRoot": "libs/ag-ui/src", "prefix": "lib", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", + "scope:website", + "scope:website-e2e" + ], "targets": { "build": { "executor": "@nx/angular:package", diff --git a/libs/chat/project.json b/libs/chat/project.json index 1b322f07..e04ed3cf 100644 --- a/libs/chat/project.json +++ b/libs/chat/project.json @@ -4,7 +4,18 @@ "sourceRoot": "libs/chat/src", "prefix": "chat", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", + "scope:website", + "scope:website-e2e" + ], "targets": { "build": { "executor": "@nx/angular:package", diff --git a/libs/cockpit-docs/project.json b/libs/cockpit-docs/project.json index d0cd10fa..67feff42 100644 --- a/libs/cockpit-docs/project.json +++ b/libs/cockpit-docs/project.json @@ -6,13 +6,23 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "dependsOn": ["^build"], - "outputs": ["{workspaceRoot}/dist/libs/cockpit-docs"], + "dependsOn": [ + "^build" + ], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-docs" + ], "options": { "outputPath": "dist/libs/cockpit-docs", "main": "libs/cockpit-docs/src/index.ts", "tsConfig": "libs/cockpit-docs/tsconfig.lib.json" } } - } + }, + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/libs/cockpit-registry/project.json b/libs/cockpit-registry/project.json index bc725a19..fb159c3d 100644 --- a/libs/cockpit-registry/project.json +++ b/libs/cockpit-registry/project.json @@ -3,11 +3,18 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/cockpit-registry/src", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ], "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/cockpit-registry"], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-registry" + ], "options": { "outputPath": "dist/libs/cockpit-registry", "main": "libs/cockpit-registry/src/index.ts", diff --git a/libs/cockpit-shell/project.json b/libs/cockpit-shell/project.json index 08903015..7084c409 100644 --- a/libs/cockpit-shell/project.json +++ b/libs/cockpit-shell/project.json @@ -6,12 +6,20 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/cockpit-shell"], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-shell" + ], "options": { "outputPath": "dist/libs/cockpit-shell", "main": "libs/cockpit-shell/src/index.ts", "tsConfig": "libs/cockpit-shell/tsconfig.lib.json" } } - } + }, + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/libs/cockpit-telemetry/project.json b/libs/cockpit-telemetry/project.json index 56670569..fafddaf3 100644 --- a/libs/cockpit-telemetry/project.json +++ b/libs/cockpit-telemetry/project.json @@ -4,11 +4,18 @@ "sourceRoot": "libs/cockpit-telemetry/src", "prefix": "lib", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ], "targets": { "build": { "executor": "@nx/angular:package", - "outputs": ["{workspaceRoot}/dist/libs/cockpit-telemetry"], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-telemetry" + ], "options": { "project": "libs/cockpit-telemetry/ng-package.json", "tsConfig": "libs/cockpit-telemetry/tsconfig.lib.json" diff --git a/libs/cockpit-testing/project.json b/libs/cockpit-testing/project.json index f845634d..162fb419 100644 --- a/libs/cockpit-testing/project.json +++ b/libs/cockpit-testing/project.json @@ -6,12 +6,20 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/cockpit-testing"], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-testing" + ], "options": { "outputPath": "dist/libs/cockpit-testing", "main": "libs/cockpit-testing/src/index.ts", "tsConfig": "libs/cockpit-testing/tsconfig.lib.json" } } - } + }, + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/libs/cockpit-ui/project.json b/libs/cockpit-ui/project.json index 352ce060..f8ace0e4 100644 --- a/libs/cockpit-ui/project.json +++ b/libs/cockpit-ui/project.json @@ -6,12 +6,20 @@ "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/cockpit-ui"], + "outputs": [ + "{workspaceRoot}/dist/libs/cockpit-ui" + ], "options": { "outputPath": "dist/libs/cockpit-ui", "main": "libs/cockpit-ui/src/index.ts", "tsConfig": "libs/cockpit-ui/tsconfig.lib.json" } } - } + }, + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ] } diff --git a/libs/design-tokens/project.json b/libs/design-tokens/project.json index f7956744..8316ca55 100644 --- a/libs/design-tokens/project.json +++ b/libs/design-tokens/project.json @@ -3,11 +3,20 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/design-tokens/src", "projectType": "library", - "tags": ["scope:shared", "type:lib"], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:shared", + "type:lib" + ], "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/design-tokens"], + "outputs": [ + "{workspaceRoot}/dist/libs/design-tokens" + ], "options": { "outputPath": "dist/libs/design-tokens", "main": "libs/design-tokens/src/index.ts", diff --git a/libs/e2e-harness/project.json b/libs/e2e-harness/project.json index 5d4e72ce..c9e4e01f 100644 --- a/libs/e2e-harness/project.json +++ b/libs/e2e-harness/project.json @@ -3,7 +3,13 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "library", "sourceRoot": "libs/e2e-harness/src", - "tags": ["scope:internal"], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:internal" + ], "targets": { "lint": { "executor": "nx:run-commands", diff --git a/libs/example-layouts/project.json b/libs/example-layouts/project.json index bd9c2114..c9bb0dd3 100644 --- a/libs/example-layouts/project.json +++ b/libs/example-layouts/project.json @@ -4,11 +4,18 @@ "sourceRoot": "libs/example-layouts/src", "prefix": "example", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples" + ], "targets": { "build": { "executor": "@nx/angular:package", - "outputs": ["{workspaceRoot}/dist/{projectRoot}"], + "outputs": [ + "{workspaceRoot}/dist/{projectRoot}" + ], "options": { "project": "libs/example-layouts/ng-package.json", "tsConfig": "libs/example-layouts/tsconfig.lib.json" diff --git a/libs/langgraph/project.json b/libs/langgraph/project.json index 0911998d..66d0d7e6 100644 --- a/libs/langgraph/project.json +++ b/libs/langgraph/project.json @@ -4,7 +4,18 @@ "sourceRoot": "libs/langgraph/src", "prefix": "lib", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", + "scope:website", + "scope:website-e2e" + ], "targets": { "build": { "executor": "@nx/angular:package", diff --git a/libs/licensing/project.json b/libs/licensing/project.json index c2fecc17..fe88b19c 100644 --- a/libs/licensing/project.json +++ b/libs/licensing/project.json @@ -4,7 +4,17 @@ "sourceRoot": "libs/licensing/src", "projectType": "library", "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", "scope:shared", + "scope:website", + "scope:website-e2e", "type:lib" ], "targets": { diff --git a/libs/render/project.json b/libs/render/project.json index c76ea21e..7a6c8eaa 100644 --- a/libs/render/project.json +++ b/libs/render/project.json @@ -4,7 +4,18 @@ "sourceRoot": "libs/render/src", "prefix": "render", "projectType": "library", - "tags": [], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", + "scope:website", + "scope:website-e2e" + ], "targets": { "build": { "executor": "@nx/angular:package", diff --git a/libs/telemetry/project.json b/libs/telemetry/project.json index 430e3c1f..21ca5ea5 100644 --- a/libs/telemetry/project.json +++ b/libs/telemetry/project.json @@ -3,11 +3,26 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/telemetry/src", "projectType": "library", - "tags": ["scope:shared", "type:lib"], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:cockpit-secret", + "scope:cockpit-smoke", + "scope:examples-chat", + "scope:library", + "scope:shared", + "scope:website", + "scope:website-e2e", + "type:lib" + ], "targets": { "build:node": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/telemetry"], + "outputs": [ + "{workspaceRoot}/dist/libs/telemetry" + ], "options": { "outputPath": "dist/libs/telemetry", "main": "libs/telemetry/src/index.ts", @@ -16,22 +31,34 @@ "libs/telemetry/src/node/postinstall.ts" ], "tsConfig": "libs/telemetry/tsconfig.lib.json", - "assets": ["libs/telemetry/README.md", "libs/telemetry/package.json"] + "assets": [ + "libs/telemetry/README.md", + "libs/telemetry/package.json" + ] } }, "build:browser": { "executor": "@nx/angular:package", - "outputs": ["{workspaceRoot}/dist/libs/telemetry/browser"], + "outputs": [ + "{workspaceRoot}/dist/libs/telemetry/browser" + ], "options": { "project": "libs/telemetry/ng-package.json", "tsConfig": "libs/telemetry/tsconfig.lib.browser.json" }, - "dependsOn": ["build:node"] + "dependsOn": [ + "build:node" + ] }, "build": { - "dependsOn": ["build:node", "build:browser"], + "dependsOn": [ + "build:node", + "build:browser" + ], "executor": "nx:run-commands", - "outputs": ["{workspaceRoot}/dist/libs/telemetry"], + "outputs": [ + "{workspaceRoot}/dist/libs/telemetry" + ], "options": { "command": "node libs/telemetry/scripts/assemble-dist.mjs" } @@ -47,6 +74,8 @@ "configFile": "libs/telemetry/vite.config.mts" } }, - "lint": { "executor": "@nx/eslint:lint" } + "lint": { + "executor": "@nx/eslint:lint" + } } } diff --git a/libs/ui-react/project.json b/libs/ui-react/project.json index 798ffbc0..f8471b01 100644 --- a/libs/ui-react/project.json +++ b/libs/ui-react/project.json @@ -3,11 +3,20 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "libs/ui-react/src", "projectType": "library", - "tags": ["scope:shared", "type:lib"], + "tags": [ + "scope:cockpit", + "scope:cockpit-deploy-smoke", + "scope:cockpit-e2e", + "scope:cockpit-examples", + "scope:shared", + "type:lib" + ], "targets": { "build": { "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/libs/ui-react"], + "outputs": [ + "{workspaceRoot}/dist/libs/ui-react" + ], "options": { "outputPath": "dist/libs/ui-react", "main": "libs/ui-react/src/index.ts", diff --git a/tools/posthog/project.json b/tools/posthog/project.json index 4f471c8f..721d2020 100644 --- a/tools/posthog/project.json +++ b/tools/posthog/project.json @@ -3,9 +3,15 @@ "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "tools/posthog", "projectType": "library", - "tags": ["scope:gtm", "type:tool"], + "tags": [ + "scope:gtm", + "scope:posthog", + "type:tool" + ], "namedInputs": { - "taxonomy": ["{workspaceRoot}/docs/gtm/taxonomy.md"] + "taxonomy": [ + "{workspaceRoot}/docs/gtm/taxonomy.md" + ] }, "targets": { "lint": { @@ -19,14 +25,20 @@ }, "sync:plan": { "executor": "nx:run-commands", - "inputs": ["default", "taxonomy"], + "inputs": [ + "default", + "taxonomy" + ], "options": { "command": "npx tsx tools/posthog/sync.ts --plan" } }, "sync:apply": { "executor": "nx:run-commands", - "inputs": ["default", "taxonomy"], + "inputs": [ + "default", + "taxonomy" + ], "options": { "command": "npx tsx tools/posthog/sync.ts --apply" } From 5f8e2bb6cee25336d825247fa5734695a45d2b7a Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 21:28:16 -0700 Subject: [PATCH 4/5] fix(ci-scope): drop stale //scripts/deploy-smoke.ts implicit dep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original spec listed this path as a fallback file (mirroring the OLD ci-scope.mjs's defensive `if (changedFile === 'apps/cockpit/scripts/ deploy-smoke.ts' || changedFile === 'scripts/deploy-smoke.ts')` check), but only the apps/cockpit/scripts/deploy-smoke.ts path exists on this repo today. The root-level path is dead — the OLD rule was overly defensive for a file that was never created at that path. The legitimate //apps/cockpit/scripts/deploy-smoke.ts entry remains. Caught by the existence check in Task 1 Step 4 of the migration plan. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/cockpit/project.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/cockpit/project.json b/apps/cockpit/project.json index 195d5c28..edddd7e9 100644 --- a/apps/cockpit/project.json +++ b/apps/cockpit/project.json @@ -222,7 +222,6 @@ "//scripts/assemble-demo.ts", "//scripts/assemble-examples.ts", "//scripts/demo-middleware.ts", - "//scripts/deploy-smoke.ts", "//scripts/generate-shared-deployment-config.ts", "//scripts/langgraph-proxy.ts", "//scripts/rate-limit.ts", From bc77c1059f7b131673a29b2ad3913ee5334c69d4 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Wed, 20 May 2026 21:39:50 -0700 Subject: [PATCH 5/5] =?UTF-8?q?fix(ci-scope):=20revert=20implicitDependenc?= =?UTF-8?q?ies=20=E2=80=94=20//path=20syntax=20broke=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR 1 added `implicitDependencies: ["//vercel.json", ...]` to apps/cockpit and apps/website. On Nx 22.5.1, this caused all cockpit-* CI gates to fail (29 failures: build/test, build-all-examples, 18 e2e matrix expansions, etc.). The `//path` syntax for file-level implicit deps either isn't recognized by Nx 22 or requires different escaping. Revert just the implicitDependencies. Keep the scope:* tags addition — those are inert metadata and use the existing tags pattern. PR 2's shim rewrite will need to either: (a) Restore implicitDependencies with the correct syntax once verified, or (b) Keep applyFallbackPathScope's file-by-file rules in ci-scope.mjs (shim ends up ~80 LOC instead of ~50). Either way, PR 1's scope-tags addition is still useful as data for the shim to read. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/cockpit/project.json | 15 +-------------- apps/website/project.json | 5 +---- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/apps/cockpit/project.json b/apps/cockpit/project.json index edddd7e9..a4ca73ff 100644 --- a/apps/cockpit/project.json +++ b/apps/cockpit/project.json @@ -215,18 +215,5 @@ "cwd": "." } } - }, - "implicitDependencies": [ - "//apps/cockpit/scripts/capability-registry.ts", - "//apps/cockpit/scripts/deploy-smoke.ts", - "//scripts/assemble-demo.ts", - "//scripts/assemble-examples.ts", - "//scripts/demo-middleware.ts", - "//scripts/generate-shared-deployment-config.ts", - "//scripts/langgraph-proxy.ts", - "//scripts/rate-limit.ts", - "//vercel.cockpit.json", - "//vercel.demo.json", - "//vercel.examples.json" - ] + } } diff --git a/apps/website/project.json b/apps/website/project.json index 28563cb7..6e1a140a 100644 --- a/apps/website/project.json +++ b/apps/website/project.json @@ -64,8 +64,5 @@ "config": "apps/website/playwright.config.ts" } } - }, - "implicitDependencies": [ - "//vercel.json" - ] + } }