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
96 changes: 96 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,102 @@ All notable changes to Writmint will land here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] — 2026-05-15

A **breaking** release. The public API splits its vocabulary into an outer
identity layer (the *capability* — the manifest as a unit of governance)
and an inner permission layer (the individual grants the manifest
declares). Adds manifest hardening that runs at submit-time so an agent
gets a structured rejection before approval, not after.

### Breaking changes

#### Outer identity layer renamed: feature → capability

The manifest itself, the lifecycle around it, and the audit envelope all
move to *capability* vocabulary.

| v0.1 | v0.2 |
|---|---|
| `FeatureManifest` | `CapabilityManifest` |
| `MemoryFeatureStore` | `MemoryCapabilityStore` |
| `FeatureStore` interface | `CapabilityStore` |
| `FeatureRecord` | `CapabilityRecord` |
| `FeatureStatus` | `CapabilityStatus` |
| `ApproveInput.featureId` | `ApproveInput.capabilityId` |
| `AuditEvent.featureId` / `featureVersionHash` | `AuditEvent.capabilityId` / `capabilityVersionHash` |
| `AuditEventKind` value `feature_emit` | `capability_emit` (and `capability_call` / `capability_denied`) |
| error code `approval.unknown_feature` | `approval.unknown_capability` |
| `src/feature-manifest.ts` | `src/capability-manifest.ts` |

#### Inner permission layer renamed: capabilities[] → permissions[]

Inside the manifest, what v0.1 called *capabilities* (one per permission
grant) is now *permissions*. The error vocabulary follows.

| v0.1 | v0.2 |
|---|---|
| `CapabilityManifest.capabilities[]` (was on FeatureManifest) | `permissions[]` |
| `ActionManifest.capabilities[]` | `permissions[]` |
| `CapabilityError` class | `PermissionError` |
| broker envelope field `capabilityId` (inner) | `permissionId` |
| `AuditTransport.emit({capabilityId,…})` | `AuditTransport.emit({permissionId,…})` |
| `createFeatureCapabilityRegistry()` | `createPermissionRegistry()` |
| `src/capabilities.ts` | `src/permissions.ts` |
| `capability.*` error codes (`capability.denied`, `capability.network.host_denied`, `capability.storage.write_denied`, `capability.undeclared`, `capability.action.unknown`, `capability.audit.no_transport`, …) | `permission.*` (same suffixes) |
| `manifest.capabilities.type` validator code | `manifest.permissions.type` |
| `action.capability_ref.type` / `.unknown` | `action.permission_ref.type` / `.unknown` |

The outer `capabilityId` on `AuditEvent` is the manifest identity; the
inner `permissionId` (also on `AuditEvent`, and on the broker emit
envelope) is the specific permission entry the event came through. Both
fields ride on every event.

#### Hash shift

`hashManifest()` is unchanged in algorithm, but every shipped manifest
will produce a different `versionHash` under v0.2 because the renamed
object keys (`permissions[]`, etc.) are part of the canonical hash input.
**Re-submit and re-approve any manifest carried over from v0.1.**

### Added

- **`hardenManifest()`** (`src/capability-manifest.ts`) — runs after
structural validation. Enforces five strictness rules that an approver
would otherwise have to check by eye:
- `permission.reason.too_short` — every `reason` must be ≥ 5 words.
- `action.description.too_short` — every action `description` must be
≥ 5 words.
- `permission.network.host_wildcard` — no `*` in any allowed host.
- `permission.storage.scope_wildcard` — no `*` in any storage scope.
- `permission.reason.no_action_ref` (warning) — every permission's
reason should mention at least one action that references it.
- **`ApprovalLifecycle.submit()`** now runs `hardenManifest()` and throws
`ApprovalError` on the first hardening error. The new return shape
`SubmitResult` extends `CapabilityRecord` with a `warnings:
ManifestWarning[]` field carrying non-blocking signals (e.g. the
no-action-ref warning).
- 13 new tests covering each hardening rule (positive + negative) and
`submit()` wiring. Total test count: **737 → 750**.
- README rewritten around a *show-by-failing* opener: the agent writes a
manifest with a wildcard host, `submit()` rejects it with a verified
structured error, the agent fixes and resubmits. Locks the tagline:
*"Writmint is a verifier for capabilities an author can't author past."*

### Changed

- `fixtures/suspicious-transaction-triage/manifest.ts` — every
`permission.reason` now opens with "Used by `<action.id>`…" so the
fixture passes hardening with **0 errors, 0 warnings**.

### Notes

- Pre-stable. Public API surface may change before v1.0.
- Requires Node ≥ 22.
- All 24 demo phases still pass.

[0.2.0]: https://github.com/razukc/writmint/releases/tag/v0.2.0

## [0.1.0] — 2026-05-01

Initial release. Five pillars + the canonical triage demo.
Expand Down
Loading
Loading