Skip to content

v0.2.0 — capability/permission vocabulary split + manifest hardening#3

Merged
razukc merged 12 commits into
mainfrom
worktree-v0.2-rename
May 15, 2026
Merged

v0.2.0 — capability/permission vocabulary split + manifest hardening#3
razukc merged 12 commits into
mainfrom
worktree-v0.2-rename

Conversation

@razukc
Copy link
Copy Markdown
Owner

@razukc razukc commented May 15, 2026

A breaking release. Splits the public-API 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

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[]

v0.1 v0.2
CapabilityManifest.capabilities[] 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 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 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. Five rules:
    • permission.reason.too_short — reason ≥ 5 words.
    • action.description.too_short — description ≥ 5 words.
    • permission.network.host_wildcard — no `*` in any host.
    • permission.storage.scope_wildcard — no `*` in any scope.
    • permission.reason.no_action_ref (warning) — every permission's reason should mention an action that references it.
  • ApprovalLifecycle.submit() now runs hardening and throws ApprovalError on the first hardening error. New return shape SubmitResult extends CapabilityRecord with warnings: ManifestWarning[].
  • 13 new tests covering each rule + wiring. Total: 737 → 750.
  • README rewritten around a show-by-failing opener. 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>…"; fixture passes hardening with 0 errors, 0 warnings.

Test plan

  • npx tsc --noEmit — 0 errors
  • npx vitest run — 750 / 750 (48 files)
  • npm run demo — 24 / 24 e2e phases pass; 54 demo assertions total
  • npm run build — clean
  • npm pack --dry-run — writmint@0.2.0 · 81.4 kB · 93 files · ships only dist/ LICENSE NOTICE README.md package.json
  • dist surface confirmed: hardenManifest, ManifestWarning, SubmitResult, PermissionError, createPermissionRegistry all exported; no stale feature-manifest.* or capabilities.* artifacts
  • README quickstart compiled and ran end-to-end (2 audit events captured)
  • Beat 2 of the README opener verified by running it — permission.network.host_wildcard matches code/where/expected/actual exactly

Migration for any v0.1 consumer

  1. Update import names per the two tables above.
  2. Rename capabilitiespermissions in every manifest object (outer and inside actions).
  3. Re-submit and re-approve every manifest (hash shift).
  4. Lengthen any reason or action description shorter than 5 words; remove any wildcard hosts/scopes; consider adding the referencing action id to each reason to clear the no-action-ref warning.

Raju KC added 12 commits May 12, 2026 21:31
…to 'capability' (wave A.5 — vocabulary follow-up)
…permissions[]` and error codes `capability.*` -> `permission.*`

- CapabilityManifest.capabilities -> .permissions (the Permission[] array)
- ActionManifest.capabilities -> .permissions (the PermissionId[] grant list)
- broker fields: capabilityId -> permissionId (NetworkBroker, StorageBroker, ClockBroker, AuditBroker, UiBroker)
- AuditTransport.emit envelope: capabilityId -> permissionId
- replay envelope: capabilityId -> permissionId
- CapabilityError -> PermissionError
- error code rename (inner-permission concerns):
    manifest.capabilities.* -> manifest.permissions.*
    action.capabilities.*   -> action.permissions.*
    action.capability_ref.* -> action.permission_ref.*
    capability.{not_object,type,duplicate_id,undeclared,denied,no_broker,action.*} -> permission.*
    capability.network.{hosts,host_value,methods,method_value,no_transport,bad_url,host_denied,method_denied} -> permission.*
    capability.storage.{mode,no_transport,read_denied,write_denied} -> permission.*
    capability.audit.no_transport -> permission.audit.no_transport
- fixHints updated to match new vocab
- fixture manifest.ts, smoke.ts, end-to-end.ts updated for field + code assertions

The outer 'capability' identifier (capabilityId, capabilityVersionHash, AuditEventKind 'capability_emit') is unchanged — that remains the manifest-level identifier per the v0.2 vocab split.

Breaking change: manifest hashes change (new shape). Existing approvals will fall back to 'submitted' on first run; require re-approval.

Gate: tsc=0, tests=737/737, demo=24/24.
…nings)

- permission.reason.too_short: reason must be >=5 words
- action.description.too_short: action description must be >=5 words
- permission.network.host_wildcard: no '*' in network hosts
- permission.storage.scope_wildcard: no '*' in storage scope
- permission.reason.no_action_ref (warning): reason should mention a referencing action id

hardenManifest() lives in capability-manifest.ts alongside validateCapabilityManifest().
….warnings

submit() now runs hardening after structural validation. If any hardening
errors exist it throws ApprovalError on the first one. Warnings are
surfaced on the new SubmitResult shape (CapabilityRecord + warnings[]).
Clears the 7 permission.reason.no_action_ref warnings the hardening
checks would otherwise surface on the suspicious-transaction-triage demo.
Each reason now opens with 'Used by <action.id>' so an approver can trace
every grant to its caller.
- Locks the tagline: 'Writmint is a verifier for capabilities an author
  can't author past.'
- 4-beat opener: agent writes manifest with a wildcard host -> submit fails
  with the verified-real permission.network.host_wildcard error -> fix and
  resubmit -> run; every brokered call lands in the audit sink.
- Updates every API name to v0.2: CapabilityManifest, permissions[],
  MemoryCapabilityStore, createPermissionRegistry, PermissionError,
  permission.* error codes, capabilityId (outer wrapper field), scope.cap().
- Mentions hardenManifest() under pillar 1.
- Updates test count 737 -> 750.
CHANGELOG entry documents the inner/outer vocabulary split
(feature -> capability outer, capabilities[] -> permissions[] inner),
manifest hardening (5 rules), and the resulting hash shift requiring
re-submit + re-approve for any manifest carried over from v0.1.

package.json: version 0.1.0 -> 0.2.0; description rewritten around the
locked tagline; keywords swap feature-manifest/capability-scoping for
capability-manifest/permission-scoping and add manifest-hardening.
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot Bot commented May 15, 2026

Kilo Code Review could not run — your account is out of credits.

Add credits or switch to a free model to enable reviews on this change.

@razukc razukc merged commit de9b27a into main May 15, 2026
2 of 3 checks passed
@razukc razukc deleted the worktree-v0.2-rename branch May 15, 2026 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant