cto/skills/cto-angular-toolkit/SKILL.md
Svrnty 3a3503aa2e feat(cto): close Python+Angular stack gaps + PROFILE-DISTRIBUTION-PROTOCOL compliance
AGENT.md: REQUIRED T2 frontmatter per §2.1.
manifest.yaml: REQUIRED governance: block per §2.2. Register new toolkit skills.
install.sh: chmod +x.

skills/cto-python-toolkit/SKILL.md (v0.1): closes Python stack gap inline.
References real workspace projects as exemplars: scripts/sot-precommit.py
(single-file CLI), bte-mcp/server.py + bte_core.py (FastMCP server),
svrnty-hermes-webui-plugin (PEP 621 + pytest.ini_options), curator/sweep.py
(mode flags + dry-run + stdlib-heavy). Sandcastle prompt template + post-
run quality-gate routing via PG-svrnty.lib-quality-gates.

skills/cto-angular-toolkit/SKILL.md (v0.1): closes Angular stack gap inline.
Anchored to adwright/adwright-console as canonical Plan B Angular reference
(Angular 21.2 + signals + standalone components + inject() + gRPC-web via
@protobuf-ts/grpcweb-transport + L6-svrnty.lib-cqrs-datasource). Sandcastle
prompt template + DESIGN.md compliance check for UI work.

CONTRACT.md §6: Python+Angular promoted from  generic → 🟡 skill-only
(no more "gap" marker). Documents path to  deep when cortex/ libs extract.
skills/cto-agent/SKILL.md: routing table updated — Python/Angular rows now
route to the toolkit skills instead of falling through to generic.

CLAUDE.md: site-map footer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 12:37:11 -04:00

205 lines
13 KiB
Markdown

