########################################################################
# Module overlay template.
#
# Copy to `modules/<YourModule>.yml` in your own project and fill it in from EVIDENCE IN YOUR OWN
# CODE. A real overlay is never shipped with this skill: it quotes module paths, directory
# spellings and file counts, so it describes one codebase and would bind another codebase's slots
# to the wrong dialect.
#
# The rule that makes this safe: an UNBOUND slot or role DISABLES its rules. Leaving one empty
# costs you those checks and the run says so. Guessing a value costs you a hundred findings against
# a module that made the other defensible choice - a migration proposal wearing a structure pass.
# Empty beats guessed.
#
# Every `*_evidence` field wants a COUNT, not a preference. The count is the only thing that
# distinguishes "this module chose a shape" from "this module has a defect". No count, no binding.
########################################################################

module: <YourModule>
path: <Path/To/Module>
role: feature # feature | common | app
registry_version: 1.0.0
updated: <YYYY-MM-DD>

description: >
  One paragraph: how many targets, which of them carry screens, and what a screen's folders are.

# --- Dialect slots -----------------------------------------------------
# See `references/rules.yml -> module_overlay_slots` for what each governs and which values are
# defensible. Bind only what you can evidence.
dialect:
  # Does every screen carry the same layer folders, or only the ones it needs?
  ScreenLayerShape:
  ScreenLayerShape_evidence: >
    Count screens against each layer folder, e.g. "N of N carry X; M of N carry Y". A uniform count
    means `layered`; a spread means `organic` and a missing folder is a fact, not a finding.

  # Where does a service operation's request/response pair live?
  ServiceModelDir:
  ServiceModelDir_evidence: >
    Count directories holding a pair under each candidate parent.

  # Does every operation model both halves, or only the ones with a body?
  ServiceModelPairing:
  ServiceModelPairing_evidence: >
    Count request models against response models. Close counts mean `both`; a large gap means
    `response-only`.

  # Does each screen ship its own construction seam, or does one factory build them all?
  ScreenAssemblyShape:
  ScreenAssemblyShape_evidence: >
    Count factory files against screens. One each means per-screen-factory.

  # Does UI state live in its own type, or on the view model?
  UIStateHolder:
  UIStateHolder_evidence: >
    Count separate state types against screens. Zero means view-model-owned.

  # Does an extracted view get a folder and a value type, or sit loose?
  SubviewShape:
  SubviewShape_evidence: >
    Count view+value pairs against loose extracted views.

  # Does the entry file compose its own fragments, or hand them to named views?
  ScreenCompositionShape:
  ScreenCompositionShape_evidence: >
    Count view members on entry files against extracted view files. This one decides whether a
    long entry file is the module's shape or a pile of un-extracted fragments; get it wrong in
    either direction and the run is useless.

  # Where is copy resolved?
  CopyResolution:
  CopyResolution_evidence: >
    Count per-screen copy types against screens.

# --- Roles -------------------------------------------------------------
# Globs are relative to `path`. A role may bind to one glob or to a list of them. Roles other than
# screen.root are relative to a screen directory.
#
# Leave a role empty when the module genuinely has no such thing - the rules that read it are then
# reported as lost coverage, which is the honest answer.
roles:
#  Unit-dialect vocabulary (bind when UnitDialect is adopted):
#    screen.viewstate:        "Presentation/Models/*ViewState.swift"
#    screen.viewaction:       "Presentation/Models/*ViewAction.swift"
#    screen.viewmodelaction:  "Presentation/Models/*ViewModelAction.swift"
#    screen.viewmodelstate:   "Presentation/Models/*ViewModelState.swift"
#  Multi-target packages (STRUCT-21):
#    target.configurator:     "Sources/*/Configuration/*DependencyConfigurator.swift"
  screen.root: # e.g. "Sources/*/Screens/*"
  screen.entry:
  screen.viewmodel:
  screen.state:
  screen.analytics:
  screen.factory:
  screen.mapper:
  service.dir:
  service.request:
  service.response:
  subview.view:
  subview.configuration:
  repository.live:
  repository.mock:
  shared.root:
  test.root:

# --- Vocabulary --------------------------------------------------------
# Names and spellings the rules need in order to generate a check. An unfilled key disables the
# rules that read it.
vocabulary:
  LayerDirs: [] # the folders `layered` expects on every screen
  PresentationDir: []
  SubviewViewSuffix:
  SubviewConfigurationSuffix:
  BooleanPrefixes: # a regex, e.g. "^(is|has|can|shows)[A-Z]"
  IndexLabel: # a regex for the accepted parameter label
  SectionHeadings: [] # the closed set of section headings, if the module declares one
  BareVerbs: [] # verbs a presentation type may not use alone
  ForbiddenTypeSuffixes: [] # last words that describe no role

#  UnitDialect: adopted | pre-unit
#    Count view models subclassing the unit generic vs the deprecated pre-unit base.
#    pre-unit is a RECORDED migration state, reported as debt - a new module binds adopted.

# --- Contracts ---------------------------------------------------------
# Only for a CONVERTED module whose screens are reached through a flow-contracts target.
# root is relative to the module root; leave the whole block out for a pre-conversion module
# and STRUCT-18 reports lost coverage instead of guessing. Bind the two contract roles under
# roles: with {screen}-substituted patterns, e.g.
#   contracts.output:    "{screen}Output.swift"
#   contracts.providing: "{screen}ScreenProviding.swift"
contracts: {}
#  root: ../../CrossDomains/Contracts/Sources/<Module>FlowContracts

# --- Limits ------------------------------------------------------------
limits:
  source_target:
  source_ceiling:
  test_target:
  test_ceiling:

# --- Carve-outs --------------------------------------------------------
# Declared per rule ID. Each is a decision with a reason, not a way to quiet the tool. A carve-out
# with no reason is a finding you hid from yourself.
#
#   screens:  [Name, ...]   the rule does not describe these screens
#   paths:    [glob, ...]   the rule does not describe files at these paths
#   patterns: [regex, ...]  the rule does not describe findings whose detail matches
exemptions: {}
#  RULE-ID:
#    screens: []
#    reason: >
#      Why this is a decision rather than a defect.
