AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Nacl Sa Domain

skill-itsalt-nacl-nacl-sa-domain · by ITSalt

|

No reviews yet
0 installs
40 views
0.0% view→install

Install

$ agentstack add skill-itsalt-nacl-nacl-sa-domain

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-itsalt-nacl-nacl-sa-domain)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Nacl Sa Domain? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

/nacl-sa-domain — Domain Model (Graph)

Purpose

Create and manage the Domain Model through Neo4j graph: entities, attributes, enumerations, inter-entity relationships, and BA-to-SA handoff edges. All data lives in Neo4j -- no markdown artifacts.

Shared references: nacl-core/SKILL.md


Neo4j Tools

| Tool | Usage | |------|-------| | mcp__neo4j__read-cypher | Read-only queries (fetch BA entities, check existing domain model) | | mcp__neo4j__write-cypher | Create/update/delete nodes and edges | | mcp__neo4j__get-schema | Introspect current graph schema |


Modes

Mode IMPORT_BA

Import BA business entities from Neo4j graph as DomainEntity candidates with full handoff traceability.

When: BA layer populated in Neo4j, SA domain model not yet created for those entities.

Parameter: module (optional) -- target module name. If omitted, imports all uncovered BA entities.

Mode CREATE

Create a single new DomainEntity interactively with attributes, enumerations, and relationships.

When: User asks to add a new domain entity not sourced from BA.

Parameter: entity_name -- name of the entity to create.

Mode MODIFY

Modify an existing DomainEntity: add/remove/change attributes, relationships, enumerations.

When: User asks to change an attribute, type, relationship, or cardinality on an existing entity.

Parameter: entity_name -- name of the entity to modify.

Mode FULL

Create the complete domain model for a module: all entities, attributes, enumerations, relationships, lifecycle states, business rules.

When: After nacl-sa-architect, building full domain model for a module from scratch.

Parameter: module -- module name (must exist as a Module node in the graph).

| Parameter | Required | Description | |-----------|----------|-------------| | --lang | No | Output language: en or ru (default: ru). |


Language

Supports --lang=en for English output. See [nacl-core/lang-directive.md](../nacl-core/lang-directive.md). When --lang=en: all generated text, node names, descriptions in English. Default: Russian (ru).


Workflow: Mode IMPORT_BA

+-----------------+    +-----------------+    +-----------------+    +-----------------+
| Step 1          |    | Step 2          |    | Step 3          |    | Step 4          |
| Read uncovered  |--->| Classify &      |--->| Create nodes    |--->| Create handoff  |
| BA entities     |    | confirm types   |    | & attributes    |    | & rel edges     |
+-----------------+    +-----------------+    +-----------------+    +-----------------+

Each step ends with a summary and user confirmation before proceeding.

Do not proceed to the next step without explicit user confirmation!


Step 1: Read uncovered BA entities

Goal: Find all BusinessEntity nodes (type: "Бизнес-объект") that have no REALIZED_AS edge yet.

Cypher -- fetch uncovered BA entities:

// handoff_uncovered_entities
MATCH (be:BusinessEntity {type: "Бизнес-объект"})
WHERE NOT (be)-[:REALIZED_AS]->(:DomainEntity)
RETURN be.id AS id, be.name AS name, be.type AS type, be.description AS description

Execute via mcp__neo4j__read-cypher.

If no results: Tell the user all BA entities are already covered. Suggest CREATE or FULL mode instead.

Cypher -- fetch all uncovered BA entities with attributes:

// ba_uncovered_entities_with_attributes
MATCH (be:BusinessEntity {type: "Бизнес-объект"})
WHERE NOT (be)-[:REALIZED_AS]->(:DomainEntity)
OPTIONAL MATCH (be)-[:HAS_ATTRIBUTE]->(ea:EntityAttribute)
RETURN be.id AS entity_id, be.name AS entity_name, be.description AS entity_desc,
       collect({
         id: ea.id,
         name: ea.name,
         data_type: ea.data_type,
         description: ea.description
       }) AS attributes
ORDER BY be.id

Also fetch BA relationships between uncovered entities:

// ba_uncovered_entity_relationships
MATCH (be1:BusinessEntity {type: "Бизнес-объект"})-[r:RELATES_TO]->(be2:BusinessEntity {type: "Бизнес-объект"})
WHERE NOT (be1)-[:REALIZED_AS]->(:DomainEntity)
   OR NOT (be2)-[:REALIZED_AS]->(:DomainEntity)
