Author a OFFICE.md (workspace root or view) for AIP-22
Use this skill when the user asks to draft, extend, or revise a
office.workspace/v1 manifest under AIP-22. The skill produces
a valid manifest (workspace-root or view), with the right collection
declarations, identity fields, org-tree rules, reporting graph, lint rules, and
cross-AIP refs, ready for defineOfficeWorkspace to load.
A OFFICE.md manifest is the machine-readable contract for an
AIP-22 organisation — its identity, which collections it tracks,
how the org tree nests, who reports to whom, which workspace-spanning lints run.
The same doctype is used in two modes: a workspace root at the company root
(no extends:), and a view in any consumer folder (with extends: pointing
at a parent). Authoring either is the same flow, with one branch on step 1.
Critical: AIP-22 delegates ALL per-item-kind concerns (fields, status state
machines, ownership cardinality, lint rules per kind) to AIP-18.
Do NOT re-specify any of those in OFFICE.md — declare collections, then let
AIP-18 own the schemas.
When to use
- "Set up a new organisation — write its
OFFICE.mdfrom scratch." - "Add a per-jurisdiction view on the existing organisation — write a view that extends the parent."
- "The eng lead needs an org-tree lens — write a view that surfaces the engineering departments and roles."
- "Bind an AIP-7 governance policy and an AIP-10 wiki to this organisation."
- "Add a reporting-graph integrity sweep workflow as the organisation default."
When NOT to use
- The user wants to author per-item-kind schemas (role fields, status
ladders, ownership rules) — that's AIP-18's
author-collectionskill. - The user wants to write individual items (a specific role record, a specific objective) — also AIP-18.
- The user wants to change the AIP-22 spec itself — manifest shape changes are governance, not authoring.
- The user wants to edit an existing
OFFICE.mdin place without considering the chain — read the parent first, run the merge in your head, then edit. Skipping the merge produces views that override fields the parent already provides correctly, or worse, trip a one-way-switch HARD refusal.
Process
Follow these steps in order. Composition and one-way switches are the central mechanics; steps 1-2 set up the right mode, steps 3-10 fill in the body, steps 11-12 validate.
1. Decide: workspace root or view?
Two questions:
- Is there an existing
OFFICE.mdupstream that this manifest should adapt? If yes → view; if no → workspace root. - Does the user want the manifest to bind to a specific consumer (operator /
skill / division)? If yes → view (set
appliesTo); if no → workspace root.
Workspace-root mode declares the BASE shape. View mode adapts the base for one
or more consumers. There is no third mode — the schema rejects manifests that
mix workspace-root and view properties (e.g. appliesTo without extends).
If the answer is workspace root, skip step 2 and proceed to step 3.
2. If view: locate parent, set extends:, understand one-way switches
For a view, the extends: field is a RELATIVE path from the new manifest's
directory to the parent OFFICE.md. The host resolves it bottom-up; recursion
is allowed.
# Jurisdictional view at /repo/companies/acme/jurisdictions/de/OFFICE.md
extends: ../../OFFICE.md
Rules:
- Use POSIX path separators in
extends:even on Windows. - Maximum chain depth is eight. Two-to-three levels is the common case; deeper chains usually mean a refactor is overdue.
- If the parent is in another tree, prefer factoring the shared bits into a
small workspace package both can
extends:locally.
One-way switches — read the parent FIRST. Four fields, once set at any ancestor, MUST NOT be relaxed by descendants. Trying to relax triggers a HARD refusal — the view fails to load. Before authoring a view, read the parent (and its parent, if any) and identify which one-way switches are already on:
| Field | One-way condition | HARD refusal code |
|---|---|---|
defaults.auditMutations |
If any ancestor is true, descendants cannot set false. |
office_audit_downgrade |
governance.signing.required (in bound governance policy) |
If any ancestor's policy is true, descendants cannot relax. |
office_signing_downgrade |
orgTree.containment.enabled |
If any ancestor is true, descendants cannot set false. |
office_orgtree_disable |
orgTree.containment.rules.maxDepth |
If any ancestor sets a value, descendants may NARROW (smaller); not WIDEN (larger). | office_orgtree_depth_widen |
If the parent has any of these set, do NOT redeclare them on the view as a
relaxation — inherit silently. If you absolutely need a different (relaxed)
value, the conversation belongs at the parent's level (or in
AIP-7 governance), not in this view. Narrowing depth is fine: a
parent's maxDepth: 6 and a view's maxDepth: 3 is allowed.
Cycle detection and depth-overflow are runtime warnings, not errors. Do not rely on the warning — write a correct chain.
3. Identity (legalName, jurisdiction, foundedAt, mission, defaultCurrency)
Every manifest, root or view, MAY declare identity fields. Each leaf field
independently overrides via merge — a view MAY narrow jurisdiction while
inheriting mission from the parent.
identity:
legalName: Acme Corporation
legalEntity: ws://companies/acme-corp
jurisdiction: US # ISO 3166-1 alpha-2
foundedAt: 2024-03-01 # ISO date
mission: |
Build the most useful AI products on the planet by giving
builders the highest leverage on every task.
defaultCurrency: USD # ISO 4217
taxId: 99-9999999
Guidance:
legalEntityis a self-ref when the manifest IS the legal entity, and a parent-entity ref when the manifest is a subsidiary.jurisdictionis uppercase ISO 3166-1 alpha-2 (US, GB, FR, DE, not "USA" or "France").defaultCurrencyis uppercase ISO 4217 (USD, EUR, GBP, JPY).- A divisional view typically inherits the parent's identity silently and overrides only what's specific to it.
4. Cross-AIP bindings
executor: ws://operators/founder
governance: ../policies/org-default.yaml
work: ws://workspaces/main-tracker
agency: ws://agencies/acme-consulting
knowledge: ws://wikis/handbook/KNOWLEDGE.md
playbook: ws://playbooks/values
| Field | Required | When to set |
|---|---|---|
executor |
optional | Default org-level operator for company-level prompts. |
governance |
optional | Set when AIP-7 approval gates apply. Workspace-root manifests usually set this; views may override only if the parent's policy permits. |
work |
optional | Set to bind the company to an AIP-20 work tracker (the default tracker for the org's items). |
agency |
optional | Set when the company also operates as a commercial AIP-21 agency. |
knowledge |
optional | Set when items reference an AIP-10 wiki by default. |
playbook |
optional | Set when an AIP-12 playbook governs the company's culture / operating rhythm. |
appliesTo |
conditional | REQUIRED in view mode (whenever extends is set AND the view binds to a consumer). MUST NOT be set in workspace-root mode. |
The host MUST refuse a view whose appliesTo references a non-existent consumer
(office_appliesto_unresolvable) — verify the consumer's workspace exists
before declaring the binding.
The host also refuses workspaces with unresolvable executor, governance,
work, agency, knowledge, or playbook refs (office_xref_unresolvable,
HARD). Do not bind to a workspace that hasn't been created yet.
5. Collections — inline vs ref vs aliased
collections: is the bridge to AIP-18. Three forms:
- Inline. Full AIP-18 collection.schema/v1 frontmatter embedded in
OFFICE.md. Useful for small, single-tenant organisations. - File ref (
./collections/<name>/COLLECTION.md). Useful when the collection is shared with peer organisations. - Registry import (
ws://collections/<slug>). Useful for third-party or org-shared collections.
Aliasing (any ref form):
collections:
- ref: ws://collections/department
alias: division # workspace-local rename
version: "1.x" # pin schema range
Two collection entries resolving to the same effective name (alias or upstream
name) is a HARD failure (office_collection_alias_conflict). Pick aliases
deliberately.
Starter collections AIP-22 ships in office-starters/:
role— a position held by an operator. Mirrors AIP-6's role.objective— a goal the org pursues. Mirrors AIP-6's objective.department— NEW: top-level org sub-unit (engineering, design, ops, etc.).team— NEW: smaller groups within departments.policy— NEW: internal HR / operating policy. NOT the same as AIP-7 governance policies — see the collection's body.
The merged collections[] array is computed across the extends: chain via
merge-by-effective-name. Inheriting from the parent is the default; only
redeclare collections you want to override.
When extending an AIP-18 starter collection (e.g.
office-starters/role) with org-specific fields, write the extended collection
inline OR as a sibling file with its own extends: — and then ref the extended
file from OFFICE.md. Do NOT mutate the starter file in place.
6. Org-tree containment (the AIP-22 distinctive concept)
The org tree is AIP-22's centre of gravity. The matrix declares which collection kinds nest under which:
orgTree:
containment:
enabled: true
field: parent
rules:
allowedKinds: [department, team, role]
allowedParentKinds:
team: [department] # team under department
role: [team, department] # role under team OR department
department: [department] # sub-departments allowed
maxDepth: 6
Guidance:
allowedKindslists the collections that participate in the tree at all.policy,objective, etc. are typically NOT in the tree — they live outside it, attached by reference rather than containment.allowedParentKindsis a matrix. Keys are CHILD kinds; values are arrays of allowed PARENT kinds. The host enforces this at item-write time. Example: arolewhose parent is apolicyis refused withoffice_orgtree_invalid_parent_kind.maxDepthcaps the tree depth. ONE-WAY on widening: once set, descendants may narrow but never widen. Pick deliberately — most orgs are 4-6 levels deep at most.enabledis the master switch. ONE-WAY on disable: once true, descendants cannot turn it off.
7. Reporting hierarchy rules
Reporting is logically separate from containment. A role's manager (reportsTo)
is independent from its containment parent (team it sits in).
orgTree:
reporting:
enabled: true
field: reportsTo
cardinality: single # or multiple (matrixed)
rules:
mustResolveTo: role # the manager MUST be a role
circularBan: true
Guidance:
cardinality: singleis the typical case (one manager per role). Usemultipleonly for matrixed orgs wherereportsTobecomes an array.mustResolveTois the kind the manager ref MUST point at. Almost alwaysrole— a role reports to another role.circularBan: trueenforces an acyclic reporting graph. Set this true unless you have a very specific reason. The host refuses cycle-closing writes withoffice_orgtree_circular_report(HARD).
8. Workspace-spanning lints
AIP-18 lints are per-collection (missing-owner, overdue, required-field).
AIP-22 lints span collections:
lints:
- id: orphan-role
kind: orphan-role
severity: error
params:
collections: [role]
- id: missing-manager-warn
kind: missing-manager
severity: warn
- id: broken-report
kind: broken-report
severity: error
- id: stale-objective-90d
kind: stale-objective
severity: warn
params:
days: 90
- id: unassigned-objective
kind: unassigned-objective
severity: warn
Workspace-spanning lint kinds:
| Kind | Purpose | params |
|---|---|---|
orphan-role |
Role item whose holder is no longer an active operator OR whose containment parent is missing. | collections: [...] |
broken-report |
Role's reportsTo ref doesn't resolve. |
none |
missing-manager |
Role declared as needing a manager (per the per-collection schema) but reportsTo is unset. |
none |
unassigned-objective |
Objective with no accountable owner / role. | none |
stale-objective |
Objective not updated in days. |
days: <n> |
custom |
Host-defined, identified by id. |
host-defined |
Severity guidance:
error— block writes that fail the lint.warn— surface in the audit log, do not block.info— surface in tooling only.
Child views may soften severity (warn → info). A parent's governance: policy
MAY forbid softening — the host enforces.
9. Routine workflow defaults
defaults:
workflow: ./workflows/monthly-report-graph-sweep/WORKFLOW.md
approvalClass: on-mutate
auditMutations: true
| Field | Values |
|---|---|
workflow |
path or ws:// ref to an AIP-15 WORKFLOW.md |
approvalClass |
auto / always / on-mutate / policy:<ref> |
auditMutations |
boolean — ONE-WAY SWITCH |
auditMutations: true is one of the four one-way switches. Enable it
deliberately: once on at any ancestor, no descendant can disable it without
triggering office_audit_downgrade (HARD). For an organisation with any
compliance posture, this should be true at the root.
10. Display / UX hints
display:
homePage: DEPT-engineering
defaultGrouping: department # kind | department | parent
defaultView: tree # list | tree | board
Pure UI hints; no validation impact. tree is the typical pick — org charts
render naturally as trees.
11. Body prose (purpose, structure, conventions, what NOT to model)
The frontmatter ends; the body is markdown. Conventional sections:
# <title>
## Purpose
What this organisation is, who it serves.
## Org structure
The human-readable rendering of the tree (departments → teams → roles).
## Conventions
When an item belongs in `department` vs `team`; when a role gets its own
collection vs extends `role`.
## What this workspace does NOT model
Set boundaries explicitly. Helps reviewers reject mis-modelled items.
## When to extend vs replace
Composition guidance for downstream view authors.
Keep the body short. The frontmatter is the contract; the body explains the choices.
12. Validate against OFFICE.schema.json; if view, dry-run merge — and CHECK no one-way switch is relaxed
Validate the new manifest's frontmatter against AIP-22's schema:
npx ajv validate \
-s apps/agentik/sites/content/docs/agentproto/resources/aip-22/draft/OFFICE.schema.json \
-d "<workspaceDir>/OFFICE.md"
Common errors:
appliesToset butextendsmissing → addextends.collections[].aliasproduces a name collision → rename or remove the alias.orgTree.containment.rules.allowedParentKinds[<child>]references collection names not inallowedKinds→ fix the matrix.orgTree.reporting.rules.mustResolveToreferences a collection not registered → register it or change the rule.lints[].idcollisions inside one manifest → ids must be unique per manifest.versionnot semver →1.0.0, not1orv1.identity.jurisdictionnot 2-letter ISO → use US, GB, FR, etc.identity.defaultCurrencynot 3-letter ISO → use USD, EUR, GBP.
If view, run the host's resolution algorithm in dry-run mode and explicitly check that no one-way switch is relaxed:
## Merge diff: uk-research (vs parent acme-uk)
Inherited (no change):
- collections: role, objective, department, team
- identity.legalName: Acme UK Limited
- identity.jurisdiction: GB
- identity.defaultCurrency: GBP
- orgTree.containment.enabled: true (one-way; descendants cannot disable)
- orgTree.containment.rules.allowedKinds: [department, role, team]
- orgTree.containment.rules.maxDepth: 4 (one-way on widen)
- orgTree.reporting: enabled, single, mustResolveTo=role, circularBan=true
- defaults.auditMutations: true (one-way; descendants cannot disable)
- governance: ../../policies/group-default.yaml
- knowledge: ws://wikis/handbook-uk/KNOWLEDGE.md
Overridden:
- orgTree.containment.rules.maxDepth: 4 → 3 (NARROWING — allowed)
- display.homePage: undefined → DEPT-research
- display.defaultView: undefined → tree
Added:
- lints.research-stale-objective (kind=stale-objective, severity=warn)
- appliesTo: [ws://operators/research-lead]
One-way switch check: PASS
- defaults.auditMutations: parent=true, view=undefined → inherits true OK
- orgTree.containment.enabled: parent=true, view=undefined → inherits true OK
- orgTree.containment.rules.maxDepth: parent=4, view=3 → narrowing OK (3 < 4)
- governance.signing.required: parent=undefined, view=undefined → no constraint
Resolution chain: 3 levels (group → acme-uk → uk-research) Warnings: none
If the merge diff shows the view RELAXING any one-way switch (e.g.
auditMutations: true → false, containment.enabled: true → false,
maxDepth: 4 → 8, signing.required: true → false), the view will be
HARD-refused at load — fix it before declaring success.
If the diff includes an unintentional override, edit the view to remove it (deletion of a field reverts to parent's value via the merge).
Final checklist
Before declaring done:
-
schema: office.workspace/v1is set. -
name,title,description,versionare present. - If view:
extends:is a valid relative path to an existingOFFICE.md;appliesTo:references existing consumers. - If workspace root:
extends:andappliesTo:are absent. -
identity.jurisdiction(if set) is uppercase 2-letter ISO. -
identity.defaultCurrency(if set) is uppercase 3-letter ISO. -
collections[]entries have unique effective names (alias orname); refs resolve; inline frontmatters validate against AIP-18's COLLECTION.schema.json. - Per-item-kind concerns (fields, statuses, ownership rules) are NOT in
OFFICE.md— they live onCOLLECTION.mdfiles. -
orgTree.containment.rules.allowedKindsreferences real collection names. -
orgTree.containment.rules.allowedParentKindskeys + values are all inallowedKinds. -
orgTree.containment.rules.maxDepthis set deliberately (one-way on widening). -
orgTree.reporting.rules.mustResolveToreferences a registered collection. -
orgTree.reporting.rules.circularBanistrueunless you have a strong reason. -
lints[]have uniqueids within this manifest; severities respect any parent governance constraints. -
defaults.auditMutationsis set deliberately (one-way). - Cross-AIP refs (
executor,governance,work,agency,knowledge,playbook) all resolve. -
metadata.<vendor>.*is namespaced. - Frontmatter validates against
OFFICE.schema.json. - Body is short and prose-only.
- If view: dry-run merge diff was reviewed; no one-way switch is relaxed.
- If governance binding changed: the change is itself routed through AIP-7 approval before the manifest lands on disk.
Output
Reply to the user with:
- The path of the manifest written.
- Mode (workspace root vs view).
- Resolution chain (for a view): root → … → leaf, one path per level.
- Effective config summary — the merged shape, in particular identity, which collections are active, the org-tree rules, the reporting configuration, and which one-way switches are now in effect.
- Bindings —
executor,governance,work,agency,knowledge,playbook,appliesTo(if set), each with a one-line note on what it does. - One-way switch report — for a view, an explicit per-switch line:
auditMutations: parent=<x>, view=<y>, status=PASS|FAIL;orgTree.containment.enabled: parent=<x>, view=<y>, status=...;orgTree.containment.rules.maxDepth: parent=<x>, view=<y>, status=...(narrow / inherit / WIDEN-FAIL);governance.signing.required: parent=<x>, view=<y>, status=.... - Validation result — schema clean, dry-run merge clean, warnings (if any).
- Open assumptions — fields you guessed (org-tree maxDepth, reporting cardinality, lint severities) that the user might want to override.
Do NOT mutate the parent manifest, the workspace root, or any existing view as a side-effect. Authoring a new manifest is a LEAF operation — touch only the file you are creating.
See also
- AIP-22 — agentoffice/v1 spec
- AIP-18 — COLLECTION.md / ITEM.md — substrate this skill composes on
- AIP-3 — SKILL.md
- AIP-6 — agentcompanies/v1 — predecessor (deprecated)
- AIP-7 — governance, approval, audit
- AIP-9 — agentoperators/v1
- AIP-10 — agentknowledge/v1 — sibling Workspace AIP
- AIP-12 — agentplaybooks/v1
- AIP-15 — WORKFLOW.md
- AIP-20 — agentwork/v2 — sibling Workspace AIP
- AIP-21 — agentagencies/v2 — sibling Workspace AIP
../../ADAPTER.md— implementer's guide../../EXAMPLES.md— reference manifests../../OFFICE.schema.json— frontmatter validator../../starters/office-starters/— starter collection library