---
name: cto-angular-toolkit
description: "Use when the user mentions 'Angular code task', 'fix bug in Angular repo', 'implement feature in Angular', 'refactor Angular', 'ng', 'ng build', 'ng test', 'angular signal', 'standalone component', 'gRPC-web', 'foblex flow', or the target stack identified by cto-agent is Angular. Encodes Plan B's Angular 21 patterns (signals + standalone components + gRPC-web to .NET CQRS), anchored to the real Adwright console codebase. Closes the Angular stack gap in cto-planb."
metadata:
version: 0.1.0
model: qwen-local/qwen3.6-35b-a3b
hermes:
requires_toolsets: [terminal, memory_tool]
tier: T2
status: active
owner: jp
source: hand
last_reviewed: 2026-05-24
---
# CTO Angular Toolkit — Sandcastle Prompt + Plan B Patterns
> **Scope:** routed-to by `cto-agent` when the target repo stack is Angular. Closes the Angular stack gap documented in `CONTRACT.md §6`. All patterns anchored to **the real Adwright console codebase** — the canonical Plan B Angular reference.
## When CTO routes here
Per `skills/cto-agent/SKILL.md` per-stack routing table — Angular row. Triggers:
- Target repo contains `angular.json`
- `package.json` declares `@angular/core` dependency
- Task description mentions Angular explicitly
- `cto-agent` analyze step detects Angular as primary stack
## The canonical Plan B Angular reference
[`adwright/adwright-console/`](../../../adwright/adwright-console/) — the Adwright console, a federated sibling workspace (registered in workspace CLAUDE.md). Read this codebase before any Angular sandcastle invocation. Pulled facts (verified 2026-05-24):
| Aspect | Value |
|---|---|
| Angular version | `^21.2.0` |
| Component style | **Standalone** (no NgModule) — `@Component({ imports: [...] })` |
| Reactivity | **Signals**`signal()`, `computed()`, `inject()` |
| Routing | `provideRouter(routes)` in `app.config.ts` |
| Styles | SCSS (`"inlineStyleLanguage": "scss"`, prefix `app`) |
| Build builder | `@angular/build:application` (modern Vite-based, not webpack) |
| Backend transport | **gRPC-web** via `@protobuf-ts/grpcweb-transport` → .NET CQRS via `L6-svrnty.lib-cqrs-datasource` |
| Generated types | `src/app/gen/<service>/<version>/*` from `.proto` files |
| Mock data | `src/app/core/mock-data.service.ts` — dev pattern; mocks signal-typed |
| Graph UI lib | `@foblex/flow`, `@foblex/2d`, `@foblex/mediator` (Adwright-specific — pattern not for every project) |
Key files to study (point sandcastle agent at these):
- [`adwright-console/src/app/app.config.ts`](../../../adwright/adwright-console/src/app/app.config.ts) — bootstrap pattern
- [`adwright-console/src/app/app.ts`](../../../adwright/adwright-console/src/app/app.ts) — standalone root component w/ signals
- [`adwright-console/src/app/core/cycles.service.ts`](../../../adwright/adwright-console/src/app/core/cycles.service.ts) — gRPC-web service w/ status-mapping (proto enums → friendly strings)
- [`adwright-console/src/app/core/connections.service.ts`](../../../adwright/adwright-console/src/app/core/connections.service.ts) — service injection pattern
- [`adwright-console/src/app/core/mock-data.service.ts`](../../../adwright/adwright-console/src/app/core/mock-data.service.ts) — mock-data signal pattern
- [`adwright-console/angular.json`](../../../adwright/adwright-console/angular.json) — schematics defaults (`skipTests: true` per project choice)
- [`adwright-console/proxy.conf.json`](../../../adwright/adwright-console/proxy.conf.json) — dev proxy to backend
## Tooling baseline (always include in sandcastle prompt)
| Tool | Purpose | Invocation | Required? | Notes from adwright |
|---|---|---|---|---|
| **npm** | Package manager | `npm install` / `npm ci` | YES (adwright uses npm@11.6.2; no pnpm/yarn migration) | Adwright pins `"packageManager": "npm@11.6.2"` in package.json |
| **ng build** | Production build | `ng build` | YES — must succeed before PR | Uses `@angular/build:application` builder |
| **ng test** | Unit test runner | `ng test --watch=false --browsers=ChromeHeadless` | If tests exist (adwright skips tests by schematic default — match repo choice) | Adwright has `skipTests: true` schematics — only add tests if explicitly scoped |
| **ng lint** | Linter (eslint) | `ng lint` | If eslint configured | Add it; default ng new projects don't include it |
| **ng serve** | Dev server | `ng serve` (CTO does NOT run this — sandboxed, no port forward) | NO in sandcastle context | Local dev only |
## Project layout pattern (per adwright-console)
```
<repo>/
├── angular.json # workspace config — schematics defaults, builder selection
├── package.json # pin Angular major; lock packageManager
├── tsconfig.json + tsconfig.app.json + tsconfig.spec.json
├── proxy.conf.json # dev proxy to backend (CQRS at :5001 or similar)
├── public/ # static assets (served as-is)
├── src/
│ ├── main.ts # bootstrapApplication(App, appConfig)
│ ├── index.html
│ ├── styles.scss # global styles
│ └── app/
│ ├── app.ts # standalone root component (signals)
│ ├── app.config.ts # provideRouter + provider list
│ ├── app.routes.ts # Routes array (lazy where appropriate)
│ ├── app.html / app.scss
│ ├── core/ # services — gRPC clients, mock data, models
│ │ ├── <name>.service.ts
│ │ └── models.ts # shared TS types (do NOT duplicate proto types here)
│ └── gen/ # generated proto types — DO NOT edit by hand
│ └── <pkg>/<ver>/*.ts
```
Sandcastle prompt MUST tell the agent:
- **Use signals + standalone** — no NgModule, no `OnInit` if `computed()` covers it
- **Inject via `inject()`**, not constructor injection (modern Angular 16+ pattern)
- **Map proto enums → friendly TS string-literal unions** in service layer (per `cycles.service.ts` `CycleStatus` pattern) — UI never sees `CYCLE_STATUS_*` constants
- **Mock-data services live in `core/mock-data.service.ts`** during dev; swap to real gRPC client when backend ready
- **Never hand-edit `src/app/gen/**`** — regenerate via proto pipeline (see `L6-svrnty.lib-cqrs-datasource` patterns)
- **Match existing schematics** — adwright defaults `skipTests: true`, prefix `app`, style `scss`. New components must match.
## Sandcastle invocation template (Angular)
```bash
SANDCASTLE_REPO="${SANDCASTLE_REPO:-$HOME/workspaces/hermes/sandcastle}"
cd "$SANDCASTLE_REPO"
npx tsx -e "
import { run, claudeCode } from '@ai-hero/sandcastle';
import { docker } from '@ai-hero/sandcastle/sandboxes/docker';
const result = await run({
agent: claudeCode('claude-opus-4-7'),
sandbox: docker({
image: 'node:22-slim',
setup: [
'apt-get update && apt-get install -y chromium-browser || true',
'cd /workspace && npm ci'
]
}),
promptFile: '${CTO_HOME}/work/${WORK_ID}/prompt.md',
cwd: '${TARGET_REPO}',
branchStrategy: { type: 'branch', branch: 'cto/${WORK_ID}' },
maxIterations: 5,
});
console.log(JSON.stringify({ commits: result.commits, branch: result.branch }, null, 2));
"
```
Prompt file (`prompt.md`) MUST instruct the sandboxed agent to:
1. Read `angular.json` — note builder (`@angular/build:application` for v17+; webpack-based for older), schematics defaults, prefix
2. Read `package.json` — note Angular major version, packageManager pin
3. Reference Adwright patterns at `adwright/adwright-console/src/app/{app.ts, core/cycles.service.ts}` for component + service shape
4. Make code changes per the brief — match existing standalone + signals + inject() patterns
5. Run `ng lint` if configured — must pass
6. Run `ng build` — must succeed (this catches type errors + template errors)
7. Run `ng test --watch=false --browsers=ChromeHeadless` if tests exist
8. Commit + report
## Post-run quality gates (mount `PG-svrnty.lib-quality-gates` if available)
After sandcastle completes, run quality gates against the diff:
```bash
QG=/home/svrnty/workspaces/cortex/PG-svrnty.lib-quality-gates
[ -d "$QG" ] && "$QG/bin/run-gates" --stack typescript --repo "${TARGET_REPO}" --branch "cto/${WORK_ID}"
```
The lib-quality-gates TypeScript/Angular suite (per `CORTEX-TOOLING.md`) covers:
- Strict TypeScript config validation (`strict: true`, `noImplicitAny`, etc.)
- Bundle size budgets (`angular.json → architect.build.options.budgets`)
- Accessibility lint (axe / Angular ESLint a11y rules)
- Dep vulns (`npm audit --production`)
If gates fail: re-prompt sandcastle with violations, max 3 iterations, then escalate to JP.
## DESIGN.md compliance (Angular UI + Stitch consumers)
Per `CONTRACT.md §7`, when CTO sandcastle-orchestrates Angular UI work AND the output feeds DESIGN.md-aware tools (Stitch, others), components MUST conform to the 8-property DESIGN.md subset: `backgroundColor`, `textColor`, `typography`, `rounded`, `padding`, `size`, `height`, `width`.
Workflow:
1. CTO ensures sandcastle agent emits components compatible with the 8-property contract
2. Post-build, run `pi-bte-plugin design-md-exporter` to emit `BRAND-DESIGN.md` from the brand's DTCG token set
3. Validate: `npx --yes @google/design.md@latest lint BRAND-DESIGN.md`
4. If validation fails: re-prompt sandcastle with the lint error + the 8-property contract
Concrete adwright opportunity: the `@foblex/flow` graph nodes could expose their styles via DESIGN.md tokens (currently they don't — that's a future task scope, not implicit in every Adwright task).
## Worked example — add a route to adwright-console
Task: "Add a `/profiles` route that lists Hermes profile distributions (from svrnty-hermes-webui-plugin's `/api/profile-catalog`)."
Sandcastle prompt would:
1. Mount `/home/svrnty/workspaces/hermes/adwright/adwright-console/` as cwd
2. Reference `src/app/core/cycles.service.ts` as the gRPC-service pattern — but note: this new endpoint is REST/JSON (svrnty-plugin route), not gRPC. So use `HttpClient` (`provideHttpClient()` added to `app.config.ts`) instead of gRPC-web transport.
3. Generate component: `ng generate component features/profiles --standalone --skip-tests` (matches adwright defaults)
4. Add route in `app.routes.ts`: `{ path: 'profiles', loadComponent: () => import('./features/profiles/profiles').then(m => m.Profiles) }`
5. Inject `HttpClient` via `inject(HttpClient)`; signal-typed response
6. Run `ng build`
7. Commit on branch `cto/<work-id>`, open PR
## Anti-patterns (sandcastle prompt MUST forbid)
- NgModule (`@NgModule({...})`) — use standalone components only (Angular 21+ pattern)
- Constructor injection (`constructor(private svc: MyService)`) — use `inject()`
- `BehaviorSubject` for UI state — use `signal()` (RxJS only for stream-of-events, not state)
- Hand-editing `src/app/gen/**` — regenerate from proto sources
- Adding tests when repo schematics declare `skipTests: true` — match the repo choice; only add tests if task scope explicit
- Bumping Angular major version (e.g., 21 → 22) without JP approval — major framework bumps = JP decision
- Adding heavy UI libs (Material/PrimeNG/etc.) without explicit task scope — Adwright is intentionally lean
- Editing `proxy.conf.json` to bypass auth or hit prod backends — dev-only file
- `<div [innerHTML]="..."` without `DomSanitizer` — XSS risk
## Stack gap roadmap
This skill closes the Angular gap inline. The future path:
1. v0.x: this inline-pattern skill (CURRENT) — anchored to adwright-console
2. v1.x: extract canonical patterns into `cortex/L6-svrnty.lib-angular-framework` (deferred — adwright is the only Angular consumer today)
3. When extracted: replace skill body with `mount cortex/L6-svrnty.lib-angular-framework`
Until then, **adwright-console is the framework reference**. If adwright pattern conflicts with this doc, adwright wins — update this doc to match.
## Related
- [`../cto-agent/SKILL.md`](../cto-agent/SKILL.md) — orchestrator that routes here
- [`../../CONTRACT.md`](../../CONTRACT.md) §6 — Angular stack disclosure (gap status); §7 — DESIGN.md compliance
- [`../../../sot/06-REGISTRY/CORTEX-TOOLING.md`](../../../sot/06-REGISTRY/CORTEX-TOOLING.md) — confirms Angular has no dedicated cortex/ lib (yet)
- [`../../../sot/03-PROTOCOLS/ADWRIGHT-PANEL-PRD.md`](../../../sot/03-PROTOCOLS/ADWRIGHT-PANEL-PRD.md) — Adwright panel inside hermes-webui PRD
- [`../../../sot/03-PROTOCOLS/ADWRIGHT-CREATIVE-EXTRACTION-PRD.md`](../../../sot/03-PROTOCOLS/ADWRIGHT-CREATIVE-EXTRACTION-PRD.md) — Adwright creative extraction PRD
- [`../cto-python-toolkit/SKILL.md`](../cto-python-toolkit/SKILL.md) — sibling skill closing Python gap
- [`../../../adwright/adwright-console/`](../../../adwright/adwright-console/) — the canonical Plan B Angular reference codebase