diff --git a/lib/kinds.js b/lib/kinds.js
index c957f54..f896834 100644
--- a/lib/kinds.js
+++ b/lib/kinds.js
@@ -12,38 +12,62 @@ const SCOPE = ['application', 'global', 'local'];
 const STORE = ['persistent', 'memory'];
 const ALLOW = ['write', 'append', 'read'];
 
+const KIND_STORED_DEFAULT_META = {
+  scope: 'application',
+  store: 'persistent',
+  allow: 'write',
+};
+
+const KIND_MEMORY_DEFAULT_META = {
+  scope: 'local',
+  store: 'memory',
+  allow: 'write',
+};
+
+const withDefaults = (meta, defaults, extra) => {
+  const result = { ...meta, ...extra };
+  for (const [key, defaultValue] of Object.entries(defaults)) {
+    if (result[key] === undefined) result[key] = defaultValue;
+  }
+  return result;
+};
+
 const projection = (kind, meta, root) => {
-  const { scope = 'local', store = 'memory', allow = 'write' } = meta;
-  const metadata = { ...meta, kind, scope, store, allow };
-  const { schema, fields } = meta;
-  if (!schema && !fields) throw new Error('Invalid Projection');
-  metadata.parent = schema;
-  const parent = root.findReference(schema);
+  const { schema: schemaName, fields: fieldNames } = meta;
+  if (!schemaName) throw new Error('Invalid Projection: "schema" expected');
+  if (!Array.isArray(fieldNames) || fieldNames.length === 0)
+    throw new Error('Invalid Projection: non-empty "fields" array expected');
+  if (!root || typeof root.findReference !== 'function')
+    throw new Error('Invalid Projection: "root" should satisfy Schema API');
   const defs = {};
-  for (const key of fields) {
-    defs[key] = parent.fields[key];
+  const referencedFields = root.findReference(schemaName)?.fields;
+  if (referencedFields) {
+    for (const name of fieldNames) defs[name] = referencedFields[name];
   }
+  const metadata = withDefaults(meta, KIND_MEMORY_DEFAULT_META, {
+    kind,
+    parent: schemaName,
+  });
   return { defs, metadata };
 };
 
 const kindStored = (kind, meta, root) => {
-  const { scope = 'application', store = 'persistent', allow = 'write' } = meta;
-  const metadata = { ...meta, kind, scope, store, allow };
+  if (!root) throw new Error('"root" schema expected');
   const id = root.name ? `${toLowerCamel(root.name)}Id` : 'id';
   const defs = { [id]: '?string' };
+  const metadata = withDefaults(meta, KIND_STORED_DEFAULT_META, { kind });
   return { defs, metadata };
 };
 
-const kindMemory = (kind, meta) => {
-  const { scope = 'local', store = 'memory', allow = 'write' } = meta;
-  return { metadata: { ...meta, kind, scope, store, allow }, defs: {} };
+const kindMemory = (kind, meta, root) => {
+  if (kind === 'projection') return projection(kind, meta, root);
+  const metadata = withDefaults(meta, KIND_MEMORY_DEFAULT_META, { kind });
+  return { defs: {}, metadata };
 };
 
 const getKindMetadata = (kind, meta = {}, root) => {
-  if (kind === 'projection') return projection(kind, meta, root);
-  if (KIND_MEMORY.includes(kind)) return kindMemory(kind, meta);
-  if (KIND_STORED.includes(kind)) return kindStored(kind, meta, root);
-  return kindMemory(kind, meta);
+  const processKind = KIND_STORED.includes(kind) ? kindStored : kindMemory;
+  return processKind(kind, meta, root);
 };
 
 module.exports = {
diff --git a/test/kinds.js b/test/kinds.js
new file mode 100644
index 0000000..b58d2e6
--- /dev/null
+++ b/test/kinds.js
@@ -0,0 +1,81 @@
+'use strict';
+
+const metatests = require('metatests');
+const { getKindMetadata } = require('../lib/kinds.js');
+
+metatests.test('Kinds: Projection', (test) => {
+  const getProjectionMeta = getKindMetadata.bind(null, 'projection');
+  {
+    const meta = {
+      schema: 'Account',
+      fields: ['login', 'password'],
+      etc: ['another', 'field'],
+    };
+    test.throws(
+      () => getProjectionMeta({}, null),
+      new Error('Invalid Projection: "schema" expected'),
+    );
+    test.throws(
+      () => getProjectionMeta({ schema: 'Account' }, null),
+      new Error('Invalid Projection: non-empty "fields" array expected'),
+    );
+    test.throws(
+      () => getProjectionMeta(meta, null),
+      new Error('Invalid Projection: "root" should satisfy Schema API'),
+    );
+    test.doesNotThrow(() =>
+      getProjectionMeta(meta, { findReference: new Function() }),
+    );
+  }
+
+  {
+    const meta = {
+      schema: 'Account',
+      fields: ['login', 'password'],
+      etc: ['another', 'field'],
+    };
+    const { defs, metadata } = getProjectionMeta(meta, {
+      findReference: new Function(),
+    });
+    test.strictEqual(metadata.kind, 'projection');
+    test.strictEqual(metadata.scope, 'local');
+    test.strictEqual(metadata.store, 'memory');
+    test.strictEqual(metadata.allow, 'write');
+    test.strictEqual(metadata.parent, 'Account');
+    test.strictEqual(metadata.fields, ['login', 'password']);
+    test.strictEqual(Object.keys(defs).length, 0);
+  }
+
+  {
+    const meta = {
+      scope: 'system',
+      store: 'persistent',
+      allow: 'append',
+      schema: 'Account',
+      fields: ['login', 'password', 'otp'],
+    };
+    const root = {
+      findReference: (schemaName) => ({
+        name: schemaName,
+        fields: {
+          login: 'any',
+          password: 'string',
+          otpNotMentioned: 'here',
+        },
+      }),
+    };
+    const { defs, metadata } = getProjectionMeta(meta, root);
+    test.strictEqual(metadata.kind, 'projection');
+    test.strictEqual(metadata.scope, 'system');
+    test.strictEqual(metadata.store, 'persistent');
+    test.strictEqual(metadata.allow, 'append');
+    test.strictEqual(metadata.parent, 'Account');
+    test.strictEqual(metadata.fields, ['login', 'password', 'otp']);
+    test.strictEqual(Object.keys(defs).length, 3);
+    test.strictEqual(defs.login, 'any');
+    test.strictEqual(defs.password, 'string');
+    test.strictEqual(defs.otp, undefined);
+  }
+
+  test.end();
+});