# ✂ docujoint template — adapt this to your system; delete this line when it is yours.
# System dashboard for the programming knowledge base.
# Every number is a query over the parsed vault; nothing is hand-maintained.

dashboard:
  title: System dashboard
  subtitle: What the system is meant to do, what is built and proven, and where the doubts are.

# Brand the app by overriding any design token, per light/dark mode. The
# override surface is the Astryx token set the page is built on — run
# `dj catalog --json` for the widget vocabulary, and see src/render/
# tokens.ts for the 186 token names (color-accent · color-background-surface ·
# color-text-primary · color-border · radius-element · radius-container ·
# font-family-body · shadow-low …). An unknown name is dropped with a warning.
# theme:
#   light: { color-accent: "#0f766e" }
#   dark:  { color-accent: "#2dd4bf" }

indicators:
  - id: unhappy-untested                  # usually the damning number
    label: Untested unhappy paths
    select: rows
    from: scenarios
    where: "kind == unhappy && state == untested"
    metric: count
    caption: failure paths documented but not proven by any test

  - id: drift
    label: Drift
    select: rows
    from: features
    where: "state == drift"
    metric: count
    caption: claims whose cited evidence the code scan no longer confirms

  - id: built-untested
    label: Built, untested
    select: rows
    from: features
    where: "state == built && !cites_test"
    metric: count
    caption: complete features with no test:// evidence

  - id: open-anomalies
    label: Anomalies
    select: rows
    from: anomalies
    where: "status == open"
    metric: count
    caption: scenarios whose documented outcome reads as a defect, not yet triaged

  - id: open-doubts
    label: Open questions
    select: rows
    from: open-questions
    where: "status == open"
    metric: count
    caption: doubts the team or coding agents have surfaced and not yet resolved (answered ones get folded into the docs, then archived)

  - id: concepts
    label: Concepts
    select: concepts
    metric: count
    caption: every documented piece of the system

  - id: stubs
    label: Stubs
    select: concepts
    where: "stub"
    metric: count
    caption: catalogued from source, documentation still pending

