diff --git a/requirements/specifications/index.md b/requirements/specifications/index.md
new file mode 100644
index 000000000..d2eda9457
--- /dev/null
+++ b/requirements/specifications/index.md
@@ -0,0 +1,70 @@
+# Firebolt Requirements
+
+Document Status: Working Draft
+
+See [Firebolt Requirements Governance](../governance.md) for more info.
+
+| Contributor | Organization |
+| -------------- | -------------- |
+| Jeremy LaCivita | Comcast |
+
+## 1. Overview
+All of the documents in this `/requirements/` directory comprise the collected set of requirements for this Firebolt version.
+
+This document covers the Firebolt Specification JSON, [./firebolt-specification.json](./firebolt-specification.json), which is a JSON document outlining all Firebolt Capabilities and whether they MUST, SHOULD, or COULD be supported.
+
+This document is written using the [IETF Best Common Practice 14](https://www.rfc-editor.org/rfc/rfc2119.txt) and should include the following summary in the Overview section:
+
+The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**NOT RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in [BCP 14](https://www.rfc-editor.org/rfc/rfc2119.txt) [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
+
+## 2. Table of Contents
+- [1. Overview](#1-overview)
+- [2. Table of Contents](#2-table-of-contents)
+- [3. Capabilities](#3-capabilities)
+ - [3.1. Capability Level](#31-capability-level)
+ - [3.1.1. Should vs Could](#311-should-vs-could)
+ - [3.1.2. Supported Capability](#312-supported-capability)
+
+## 3. Capabilities
+All Firebolt APIs are assigned a capability by either using it, managing it, or providing it.
+
+See [Capabilities](./general/capabilities/capabilities.md) for more info.
+
+### 3.1. Capability Level
+In the Firebolt Specification JSON, every capability is given a `level` of either:
+
+- `"must"`
+- `"should"`
+- `"could"`
+
+A capability with the level set to `"must"` **MUST** be supported or the implementation in question is not a certified Firebolt implementation.
+
+A capability with the level set to `"should"` **SHOULD** be supported, especially if the implementation supports similar requirements in a non-Firebolt interface.
+
+A capability with the level set to `"could"` **COULD** be supported at the implementations discretion.
+
+#### 3.1.1. Should vs Could
+This section is non-normative and does not denote any requiements.
+
+"Should" means that not supporting a capability has a detremental impact on a Firebolt platform, and care should be taken when deciding not to support a `"should"` capability.
+
+For example, a capability supporting HDMI outputs would likely be assigned a level of `"should"`, since most STB devices have HDMI outputs, while most TV devices do not.
+
+If a capability has a level of `"should"` and the device exposes the functionality in question through hardware or software, then it should either be treated as a `"must"` or a really good reason should exist for why it was not implemented.
+
+If a device has HDMI output ports, then it probably should implement this capability. While a device w/out HDMI output ports does not need to.
+
+"Could" means that this API is truly optional, for example a capability to expose the postal code of the device might be assigned a `level` of `"could"` since Firebolt platforms can justifiably opt not to support this.
+
+#### 3.1.2. Supported Capability
+A supported capability **MUST** have all of its `x-uses` APIs surfaced, supported, and passing Firebolt Compliance tests.
+
+A supported capability **MUST** have all of its `x-provides` APIs surfaced, supported, and passing Firebolt Compliance tests.
+
+A supported capability **MUST** either:
+
+> have all of its `x-manages` APIs surfaced, supported, and passing Firebolt Compliance tests.
+>
+> **OR**
+>
+> have a Firebolt Certification translation layer that translates all of its `x-manages` APIs into local, non-firebolt APIs so that certification can run.
diff --git a/src/js/github.io/index.mjs b/src/js/github.io/index.mjs
index 7681a9d7f..010fc49f7 100644
--- a/src/js/github.io/index.mjs
+++ b/src/js/github.io/index.mjs
@@ -116,32 +116,36 @@ const capabilities = () => {
}
})
- let manifest = '\n'
+ Object.entries(specification.capabilities).forEach( ([c, v]) => {
+ capabilities[c].level = v.level
+ })
+
+ let manifest = '\n## Capailities\n| Capability | Level | Uses | Provides | Manages |\n|-|-|-|-|-|\n'
const linkify = (method) => `[${method}](./${corerpc.methods.find(m => m.name === method) ? 'core' : 'manage'}/${method.split('.').shift()}/#${method.match(/\.on[A-Z]/) ? method.split('.').pop().charAt(2).toLowerCase() + method.split('.').pop().substring(3).toLowerCase() : method.split('.').pop().toLowerCase()})`
Object.keys(capabilities).sort().forEach(c => {
- manifest += `### \`${c}\`\n`
+ const use = capabilities[c].uses.length ? `${capabilities[c].uses.length}
${capabilities[c].uses.map(linkify).join('
')} ` : ''
+ const man = capabilities[c].manages.length ? `${capabilities[c].manages.length}
${capabilities[c].manages.map(linkify).join('
')} ` : ''
+ const pro = capabilities[c].provides.length ? `${capabilities[c].provides.length}
${capabilities[c].provides.map(linkify).join('
')} ` : ''
- if (capabilities[c].uses.length) {
- manifest += '\n| Uses |\n'
- manifest += '| ---- |\n'
- manifest += `| ${capabilities[c].uses.map(linkify).join('
')} |\n`
- manifest += '\n\n'
- }
+ manifest += `| \`${c}\` | **${capabilities[c].level.toUpperCase()}** | ${use} | ${pro} | ${man} |\n`
+ })
- if (capabilities[c].manages.length) {
- manifest += '\n| Manages |\n'
- manifest += '| ------- |\n'
- manifest += `| ${capabilities[c].manages.map(linkify).join('
')} |\n`
- manifest += '\n\n'
- }
- if (capabilities[c].provides.length) {
- manifest += '\n| Provides |\n'
- manifest += '| -------- |\n'
- manifest += `| ${capabilities[c].provides.map(linkify).join('
')} |\n`
- manifest += '\n\n'
- }
+ manifest += '\n## Methods\nCapability prefix `xrn:firebolt:capability` let off for readability.\n| Method | Level | Uses | Provides | Manages |\n|-|-|-|-|-|\n'
+
+ openrpc.methods.forEach(method => {
+ let uses = Object.entries(capabilities).filter( ([c, v]) => v.uses.includes(method.name)).map(([c, v]) => c)
+ let mans = Object.entries(capabilities).filter( ([c, v]) => v.manages.includes(method.name)).map(([c, v]) => c)
+ let pros = Object.entries(capabilities).filter( ([c, v]) => v.provides.includes(method.name)).map(([c, v]) => c)
+ let level = Array.from(new Set(uses.concat(mans).concat(pros).map(c => capabilities[c].level))).join(', ')
+
+ uses = uses.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ')
+ pros = pros.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ')
+ mans = mans.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ')
+ level = level.includes('must') ? 'must' : level.includes('should') ? 'should' : 'could'
+
+ manifest += `| ${linkify(method.name)} | **${level.toUpperCase()}** | ${uses} | ${pros} | ${mans} |\n`
})
diff --git a/src/js/version-specification/capabilities.mjs b/src/js/version-specification/capabilities.mjs
index bd5120bdb..ec23a1825 100644
--- a/src/js/version-specification/capabilities.mjs
+++ b/src/js/version-specification/capabilities.mjs
@@ -49,7 +49,7 @@ const doImport = (source, target, clear=false, report=false) => {
logSuccess(`${report ? 'Missing' : 'Adding'} capability ${capability}.`)
}
result.capabilities[capability] = Object.assign({
- level: 'must',
+ level: 'could',
use: {
public: uses.includes(capability),
negotiable: true && uses.includes(capability)
diff --git a/src/json/firebolt-specification.json b/src/json/firebolt-specification.json
index 13f662b93..a6f9f717e 100644
--- a/src/json/firebolt-specification.json
+++ b/src/json/firebolt-specification.json
@@ -1,164 +1,213 @@
{
- "schemaVersion": {
- "major": 1,
- "minor": 0,
- "patch": 0,
- "readable": "Firebolt Version Manifest v1.0.0"
- },
- "capabilities": {
- "xrn:firebolt:capability:lifecycle:initialize": {
- "level": "must",
- "use": {
- "public": true,
- "negotiable": false
- },
- "manage": {
- "public": false,
- "negotiable": false
- },
- "provide": {
- "public": false,
- "negotiable": false
- }
- },
- "xrn:firebolt:capability:lifecycle:ready": {
- "level": "must",
- "use": {
- "public": true,
- "negotiable": false
- },
- "manage": {
- "public": false,
- "negotiable": false
- },
- "provide": {
- "public": false,
- "negotiable": false
- }
- },
- "xrn:firebolt:capability:lifecycle:state": {
- "level": "must",
- "use": {
- "public": true,
- "negotiable": false
- },
- "manage": {
- "public": true,
- "negotiable": true
- },
- "provide": {
- "public": true,
- "negotiable": true
- }
- },
- "xrn:firebolt:capability:advertising:configuration": {
- "level": "could"
- },
- "xrn:firebolt:capability:account:id": {
- "level": "could"
- },
- "xrn:firebolt:capability:advertising:identifier": {
- "level": "could"
- },
- "xrn:firebolt:capability:discovery:sign-in-status": {
- "level": "could"
- },
- "xrn:firebolt:capability:discovery:watch-next": {
- "level": "could"
- },
- "xrn:firebolt:capability:discovery:watched": {
- "level": "could"
- },
- "xrn:firebolt:capability:input:keyboard": {
- "level": "should"
- },
- "xrn:firebolt:capability:grants:state": {
- "level": "should"
- },
- "xrn:firebolt:capability:localization:locality": {
- "level": "could"
- },
- "xrn:firebolt:capability:localization:location": {
- "level": "could"
- },
- "xrn:firebolt:capability:localization:postal-code": {
- "level": "could"
- },
- "xrn:firebolt:capability:localization:time-zone": {
- "level": "could"
- },
- "xrn:firebolt:capability:protocol:dial": {
- "level": "could"
- },
- "xrn:firebolt:capability:protocol:wifi": {
- "level": "could"
- },
- "xrn:firebolt:capability:privacy:settings": {
- "level": "could"
- },
- "xrn:firebolt:capability:token:account": {
- "level": "should"
- },
- "xrn:firebolt:capability:token:device": {
- "level": "should"
- },
- "xrn:firebolt:capability:token:platform": {
- "level": "should"
- },
- "xrn:firebolt:capability:token:root": {
- "level": "should"
- },
- "xrn:firebolt:capability:token:session": {
- "level": "should"
- },
- "xrn:firebolt:capability:usergrant:acknowledgechallenge": {
- "level": "should"
- },
- "xrn:firebolt:capability:usergrant:pinchallenge": {
- "level": "should"
- },
- "xrn:firebolt:capability:capabilities:info": {
- "level": "must",
- "use": {
- "public": true,
- "negotiable": false
- },
- "manage": {
- "public": false,
- "negotiable": false
- },
- "provide": {
- "public": false,
- "negotiable": false
- }
- },
- "xrn:firebolt:capability:capabilities:request": {
- "level": "must",
- "use": {
- "public": true,
- "negotiable": false
- },
- "manage": {
- "public": false,
- "negotiable": false
- },
- "provide": {
- "public": false,
- "negotiable": false
- }
- },
- "xrn:firebolt:capability:privacy:advertising": {
- "level": "could"
- },
- "xrn:firebolt:capability:metrics:distributor": {
- "level": "could"
- },
- "xrn:firebolt:capability:storage:secure": {
- "level": "could"
- },
- "xrn:firebolt:capability:account:uid": {
- "level": "should"
- }
- },
- "apis": {}
+ "schemaVersion": {
+ "major": 1,
+ "minor": 0,
+ "patch": 0,
+ "readable": "Firebolt Version Manifest v1.0.0"
+ },
+ "capabilities": {
+ "xrn:firebolt:capability:accessibility:audiodescriptions": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:accessibility:closedcaptions": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:accessibility:voiceguidance": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:account:id": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:account:uid": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:advertising:configuration": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:advertising:identifier": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:approve:content": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:approve:purchase": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:capabilities:info": {
+ "level": "must",
+ "use": {
+ "public": true,
+ "negotiable": false
+ }
+ },
+ "xrn:firebolt:capability:capabilities:request": {
+ "level": "must",
+ "use": {
+ "public": true,
+ "negotiable": false
+ }
+ },
+ "xrn:firebolt:capability:device:distributor": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:device:id": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:device:info": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:device:make": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:device:model": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:device:name": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:device:sku": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:device:uid": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:discovery:content-access": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:discovery:entity-info": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:discovery:interest": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:discovery:navigate-to": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:discovery:policy": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:discovery:purchased-content": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:discovery:sign-in-status": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:discovery:watch-next": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:discovery:watched": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:grants:state": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:input:keyboard": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:inputs:hdmi": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:lifecycle:initialize": {
+ "level": "must",
+ "use": {
+ "public": true,
+ "negotiable": false
+ }
+ },
+ "xrn:firebolt:capability:lifecycle:launch": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:lifecycle:ready": {
+ "level": "must",
+ "use": {
+ "public": true,
+ "negotiable": false
+ }
+ },
+ "xrn:firebolt:capability:lifecycle:state": {
+ "level": "must",
+ "use": {
+ "public": true,
+ "negotiable": false
+ }
+ },
+ "xrn:firebolt:capability:localization:additional-info": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:localization:country-code": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:localization:language": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:localization:locale": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:localization:locality": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:localization:location": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:localization:postal-code": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:localization:time-zone": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:metrics:distributor": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:metrics:general": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:metrics:media": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:network:status": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:privacy:advertising": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:privacy:settings": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:profile:flags": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:protocol:dial": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:protocol:wifi": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:rpc:discover": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:storage:secure": {
+ "level": "should"
+ },
+ "xrn:firebolt:capability:token:account": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:token:device": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:token:platform": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:token:root": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:token:session": {
+ "level": "could"
+ },
+ "xrn:firebolt:capability:usergrant:acknowledgechallenge": {
+ "level": "must"
+ },
+ "xrn:firebolt:capability:usergrant:pinchallenge": {
+ "level": "should"
+ }
+ }
}
\ No newline at end of file