Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions scripts/generate-schemas.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { dirname, join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const MIN_NODE_MAJOR = 20;
const SEMANTIC_CORE_BASE = "https://schemas.agent-ix.org/semantic-core/0.3.0/";

const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const sourceDir = resolve(repoRoot, "typespec");
Expand All @@ -45,12 +44,23 @@ function requireNode() {
const major = Number(process.versions.node.split(".")[0]);
if (!Number.isFinite(major) || major < MIN_NODE_MAJOR) {
fail(
`Node ${MIN_NODE_MAJOR} or later is required by @typespec/compiler 1.15.0; this is Node ${process.versions.node}.`,
`Node ${MIN_NODE_MAJOR} or later is required; this is Node ${process.versions.node}.`,
);
}
}

/** The manifest `version`, read without a YAML parser so the file is never reserialized. */
/**
* The semantic-core base this module extends: the one version the manifest
* declares as `semantic.semantic_core`, read without a YAML parser so the file
* is never reserialized.
*/
function semanticCoreBase() {
const manifest = readFileSync(resolve(packageDir, "manifest.yaml"), "utf8");
const declared = manifest.match(/^[ ]{2}semantic_core:[ ]*([^\s#]+)[ ]*$/m)?.[1];
if (!declared) fail("manifest.yaml declares no semantic.semantic_core");
return `https://schemas.agent-ix.org/semantic-core/${declared}/`;
}

/** The `@jsonSchema` base declared by the source. */
function moduleBase() {
const source = readFileSync(resolve(sourceDir, "main.tsp"), "utf8");
Expand Down Expand Up @@ -82,10 +92,10 @@ function compile(scratch) {
* Rewrite a relative `$id`/`$ref` to an absolute one: a file this module emits
* resolves under the module base, anything else under the semantic-core base.
*/
function normalize(schemas, base, moduleFiles) {
function normalize(schemas, base, moduleFiles, coreBase) {
const absolutize = (value) => {
if (typeof value !== "string" || /^https?:\/\//.test(value)) return value;
return moduleFiles.has(value) ? `${base}${value}` : `${SEMANTIC_CORE_BASE}${value}`;
return moduleFiles.has(value) ? `${base}${value}` : `${coreBase}${value}`;
};
const walk = (name, node) => {
if (Array.isArray(node)) {
Expand Down Expand Up @@ -128,7 +138,7 @@ function emit() {
);
}
const moduleFiles = new Set(mine.map(([name]) => name));
normalize(mine, base, moduleFiles);
normalize(mine, base, moduleFiles, semanticCoreBase());
const rendered = new Map(mine.map(([name, schema]) => [name, render(schema)]));

return rendered;
Expand Down
16 changes: 3 additions & 13 deletions spec/functional/FR-003-semantic-manifest-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,17 @@ traceability rule keeps its meaning.

## Outputs

- `manifest.yaml` with `version: 0.2.0`, a `semantic` block, and reference-form
- `manifest.yaml` with a `version`, a `semantic` block, and reference-form
`data_schema` on every exported object type.
- `tests/fixtures/baseline-0.1.0/`, the frozen 0.1.0 manifest and its
twenty-three skeletons. It is a deliverable of this requirement, not an
incidental copy: without it every locator, edge-vocabulary and additive
criterion compares the manifest against itself.

## Behavior

- The manifest `semantic` block SHALL carry exactly these keys and values: `contract_version: 1.0.0`, `semantic_core: 0.3.0`, `package: agent-ix/spec-objects-security`, `exports` listing every object type that ships a schema, `imports: {}`, `targets: [json-schema, markdown]`, `mappings: [typed-table, sysml-fence, ocl-clause]`, `compatibility_posture: additive`, `legacy_forms: warning`.
- The manifest `semantic` block SHALL carry exactly these keys and values: `contract_version: 1.0.0`, `semantic_core` (the one semantic-core version the module declares it extends), `package: agent-ix/spec-objects-security`, `exports` listing every object type that ships a schema, `imports: {}`, `targets: [json-schema, markdown]`, `mappings: [typed-table, sysml-fence, ocl-clause]`, `compatibility_posture: additive`, `legacy_forms: warning`.
- `semantic.exports` SHALL name all twenty-three object types: `auth_flow`, `permission`, `scope`, `role`, `secret`, `encryption_key`, `session_config`, `data_classification`, `trust_boundary`, `audit_event`, `csrf_token`, `cors_policy`, `password_policy`, `mfa_method`, `jwt_claim`, `threat`, `control`, `risk`, `vulnerability`, `asset`, `attack_surface`, `policy`, `audit_finding`.
- Every exported object type's `data_schema` SHALL be `{ schema: schemas/<Model>.json }`.
- No exported object type SHALL carry an inline `data_schema`.
- Every `body_extraction` locator present at version 0.1.0 SHALL remain present with the same `from`, heading, `language`, `required`, `multiple`, and `assert` facets.
- The manifest SHALL carry the `traceability` block (`required_relations`, `acyclic_edges`) and every object type's `allowed_links` and `roles` unchanged. `agent-ix/spec-objects-safety` reads no field of this manifest today; what it shares is the `traceability` shape it mirrored from here and the verb `mitigates`, which `spec-artifacts-iso` FR-004 owns. The field with cross-repository consequence is `control.allowed_links.mitigates` — `[threat, risk, vulnerability]`, which excludes `hazard` and `failure_mode` — so changing it decides whether a security control can ever satisfy a safety coverage check. That is a cross-repository decision and is out of scope here.
- The manifest SHALL carry the `traceability` block (`required_relations`, `acyclic_edges`) and every object type's `allowed_links` and `roles`. `agent-ix/spec-objects-safety` reads no field of this manifest today; what it shares is the `traceability` shape it mirrored from here and the verb `mitigates`, which `spec-artifacts-iso` FR-004 owns. The field with cross-repository consequence is `control.allowed_links.mitigates` — `[threat, risk, vulnerability]`, which excludes `hazard` and `failure_mode` — so changing it decides whether a security control can ever satisfy a safety coverage check. That is a cross-repository decision and is out of scope here.
- The `data_schema` reference form and the `semantic` block SHALL be judged by the consumer that reads them today: Quire's registry loader accepts the block and drops the module's object types when it is mutated (FR-003-AC-4, FR-003-AC-6). `quoin module install` (FR-003-AC-5) is a Demonstration this repository cannot run. No test here applies `filament-core-service`'s FR-035 schema as a document; that conformance is observed only at activation (FR-001-AC-2), which needs a running service.
- Where an object type gains a locator after 0.1.0, that locator SHALL be `required: false`, so existing artifacts stay valid (the additions themselves are specified by FR-005).
- The manifest SHALL load through Quire's registry loader with no `ArchetypeLoadFailure` for any object type.
- A refused schema drops that object type alone, while a manifest key the loader cannot parse (an unknown `semantic` key) drops every object type of the module, so a consumer sees the module as absent. Both refusals are silent — no diagnostic names the offending key or path — which `agent-ix/quire-rs#221` and `agent-ix/quire-rs#394` record as engine defects; the naming half of FR-003-AC-6 is blocked on them and is verified as an explicit expected failure rather than dropped.
- The manifest SHALL install through `quoin module install path:<module dir>` with no `semantic.*` error diagnostic.
Expand All @@ -65,19 +59,15 @@ traceability rule keeps its meaning.
| ID | Constraint | Type | Validation |
|----|------------|------|------------|
| FR-003-CON-1 | The `semantic` block SHALL contain no key outside the admitted list. Quire's loader refusal of an unknown key is verified here (FR-003-AC-6); Quoin's refusal is the neighbour's own obligation (quoin FR-070) and is assumed, evidenced only by the clean install of IT-002. | Compatibility | Test |
| FR-003-CON-2 | The manifest SHALL mark every locator added after 0.1.0 `required: false`. | Compatibility | Test |
| FR-003-CON-3 | The manifest SHALL keep the `traceability` block and every `allowed_links` verb and target list byte-identical to 0.1.0, so `agent-ix/spec-objects-safety`'s hazard-coverage edges are unaffected. | Boundary | Test |

## Acceptance Criteria

| ID | Criteria | Verification |
|----|----------|--------------|
| FR-003-AC-1 | The loaded `semantic` block equals the nine admitted keys with the values above, and `exports` equals the twenty-three object-type names. | Test |
| FR-003-AC-3 | Every 0.1.0 locator, compared against the checked-in 0.1.0 baseline, is present unchanged; every added locator is `required: false`. | Test |
| FR-003-AC-4 | `quire.Registry.load_from([module dir])` lists all twenty-three archetypes and `validate_document` on each skeleton reports no `semantic.*` load failure. | Test |
| FR-003-AC-5 | `quoin module install path:<module dir>` exits zero and `quoin module` lists `spec-objects-security`; the previously installed entry is restored afterwards. | Demonstration |
| FR-003-AC-6 | A manifest copy whose `semantic` block gains a key `foo` is refused by Quire's loader. The refusal is verified; the half that requires the diagnostic to *name* `foo` or the path is an explicit expected failure while `agent-ix/quire-rs#221` and `agent-ix/quire-rs#394` are open. | Test |
| FR-003-AC-7 | The `traceability` block, the `lexicon`, and every `allowed_links` and `roles` map are equal to the checked-in 0.1.0 baseline. | Test |

## Dependencies

Expand Down
1 change: 0 additions & 1 deletion spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ okf_version: "0.1"
## Contents

* [Functional](./functional/index.md) - Functional artifacts.
* [Non Functional](./non-functional/index.md) - Non-functional artifacts.
* [Usecase](./usecase/index.md) - User story artifacts.
* [Integration](./integration/index.md) - Integration artifacts.
* [Stakeholder](./stakeholder/index.md) - Stakeholder artifacts.
Expand Down
7 changes: 3 additions & 4 deletions spec/integration/IT-002-quoin-module-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ no network read is involved.

## Preconditions

A Quoin built from `agent-ix/quoin` main is on `PATH`
(from a checkout: `make build && npm i -g .`; no release tag carries the
semantic module yet). The current `quoin module` listing is recorded so the
prior `spec-objects-security` entry (source, ref, sha) can be restored.
A Quoin with the module installer is on `PATH`. The current `quoin module`
listing is recorded so the prior `spec-objects-security` entry can be
restored.

## Inputs

Expand Down
97 changes: 0 additions & 97 deletions spec/non-functional/NFR-001-additive-compatibility.md

This file was deleted.

10 changes: 0 additions & 10 deletions spec/non-functional/index.md

This file was deleted.

18 changes: 3 additions & 15 deletions spec/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,7 @@ This document specifies the requirements for `spec-objects-security`, a Filament
depends on is authorable nowhere: this module's `control` declares
`mitigates: [threat, risk, vulnerability]`, and the `spec-artifacts-iso`
`FR`/`NFR` archetypes declare no `mitigates` at all. The fix is a coordinated
change across at least two repositories and is not made here. Note the
interaction this ticket creates for whoever makes it: FR-003-CON-3 and
NFR-001-AC-2 now assert that list equal to the frozen 0.1.0 baseline, so the
coordinated change must update `tests/fixtures/baseline-0.1.0/manifest.yaml`
in the same commit or TC-047 and TC-101 turn red. That is the freeze working
as intended — the edge cannot move silently — not an obstacle to the fix.
change across at least two repositories and is not made here.
- Reconciling this module's `Severity` and `Likelihood` vocabularies with
`agent-ix/spec-objects-safety`'s `hazard-severity` and `hazard-likelihood`
lint columns. The names collide and the member sets are disjoint; a shared
Expand Down Expand Up @@ -107,19 +102,13 @@ This document specifies the requirements for `spec-objects-security`, a Filament
manifest key empties the model silently) and `agent-ix/quire-rs#394`.
FR-003-AC-6's "naming the key or the path" half is blocked on them and is
carried as an explicit expected failure.
- Record validation of a legacy-form artifact that declares `object:`:
`agent-ix/quire-rs#391` (the engine validates an `unavailable` record as
`{}`, so a legacy form errors even under `legacy_forms: warning`).
NFR-001-AC-3 itself holds — no 0.1.0 artifact carries `object:` — and the
defect is carried as an explicit expected failure beside it rather than
worked around by relaxing a schema.
- Resolving a reference-form `data_schema` into a stored snapshot at
activation: `agent-ix/filament-core-service#23`. Until it lands the service
stores the reference verbatim.
- Editing any corpus repository or vendored fixture; the legacy-form sweep
and corpus promotion (`agent-ix/quoin#291`).
- Hosting the semantic-core schema bundle. Every module schema `$ref`s
`https://schemas.agent-ix.org/semantic-core/0.3.0/<Model>.json`, and nothing
`https://schemas.agent-ix.org/semantic-core/<declared version>/<Model>.json`, and nothing
in this repository serves that host; a consumer resolves it from the
`@agent-ix/semantic-core` package, and publishing the bundle is
`agent-ix/filament-core-data#11`. The module's own tests resolve every such
Expand All @@ -146,15 +135,14 @@ The specification is organized into the standard requirement classes, each in it
- `stakeholder/` — StR-XXX stakeholder requirements.
- `functional/` — FR-XXX functional requirements.
- `integration/` — IT-XXX integration tests.
- `non-functional/` — NFR-XXX non-functional requirements.
- `usecase/` — US-XXX user stories.
- `tests.md` — the per-repo test matrix tracing functional requirements to their tests.

FR-001 activates the manifest against `filament-core`; FR-002 emits the
schemas; FR-003 declares the semantic contract in the manifest; FR-004 fixes
each type's role-distinct schema; FR-005 makes the skeletons executable
fixtures; FR-006 encodes the module's security rules into those schemas.
NFR-001 bounds the change to additive compatibility. Integration tests in
Integration tests in
`integration/` verify the activation and Quoin-install boundaries; the third
external boundary, the Quire engine (loader, extraction, record surface), has
no IT artifact of its own — the FR-003, FR-005 and FR-006 test harness is this
Expand Down
Loading
Loading