# A composed view: an ordered list of widgets, arranged by the definition
# rather than a fixed template. Pick the names from `dj catalog`.
views:
  # The home view. Composed from widgets rather than the retired `pack:`
  # shorthand: a preset carries the same bind and produces the same tiles,
  # but lives in the one composition path everything else uses.
  - id: overview
    label: Overview
    widgets:
      - { widget: note, text: "What the system must do, what is built and proven, and where the doubts are — every number a query over the parsed vault." }
      - widget: stat-row  # features across apps/pages/modules/services
        preset: traceability
        bind:
          rows: features
          label: Features
          complete: "state == built"
          complete_label: built
          open: "state in [partial, missing, drift, unspecified]"
          open_label: Open features
          open_caption: partial, missing, drifted or blocked on an open question

      - widget: stat-row  # scenario coverage across flows
        preset: traceability
        bind:
          rows: scenarios
          label: Scenarios
          complete: "state == covered"
          complete_label: covered
          open: "state in [untested, drift]"
          open_label: Untested paths
          open_caption: scenarios with no test evidence

      - widget: coverage
        bind:
          # what THIS format calls the two counts — the engine has no word of
          # its own, and shows a meaning-missing marker if you leave them out
          built_label: built
          tested_label: tested
          rows: features
          types: [page, module, service]
          built: "state == built"
          tested: "cites_test"
          legend:
            built: built — implemented, no gap recorded
            partial: partial — implemented with a recorded gap
            missing: missing — gap only, nothing implemented
            drift: drift — cites evidence the code scan no longer confirms
            unspecified: unspecified — blocked on an open question
            unknown: unknown — not yet audited

      # With an artifact inventory (--inventory artifacts.json), surface the
      # reverse gap — built things no document references:
      # - { widget: reverse-gap, bind: { group_by: kind } }

  # The development backlog: work whose state is derived from evidence, the
  # same way features are. Charts bucket by derived state and by the epic that
  # owns each ticket; the rows view is the whole list, searchable.
  - id: backlog
    label: Backlog
    widgets:
      - { widget: note, text: "Work, held to the documents' own rule: a ticket is done when its evidence says so." }
      - { widget: chart, label: Tickets by state, from: tickets,
          x: { by: state }, series: [ { mark: bar, label: tickets } ] }
      - { widget: chart, label: Tickets by epic, from: tickets,
          x: { by: document }, series: [ { mark: bar, label: tickets } ] }
      - { widget: rows, label: All tickets, from: tickets,
          search: { by: [ state, Kind, document, Gap ] } }

  - id: triage
    label: Triage
    widgets:
      - { widget: note, text: What needs a human decision, in one place. }
      # a live search box scoped to one block: full-text where the host has an
      # index (dj dashboard), a payload scan in the self-contained artifact
      - { widget: search, label: Find an anomaly, block: anomalies,
          placeholder: "Search every anomaly…", limit: 20 }
      - { widget: stat-row, indicators: [open-anomalies, open-doubts] }
      # an aggregated chart: from/where select rows exactly as a rows widget
      # does, x.by buckets them (a column, or state · document · type), and
      # each series counts — or sums a numeric column — per bucket. The
      # aggregation happens at build time; the page gets plain numbers.
      - { widget: chart, label: Features by state, from: features,
          x: { by: state }, series: [ { mark: bar, label: features } ] }
      - { widget: rows, label: Anomalies, from: anomalies,
          filter: { by: Status, label: Status, default: open } }
      - { widget: rows, label: Open questions, from: open-questions,
          filter: { by: Status, label: Status, default: open } }

  # A view TEMPLATE: `for_each` instantiates it once per document of a type,
  # substituting that document's fields into every string below — so one
  # definition becomes one board per person. Match on `{path}`: the path is
  # identity, and it is what the Owner cell's link actually contains.
  - id: person-board
    label: "{title}"
    for_each: { type: person }
    widgets:
      - { widget: note, text: "Everything {title} holds — what is still open, and what is unproven." }
      - widget: stat-row
        indicators:                       # declared inline, so the numbers are theirs
          - { label: Pending features, select: rows, from: features,
              where: "owner contains '{path}' && state != built",
              caption: assigned and not complete }
          - { label: Open questions, select: rows, from: open-questions,
              where: "owner contains '{path}' && status == open",
              caption: doubts assigned to them, unanswered }
          - { label: Open anomalies, select: rows, from: anomalies,
              where: "owner contains '{path}' && status == open",
              caption: scenarios reading as a defect, not yet triaged }
          - { label: Pending gaps, select: rows, from: features,
              where: "owner contains '{path}' && gap",
              caption: features with a recorded diff against the documentation }
          - { label: Untested, select: rows, from: features,
              where: "owner contains '{path}' && state == built && !cites_test",
              caption: complete, but no test:// evidence proves it }
      # scoped to THEIR rows — an Owner cell holds a link to {path}, and the
      # path is identity where a name is not
      - { widget: search, label: Search their work, block: features,
          contains: "{path}", placeholder: "Search {title}'s features…", limit: 20 }
      # one panel at a time, each tab carrying its own count
      - widget: tabs
        tabs:
          - { widget: rows, label: Features, from: features,
              where: "owner contains '{path}'",
              search: { by: [state, Kind, Gap, type, document] } }
          - { widget: rows, label: Questions, from: open-questions,
              where: "owner contains '{path}'",
              search: { by: [Status, About, type, document] } }
          - { widget: rows, label: Anomalies, from: anomalies,
              where: "owner contains '{path}'",
              search: { by: [Status, About, type, document] } }

# One tab per type; Pages/Modules/Tables carry a declarative facet filter
# (`filter: {by: …}`) over the docs' own frontmatter — rendered as a
# segmented control that narrows the query members, and impossible to drift
# because the facet values come from what the documents declare.
navigation:
  - group: Overview
    items:
      - { label: Overview, view: overview }
      - { label: Triage, view: triage }
      - { label: Backlog, view: backlog }
      # Row-backed pages: every anomaly / question in the vault, grouped by the
      # document that owns it and answerable in place. The Status facet opens
      # on the untriaged ones, with the answered set one click away.
      - { label: Anomalies, select: rows, from: anomalies,
          filter: { by: Status, label: Status, default: open } }
      # Work, grouped by its document. Power search composes several questions
      # at once — assigned to X, of this kind, still lacking evidence. Every
      # property and value comes from the format or the vault, never the engine.
      - { label: Work, select: rows, from: features,
          search: { by: [ { property: Owner, label: Assigned to, from_concepts: person },
                          state, Kind, type, document, Gap ] } }
      - { label: Open questions, select: rows, from: open-questions,
          filter: { by: Status, label: Status, default: open } }
      # A generated relationship graph: nodes are a concept query, edges come
      # from the IR (resolved doc links + declared metadata joins), so the
      # picture cannot drift from the documents.
      - label: System graph
        graph:
          nodes: { where: "type in [app, page, module, service, integration, event, database, table, enum, flow, actor]" }
          edges: [links, "meta:app", "meta:database"]

  - group: Product
    items:
      - { label: Apps, select: concepts, where: "type == app" }
      - { label: Pages, select: concepts, where: "type == page",
          filter: { by: app, label: App } }
      - { label: Modules, select: concepts, where: "type == module",
          filter: { by: app, label: App, empty: Shared } }
      # flows name their parent in frontmatter, so show that hierarchy
      - { label: Flows, select: concepts, where: "type == flow",
          tree: { by: parent_flow } }
      - { label: Actors, select: concepts, where: "type == actor" }
      - { label: Team, select: concepts, where: "type == person" }

  - group: Boards
    items:
      - { views: person-board }          # one page per person, from one definition

  - group: Platform
    items:
      - { label: Services, select: concepts, where: "type in [service, integration]" }
      - { label: Events, select: concepts, where: "type == event" }
      - { label: Databases, select: concepts, where: "type == database" }
      - { label: Tables, select: concepts, where: "type == table",
          filter: { by: database, label: Database } }
      - { label: Enums, select: concepts, where: "type == enum" }

  - group: Engineering
    items:
      - { label: Decisions, select: concepts, where: "type == decision" }
      - { label: Notes & skills, select: concepts, where: "type in [engineering-note, convention]" }

  - group: Reference
    items:
      - { label: All concepts, select: concepts }
      - { label: Mappings, view: ref-map }