RETURN be1.id AS source_id, be1.name AS source_name,
       r.rel_type AS rel_type, r.cardinality AS cardinality,
       be2.id AS target_id, be2.name AS target_name

Also fetch BA entity states (for lifecycle mapping later):

// ba_uncovered_entity_states
MATCH (be:BusinessEntity {type: "Бизнес-объект"})-[:HAS_STATE]->(st:EntityState)
WHERE NOT (be)-[:REALIZED_AS]->(:DomainEntity)
OPTIONAL MATCH (st)-[t:TRANSITIONS_TO]->(st2:EntityState)
RETURN be.id AS entity_id, be.name AS entity_name,
       st.id AS state_id, st.name AS state_name, st.description AS state_desc,
       t.condition AS transition_condition,
       st2.name AS target_state

Also check which non-"Бизнес-объект" BA entities exist (for user awareness):

// ba_other_entity_types
MATCH (be:BusinessEntity)
WHERE be.type <> "Бизнес-объект"
  AND NOT (be)-[:REALIZED_AS]->(:DomainEntity)
RETURN be.id AS id, be.name AS name, be.type AS type, be.description AS description

Present to user:

**Uncovered BA entities found: {count}**

**Бизнес-объекты (will create DomainEntity):**
1. {name} ({id}) -- {description}
   Attributes: {attr_count}
   States: {state_count}

**Other types (need decision):**
- {name} ({id}, type: {type}) -- needs SA entity? (Y/N)

Proceed with import?

Step 2: Classify and confirm SA types

Goal: For each BA entity, propose SA attribute types and get user confirmation.

BA-to-SA type mapping reference:

| BA datatype (EntityAttribute) | Suggested SA datatype (DomainAttribute) | Notes | |-------------------------------|------------------------------------------|-------| | Текст | String | Default text type | | Число | Int or Decimal | Ask user: integer or decimal? | | Дата | Date or DateTime | Ask user: date only or with time? | | Ссылка | Reference | Becomes a RELATES_TO edge | | Перечисление | Enum | Create Enumeration node | | Логическое | Boolean | |

Present for each entity:

**Import BA entity: {BA name} ({BA id})**

BA attributes (business types):
| # | BA Attribute | BA Type     | Proposed SA Name | Proposed SA Type | Nullable |
|---|-------------|-------------|------------------|-----------------|----------|
| 0 | (auto)      | --          | id               | UUID            | false    |
| 1 | {ba_attr}   | {ba_type}   | {sa_name}        | {sa_type}       | {t/f}    |

Proposed SA entity:
- SA name: {EnglishName} (e.g. "Order", "Customer")
- SA id: DE-{EnglishName}
- Module: {module}

Questions:
1. Are the proposed SA types correct?
2. Add or remove attributes?
3. Which module should this entity belong to?

Rules for SA name generation:

  • Use English PascalCase: "Заказ" -> "Order", "Позиция заказа" -> "OrderItem"
  • Always add id attribute (UUID, not nullable) as the first attribute
  • Convert "Ссылка" attributes to relationship edges (do not create as DomainAttribute)
  • Convert "Перечисление" attributes to Enum type (will create Enumeration node in Step 3)

Step 3: Create nodes and attributes in Neo4j

Goal: Create DomainEntity, DomainAttribute, and Enumeration nodes.

Pre-check -- get next available DomainAttribute IDs:

// next_domain_attribute_id
MATCH (da:DomainAttribute)
WHERE da.id STARTS WITH $prefix
WITH max(toInteger(split(da.id, '-A')[1])) AS maxNum
RETURN $prefix + '-A' + apoc.text.lpad(toString(coalesce(maxNum, 0) + 1), 2, '0') AS nextId

If apoc is not available, compute the next ID in the agent and pass it as a parameter.

Cypher -- create DomainEntity:

// create_domain_entity
MERGE (de:DomainEntity {id: $id})
SET de.name = $name,
    de.module = $module,
    de.description = $description,
    de.shared = $shared

Parameters:

  • $id -- format DE-{EnglishName} (e.g. "DE-Order")
  • $name -- English PascalCase (e.g. "Order")
  • $module -- module name (e.g. "orders")
  • $description -- Russian description from BA entity
  • $shared -- boolean. true if this entity is intentionally referenced from multiple modules (typical for User, Group, GroupLecture, organization-wide aggregate roots); false for module-local entities. Used by validator L6.1 to skip cross-module attribute consistency checks on intentionally-shared entities. If forgotten, nacl-sa-flags backfill-all defaults it to false; refine selectively via /nacl-sa-flags set-shared --entity true.

