@gregiteen/ssss-cli 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.10.1] - 2026-09-28
11
+ ### Fixed
12
+ - A required list field set to an empty list (`deferred_steps: []`) was
13
+ rejected as "Missing required field". Required means present: only an
14
+ absent, `null` or empty-string value is missing now. Hosts had to patch the
15
+ package to write valid documents with empty lists.
16
+
17
+ ### Behavior changes hosts should check
18
+ - Documents with a required list field set to `[]` now validate.
19
+ - New conformance checks: an empty list satisfies a required list field;
20
+ absent, `null` and empty-string values are still missing.
21
+
10
22
  ## [0.10.0] - 2026-09-28
11
23
  ### Added
12
24
  - **`asset` primitive**: Text-encoded files (Base64 for small binaries, UTF-8 for code/SVGs) and pointer metadata for large files (`resource_ref`, `storage_uri`, `hash`).
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.10.1
@@ -106,7 +106,7 @@
106
106
  "file_count": 14,
107
107
  "provenance": {
108
108
  "content_hash": "sha256:7febd673938a3dc04c068e0bf08097a9783c09e612606d2df9a4db8dcb8907f5",
109
- "exporter": "@gregiteen/ssss-cli@0.10.0"
109
+ "exporter": "@gregiteen/ssss-cli@0.10.1"
110
110
  }
111
111
  },
112
112
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gregiteen/ssss-cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "CLI tools and the reference engine for the Structured Semantic Syntax System",
5
5
  "main": "src/engine.mjs",
6
6
  "exports": {
@@ -9,6 +9,7 @@ import {
9
9
  createRegistryLock,
10
10
  loadRegistries,
11
11
  resolvePrimitiveDefinition,
12
+ validateDataConstraints,
12
13
  verifyRegistryLock,
13
14
  } from '../src/registry.mjs';
14
15
  import { createValidator } from '../src/validator.mjs';
@@ -258,6 +259,16 @@ function checkFrontmatterSubset(check) {
258
259
  JSON.stringify(parseDocument(serializeDocument(data, '\nbody')).data) === JSON.stringify(data));
259
260
  }
260
261
 
262
+ // A required field is satisfied by any present value, including an empty
263
+ // list; only an absent, null or empty-string value is missing.
264
+ function checkRequiredPresence(check) {
265
+ const def = { required_fields: ['steps', 'name'] };
266
+ const missing = (data) => validateDataConstraints(def, data).map((i) => i.field).sort().join(',');
267
+ check('required fields: an empty list satisfies a required list field', missing({ steps: [], name: 'x' }) === '');
268
+ check('required fields: absent, null and empty-string values are missing',
269
+ missing({ name: '' }) === 'name,steps' && missing({ steps: null, name: 'x' }) === 'steps');
270
+ }
271
+
261
272
  // Capabilities (spec §6.6): trailing-`*` grants are prefix scoped, and the
262
273
  // pre-0.9 façade still honors the role permissions it always documented.
263
274
  async function checkCapabilities(root, check) {
@@ -606,6 +617,7 @@ export async function runKernel09Conformance() {
606
617
  check('UI planning redacts prompt-injection content and falls back safely', planned.generated && planned.manifest.components[0].bind === 'status');
607
618
  await checkResourceLifecycle(check);
608
619
  checkFrontmatterSubset(check);
620
+ checkRequiredPresence(check);
609
621
  await checkCapabilities(temp, check);
610
622
  } finally {
611
623
  fs.rmSync(temp, { recursive: true, force: true });
package/src/registry.mjs CHANGED
@@ -355,8 +355,9 @@ export function documentHash(content) {
355
355
  }
356
356
 
357
357
  function isEmpty(value) {
358
- return value === undefined || value === null || value === '' ||
359
- (Array.isArray(value) && value.length === 0);
358
+ // "Required" means present: an empty array (e.g. `deferred_steps: []`) is a
359
+ // valid value for a required list field, so it is not treated as missing.
360
+ return value === undefined || value === null || value === '';
360
361
  }
361
362
 
362
363
  /**