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
98 changes: 98 additions & 0 deletions .changeset/15932-plugin-security-scan-result-surface-retired.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
'@objectstack/spec': minor
---

feat(spec)!: retire the plugin-security scan-result surface — zero consumers after the scanner retirement (#15932)

**BREAKING** — the plugin-security scan-result family is removed. ADR-0049
enforce-or-remove; maintainer ruling 2026-09-07 (director seat, decision batch
#65), adopted verbatim 「同意」.

This is the second half of the scanner retirement — issue 14919, a number since
deleted from the board, landed as PR #15930. That change retired `PluginSecurityScanner`,
the `@objectstack/core` class that shipped as a security control and returned
`status: "passed"` for every plugin it was ever handed. The **schemas** it fed
survived it — and that scanner's type-only import was their only importer of any
kind, so the family went from one type-only importer to **zero consumers** while
staying fully published: 27 authorable rows on `authorable-surface/kernel.json`,
six `api-surface` exports, two authorable defaults and two json-schema manifest
keys, with no `.parse` or `.safeParse` site against either schema anywhere. An
author could write any of it, be accepted, and get nothing. That is the
declared-not-enforced shape, one layer out from the class removed for the same
reason. "Declare an owner to enforce" was refused by name: it would rebuild the
scanner just retired.

### FROM → TO

| removed | what to write instead |
| --- | --- |
| `KernelSecurityScanResult`, `KernelSecurityScanResultParsed`, `KernelSecurityScanResultSchema` (exports) | nothing — delete the import. No replacement type exists. |
| `KernelSecurityVulnerability`, `KernelSecurityVulnerabilityParsed`, `KernelSecurityVulnerabilitySchema` (exports) | nothing — delete the import. No replacement type exists. |
| `PluginSecurityManifest.scanResults` | delete the key |
| `PluginSecurityManifest.vulnerabilities` | delete the key |
| `PluginQualityMetrics.securityScan` | delete the key |

**The one-line fix: delete the keys and every import of the two types.** Plugin
security scanning is not a platform capability and there is no replacement
schema. What the platform does still enforce is unchanged: `permissions` and
`sandbox` on the same `PluginSecurityManifest`, and artifact provenance through
`verifyPluginArtifactIntegrity` and the plugin signature verifier — which tell
you an artifact is the one its publisher signed, and never that it is safe. For
dependency vulnerabilities use the tools built for it against your own project
(`npm audit` / `pnpm audit`, Dependabot, the GitHub Advisory Database, OSV), and
treat an unaudited third-party plugin as untrusted code. A publisher who used
`scanResults` to advertise diligence keeps the surviving `securityContact` and
`vulnerabilityDisclosure` blocks, which are contact terms rather than a verdict.

⚠️ Runtime behaviour is deliberately **unchanged**. Nothing ever read any of
these keys, so deleting one removes no check that was running. A consumer that
gated on `securityScan.passed === true` was gating on nothing — the remediation
is to audit with a real tool, not to find a replacement key.

### The retirement kit

- The two **defs** leave the build whole — `RETIRED_DEFS_BY_MAJOR[18]`
(`kernel/KernelSecurityScanResult`, `kernel/KernelSecurityVulnerability`) —
because nothing parses them, so there is no author a tombstone could reach.
- The three **authorable keys** are `retiredKey()` tombstones registered in
`RETIRED_KEYS_BY_MAJOR[18]`. Neither carrying shape is `.strict()`, so a bare
deletion would strip an authored key in silence (ADR-0104): the tombstone is
audible in both channels — `tsc` (input type `never`) and the parse, which
raises the prescription itself.
- **No D2 conversion.** A plugin security manifest and a plugin registry entry
are package artifacts a publisher ships, never stack collection members and
never stored `sys_metadata` rows, so the chain has no seam that would see one
— the disposition the sibling `kernel-plugin-security-durations-unit-in-key`
entry already records for this same manifest. The D3 semantic entry
`plugin-security-scan-result-surface-retired` carries the judgement.
- `PluginSecurityManifest.vulnerabilities` is a **forced consequence**, not one
of the four names the ruling listed: it was the last authorable referent of
`KernelSecurityVulnerability` and could not outlive the def.
- **No deprecation window** (maintainer 2026-08-27: 「项目在创业阶段,用户也很少,短期不考虑渐进」).

⚠️ **The out-of-repo consumer population is NOT MEASURED.** `@objectstack/spec`
is published, so this is breaking for consumers no download, dependent or source
telemetry was consulted for — exactly as that retirement's own changeset says of its
three exports. That was an input to the ruling, not a reason to soften the removal.

⛔ **Untouched, and not checked:** the marketplace `'scanning'` status
(`marketplace.zod.ts`). The ruling made it conditional on a producer grep of
`objectstack-ai/cloud`, and that repository was not reachable from the session
that executed this card, so it stays exactly as it is and its absence from this
diff is not evidence about it.

⚠️ **The two members the ruling paired with it were ALREADY GONE** — measured on
this tree, not assumed. The incident `'malware'` type was a member of
`system/IncidentCategory`, and the whole incident-response family was retired by
#15513 (maintainer ruling 2026-09-05 — two days *before* the 2026-09-07 ruling
that made it conditional). `marketplace-admin.zod.ts` was deleted outright with
the cloud subpath (#16526). Both files return zero tree entries here, against a
lit control where `'scanning'` still returns a live declaration. So the
conditional question is **one** enum member wide, not three.

`Clause-②: yes (narrowing)` — a published surface is removed: six exports leave
the built `.d.ts` and three authorable keys stop being writable, so the accept
set a consumer writes against narrows. Nothing is widened and nothing is
renamed. Contract-review tier.

<!-- adr-0087: registered plugin-security-scan-result-surface-retired -->
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1533 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1531 schemas across 14 protocol modules
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand All @@ -25,15 +25,15 @@ counts are sums of the rows they head. Regenerate with
| [Data Protocol](/docs/references/data) | 29 | 175 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
| [Integration Protocol](/docs/references/integration) | 1 | 24 | The single connector protocol (ADR-0097) — catalog descriptors and provider-bound instances. |
| [Kernel Protocol](/docs/references/kernel) | 30 | 159 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
| [Kernel Protocol](/docs/references/kernel) | 30 | 157 | Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry. |
| [Marketplace Protocol](/docs/references/marketplace) | 4 | 30 | The package & marketplace format — package identity and versions, listing, publish, review, search, install, template manifests. |
| [QA Protocol](/docs/references/qa) | 1 | 8 | Declarative test suites — scenarios, steps, actions and assertions. |
| [Security Protocol](/docs/references/security) | 5 | 30 | Permission sets, row-level security, sharing rules, tenancy posture. |
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
| [System Protocol](/docs/references/system) | 34 | 273 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
| [UI Protocol](/docs/references/ui) | 16 | 158 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
| **Total** | **195** | **1533** | 14 protocol modules |
| **Total** | **195** | **1531** | 14 protocol modules |

---

Expand Down Expand Up @@ -197,7 +197,7 @@ The single connector protocol (ADR-0097) — catalog descriptors and provider-bo

## Kernel Protocol

**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **30 pages, 159 schemas**
**Source:** `packages/spec/src/kernel/` · **Import:** `@objectstack/spec/kernel` · **30 pages, 157 schemas**

Plugin lifecycle and manifests, capabilities and security, metadata loading, service registry.

Expand Down Expand Up @@ -227,7 +227,7 @@ Plugin lifecycle and manifests, capabilities and security, metadata loading, ser
| [`plugin-loading.zod.ts`](/docs/references/kernel/plugin-loading) | `PluginLoadingEvent`, `PluginLoadingState` |
| [`plugin-registry.zod.ts`](/docs/references/kernel/plugin-registry) | `PluginInstallConfig`, `PluginQualityMetrics`, `PluginRegistryEntry`, `PluginSearchFilters`, `PluginStatistics`, `PluginVendor` |
| [`plugin-security.zod.ts`](/docs/references/kernel/plugin-security) | `DependencyGraph`, `DependencyGraphNode`, `PackageDependencyConflict`, `PackageDependencyResolutionResult`, `PluginProvenance`, `PluginTrustScore`, `ResolvedPackageDependency`, `SBOM`, `SBOMEntry`, `SecurityPolicy`, `SecurityScanResult`, `SecurityVulnerability`, `VulnerabilitySeverity` |
| [`plugin-security-advanced.zod.ts`](/docs/references/kernel/plugin-security-advanced) | `KernelSecurityPolicy`, `KernelSecurityScanResult`, `KernelSecurityVulnerability`, `PermissionAction`, `PermissionScope`, `PluginPermission`, `PluginPermissionSet`, `PluginSecurityManifest`, `PluginTrustLevel`, `ResourceType`, `RuntimeConfig`, `SandboxConfig` |
| [`plugin-security-advanced.zod.ts`](/docs/references/kernel/plugin-security-advanced) | `KernelSecurityPolicy`, `PermissionAction`, `PermissionScope`, `PluginPermission`, `PluginPermissionSet`, `PluginSecurityManifest`, `PluginTrustLevel`, `ResourceType`, `RuntimeConfig`, `SandboxConfig` |
| [`plugin-structure.zod.ts`](/docs/references/kernel/plugin-structure) | `OpsDomainModule`, `OpsFilePath`, `OpsPluginStructure` |
| [`plugin-validator.zod.ts`](/docs/references/kernel/plugin-validator) | `PluginMetadata`, `ValidationError`, `ValidationResult`, `ValidationWarning` |
| [`plugin-versioning.zod.ts`](/docs/references/kernel/plugin-versioning) | `BreakingChange`, `CompatibilityLevel`, `CompatibilityMatrixEntry`, `DependencyConflict`, `DeprecationNotice`, `MultiVersionSupport`, `PluginCompatibilityMatrix`, `PluginDependencyResolutionResult`, `PluginVersionMetadata`, `SemanticVersion`, `VersionConstraint` |
Expand Down
14 changes: 12 additions & 2 deletions content/docs/references/kernel/plugin-registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const result = PluginInstallConfigSchema.parse(data);
| **testCoverage** | `number` | optional | |
| **documentationScore** | `number` | optional | |
| **codeQuality** | `number` | optional | |
| **securityScan** | `{ lastScanDate?: string; vulnerabilities?: object; passed: boolean }` | optional | |
| **securityScan** | `never` | optional | [REMOVED] `PluginQualityMetrics.securityScan` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — the block declared a last-scan date, per-severity vulnerability counts and a `passed` verdict, and no scanner, registry, installer or UI ever produced or read one, so a plugin could publish `passed: true` with nothing at all behind it. Delete the key. There is no replacement key: plugin security scanning is not a platform capability, and the scan-result family this block mirrored was retired with it. The sibling quality metrics — `testCoverage`, `documentationScore`, `codeQuality` and `conformanceTests` — are unchanged. Audit dependencies with a tool built for it (npm audit, pnpm audit, Dependabot, the GitHub Advisory Database, OSV). |
| **conformanceTests** | `{ protocolId: string; passed: boolean; totalTests: integer; passedTests: integer; … }[]` | optional | |

### Nested Shape: `PluginQualityMetrics.conformanceTests[number]`
Expand Down Expand Up @@ -85,7 +85,7 @@ const result = PluginInstallConfigSchema.parse(data);
| **compatibility** | `{ minObjectStackVersion?: string; maxObjectStackVersion?: string; nodeVersion?: string; platforms?: Enum<'linux' \| 'darwin' \| 'win32' \| 'browser'>[] }` | optional | |
| **links** | `{ homepage?: string; repository?: string; documentation?: string; bugs?: string; … }` | optional | |
| **media** | `{ icon?: string; logo?: string; screenshots?: string[]; video?: string }` | optional | |
| **quality** | `{ testCoverage?: number; documentationScore?: number; codeQuality?: number; securityScan?: object; … }` | optional | |
| **quality** | `{ testCoverage?: number; documentationScore?: number; codeQuality?: number; conformanceTests?: object[] }` | optional | |
| **statistics** | `{ downloads: integer; downloadsLastMonth: integer; activeInstallations: integer; ratings?: object; … }` | optional | |
| **license** | `string` | optional | SPDX license identifier |
| **pricing** | `{ model: Enum<'free' \| 'freemium' \| 'paid' \| 'enterprise'>; price?: number; currency?: string; billingPeriod?: Enum<'one-time' \| 'monthly' \| 'yearly'> }` | optional | |
Expand Down Expand Up @@ -117,6 +117,16 @@ const result = PluginInstallConfigSchema.parse(data);
| **extensionPoints** | `{ id: string; name: string; description?: string; type: Enum<'action' \| 'hook' \| 'widget' \| 'provider' \| 'transformer' \| 'validator' \| 'decorator'>; … }[]` | optional | Points where other plugins can extend this plugin |
| **extensions** | `{ targetPluginId: string; extensionPointId: string; implementation: string; priority: integer }[]` | optional | Extensions contributed to other plugins |

### Nested Shape: `PluginRegistryEntry.quality`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **testCoverage** | `number` | optional | |
| **documentationScore** | `number` | optional | |
| **codeQuality** | `number` | optional | |
| **securityScan** | `never` | optional | [REMOVED] `PluginQualityMetrics.securityScan` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — the block declared a last-scan date, per-severity vulnerability counts and a `passed` verdict, and no scanner, registry, installer or UI ever produced or read one, so a plugin could publish `passed: true` with nothing at all behind it. Delete the key. There is no replacement key: plugin security scanning is not a platform capability, and the scan-result family this block mirrored was retired with it. The sibling quality metrics — `testCoverage`, `documentationScore`, `codeQuality` and `conformanceTests` — are unchanged. Audit dependencies with a tool built for it (npm audit, pnpm audit, Dependabot, the GitHub Advisory Database, OSV). |
| **conformanceTests** | `{ protocolId: string; passed: boolean; totalTests: integer; passedTests: integer; … }[]` | optional | |


---

Expand Down
Loading
Loading