# Grammarly License Governor

> Produce a review-only plan from a sanitized Grammarly License Management snapshot, identifying non-admin users whose last activity is before an explicit cutoff. Use when governing unused seats without exposing identity data or mutating Grammarly. Trigger with "review Grammarly inactive licenses", "Grammarly seat audit", or "analyze a sanitized Grammarly license snapshot".

- Skill: `gabrielmoreira/grammarly-license-governor` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/grammarly-license-governor`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/grammarly-license-governor/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- License: MIT
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/gabrielmoreira/grammarly-license-governor

---


# Grammarly License Governor

This skill creates a human-review queue from a sanitized License Management API
snapshot. It never calls Grammarly, resolves identities, sends a mutation, or
deletes a user or invitee. A candidate is only a non-admin user whose recorded
`last_activity_at` is strictly earlier than the explicit `inactive_before` cutoff.

## Overview

Use this skill for a bounded seat-review decision from sanitized user activity
metadata, not for identity resolution or live account administration.

## Prerequisites

No credentials, network access, Grammarly account, names, email addresses, or raw
IDs are needed. Python 3.10+ is the only runtime requirement. If a separate live
integration is being designed, consult the official [OAuth 2.0 credentials documentation](https://developer.grammarly.com/oauth-credentials.html)
(accessed 2026-09-04) for scopes; live authentication and mutation are outside this
skill.

## Instructions

### Step 1: Run the analyzer

```bash
python3 scripts/analyze_license_snapshot.py snapshot.json
cat snapshot.json | python3 scripts/analyze_license_snapshot.py -
python3 scripts/analyze_license_snapshot.py --self-test
```

The input contract is intentionally narrow. It accepts one snapshot object with an
explicit snapshot timestamp and UTC cutoff, producer attestation for keyed
HMAC-SHA256 pseudonymization, and records containing only resource pseudonyms,
last-activity timestamps, and the admin flag. Names,
email addresses, raw numeric/string IDs, institution identifiers, secrets, tokens,
headers, and unknown
fields fail closed, including when nested.

### Step 2: Apply the review rules

- Require `snapshot_generated_at` and `inactive_before`; never use the current time or
  an implicit default, and reject a cutoff later than the snapshot.
- Compare timestamps in UTC and classify only `last_activity_at < inactive_before`.
  Equal timestamps are not inactive-before evidence.
- Exclude every record with `is_admin: true`. The official API says admin licenses
  cannot currently be removed; admins must not enter the candidate plan.
- Require the exact producer-attestation object and keep candidate identity to
  `resource_id_hmac_sha256`, generated outside this tool with an
  organization-controlled HMAC key. The analyzer validates attestation and digest
  shape but cannot cryptographically prove upstream generation. Do not reverse, join,
  enrich, or display identity attributes; a plain unsalted hash is not enough.
- Emit `HUMAN_REVIEW_ONLY` candidates. The output is not an execution manifest and
  contains no delete operation, endpoint call, or authorization instruction.
- Missing activity evidence, duplicate digests, malformed timestamps, and raw or
  identity-bearing fields are review blockers, not reasons to guess.

Invitees are outside this analyzer's inactivity rule because the documented invitee
shape has creation and invitation status, not `last_activity_at`. Keep them in a
separate human review if they need governance; do not silently treat an invitee's
creation date as inactivity.

### Step 3: Stop on the unresolved official path conflict

The official License Management API page gives two different paths for the institution
summary: its endpoint heading says `/ecosystem/api/v1/institutions-summary`, while its
example request omits `/v1` and uses `/ecosystem/api/institutions-summary`. This is
unresolved documentation evidence. Treat it as a fail-closed stop: verify the path
with Grammarly before any human-authorized integration work. This skill does not
automate that endpoint and the offline script never performs network access.

Read the focused references when needed:

- [`references/snapshot-schema.md`](references/snapshot-schema.md) — exact sanitized
  input and review-plan output schema.
- [`references/license-management-contract.md`](references/license-management-contract.md)
  — official user/admin/invitee facts and the preserved path conflict.

## Output

The script emits `plan_type: REVIEW_ONLY`, `mutation_performed: false`, snapshot time,
cutoff, the bounded producer-attestation claim, counts, and sorted candidates containing
only pseudonyms and the `HUMAN_REVIEW_ONLY` action. It never emits names, emails, raw
IDs, or a delete request.

## Error Handling

Invalid JSON, missing or implicit cutoff, non-UTC timestamps, malformed or duplicate
digests, admins with missing fields, forbidden identity/secret keys, and unknown
fields fail closed with a non-zero exit. No partial candidate plan is emitted.

## Examples

- A non-admin active before the cutoff becomes a review candidate.
- An admin with the same old activity is counted as excluded and never becomes a
  candidate.
- An invitee is not classified from `created_at`; its activity evidence is absent.
- Either institution-summary path variant stops live integration work for human
  verification; the offline analyzer does not select one.

## Resources

- [`references/snapshot-schema.md`](references/snapshot-schema.md) — exact input and
  review-plan output schemas.
- [`references/license-management-contract.md`](references/license-management-contract.md)
  — official facts and unresolved summary-path conflict.
- [Grammarly License Management API](https://developer.grammarly.com/license-management-api.html)
  (accessed 2026-09-04).

## Official source

[Grammarly License Management API](https://developer.grammarly.com/license-management-api.html)
(accessed 2026-09-04).

