Files
PaperClipAI/doc/company-skill-policy.md
T
DottaandClaude Opus 5.5 9b21dcbdda refactor: remove SPEC docs and repoint spec citations (#20565)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Contributors and agents read the docs under `doc/` before they
change code
> - `doc/SPEC.md` and `doc/SPEC-implementation.md` were out of date.
`doc/SPEC-implementation.md` became an append-only log: in 60 days it
got about 1,645 new lines and lost 181, across 140 commits
> - #20557 changed `AGENTS.md` so the code and its tests are the source
of truth, and the specs are only background
> - This pull request removes the two spec docs. It moves only the
sections that live code comments cite into focused docs, and it repoints
all other references
> - The benefit is one less stale document that agents read and append
to

## Linked Issues or Issue Description

Refs #20557 (merged). This PR is the follow-up that removes the docs. It
is based on `master`, which already contains #20557.

**Issue type**
Outdated content. The docs are out of date and conflict with the code.

**Where is the issue?**
`doc/SPEC.md`, `doc/SPEC-implementation.md`, and the code comments and
docs that cite them.

**What's wrong?**
`doc/SPEC.md` and `doc/SPEC-implementation.md` are out of date, but
agents still read them and append a paragraph with each fix.

**Suggested fix**
Delete both files. Move the sections that code cites into focused docs.
Repoint the references.

## What Changed

- Deleted `doc/SPEC.md` and `doc/SPEC-implementation.md`.
- Moved these spec sections, because live code comments cite them:
- §9.3.1 "Shared default-open issue writes" → new section in
`doc/agent-permission-defaults.md`.
- §9.8.1 "Issue-thread interaction resolver contract" →
`doc/execution-semantics.md`, "Issue-thread interaction resolution".
That section already had most of the contract. I added only the missing
parts: the `supersedeOnUserComment` default, the canonical policy list,
the addressee and cap rules, the rule that surfaces show the server's
effective audience, and the cross-issue budget charge. The short policy
summary at the top of the section now points to the list, so the
definitions occur one time only.
- §9.9 "Task Watchdog Authority Contract" → new "Authority contract"
section in `doc/TASK-WATCHDOG.md`. It holds the allowed and disallowed
mutation lists and the deferred capabilities. The atomic recovery batch,
restoration verification, and interaction rules were already in
`doc/execution-semantics.md` §11, so the new section links there and
does not repeat them. I did not move the "Downstream acceptance
criteria" checklist or the sentence about the CTO and Security sign-off.
These were implementation-time notes; the tests now hold that coverage.
- §9.10 "Company Skill Policy Contract" → new
`doc/company-skill-policy.md`. No focused doc had this contract. I did
not move the "Required regression tests" phase checklist.
- Did not move any other part of the specs.
- Repointed code comments that cited spec sections:
- `packages/shared/src/issue-write-denial.ts` (§9.3) →
`doc/agent-permission-defaults.md`
- `server/src/routes/issues.ts` (§9.9) → `doc/TASK-WATCHDOG.md`,
"Authority contract"
- `server/src/routes/issues.ts` (§9.3, §9.8.1 cross-issue budget) →
`doc/agent-permission-defaults.md`
- `ui/src/lib/interaction-audience.ts` and
`ui/src/components/InteractionGovernancePanel.tsx` (§9.8.1) →
`doc/execution-semantics.md`
- `ui/src/lib/skill-policy-denial.ts`, `ui/src/pages/CompanySkills.tsx`,
`ui/src/pages/CompanySkills.production.tsx`,
`ui/src/pages/SkillStudio.tsx` (§9.10) → `doc/company-skill-policy.md`
- `server/src/services/recovery/ownership-invariant-report.ts` (monitor
deadline wait path) → `doc/execution-semantics.md` §8. That section
already lists the one-shot monitor as a valid wait path.
- Resynced the git blob pin for
`packages/shared/src/issue-write-denial.ts` in
`tests/runner-e2e/source-sdk-public-inputs.json`. The comment edit
changed the file's hash, and the Runner support suite checks that pin. I
used `node tests/runner-e2e/source-sdk-public-inputs.mjs <HEAD>
--write`; only that one pin changed.
- Updated `AGENTS.md` §1 so it does not name the deleted files.
- Removed the links to the deleted files from
`doc/plugins/PLUGIN_SPEC.md`.
- Updated the `doc-maintenance` agent skill
(`.agents/skills/doc-maintenance/`) so it no longer audits or stages
`doc/SPEC.md`.
- Removed or replaced references in `maps/README.md`,
`maps/feature-map/README.md`, `maps/storybook-map/README.md`,
`doc/PRODUCT.md`, `doc/CLIPHUB.md`, `doc/DEPLOYMENT-MODES.md`,
`doc/DEVELOPING.md`, `doc/evals.md`, `doc/spec/agent-runs.md`,
`doc/AGENTCOMPANIES_SPEC_INVENTORY.md`,
`doc/connections/CONNECTOR-PLAYBOOK.md`,
`doc/plugins/ideas-from-opencode.md`, `doc/execution-semantics.md`,
`doc/TASK-WATCHDOG.md`, and `.devin/wiki.json`.
- In `doc/execution-semantics.md`, the "admission is independent of
cleanup and accounting" reference now states the rule inline: only a
hard stop, an explicit pause, or an inactive company refuses admission.
- Kept these references as they are: dated plans in `doc/plans/` and
`docs/plans/`, the dated log
`doc/logs/2026-05-24-cli-api-parity-e2e-log.md`, `releases/`, the
transcript fixture `ui/src/fixtures/runTranscriptFixtures.ts` (it uses
the filename only as sample text), and
`docs/specs/external-task-protocol.md` (it refers to the Symphony
project's own `SPEC.md`).

## Verification

- `git grep -n "SPEC-implementation\|SPEC\.md"` (without `releases/`,
and ignoring `PLUGIN_SPEC.md` citations) finds only the kept references
listed above.
- `grep -rn "§9\.\(3\|8\|9\|10\)" ui/src server/src packages/shared/src`
finds no results.
- `node scripts/check-maps.mjs`: "Maps aligned: 35 areas, 192 capability
IDs".
- `pnpm check:token-gates`: "All gates clean".
- `pnpm exec vitest run --config tests/runner-e2e/vitest.config.ts
tests/runner-e2e/source-sdk-public-type.test.ts
tests/runner-e2e/source-sdk-evidence.test.ts`: 45 passed. These failed
in CI before the pin resync.
- `pnpm verify`: exit 2 ("Broader verification required") because
`AGENTS.md` is a global CI input. Required CI supplies the broad
coverage for this revision.
- All code changes are comments only. No runtime behavior changes. The
title uses `refactor:` because comments in source files changed.

## Risks

- Low risk. The change removes docs and edits comments only.
- A reader can lose context from spec sections that I did not move. The
text stays in git history. The code and its tests are the source of
truth.
- External links to the two deleted files will return 404.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), in Claude Code with
tool use (shell, file edits). Standard context window.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (focused checks above; CI
supplies broad coverage)
- [x] I have added or updated tests where applicable (not applicable:
docs and comments only)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 13:07:59 -06:00

9.4 KiB

Company skill policy

This document describes the company skill policy contract. The code and its tests are the source of truth; see server/src/services/company-skill-policy.ts and its tests.

Product default

An authenticated agent may perform normal company-skill work without a skill-specific grant when the target company has no explicit skill policy. This includes creating, importing, installing, editing, updating, testing, resetting, and removing skills. Core MUST NOT introduce a skills:author prerequisite, a draft-only default, or an activation-approval default.

Authorization order is fixed:

  1. Enforce non-configurable platform invariants.
  2. Evaluate the company's explicit skill policy when one exists.
  3. Otherwise allow the authenticated company agent.

Non-configurable invariants include authenticated actor identity, exact company scoping, source and workspace path containment, package and frontmatter validation, secret redaction/non-export, immutable audit attribution, and any hard runtime isolation rule. A policy rule, legacy grant, plugin, or EE configuration cannot override these invariants.

For avoidance of doubt:

  • Local-path imports, updates, resets, and project scans MUST resolve under a Paperclip-known local workspace root or a Paperclip-managed skill root. Arbitrary host filesystem paths are invalid even when the caller is otherwise authorized. Caller-supplied source, sourceLocator, or similar path strings are descriptive input only; they MUST NOT expand authority beyond those approved roots.
  • Remote imports and updates MUST normalize to a known source category, require validated HTTPS or catalog sources, and resolve immutable content before install (for example pinned Git commit/content hash or pinned package version). Unknown schemes, unknown source categories, symlink escapes, and out-of-tree files fail closed before persistence.
  • Unsafe executable content, fetch-and-exec patterns, and secret exfiltration or non-redacted secret material are platform safety failures. Policy cannot waive them; the route MUST reject the operation before any new skill version, install, update, or reset is persisted.
  • Mandatory activity attribution is part of the invariant boundary. If the required audit record for a skill mutation or policy mutation cannot be persisted, the mutation MUST fail or roll back; do not return success with missing auditability.

Canonical actions and resources

The version 1 evaluator uses these stable action identifiers:

  • skills.create: create or fork a company-authored skill and create skill versions
  • skills.import: import or scan skills from a workspace, Git source, URL, or package
  • skills.install: install a catalog or externally sourced skill into the company
  • skills.edit: change skill metadata, name, files, test inputs, or test templates
  • skills.update: install a newer upstream revision
  • skills.test: start, cancel, or remove a skill test run and run a skill audit
  • skills.reset: restore the installed/upstream revision
  • skills.remove: delete a company skill

Policy resources may include skillId, stable skillKey, sourceType, and sourceLocator. The stable source categories are workspace, catalog, git, external_package, generated, and unknown; adapters may preserve a more specific source value as metadata, but policy evaluation MUST normalize it to one of these categories. Core derives actor and company identity from authentication and derives known resource fields from stored data; a mutation client cannot authorize itself by supplying actor or resource identity fields.

Version 1 policy document

Absence of a policy record is semantically equivalent to the following document, but core SHOULD avoid materializing records for untouched companies:

{
  "schemaVersion": 1,
  "revision": 0,
  "defaultEffect": "allow",
  "rules": []
}

An explicit policy has a monotonically increasing revision, a defaultEffect of allow or deny, and ordered rules. Each rule contains a stable id, integer priority, effect (allow or deny), a subject selector (all_agents, agent ids, or role names), one or more canonical actions, and optional resource selectors for skill ids/keys and normalized source types/locators. An omitted resource selector matches every resource for the listed action.

Rules are evaluated by ascending priority, then stable rule id; the first matching rule decides. If no rule matches, defaultEffect decides. This supports both the normal open policy with targeted deny rules and an opt-in restricted preset with default deny plus explicit allow rules. Core MUST validate policy documents atomically and reject ambiguous, unknown-version, unknown-action, cross-company, or malformed selectors with 422.

Every decision returned by the evaluator has this stable shape:

{
  "allowed": false,
  "action": "skills.install",
  "reason": "explicit_rule",
  "policyRevision": 7,
  "matchedRuleId": "deny-external-packages",
  "remediation": "Contact a company administrator to change the skill policy."
}

reason is one of platform_invariant, no_policy_default, explicit_rule, policy_default, or legacy_compatibility. Mutation routes MUST use this evaluator and return 403 with code skill_policy_denied and the non-sensitive decision fields when an explicit restriction denies an operation. Denials must identify the action and remediation without exposing hidden rule data, secrets, or another company's policy.

Platform-invariant failures are not policy denials and MUST use stable machine-readable error codes so clients can distinguish non-overridable safety failures from optional administrative restrictions. Version 1 requires a finite code set covering at least:

  • skill_authentication_required
  • skill_company_boundary_denied
  • skill_workspace_boundary_denied
  • skill_source_validation_failed
  • skill_unsafe_content_blocked
  • skill_secret_handling_blocked
  • skill_policy_admin_required

Core Skill Studio and Paperclip EE MUST treat those codes as hard platform failures, not as prompts to loosen policy.

Core API and ownership boundary

Core owns and ships these company-scoped endpoints:

  • GET /companies/:companyId/skill-policy returns the effective versioned policy, its revision, and whether it is materialized or the open default.
  • PUT /companies/:companyId/skill-policy atomically replaces the policy and requires the caller's expected revision; stale writes return 409.
  • DELETE /companies/:companyId/skill-policy removes explicit configuration and restores the open default.
  • POST /companies/:companyId/skill-policy/evaluate simulates decisions for administrative tooling without performing a skill mutation.

Policy reads, writes, deletion, and simulation enforce company access. Policy mutation and cross-principal simulation require board administration authority or the existing users:manage_permissions capability; ordinary skill access does not. Every policy mutation writes an activity event containing the actor, previous revision, new revision, and a redacted change summary. Skill mutation activity logging remains required independently of the policy decision.

Paperclip EE owns the detailed editor, presets, protected-skill management, policy simulation UX, and policy-specific audit views. EE consumes the core endpoints and does not implement a second evaluator. Core may expose a concise effective-policy summary and denial state, but MUST NOT depend on EE for enforcement or make EE installation a prerequisite for normal skill work.

Compatibility and availability

  • Existing companies with no explicit restriction adopt the open default, including companies that previously depended on missing grants to deny skill changes. Release notes and upgrade guidance MUST call out this behavior change.
  • Existing explicit restriction policies remain effective after migration.
  • Legacy skills:create and skills:suggest-changes positive grants remain accepted in APIs and portability packages. Historically either positive grant authorized the broad company-skill mutation surface, so in an explicit restricted policy either grant remains a compatibility allow fallback for all eight canonical skill actions only when no explicit rule matched. They never override an explicit deny or a platform invariant. With no explicit policy they are redundant because the default already allows the action.
  • Legacy skills:suggest-changes consent state is not a platform invariant for company skills and does not add a second mutation gate under the open-default policy. Companies that require approval or consent before skill changes must express that restriction through explicit skill-policy rules; authentication, company boundaries, source containment, validation, auditability, and runtime safety remain non-configurable invariants.
  • Import preview MUST report whether a package contains an explicit skill policy or legacy grants and how each will map. Import apply MUST preserve explicit policies, normalize supported legacy grants, and reject unknown policy versions rather than silently weakening them.
  • Export MUST include explicit skill policy configuration and retained legacy grants in .paperclip.yaml, never secret values or environment-specific paths. An unconfigured company exports no synthetic restriction.
  • If Paperclip EE is unavailable or removed, core continues to enforce stored policies and expose the policy API. Normal skill work remains available under the open default; explicit denials use core remediation text rather than a broken EE-only link.