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

Nacl Sa Finalize

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

|

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

Install

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

✓ 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-finalize)

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 Finalize? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

/nacl-sa-finalize -- Specification Finalization (Graph)

Purpose

Finalize the specification by computing all summary artifacts from Neo4j aggregation queries: statistics summary, glossary extract, ADR records, traceability matrix, and readiness assessment. No file reading -- everything is aggregated from the graph. Data stays in the graph and is viewable via /nacl-render or /nacl-publish.

Shared references: nacl-core/SKILL.md


Neo4j Tools

| Tool | Usage | |------|-------| | mcp__neo4j__read-cypher | All aggregation queries (statistics, glossary, readiness, traceability) | | mcp__neo4j__write-cypher | Create/update :Decision nodes + JUSTIFIES edges; backfill legacy Requirement{type:'adr'}:Decision | | mcp__neo4j__get-schema | Introspect current graph schema before finalization |


Modes

Mode full (default)

Full finalization: all five phases sequentially.

When: After successful validation (/nacl-sa-validate full).

Mode module

Finalize a single module (scoped statistics + readiness).

When: After completing work on one module.

Parameter: module -- module ID (e.g. mod-orders).

Mode stats-only

Statistics and readiness only -- no ADR creation, no glossary update.

When: User wants to see current progress without modifying the graph.


Workflow Overview

+-----------------+    +-----------------+    +-----------------+
| Phase 1         |    | Phase 2         |    | Phase 3         |
| Statistics      |--->| Glossary +      |--->| Traceability    |
| Summary         |    | ADR             |    | Matrix          |
+-----------------+    +-----------------+    +-----------------+
                                                     |
                                                     v
                       +-----------------+    +-----------------+
                       | Phase 5         |    | Phase 4         |
                       | Report to       | STOP: "No modules in graph. Run /nacl-sa-architect first."
3. Verify UseCase nodes exist: `MATCH (uc:UseCase) RETURN count(uc) AS cnt`
   - If cnt = 0 -> WARN: "No use cases found. Statistics will be incomplete."
4. If mode = `module`, verify the module exists:
   ```cypher
   MATCH (m:Module {id: $moduleId}) RETURN m
   ```
   - Not found -> STOP: "Module {moduleId} not found in graph."

---

## Phase 1: Statistics Summary

**Goal:** Compute aggregate counts across all specification node types.

### Query: `sa_statistics_summary`

Source: `graph-infra/queries/sa-queries.cypher`

```cypher
MATCH (m:Module) WITH count(m) AS modules
MATCH (uc:UseCase) WITH modules, count(uc) AS ucs
MATCH (de:DomainEntity) WITH modules, ucs, count(de) AS entities
MATCH (da:DomainAttribute) WITH modules, ucs, entities, count(da) AS attributes
MATCH (f:Form) WITH modules, ucs, entities, attributes, count(f) AS forms
MATCH (ff:FormField) WITH modules, ucs, entities, attributes, forms, count(ff) AS fields
MATCH (sr:SystemRole) WITH modules, ucs, entities, attributes, forms, fields, count(sr) AS roles
MATCH (rq:Requirement) WITH modules, ucs, entities, attributes, forms, fields, roles, count(rq) AS requirements
MATCH (c:Component) WITH modules, ucs, entities, attributes, forms, fields, roles, requirements, count(c) AS components
RETURN modules, ucs, entities, attributes, forms, fields, roles, requirements, components;

Additional counts (run separately)

Enumerations:

MATCH (en:Enumeration) RETURN count(en) AS enumerations;

ADRs:

MATCH (rq:Requirement {type: 'adr'}) RETURN count(rq) AS adrs;

Open questions (if stored as Requirement with type='question'):

MATCH (rq:Requirement {type: 'question', status: 'open'}) RETURN count(rq) AS open_questions;

UC detail breakdown:

MATCH (uc:UseCase)
RETURN count(uc) AS total,
       count(CASE WHEN uc.priority = 'primary' THEN 1 END) AS primary_ucs,
       count(CASE WHEN uc.priority = 'secondary' THEN 1 END) AS secondary_ucs,
       count(CASE WHEN uc.detail_status = 'complete' THEN 1 END) AS detailed_ucs;

Connected-spec extension counts (2.15+)

Query: sa_statistics_extensions

Source: graph-infra/queries/sa-queries.cypher

RETURN
  COUNT { (n:Decision) }          AS decisions,
  COUNT { (n:Screen) }            AS screens,
  COUNT { (n:ScreenState) }       AS screen_states,
  COUNT { (n:ScreenEvent) }       AS screen_events,
  COUNT { (n:Transition) }        AS screen_transitions,
  COUNT { (n:ScreenEffect) }      AS screen_effects,
  COUNT { (n:AnalyticsEvent) }    AS analytics_events,
  COUNT { (n:Slice) }             AS slices,
  COUNT { (n:DomainError) }       AS domain_errors,
  COUNT { (n:ErrorPresentation) } AS error_presentations,
  COUNT { (n:CachePolicy) }       AS cache_policies,
  COUNT { (n:DegradationRule) }   AS degradation_rules;

These layers are opt-in (vacuous PASS in /nacl-sa-validate L10-L13 when empty). All-zero counts for a layer mean "not adopted", not "incomplete" — report them as such, do not treat zero as a readiness failure.

Output format

Present to user as a table:

| Metric             | Count |
|--------------------|-------|
| Modules            | {N}   |
| Domain Entities    | {N}   |
| Attributes (total) | {N}   |
| Enumerations       | {N}   |
| Roles              | {N}   |
| Use Cases (total)  | {N}   |
|   - Primary        | {N}   |
|   - Secondary      | {N}   |
|   - Detailed       | {N}/{total} |
| Forms              | {N}   |
| Form Fields        | {N}   |
| Requirements       | {N}   |
| Components         | {N}   |
| ADRs               | {N}   |
| Open Questions     | {N}   |
| Decisions          | {N}   |
| Screens            | {N} ({N} states, {N} transitions) |
| Behavior Slices    | {N}   |
| Domain Errors      | {N} ({N} presentations) |
| Cache Policies     | {N}   |
| Degradation Rules  | {N}   |

Extension rows with zero counts are rendered as — (not adopted) instead of 0.

For mode module, scope all queries with:

MATCH (m:Module {id: $moduleId})-[:CONTAINS_UC]->(uc:UseCase) ...
MATCH (m:Module {id: $moduleId})-[:CONTAINS_ENTITY]->(de:DomainEntity) ...

Phase 2: Glossary Extract + ADR

Goal: Extract glossary terms from graph; compile Architecture Decision Records.

2a: Glossary Extract

Query: sa_glossary_extract

Source: graph-infra/queries/sa-queries.cypher

MATCH (de:DomainEntity)
RETURN 'DomainEntity' AS source_type, de.id AS id, de.name AS term
UNION ALL
MATCH (en:Enumeration)
RETURN 'Enumeration' AS source_type, en.id AS id, en.name AS term
UNION ALL
MATCH (sr:SystemRole)
RETURN 'SystemRole' AS source_type, sr.id AS id, sr.name AS term;
Cross-reference with BA glossary (if BA layer exists)
MATCH (g:GlossaryTerm)
OPTIONAL MATCH (g)-[:REFERS_TO]->(de:DomainEntity)
RETURN g.id AS glo_id, g.term AS ba_term, g.definition AS definition,
       de.id AS sa_entity_id, de.name AS sa_entity_name;
Actions
  1. Run sa_glossary_extract to collect all SA-layer terms
  2. For each term without a definition, generate a one-sentence definition from its graph context

(attributes, relationships, module membership)

  1. Cross-reference with BA GlossaryTerm nodes to map BA term -> SA entity
  2. Present combined glossary to user (do NOT write to files)

2b: Architecture Decisions (graph-native)

Architecture decisions are stored as first-class :Decision nodes (level:'architecture', created_by:'nacl-sa-finalize') linked to the artifacts they justify via JUSTIFIES — the same provenance model that nacl-sa-feature and nacl-tl-fix write. This is the authority; markdown ADRs are projections. (Legacy projects stored ADRs as Requirement {type:'adr'} with no link to what they justified — those are migrated below so the year-later "why" query reaches them.)

Read existing decisions (both modern and legacy)
MATCH (d:Decision)
RETURN d.id AS id, d.title AS title, d.rationale AS rationale,
       d.status AS status, d.level AS level
ORDER BY d.id;
// Legacy ADRs not yet migrated to :Decision
MATCH (rq:Requirement {type: 'adr'})
RETURN rq.id AS id, rq.description AS title, rq.context AS context,
       rq.decision AS decision, rq.alternatives AS alternatives,
       rq.consequences AS consequences
ORDER BY rq.id;
One-time backfill: legacy Requirement{type:'adr'}:Decision

For each legacy ADR, create a :Decision carrying the same content (rationale from decision+consequences), source:'ADR-NNN (imported)', and — where the ADR's subject is identifiable — a JUSTIFIES edge to the module/entity/role it concerns. Present to the user before writing; never delete the legacy node (keep lineage), just mark it migrated_to: .

// mcp__neo4j__write-cypher  (per legacy ADR, after user confirms the JUSTIFIES target)
MATCH (rq:Requirement {id: $adrId, type:'adr'})
MERGE (d:Decision {id: $decId})
SET d.title = rq.description, d.context = rq.context,
    d.chosen = rq.decision, d.rationale = coalesce(rq.decision,'') + ' — ' + coalesce(rq.consequences,''),
    d.alternatives_considered = CASE WHEN rq.alternatives IS NULL THEN [] ELSE [rq.alternatives] END,
    d.status = coalesce(rq.status,'accepted'), d.created_at = coalesce(rq.created_at, datetime()),
    d.created_by = 'nacl-sa-finalize', d.source = rq.id + ' (imported)', d.level = 'architecture'
SET rq.migrated_to = $decId
RETURN d.id;

Also import any standalone docs/adr/*.md files the same way (source:' (imported)').

Backfill: FeatureRequests without a Decision (provenance gap-closure)

Projects created before the provenance feature have :FeatureRequest nodes with no linked :Decisionnacl-sa-validate L9.1 flags every one. Close the gap by writing one honest :Decision per FR, with rationale drawn from the project's own recorded text (never invented). Full runbook: nacl-tl-core/references/provenance-gap-closure.md.

Per FR without IMPLEMENTS -> :Decision, resolve rationale in priority order:

  1. FR node description (if non-empty).
  2. FR markdown at markdown_path — the ## Feature Description section, plus the

Source: metadata line (the original /sa-feature "..." intent). This is real recorded rationale, not fabrication.

  1. git log of the FR markdown / its UCs.
  2. None recoverable → do NOT invent. Grandfather via nacl-sa-flags-style flag

(decision_exempt=true, decision_exempt_reason, decision_exempt_since), which L9.1 skips and L9.5 surfaces as visible debt.

For a recoverable FR, write the Decision and wire it to the FR's own scope:

// mcp__neo4j__write-cypher  (per FR; $rationale extracted from md/description; $decId = next DEC-NNN)
MATCH (fr:FeatureRequest {id: $frId})
MERGE (d:Decision {id: $decId})
SET d.title = coalesce(fr.title, $frId), d.chosen = $chosen,
    d.rationale = $rationale,                              // REQUIRED, non-empty, from project's own records
    d.context = $context, d.alternatives_considered = [],
    d.status = CASE WHEN fr.status IN ['shipped','implemented','dev-complete'] THEN 'accepted' ELSE 'accepted' END,
    d.created_at = coalesce(fr.created_at, datetime()),
    d.created_by = 'nacl-sa-finalize',
    d.source = $frId + ' (backfilled from ' + $rationaleSource + ')',   // 'description' | 'markdown' | 'git'
    d.level = 'feature'
MERGE (fr)-[:IMPLEMENTS]->(d)
WITH d, fr
MATCH (fr)-[r:INCLUDES_UC]->(uc:UseCase)
MERGE (d)-[:JUSTIFIES {role: CASE WHEN r.kind = 'new' THEN 'creates' ELSE 'shapes' END}]->(uc)
RETURN d.id;

Verify-before-bulk: backfill ONE FR, confirm the Decision reads honestly and sa-validate --scope L9 clears for it, then batch the rest. Present a sample to the user before the bulk write.

Detect implicit decisions not yet recorded

Check for architectural patterns that imply a decision worth recording:

MATCH (m:Module) RETURN count(m) AS module_count, collect({id:m.id, name:m.name}) AS modules;
MATCH (sr:SystemRole) RETURN count(sr) AS role_count, collect({id:sr.id, name:sr.name}) AS roles;
Create missing Decision nodes (with JUSTIFIES)

For each detected decision not yet in the graph, allocate DEC-NNN (max(toInteger(replace(d.id,'DEC-',''))) + 1) and write with mcp__neo4j__write-cypher, linking it to every artifact it shaped:

MERGE (d:Decision {id: $decId})
SET d.title = $title, d.chosen = $chosen, d.rationale = $rationale,
    d.context = $context, d.alternatives_considered = $alternatives,
    d.status = 'accepted', d.created_at = coalesce(d.created_at, datetime()),
    d.created_by = 'nacl-sa-finalize', d.source = 'finalize', d.level = 'architecture'
WITH d
UNWIND $justifiesIds AS x
  MATCH (target {id: x})           // Module / DomainEntity / SystemRole / …
  MERGE (d)-[:JUSTIFIES {role:'shapes'}]->(target)
RETURN d.id;
Typical decision categories to check
  1. Module decomposition rationale (JUSTIFIES → the Modules)
  2. Domain model key decisions — entity boundaries, shared vs separate (JUSTIFIES → the DomainEntities)
  3. Role model and authorization approach (JUSTIFIES → the SystemRoles)
  4. Interface architecture decisions (SPA, navigation patterns)
  5. Technology-specific decisions

Present each new Decision to the user for confirmation before writing. Every Decision MUST have a non-empty rationale and at least one JUSTIFIES edge — nacl-sa-validate L9 enforces this.

IMPORTANT: In stats-only mode, SKIP Phase 2 entirely.


Phase 3: Traceability Matrix

Goal: Compute full BA-to-SA traceability and coverage from the graph.

Query: handoff_traceability_matrix

Source: graph-infra/queries/handoff-queries.cypher

MATCH (ws:WorkflowStep)-[:AUTOMATES_AS]->(uc:UseCase)
RETURN 'Step->UC' AS category, ws.id AS ba_id, ws.function_name AS ba_name, uc.id AS sa_id, uc.name AS sa_name
UNION ALL
MATCH (be:BusinessEntity)-[:REALIZED_AS]->(de:DomainEntity)
RETURN 'Entity->Domain' AS category, be.id AS ba_id, be.name AS ba_name, de.id AS sa_id, de.name AS sa_name
UNION ALL
MATCH (br:BusinessRole)-[:MAPPED_TO]->(sr:SystemRole)
RETURN 'Role->SysRole' AS category, br.id AS ba_id, br.full_name AS ba_name, sr.id AS sa_id, sr.name AS sa_name
UNION ALL
MATCH (brq:BusinessRule)-[:IMPLEMENTED_BY]->(rq:Requirement)
RETURN 'Rule->Req' AS category, brq.id AS ba_id, brq.name AS ba_name, rq.id AS sa_id, rq.description AS sa_name;

Query: handoff_coverage_stats

Source: graph-infra/queries/handoff-queries.cypher

MATCH (ws:WorkflowStep {stereotype: "Автоматизируется"})
WITH count(ws) AS total_auto
OPTIONAL MATCH (ws2:WorkflowStep {stereotype: "Автоматизируется"})-[:AUTOMATES_AS]->(:UseCase)
WITH total_auto, count(ws2) AS covered_auto
WITH total_auto, covered_auto,
     CASE WHEN total_auto > 0 THEN round(100.0 * covered_auto / total_auto) ELSE 0 END AS step_pct
MATCH (be:BusinessEntity {type: "Бизнес-объект"})
WITH total_auto, covered_auto, step_pct, count(be) AS total_entities
OPTIONAL MATCH (be2:BusinessEntity {type: "Бизнес-объект"})-[:REALIZED_AS]->(:DomainEntity)
WITH total_auto, covered_auto, step_pct, total_entities, count(be2) AS covered_entities
WITH total_auto, covered_auto, step_pct, total_entities, covered_entities,
     CASE WHEN total_entities > 0 THEN round(100.0 * covered_entities / total_entities) ELSE 0 END AS entity_pct
MATCH (br:BusinessRole)
WITH step_pct, entity_pct, total_auto, covered_auto, total_entities, covered_entities, count(br) AS total_roles
OPTIONAL MATCH (br2:BusinessRole)-[:MAPPED_TO]->(:SystemRole)
WITH step_pct, entity_pct, total_auto, covered_auto, total_entities, covered_entities, total_roles, count(br2) AS covered_roles
WITH step_pct, entity_pct, total_auto, covered_auto, total_entities, covered_entities, total_roles, covered_roles,
     CASE WHEN total_roles > 0 THEN round(100.0 * covered_roles / total_roles) ELSE 0 END

…

## 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.