| name | sdcorejs-angular |
| description | Angular Core UI portal executor for confirmed frontend implementation. Use for new SDCoreJS portal init, existing @sdcorejs/angular or @sd-angular/core portals, PO/BA mock-first portal prototypes, admin modules, CRUD entities, list/detail screens, forms/validators, approval/bulk/export actions, or approved Core UI migration. Do not use for plain Angular apps without Core UI. Runtime-localized. |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash, TodoWrite |
07 — Write Code (Orchestrator)
Shared Protocols
Before executing this skill:
- Read and apply
_refs/shared/tasklist.md for non-trivial execution tasks.
- Read and apply
_refs/shared/persona.md if a project persona exists.
- Read and apply
_refs/shared/project-context.md for project memory, resume checkpoints, summaries, specs/plans, tasks, and relevant memories.
- Current user request, current files, diffs, logs, failing tests, and command output override stored context.
- Before presenting user-facing choices, approval gates, yes/no questions, or mode selections, read and apply
_refs/shared/user-choice-prompt.md so options are presented as sequential numbered choices.
Purpose
Single entry point for generating SDCoreJS/Core UI Angular portal code.
Transforms an approved plan from sdcorejs-execute-plan plus the user's
confirmed Core UI portal scope into complete CRUD entity code:
- Model (DTO, SaveReq, validators)
- Service (mock-first CRUD via centralized seed data file)
- Routes (lazy-loaded)
- Components (List, Detail)
- Workflow / bulk / custom action buttons
Eligibility preflight
Before reading any Angular write-code reference or generating files, classify the
target project using the same vocabulary as sdcorejs-execute-plan:
| Classification | This skill behavior |
|---|
core-ui-angular | Continue. Use @sdcorejs/angular imports and fetch version-matched Core UI docs with --require-installed. |
legacy-core-ui-angular | Continue. Preserve @sd-angular/core imports and fetch version-matched Core UI docs with --require-installed. |
| New SDCoreJS portal creation | Continue through init-portal.md; use _refs/angular/core-version.md as the install/version source before the package exists. |
Approved migration-request | Continue only when the approved spec/plan explicitly includes installing or migrating to SDCoreJS Core UI. |
plain-angular | Stop and return to sdcorejs-execute-plan generic harness. Do not fetch Core UI docs, import Core UI APIs, emit Core UI summaries, force admin screens, or assume src/libs/**/features/**. |
Plain Angular includes existing Angular apps that have angular.json,
@angular/core, components, routes, Angular Material, Bootstrap, PrimeNG,
Tailwind, or local/shared components, but do not depend on either
@sdcorejs/angular or @sd-angular/core. For those projects, reuse existing
local/shared/design-system components first and use installed UI libraries only
when they are already direct dependencies. Ask for explicit approval before
adding @sdcorejs/angular, @sd-angular/core, or @angular/material.
It also supports PO-friendly UI prototyping from PRDs plus mock API contracts:
after the Core UI starter template exists, generate navigable screens and
mock-first services that match the provided endpoint/request/response shape
closely enough for PO/QC users to interact with the feature before a live
backend is available.
For normal feature implementation from PRDs, user stories, or acceptance
criteria, a design handoff is the preferred UI source of truth. If no matching
design/specs/ or design/wireframes/ artifact exists and the user did not
explicitly request a quick PO/BA prototype with no design, route the work to
sdcorejs-design first so layout, states, copy, and visual direction are
settled before code generation.
PO/BA Prototype Portal Mode
Use PO/BA Prototype Portal Mode when the request asks to initialize a PO/BA
portal demo, generate a portal prototype from PRD/user story/AC text, work
without API/backend/design, align module/screens with a client, convert PRD to UI
prototype, or build a mock-first portal. This mode is PRD-first and demo-first:
infer safe prototype fields, seed believable data, keep services mock-first, and
let PO/BA users navigate routes/sidebar/menu locally before the real backend
exists.
Template-first is mandatory. A PO/BA prototype is not a freeform app design:
- New portal: run
init-portal.md first, preserve the Core UI starter template
shell/layout/sidebar/permission bootstrap, then add admin/module/entity
screens inside that baseline.
- Existing portal: extend the existing Core UI portal shell, routes, menu, and
local component conventions instead of creating a second shell.
- Do not create a parallel custom portal shell, bespoke navigation system,
landing/dashboard layout, or hand-built list/detail/form primitives when the
Core UI starter template and screen refs already cover the need.
This skill is an orchestrator: it does NOT inline the full generation rules for every file type. It picks the right reference pack for each scope item and reads it on demand. The detailed rules + code-template links for each concern live in _refs/angular/write-code/*.md (formerly the 10/11/12/20/21/31 sub-skills — consolidated here so the track exposes one skill instead of seven).
Dispatch table
For each scope item in the approved plan (or a direct request whose requirements are already confirmed), READ the matching reference pack and follow it:
Read ON DEMAND only — load the one reference for the step you are executing, not all of them. Each reference further links to the literal code templates under _refs/angular/templates/.
Step 0 — Pre-flight: ensure project summary
Before dispatching ANY reference, run sdcorejs-explore (summary-read). If <target>/.sdcorejs/summary.md is missing or stale in this write-approved executor, run summary-refresh first so generation uses the real project map and does not hallucinate paths or duplicate shared abstractions. If the summary exists and is fresh enough, read it and continue. Exception: when this run is itself a brand-new init-portal, there is no project to summarize yet; run summary-refresh at the END of init instead (see init-portal.md Post-init).
For any UI-affecting request, and always when the input includes a screenshot,
wireframe, mockup, Figma export, PRD, requirement document, user story, feature
description, acceptance criteria, mock API, OpenAPI/Swagger file, Postman
collection, MSW handler, endpoint table, JSON fixture, schema, or sample cURL,
read _refs/angular/write-code/input-analysis.md before choosing components,
services, or templates. For eligible Core UI projects this preflight owns Core
UI docs registry resolution; for plain Angular projects the work should already
have been routed away from this skill. It also owns project-local reuse
scanning, image decomposition, PRD requirement mapping, mixed image+PRD mapping,
API/service assumptions, and the mandatory post-implementation UI check. Present
the required reuse analysis/mapping before implementation.
If the request is PO/BA Prototype Portal Mode, also read
_refs/angular/write-code/po-ba-prototype.md immediately after
input-analysis.md. Missing API/backend/design is not a blocker in this mode.
Ask only for a truly unsafe missing project/module/entity name; otherwise infer
fields, screens, routes, validators, actions, and mock data from the PRD/user
story/AC/business description. Enforce the template-first invariant from that
reference before code generation: new portals start from the Core UI starter
template, and existing portals are modified in place.
If a mock API, OpenAPI/Swagger file, Postman collection, MSW handler, mock
endpoint list, JSON fixture, API schema, or sample cURL drives the UI, also read
_refs/angular/write-code/mock-api-input.md before building EntitySchema or
writing models/services. The PRD/acceptance criteria are the behavior source of
truth; the API artifact is the data/endpoint contract. A mock API document alone
does not authorize live API integration.
Before generating or extending any model, interface, type, service, store, repository, or API client, also run the entity reuse preflight in _refs/angular/write-code/reuse-existing-entities.md. This is mandatory for API docs, mock API contracts, PRDs, Figma/image/screenshot input, business descriptions, schemas, and any feature with related entities. The codebase is the source of truth for reuse decisions; the external artifact is only the new contract.
Before writing any helper, formatter, validator, mapper, pipe utility, paging/filter helper, random-id helper, query-param helper, upload/download helper, or clipboard/browser helper, read _refs/shared/sdcorejs-utils.md and reuse @sdcorejs/utils when it covers the behavior. The package must be a direct target-project dependency before generated code imports it; do not rely on Core UI's transitive dependencies.
Execution order + hand-off
Execution order: portal → admin-screens → module → entity → screens → actions. portal means the init-portal Core UI starter template baseline, not a custom shell. admin-screens ALWAYS runs after init-portal and before any domain module work. If the plan touches multiple items, run them in this order; do not parallelize. After all referenced steps finish, hand off as follows:
PO/BA prototype flow: input-analysis -> po-ba-prototype -> init-portal if needed -> admin-screens -> init-module -> init-entity -> screen-list/screen-detail/actions -> finish gate.
MANDATORY: Core UI usage summary (show the user right after generating)
Right after the code is written and BEFORE the finish gate, emit a short table of every @sdcorejs/angular Core UI piece the feature actually uses — component, service, or directive — each with a one-line purpose tied to this feature, in the user's language. This gives the user (especially non-tech) a plain-language overview of the building blocks. Build the rows from what you ACTUALLY imported/used — never list a component you didn't use, never describe it generically.
Core UI used in <feature name>:
| Core UI | Role in this feature |
|---|---|
| `SdTable` | Shows the <entity> list with pagination, filtering, and sorting |
| `SdNotifyService` | Shows success/error feedback when saving or deleting |
| `SdSection` | Groups fields on the detail screen |
| `sd-button` | Renders save, cancel, and action buttons |
Keep each purpose one line, concrete to the feature (not "a table component"). Persona-aware: plain wording for non-tech. The same table is persisted into the module's user guide at the write-user-guide step.
This summary is Core UI only. If the target project is classified as
plain-angular, this skill must not run and no Core UI usage summary is
emitted.
For UI-affecting work, the final response must also include the concise
sections named Core reuse summary and UI check, as defined in
_refs/angular/write-code/input-analysis.md.
MANDATORY FINISH GATE (always — standalone trigger OR full SDLC flow)
STOP and present the consolidated finish gate from _refs/shared/finish-gate.md before running ANY tail step. This is UNCONDITIONAL: it fires even when this skill was triggered directly for a one-line request (e.g. "add entity", "create module X") — NOT only inside the spec→plan flow. The gate surfaces tests / user-guide / technical-doc / review choices with defaults so the user always knows these steps exist and can opt out of new user/technical docs. "Small change" is not a reason to skip the gate. Read the ref for the exact prompt + rules.
Then run the tail in this order, honoring the gate's answers (skip = omit
that step; everything not skipped runs):
Documentation supplement: immediately after the Finish Gate test decision, run
sdcorejs-documentation (documentation-gate mode) and read
_refs/documentation/gate.md. This gate asks or loads saved project
preferences from <target>/.sdcorejs/documentation/preferences.md for
user-guide and technical-doc only. It must ask before
creating a missing corresponding user-guide or technical-doc for a new feature.
code-documentation is automatic for touched source files and is not controlled
by this approval gate.
- (if Tests not skipped)
sdcorejs-test (sdcorejs-test) - RUN the .spec.ts files already written RED-first during the TDD gate and report pass/fail + failing names; add happy-path e2e only when a dev server/browser is available (else report the exact local command). Unit specs are NOT optional unless the user chose skip in the gate; if any testable file still lacks one, write it here.
- (if Review not skipped)
sdcorejs-review (skills/shared/workflow/review.md; auto-detects Angular and loads _refs/angular/review-code.md) - convention check; actionable Angular code-review table with severity, group, file/line, risk, fix, and gate
- (if Review not skipped)
sdcorejs-repair-loop - apply findings, iterate until BLOCKER/REQUIRED findings are fixed or explicitly deferred
sdcorejs-documentation (code-documentation mode) - automatically apply concise source-code documentation rules to touched source files. Do NOT ASK for approval. Cross-track baseline + per-track addenda live in _refs/documentation/code-documentation.md
- (if UI-affecting) Angular UI check from
_refs/angular/write-code/input-analysis.md - run browser/preview verification when available; otherwise perform and report a code-level UI review. Fix obvious UI issues before continuing. If this changes code, rerun the smallest relevant check.
sdcorejs-product (when user-visible feature traceability is needed) - seed/update .sdcorejs/docs/product/ with requirement, implementation, and test mapping
- (if Technical doc approved)
sdcorejs-documentation (write-technical-doc mode) - create/update the approved technical doc from source evidence.
_refs/orchestration/tail/auto-docs.md (always) - session summary written to <target>/.sdcorejs/docs/angular/
- (if User guide approved)
sdcorejs-documentation (write-user-guide mode) - create/update the touched module's .sdcorejs/documentation/user-guides/<module>.md only when approved by the documentation gate or explicitly requested. Per-module incremental; the aggregate rebuilds under .sdcorejs/documentation/ at ship.
_refs/orchestration/tail/auto-task-tracker.md (always) - tick [x] completed tasks, append new ones from the doc's "Next suggested action" / "Open questions"
sdcorejs-explore (memories mode) - only if durable knowledge surfaced (recurring convention, stakeholder constraint, anti-pattern)
sdcorejs-ship (verify-before-done mode) (always) - BLOCK "done" until acceptance criteria from the selected scope are verified or explicitly deferred
sdcorejs-ship (branch-ready mode) (always) - final read-only branch-ready gate over the final diff before any Git artifact handoff. No writes after branch-ready unless branch-ready is run again.
The FINISH GATE itself is mandatory and unconditional. The always-on plumbing steps (auto-docs tail ref, auto-task-tracker tail ref, memories, sdcorejs-ship (verify-before-done mode), and final sdcorejs-ship (branch-ready mode)) run regardless of gate answers. Do NOT skip sdcorejs-ship (verify-before-done mode); that is how acceptance criteria silently slip.
When to Use
When user requests a new entity in a module, or any of the dispatch-table scope items:
- "Generate product CRUD in sample module" → init-entity
- "Initialize portal-shop with dev/qc/uat/prod" → init-portal
- "Create catalog module" → init-module
- "Create user list screen" → screen-list
- "Add validator to product form" → screen-detail
- "Add an approval button for orders" → actions
- "Initialize PO/BA portal demo from this PRD with no API/backend/design" -> po-ba-prototype
- "Generate a mock-first portal prototype from PRD so BA can align screens with client" -> po-ba-prototype
- "Create module-only contract-management prototype in the existing portal, no API/backend" -> po-ba-prototype
Input Resolution
PO/BA Prototype Portal Mode input resolution
For PRD-only/no API/no backend/no design prototype requests:
- Do not block on missing API artifacts, wireframes, exact field lists, endpoint
URLs, auth setup, or permission data.
- Infer module, primary entity, related entities/lookups, list columns, filters,
form fields, detail facts, validators, and workflow actions from the PRD, user
story, acceptance criteria, business description, and local project
conventions.
- Ask only for unsafe unknowns that cannot be inferred, such as a new portal
project name or an ambiguous module/entity owner.
- Record inferred details under
Prototype assumptions and present the required
PO/BA Prototype Plan block from
_refs/angular/write-code/po-ba-prototype.md before writing code.
- Run
reuse-existing-entities.md before creating or extending any entity
contract, even in mock-first prototype mode.
Before generating an entity, clarify with user:
-
Module: Which module does this entity belong to? (module)
- If missing: Ask which existing module, or propose creating new module first (init-module)
-
Entity Name: What's the entity name? (entity, entityPascal)
- Examples: product, employee, purchase-order, sales-invoice
-
Display Label: What label should appear in UI? (entityLabel, entityLabelPlural)
- Examples: "Product", "Employee", "Purchase Order"
-
Fields: What fields should this entity have?
- Ask user to describe fields OR infer a semantic schema when fields are omitted
- For each field: name, type (string/number/date/select/etc), required?, label
- When inferring, derive concrete domain fields from the entity meaning and current portal conventions
- For localized portals, all generated labels must use proper diacritics
- UI Preferences:
- Detail layout: auto-select side-drawer or full-page from inferred complexity, unless user overrides
- Has search? filter? delete? excel? (defaults: yes/yes/yes/no). If asking,
ask each toggle sequentially with
_refs/shared/user-choice-prompt.md
using 1. Yes / 2. No.
- Permissions: use default naming pattern or custom? (default: {{ MODULE }}_{{ ENTITY }}_CREATE, etc.)
Semantic Inference Fallback
If the user gives only the entity name or only a very vague description, do not stop at a generic skeleton. Build a first-pass EntitySchema from the entity semantics.
Use this inference order:
- Entity noun meaning in localized/English
- Existing portal conventions already confirmed in this repository
- Common business fields for that entity class
- Safe defaults for status/audit/search fields
Inference rules:
- Always infer an identity pair:
code + name or title
- Add one or more classification fields when the entity naturally belongs to a type/category/group
- Add amount/date/status fields when the entity semantics imply pricing, lifecycle, scheduling, or accounting
- Add note/description only when the entity likely needs free-text explanation
- Add attachment/upload only when the entity likely carries documents/images/files
- Separate writable fields (
SaveReq) from read-only/detail fields (DTO) such as approval status, created info, updated info, or derived totals
- Produce at least 3 meaningful list columns when the entity supports them
- Keep the inferred schema small enough to stay maintainable, but rich enough to render a believable business screen
- If the inferred form fits one compact business section, use
side-drawer; otherwise use full page
Generation Process
TDD Gate — mandatory before each code-generating step (NEVER skipped, NEVER gated behind a question)
Tests are a REQUIRED deliverable of this skill, not an optional add-on. Every code-gen run MUST leave a runnable .spec.ts next to every testable production file. Do NOT ask the user whether to write tests, and do NOT ask which coverage level first — default to standard coverage and proceed. Only switch to minimal / full if the user EXPLICITLY requested a different level (e.g. in sdcorejs-brainstorming). A missing spec is a generation defect, not a style choice. RED-first is the DEFAULT ordering (not post-hoc).
Before writing any production file (model / service / list / detail), invoke sdcorejs-test (tdd mode):
- Write the failing
.spec.ts for that chunk first → run test → confirm RED
- Generate the production file with minimal passing code → run test → confirm GREEN
- Refactor if needed → run test → confirm still GREEN
Applies to: model (validators / type contracts), service (CRUD method contracts), list component (rendering + actions), detail component (form + state transitions). Default standard coverage = should create + route-permission + list data/sort + detail save-flow/state, per _refs/angular/templates/entity-tests.md.
Skip RED-first for: mock-data seed rows (pure data, no testable logic) and routes.ts (Angular config — but its *.routes.spec.ts permission-validation spec IS still written).
Step 1: Build EntitySchema (shared input for every reference)
From user input, product docs, design handoff, mock API contracts, or semantic inference, construct EntitySchema with all field metadata. The schema is the single input every reference pack consumes — init-entity, screen-list, and screen-detail all read these names + field flags (visibleInList, visibleInDetail, type, required, permission codes).
Before building the schema from visual or requirement input, complete the
_refs/angular/write-code/input-analysis.md planning output. Use the PRD or
acceptance criteria as the behavior source of truth, visual input as layout
direction, and Core UI/local project conventions as implementation primitives.
For PO/BA Prototype Portal Mode, complete
_refs/angular/write-code/po-ba-prototype.md before finalizing EntitySchema.
Use PRD/user story/AC first, then existing conventions, domain semantics, Core UI
patterns, and safe prototype defaults. Keep Service contracts separate as DTO,
ListRes, DetailRes, CreateReq, UpdateReq, SaveReq, and Component ViewModel when
the prototype needs different read/write/UI shapes.
Before building the schema from mock API/API-contract input, complete
_refs/angular/write-code/mock-api-input.md. Use its endpoint inventory and
contract mapping to decide request types, response DTOs, validators, list
columns, detail read-only fields, lookup relations, custom actions, and mock
service behavior.
If a matching design/specs/ or design/wireframes/ handoff exists, read it before generating UI. Follow its screen/state/copy contract unless it conflicts with approved product criteria; if it conflicts, stop and surface the mismatch instead of silently choosing one.
If the input is a PRD, user story, acceptance criteria, or product description
for normal implementation and no matching design handoff exists, do not invent a
new visual direction inside sdcorejs-angular. Stop and route to
sdcorejs-design first, unless the approved plan or current user request
explicitly says this is PO/BA Prototype Portal Mode, mock-first client
alignment, or no-design prototype work.
Before finalizing EntitySchema, identify the primary entity and every related entity, scan existing model/interface/type/dto/service/api/repository/store files, and record one decision per entity: reuse, extend, or create new. Relationship fields must point to existing imported types or minimal summary types when those contracts exist; use <entity>Id when the API only returns an id. Do not inline a related entity object or create a duplicate model/service after an existing contract is found.
Before outputting code, present a short reuse summary: existing model/service found, imports to reuse, files to extend, files to create, and why duplicate contracts are not being created.
For two worked examples (user-supplied Product fields + inferred Promotion schema), see _refs/angular/templates/orchestrator-step-examples.md#step-1--build-entityschema.
Step 2: Generate per the dispatched reference
Once the EntitySchema exists, generate each file by following the reference pack the dispatch table routed you to. The per-file rules, decision heuristics, and code-template links all live in the reference — do NOT re-derive them here. Map:
For a full new entity, read init-entity.md end-to-end (it covers model → service → routes → list → detail in one pass). For a single-file refinement on an existing entity, read just the screen-list / screen-detail / actions reference.
Code Generation Rules
These cross-cutting rules apply regardless of which reference is dispatched.
1. File Naming & Paths
src/libs/{{ module }}/features/{{ entityKebab }}/
├── services/
│ ├── {{ entityKebab }}.model.ts (DTO, SaveReq, constants)
│ ├── {{ entityKebab }}.mock-data.ts (20–40 domain-realistic seed rows)
│ ├── {{ entityKebab }}.service.ts (mock-first CRUD via MockCrudStore)
│ └── index.ts (exports)
├── pages/
│ ├── list/
│ │ └── list.component.ts (CRUD list with SdTable)
│ └── detail/
│ └── detail.component.ts (CRUD form, CREATE/UPDATE/DETAIL)
└── {{ entityKebab }}.routes.ts (lazy-loaded routes)
2. Naming Conventions
- entity (kebab-case): product, purchase-order
- entityPascal (PascalCase): Product, PurchaseOrder
- entityCamel (camelCase): product, purchaseOrder
- entityConstant (CONSTANT_CASE): PRODUCT, PURCHASE_ORDER
Apply to:
- Service class name:
{{ entityPascal }}Service
- Service injection token:
#{{ entityCamel }}Service
- Model types:
{{ entityPascal }}DTO, {{ entityPascal }}SaveReq
- Component selector:
{{ entityKebab }}-list, {{ entityKebab }}-detail
- Route paths:
/{{ entity }}, /{{ entity }}/create
2a. Existing entity/service reuse
- Search the target codebase for related entity contracts before creating new model/service/type files.
- Import and reuse existing related entity models, DTOs, summary types, options, and services.
- Extend an existing model/service minimally when it lacks the field/method required by the new feature.
- Preserve compatibility: prefer optional additions, do not rename existing fields without checking usages, and do not change a contract used by another feature without evidence.
- Create a new model/service only after the reuse search confirms no suitable existing contract and the folder/naming convention is clear.
- Never inline a full related entity object inside another model when a related entity type already exists.
3. TypeScript Strict Mode
- Use non-null assertions (!) only when 100% certain
- Avoid
any type - use proper generics
- Inject services with
readonly #service = inject(...)
- Use
@ViewChild with proper typing
4. Change Detection & Template Binding Discipline
- Every generated component uses
changeDetection: ChangeDetectionStrategy.OnPush; import ChangeDetectionStrategy from @angular/core.
- UI state is signal-first: use
signal() for mutable state and computed() for derived display values, permissions, titles, disabled states, counts, labels, colors, and visibility flags.
- Do not call component methods or getters from interpolation, property bindings, class/style bindings, or structural conditions to compute displayed/derived values. Precompute them in
computed(), a signal, a pure pipe, or a typed view model.
- Allowed template calls are event handlers such as
(click)="onSave()", Angular signal reads such as state(), and pure-pipe transforms. If a signal is read 2+ times, use @let or a computed().
5. Service Contract, API Mapping & View Models
- Treat Service input/output models (
SaveReq, CreateReq, UpdateReq, DTO, ListRes, DetailRes) as the public contract between Service and Component, not as a required 1:1 copy of the raw backend API contract.
- A Service may map, normalize, derive, add, rename, or omit fields at the boundary, but every public Service model field must be accepted, processed, returned, or guaranteed by that Service/mapper.
- When the raw backend shape differs, define internal raw API types near the Service/mapper (for example
ProductApiRes) and map them into the public Service DTO before exposing data to components.
- Components must not mutate or extend Service DTOs with UI-only fields such as
label, displayName, checked, selected, disabled, expanded, children, color, or icon.
- If UI needs extra fields, either the Service mapper owns and documents them as part of the Service contract, or the Component defines a local
ViewModel/RowVM/TreeNodeVM and maps DTO -> VM.
- During generation and review, label ambiguous fields by layer:
BE API field, Service input/output field, Component ViewModel field, or UI state field.
6. Form Validation
- Use
Validators.required for required fields
- Use
Validators.maxLength(n) for string length
- Use
Validators.min(n), Validators.max(n) for numbers
- Use
Validators.pattern(regex) for patterns
- Custom validators for complex rules (see
screen-detail.md form refinement)
6a. Parent Detail-Scoped Child CRUD
- When a parent DETAIL screen contains child collections/tabs/tables (for example
Customer Detail -> Orders), independent child create/edit/view/delete actions stay inside the parent DETAIL screen.
- Use a modal or side-drawer for child create/edit/view flows. Do not navigate from the parent detail to child routes such as
/orders/create, /orders/:id/edit, or /orders/:id.
- Show child CRUD actions only in the parent DETAIL state. Hide them in parent CREATE because there is no persisted parent id, and hide them in parent UPDATE because the parent form may contain unsaved dirty state.
- Place child create/edit actions near the child collection: tab header, section toolbar, table toolbar, or empty-state CTA. Do not add a separate action row when a toolbar/header already exists.
- Child forms receive the current parent id from the loaded parent detail record, prefill/lock the foreign key (for example
customerId), and must not ask the user to select the same parent again.
- After child create/update/delete succeeds, close the modal/drawer, refresh only the child collection, and preserve the parent DETAIL route plus active tab/section.
- Gate child actions with child-entity permissions (for example
ORDER_CREATE for creating an Order inside Customer Detail), not parent entity permissions.
- Use inline
FormArray in CREATE/UPDATE only when child rows are saved in the same parent payload. Independent child CRUD waits until the parent exists and runs from DETAIL.
7. Error Handling
- Wrap service calls in try-catch
- Call
SdLoadingService.show()/hide() around async operations
- Call
SdNotifyService.error/success/warning/info() for feedback
- Handle cancel/back cases gracefully
- Revert form state if save fails
8. Code Documentation
- Automatically apply
sdcorejs-documentation (code-documentation mode) for public method/API contracts and complex implementation logic (e.g., form state transitions) whenever this skill creates or modifies source code. Do not ask for approval before adding or updating source-code documentation.
- Document special cases (e.g., bulk updates)
- Keep comments concise and up-to-date
Rules
MUST DO
- Show a live progress checklist with TodoWrite from the START of generation — one checkbox item per planned unit (each file / screen / entity / pack step) PLUS the finishing steps (tests, review, code-documentation, technical-doc, user-guide). Keep exactly one item
in_progress; flip it to completed the moment that unit is done and start the next. Update after EACH task, never batch at the end — this is how the user tracks progress. Create it before writing the first file.
- Run the entity reuse preflight before generating model/service/entity code; identify primary + related entities, scan existing model/interface/type/dto/service/api/repository/store files, and decide reuse/extend/create new before writing code.
- Run
_refs/angular/write-code/input-analysis.md before UI-affecting work, image/screenshot/Figma input, PRDs, user stories, feature descriptions, or acceptance criteria. Produce the SDCoreJS Core reuse analysis and the matching UI decomposition, requirement mapping, or image+PRD mapping before implementation.
- Run
_refs/angular/write-code/po-ba-prototype.md for PO/BA portal demo, PRD-to-UI prototype, no API/backend/design, module/screens-for-client-alignment, or mock-first portal requests. Emit the required PO/BA Prototype Plan, keep services mock-first, and record Prototype assumptions before code generation.
- Enforce template-first PO/BA prototype generation: new portal prototypes run
init-portal.md and preserve the Core UI starter template before domain work; existing portal prototypes extend the existing Core UI shell/routes/menu. Do not create a parallel custom portal shell or bespoke list/detail/form layout system.
- Run
_refs/angular/write-code/mock-api-input.md when UI generation is driven by mock API docs, OpenAPI/Swagger, Postman/Insomnia, MSW/WireMock/Prism/JSON Server specs, endpoint tables, schemas, JSON fixtures, or sample cURL. Produce the mock API contract mapping before writing models, services, or screens.
- Run the
@sdcorejs/utils reuse preflight before writing helper/formatter/validator/mapper/pipe utility code; report which utilities were reused and why any custom helper remains necessary.
- After generating UI, show the Core UI usage summary table (every
@sdcorejs/angular component/service/directive actually used + a one-line, feature-specific purpose, in the user's language) so the user sees the building blocks at a glance. List only what was used. Persist the same table into the module user guide at write-user-guide.
- Present the MANDATORY FINISH GATE (
_refs/shared/finish-gate.md) after EVERY code-gen — standalone trigger or full SDLC flow. It surfaces tests / user-guide / technical-doc / review so the user always knows these exist. NEVER silently end after generating code, and NEVER skip the gate because the request was a one-liner.
- Tests are mandatory and written RED-first at
standard coverage by default (see the TDD Gate). They are surfaced (default ON) in the finish gate so the user can opt out or change level — but never silently skipped. NEVER ask a separate "which coverage level?" question outside the gate.
- Every generated portal includes the admin screens (
admin-screens) so end users administer accounts/roles in-app — never the Keycloak console. Run admin-screens right after init-portal, before any domain module work.
- Detect the installed Core UI package FIRST — a project is a Core UI portal if
package.json, a lockfile, installed package metadata, or existing imports show EITHER @sdcorejs/angular (new default) OR @sd-angular/core (legacy alias — same code, same version, actively co-deployed). Treat both as equal: NEVER skip doc discovery just because the project uses the legacy name, and generate imports with whichever prefix the project installed. If neither package is present and the request is not new portal creation or an approved migration, stop and return to sdcorejs-execute-plan generic harness as plain-angular.
- Discover Core UI on-demand before generating (docs are NOT committed — pulled fresh from the published site, version-matched, cached): for existing Core UI projects run
node _refs/angular/core-docs-fetch.mjs --cwd <target-project> --require-installed --list to see the component inventory, then node _refs/angular/core-docs-fetch.mjs --cwd <target-project> --require-installed <id> (e.g. sd-button, or --print <id> for inline content) to read a component's full API BEFORE using it. Before writing any template styling, ALWAYS fetch the Core UI style guide first — node _refs/angular/core-docs-fetch.mjs --cwd <target-project> --require-installed --print assets/STYLE-GUIDE — the single authoritative list of shipped utility classes. New portal creation may pass --version <CORE_VERSION> from _refs/angular/core-version.md before install. Never rely on hardcoded/memorized class names. Prefer a Core UI component when one fits; if none does, scaffold a skeleton + alert('TODO: ...') and flag it. It mojibake-guards upstream + falls back to cache offline. If a Core UI package exists but no remote/cache docs are available, continue only from local Core UI evidence and explicitly report the docs gap. If no Core UI package exists, do not use this docs fallback; route as plain-angular.
- For any detail/create/update screen, apply the Core UI component selection gate in
_refs/angular/write-code/screen-detail.md before writing markup. Child arrays/line items saved with the parent payload must use the documented FormArray + Core UI table/grid or sectioned row-editor pattern; independent child CRUD must use the parent detail-scoped modal/drawer pattern and never self-draw a native table or unmanaged repeated divs.
- Before generating an entity detail or side-drawer view, classify fields by role via
_refs/angular/write-code/init-entity.md: business identifiers, primary display, lifecycle/status, long text, visual identity, and server/audit. Business identifiers are create-only/edit-locked by default; DETAIL/side-drawer view should prefer compact read-only facts over a disabled edit form for simple data.
- Keep independent child CRUD scoped to the parent DETAIL screen: use modal/drawer flows, pass the current parent id into the child form, refresh the child collection after success, and preserve the parent route plus active tab/section.
- Style utility-first — see
_refs/angular/styling.md. Lean on the Core UI utility classes from the STYLE-GUIDE (d-flex, flex-1, justify-content-between, gap-16, m-*/p-*, w-full, rounded-8, text-primary, T14M, row/col-*, grid-container/grid-cols-*, mat-elevation-z*) for layout / spacing / sizing / color / typography. If the consumer app ships Tailwind (tailwind.config.* or a tailwindcss dependency), use Tailwind utilities too, matching whatever the existing components use. Core UI spacing/sizing utilities are px-based, integer 0–200 (mb-16 = 16px — NOT a Bootstrap multiplier); use multiples of 4. Write custom component .scss ONLY when no utility fits, keep it token-based (var(--sd-*)), and flag each rule with a one-line // why:.
- Enforce the execution order (portal → admin-screens → module → entity → screens → actions) and do not skip or reorder steps.
- Generate every component with
changeDetection: ChangeDetectionStrategy.OnPush; treat a missing OnPush decorator entry or missing import as a generation defect to fix before review.
- Precompute all values displayed or bound in templates with
signal(), computed(), pure pipes, or view-model fields. Do not bind to component methods/getters for display, visibility, title/color, disabled/loading, permission, or list-derived values.
- Keep Service models as Service-owned contracts. Do not force them to equal the raw backend API when the Service maps the payload, and do not add UI-only fields to
SaveReq/DTO unless the Service actually accepts/returns/derives those fields.
- For PO prototype flows, stay mock-first unless a runnable backend endpoint, base URL/configuration, auth expectation, and project service convention are explicitly available or the user explicitly asks for live API integration.
- For UI-affecting changes, perform the mandatory UI check from
_refs/angular/write-code/input-analysis.md before final response. Prefer an actual browser/preview check when available; otherwise perform a code-level UI review and state that limitation honestly.
- Run the full tail chain after the last step.
- Never handle
plain-angular inside this skill. The generic harness owns plain Angular and must never import @sdcorejs/angular or @sd-angular/core, fetch Core UI docs, emit Core UI usage summaries, force admin screens, or assume src/libs/**/features/**.
Documentation Gate Rule
- Inside the mandatory finish gate, run
_refs/documentation/gate.md immediately after the test decision. It owns user-guide / technical-doc creation or update approval. code-documentation is automatic and is not controlled by this gate.
MUST NOT
-
Hand-write CSS for flex / spacing / alignment / color / typography that a STYLE-GUIDE utility class already covers, or fill a component .scss with rules that duplicate shipped utilities — this is the "too many unnecessary CSS classes" anti-pattern. Put the utilities on the template; keep the .scss near-empty. Never use Bootstrap class names (btn, card, form-control, modal — they don't exist) or Tailwind syntax when the consumer has no Tailwind.
-
Self-draw Core UI equivalents: native form fields, raw buttons, custom page headers, custom table HTML, or unstructured repeated row divs when a Core UI component or the detail row-editor fallback applies.
-
Design a custom portal shell, landing page, dashboard, navigation system, page layout system, or form/table primitives for PO/BA prototypes outside the init-portal Core UI starter template and existing target portal conventions.
-
Create custom primitive controls, project-level shared components, or feature-specific components when Core UI or an existing local shared asset fits. Feature-specific components are for domain composition and behavior, not tiny markup fragments.
-
Invent behavior, UI labels, routes, roles, fields, component APIs, or SDCoreJS Angular APIs from image or PRD input. If the Core UI docs cannot be checked, use local evidence and report the fallback.
-
Treat a mock API document as a live backend integration target, hard-code sample absolute URLs, or skip mock-first services for a PO prototype unless live integration was explicitly requested and configured.
-
Block PO/BA Prototype Portal Mode because API/backend/design/permission data is missing; infer safe defaults, document Prototype assumptions, and keep the demo local/mock-first.
-
Skip test generation, defer it to "later", or block spec writing behind a coverage-level question — specs are a required deliverable, written RED-first at standard by default.
-
Generate portal code that requires end users to open the Keycloak console to manage accounts or roles.
-
Skip the admin-screens pack even when the user's request focuses on a domain entity — the admin layer is always present.
-
Create duplicate model/service/type/store/repository/API-client files for a related entity that already has a usable contract in the project.
-
Inline a full related entity object inside another model when an imported related model or summary type exists.
-
Navigate from a parent DETAIL child collection to independent child routes such as /child/create, /child/:id/edit, or /child/:id; use a modal/drawer scoped to the parent DETAIL screen.
-
Show independent child create/edit/delete actions in parent CREATE or UPDATE states.
-
Use inline FormArray for independently persisted child CRUD; reserve FormArray for child rows saved together with the parent payload.
-
Recreate helper behavior already covered by @sdcorejs/utils (DateUtilities, NumberUtilities, StringUtilities, ValidationUtilities, ArrayUtilities, FilterUtilities, Utilities, ObjectUtilities, ColorUtilities, BrowserUtilities) or deep-import from @sdcorejs/utils/dist/*.
-
Omit ChangeDetectionStrategy.OnPush from generated components.
-
Call component methods/getters from HTML to compute displayed/bound values, including {{ getLabel(...) }}, [title]="buildTitle()", [disabled]="isDisabled()", @if (canEdit()), or [class.foo]="hasFoo()". Use computed()/signals/pure pipes/view models instead. Event handlers remain valid.
-
Treat Service DTOs as scratch objects for UI-only state (checked, selected, expanded, children, displayName, etc.). Use a component-local ViewModel or a documented Service mapper output instead.
Validation Checklist
Before returning generated code:
✅ Mock API/OpenAPI/Postman/cURL input has a mock API contract mapping before implementation
✅ PO/BA Prototype Portal Mode input has a PO/BA Prototype Plan, Prototype assumptions, mock-first service decision, permission mode, and no live API dependency unless explicitly configured
✅ PO/BA prototype used a template baseline: init-portal Core UI starter template for a new portal, or the existing Core UI portal shell for an existing app; no parallel custom shell was created
✅ Each production file (model / service / list / detail) has a corresponding .spec.ts written RED before the file was created
✅ UI-affecting image/PRD/feature input has SDCoreJS Core reuse analysis and the matching decomposition/mapping before implementation
✅ Every generated component imports and declares changeDetection: ChangeDetectionStrategy.OnPush
✅ Templates bind only precomputed state (signal, computed, pure pipe, view model); no method/getter calls for displayed/derived values
✅ Service public models match the Service/mapper contract, with raw API-only fields isolated behind mapper/internal types
✅ UI-only fields live in component ViewModels/signals unless the Service explicitly derives and guarantees them
✅ All imports are correct (no circular dependencies)
✅ All fields from EntitySchema are included
✅ Field-role analysis was applied before rendering detail/side-drawer UI; business identifiers are locked on UPDATE after any whole-form enable and mapped safely in update payloads
✅ Simple DETAIL/side-drawer views use Core UI-first compact read-only facts, promote useful business identifiers, show status with existing status UI, and avoid duplicate promoted fields in the body
✅ Existing related models/services were scanned and reused or minimally extended before any new contract was created
✅ @sdcorejs/utils was checked before writing helper/formatter/validator/mapper/pipe utility code
✅ No duplicate model/service/type exists for the same domain entity
✅ Relationship fields use imported existing types or <entity>Id instead of unnecessary inline object shapes
✅ Parent detail-scoped child CRUD, when present, uses child-entity permissions, modal/drawer create/edit/view flows, parent-id prefill/lock, child collection refresh after success, and preserves the parent DETAIL route plus active tab/section
✅ Form validation matches field requirements
✅ {{ entityKebab }}.mock-data.ts exists with 20–40 domain-realistic seed rows
✅ PO/BA prototype listings default to 25 realistic rows when no row count is provided, with 20-30 accepted and at least one record per important status
✅ Seed rows use realistic domain values derived from inferred field schema, not generic placeholders
✅ Service methods are wired to mock store by default
✅ Mock store reseeds if persisted dataset is empty or corrupted
✅ Component decorators (@SdTabComponent) are present
✅ State management (CREATE/UPDATE/DETAIL) works correctly
✅ DETAIL route handles stale entity IDs by recovering to list instead of rendering blank fields
✅ Routes are lazy-loaded
✅ Column visibility matches schema
✅ Error handling is comprehensive
✅ TypeScript strict mode compliance
✅ No hardcoded values (use constants from schema)
✅ Naming conventions consistent throughout
✅ Comments explain complex logic
✅ UI-affecting changes have a UI check summary, with browser/preview verification claimed only when it actually ran
Example: Complete Employee Entity Generation
A worked end-to-end example (user request → EntitySchema → final file tree, following init-entity.md) lives in _refs/angular/templates/orchestrator-step-examples.md#worked-end-to-end--employee-entity. Read it when you want to see how the dispatch table cashes out on a real request.