Cypher -- link entity to module:

// link_entity_to_module
MATCH (m:Module {name: $moduleName}), (de:DomainEntity {id: $entityId})
MERGE (m)-[:CONTAINS_ENTITY]->(de)

Cypher -- create DomainAttribute:

// create_domain_attribute
MERGE (da:DomainAttribute {id: $id})
SET da.name = $name,
    da.data_type = $dataType,
    da.nullable = $nullable,
    da.description = $description,
    da.internal = $internal

Parameters:

  • $id -- format {EntityName}-A{NN} (e.g. "Order-A01")
  • $name -- camelCase attribute name (e.g. "orderNumber")
  • $dataType -- one of: UUID, String, Int, Decimal, Boolean, Date, DateTime, Enum, JSON, Reference
  • $nullable -- boolean
  • $description -- Russian description
  • $internal -- boolean. true for system attributes that should never appear in any user form (surrogate keys, foreign keys, timestamps, password hashes, refresh tokens, telemetry IDs, third-party-system internal references). false for user-facing attributes. Used by validator L4.2 to skip "attribute not referenced by any FormField" checks on internal attributes. If forgotten, nacl-sa-flags backfill-all --detect-internal will auto-flag attributes whose names match common system patterns (id, *_id, *_at, *_token, *_hash); the user reviews and refines the rest.

Cypher -- link attribute to entity:

// link_attribute_to_entity
MATCH (de:DomainEntity {id: $entityId}), (da:DomainAttribute {id: $attrId})
MERGE (de)-[:HAS_ATTRIBUTE]->(da)

Cypher -- create Enumeration with values:

// create_enumeration
MERGE (en:Enumeration {id: $id})
SET en.name = $name,
    en.description = $description

Parameters:

  • $id -- format ENUM-{Name} (e.g. "ENUM-OrderStatus")
  • $name -- English PascalCase (e.g. "OrderStatus")

Cypher -- create EnumValue:

// create_enum_value
MERGE (ev:EnumValue {id: $id})
SET ev.value = $value,
    ev.description = $description

Parameters:

  • $id -- format {ENUM_ID}-V{NN} (e.g. "ENUM-OrderStatus-V01")

Cypher -- link enum to entity and values:

// link_enum_to_entity
MATCH (de:DomainEntity {id: $entityId}), (en:Enumeration {id: $enumId})
MERGE (de)-[:HAS_ENUM]->(en)
// link_enum_value
MATCH (en:Enumeration {id: $enumId}), (ev:EnumValue {id: $valueId})
MERGE (en)-[:HAS_VALUE]->(ev)

If BA entity has states (EntityState), create a corresponding Enumeration from them:

  1. Map each EntityState.name to an EnumValue (uppercase, underscored: "Новый" -> "NEW")
  2. Create Enumeration node
  3. Link to entity via HAS_ENUM
  4. Add the status DomainAttribute with data_type: "Enum"

Step 3.5: Auto-create Enumerations from BA EntityStates

Goal: When a BA entity has EntityState nodes AND the corresponding DomainEntity has an attribute with data_type: "Enum" (typically status), automatically create Enumeration and EnumValue nodes.

This step runs after attribute creation (Step 3) and before relationship/handoff edges (Step 4).

Cypher -- find BA states for a source entity:

// ba_entity_states_for_enum
MATCH (be:BusinessEntity {id: $baEntityId})-[:HAS_STATE]->(es:EntityState)
RETURN es.name ORDER BY es.id

Cypher -- check if DomainEntity has an Enum attribute without an Enumeration:

// enum_attr_without_enumeration
MATCH (de:DomainEntity {id: $entityId})-[:HAS_ATTRIBUTE]->(da:DomainAttribute {data_type: "Enum"})
WHERE NOT (de)-[:HAS_ENUM]->(:Enumeration)
RETURN da.id AS attr_id, da.name AS attr_name

If both queries return results, create the Enumeration automatically:

  1. Create Enumeration node:
MERGE (e:Enumeration {id: "ENUM-" + $entityName + "Status"})
SET e.name = $entityName + "Status",
    e.description = "Auto-generated from BA EntityStates for " + $entityName
  1. For each EntityState, create an EnumValue node:
