Skip to main content

Workspace Rules

Every workspace in a Nova monorepo is classified by a role and a policy.

These two properties, defined in nova.config.json, control how recipes read, create, modify, and remove fields in each workspace's package.json.

Roles

A role describes what the workspace is. It determines which package.json fields are allowed, which are removed, and which are created when missing.

RolePurpose
projectThe monorepo root. Owns the workspaces array and the packageManager field.
docsA documentation site (e.g., Docusaurus). Does not publish to a package registry.
configA shared configuration package (e.g., ESLint presets, TSConfig presets).
appAn application workspace (e.g., a web app, an API server). Not intended for registry publishing.
packageA publishable library. Retains artifact fields (files, bin, man, directories) and bundler convention fields (types, module, sideEffects, esnext).
toolAn internal CLI tool. Similar to package but does not retain sideEffects or esnext.
templateA template workspace used by scaffolding commands. Not intended for registry publishing.

Role-Gated Fields

The following table shows which package.json fields are retained or removed based on role. Fields not listed here are handled independently of role.

FieldRetained byRemoved from all others
workspacesprojectYes
filespackage, toolYes
binpackage, toolYes
manpackage, toolYes
directoriespackage, toolYes
typespackage, toolYes
modulepackage, toolYes
sideEffectspackageYes
esnextpackageYes

Policies

A policy describes how the workspace is versioned and published. It determines whether the workspace is locked, tracked, or released to a registry.

PolicyVersion behaviorPublishingprivate value
freezableLocked at 0.0.0. Never incremented.Never published.true
trackableTracked via semver. Can be incremented.Never published.true
distributableTracked via semver. Can be incremented.Published to a registry.false

Policy-Gated Fields

The following table shows which package.json fields are retained or removed based on policy. Fields not listed here are handled independently of policy.

FieldRetained byRemoved from all others
descriptiondistributableYes
keywordsdistributableYes
homepagedistributableYes
bugsdistributableYes
authordistributableYes
contributorsdistributableYes
fundingdistributableYes
repositorydistributableYes
publishConfigdistributableYes

Required vs. Conditional

Each field that a recipe handles is classified as either required or conditional. This classification describes when the recipe will act on the field.

ClassificationMeaning
RequiredThe recipe ensures the field exists. If missing, it is created with a computed or default value. If invalid, it is corrected.
ConditionalThe recipe only acts when the field is already present, or when a specific recipe setting, workspace role, or workspace policy triggers it. A conditional field is never created from nothing without an explicit trigger.

How to Read Recipe Documentation

In each recipe's Behavior section, every field is tagged with (Required) or (Conditional):

  • name (Required) means the recipe guarantees this field will exist after it runs. If the field is missing, the recipe creates it.
  • description (Conditional) means the recipe will only touch this field if it is already present or if the workspace meets certain criteria (e.g., a specific policy and an enabled setting).

This distinction matters because required fields are safe to omit from a new package.json — the recipe will create them automatically.

Conditional fields, on the other hand, are only managed if they already exist or if you explicitly opt in through configuration.