Lesson 0004 · ~12 minutes

Shared tasks: define once, inherit many

One skill: put standardization work in .moon/tasks, control who inherits and how locals merge — so maintenance means one edit, not N copy-pastes.

Win for this lesson

You can design a shared lint/test surface for a monorepo: where the file lives, who gets it (inheritedBy), and how one app tweaks args without forking the whole task.

1. Why inheritance exists (mission hook)

Lesson 1’s monorepo win was consistent tooling. moon’s answer is not “paste eslint into every moon.yml.” It is task inheritance: define once at the workspace level, inherit many or all projects.

Classic shared set (from create a project): format, lint, test, typecheck — then per-app build / dev stay local.

2. Who inherits: inheritedBy

Each file under .moon/tasks can limit itself with inheritedBy. Omit it (or leave empty) → all projects inherit that file.

# .moon/tasks/node-frontend-library.yml
inheritedBy:
  toolchain: 'node'
  stack: 'frontend'
  layer: 'library'

tasks:
  lint:
    command: 'eslint --no-error-on-unmatched-pattern .'

Projects declare identity with language, layer, stack, tags in their moon.* — inheritance only works if that metadata is honest.

3. Project still decides: include / exclude / rename

After global match, a project can filter with workspace.inheritedTasks:

# apps/special/moon.yml
workspace:
  inheritedTasks:
    include: ['lint', 'test']
    exclude: ['format']
    rename:
      buildApplication: 'build'

Order is fixed: include → exclude → rename.

4. Same name = merge (not silent overwrite)

Global task build + local task build → one merged task. Field strategies (mergeArgs, mergeDeps, mergeInputs, …):

Command vs script

Prefer command when you want inheritance merges. script does not merge — it replaces. See commands vs scripts.

5. Paths, inputs, cache (one beat)

6. Practice

Equal-length options. Design-review the inheritance surface.

Question 1

Best place for a lint task every TypeScript library should share?

Question 2

A tasks file has inheritedBy: { toolchain: node, layer: library }. Who inherits?

Question 3

Global lint exists. One app needs one extra eslint flag. Best approach?

Question 4

Order of workspace.inheritedTasks filters?

7. Primary source

Recommended: Task inheritance (conditions + merge strategies). Then skim .moon/tasks config and keep the inheritance sheet open.

Ask your teacher Sketch inheritedBy for your real stacks (frontend libs vs apps), or debug “why didn’t this project get lint?” Chat is part of the course.