MERGE (ev:EnumValue {id: "ENUM-" + $entityName + "Status-V" + $seqNum})
SET ev.value = $stateName,
    ev.label = $stateName
  1. Link values to enumeration and enumeration to entity:
MATCH (e:Enumeration {id: $enumId}), (ev:EnumValue {id: $valueId})
MERGE (e)-[:HAS_VALUE]->(ev)
MATCH (de:DomainEntity {id: $entityId}), (e:Enumeration {id: $enumId})
MERGE (de)-[:HAS_ENUM]->(e)

Example: BA entity OBJ-UAZ-01 has EntityStates Новый, Подтверждён, Отгружен. DomainEntity DE-SparePartList has attribute status with data_type: "Enum". This step creates:

  • Enumeration {id: "ENUM-SparePartListStatus", name: "SparePartListStatus"}
  • EnumValue {id: "ENUM-SparePartListStatus-V01", value: "Новый", label: "Новый"}
  • EnumValue {id: "ENUM-SparePartListStatus-V02", value: "Подтверждён", label: "Подтверждён"}
  • EnumValue {id: "ENUM-SparePartListStatus-V03", value: "Отгружен", label: "Отгружен"}
  • Edges: (DE-SparePartList)-[:HAS_ENUM]->(ENUM-SparePartListStatus), (ENUM-SparePartListStatus)-[:HAS_VALUE]->(each EV)

Step 4: Create handoff and relationship edges

Goal: Create BA->SA traceability edges and inter-entity relationships.

Cypher -- create REALIZED_AS handoff (BusinessEntity -> DomainEntity):

// handoff_realized_as
MATCH (be:BusinessEntity {id: $baEntityId}), (de:DomainEntity {id: $saEntityId})
MERGE (be)-[:REALIZED_AS]->(de)

Cypher -- create TYPED_AS handoff (EntityAttribute -> DomainAttribute):

// handoff_typed_as
MATCH (ea:EntityAttribute {id: $baAttrId}), (da:DomainAttribute {id: $saAttrId})
MERGE (ea)-[:TYPED_AS]->(da)

Cypher -- create RELATES_TO between DomainEntities:

Inherit relationships from BA layer. Map BA rel_type/cardinality to SA equivalents:

| BA reltype | SA reltype | |-------------|-------------| | агрегация | composition | | ассоциация | association | | зависимость | dependency |

// create_entity_relationship
MATCH (de1:DomainEntity {id: $sourceId}), (de2:DomainEntity {id: $targetId})
MERGE (de1)-[:RELATES_TO {rel_type: $relType, cardinality: $cardinality}]->(de2)

Parameters:

  • $sourceId, $targetId -- DomainEntity IDs
  • $relType -- "composition", "association", "dependency"
  • $cardinality -- "1:1", "1:N", "N:1", "N:M"

To find which BA relationships to inherit:

// ba_relationships_for_import
MATCH (be1:BusinessEntity)-[r:RELATES_TO]->(be2:BusinessEntity)
MATCH (be1)-[:REALIZED_AS]->(de1:DomainEntity)
MATCH (be2)-[:REALIZED_AS]->(de2:DomainEntity)
WHERE NOT (de1)-[:RELATES_TO]->(de2)
RETURN be1.id AS ba_source, be2.id AS ba_target,
       de1.id AS sa_source, de2.id AS sa_target,
       r.rel_type AS ba_rel_type, r.cardinality AS cardinality

After all edges are created, verify with coverage stats:

// handoff_coverage_stats (from handoff-queries.cypher)
MATCH (be:BusinessEntity {type: "Бизнес-объект"})
WITH count(be) AS total_entities
OPTIONAL MATCH (be2:BusinessEntity {type: "Бизнес-объект"})-[:REALIZED_AS]->(:DomainEntity)
WITH total_entities, count(be2) AS covered_entities
RETURN total_entities, covered_entities,
       CASE WHEN total_entities > 0
            THEN round(100.0 * covered_entities / total_entities)
            ELSE 0 END AS coverage_pct

Present final summary:

**IMPORT_BA complete**

Created:
- DomainEntity nodes: {count}
- DomainAttribute nodes: {count}
- Enumeration nodes: {count}
- EnumValue nodes: {count}

Handoff edges:
- REALIZED_AS (B

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [ITSalt](https://github.com/ITSalt)
- **Source:** [ITSalt/NaCl](https://github.com/ITSalt/NaCl)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.