Part 6 of 8 — the concrete wiring: where stories live, where docs live, how the agent reaches both through MCP, and the traceability round-trip that ties a Jira key to a doc path.
The architecture (chapter 2) names two systems of record: Jira for user stories and GitHub for documentation-as-code. That split is not an accident of tooling — it reflects what each system is genuinely good at, and it is the line along which the two subagents divide. The Analyst lives on the Jira side; the Librarian lives on the GitHub side; the catalog they share physically lives in the GitHub repo. This chapter is the plumbing.
Each subagent reaches its system of record through a dedicated MCP server. The dotted links are the traceability round-trip: the story points at the docs it touches, and the docs commits carry the story key back.
The agent does not introduce a new place to plan work. The organization already runs its delivery on Jira — sprints, boards, backlog, status transitions, the whole apparatus that the engineering team checks every morning. Inventing a parallel store for "the agent's stories" would create exactly the silo this system is supposed to dissolve. So the rule is simple: the agent meets the team where it is. The Analyst writes into the same PromoDesk project (`PROMO`) the humans already use.
What lives in Jira is the work: the user story, its acceptance criteria, its place in the backlog, its status as it moves from refinement to done. What does not live in Jira is the system knowledge. A Jira story is a transient unit — it is created, worked, closed, and then it stops being read. The description of how PromoDesk's fund-balance calculation actually works must outlive any single story. That description belongs in the catalog, in GitHub. Jira tells you what changed; the catalog tells you what is true.
| Lives in Jira (transient work) | Lives in GitHub (durable knowledge) |
|---|---|
| User stories & epics | The 11(+2) entity catalog (chapter 3) |
| Acceptance criteria for a given story | The canonical rules, schema, screens, processes |
| Backlog ordering & sprint assignment | The conceptual & physical data models |
| Status / workflow transitions | Decision log / ADRs — why choices were made |
| Story-level estimates & assignees | The agent's own config (skills, subagents, CLAUDE.md) |
The catalog is the asset that must not rot, and the discipline that keeps a codebase from rotting is exactly the discipline we borrow. Treating documentation as code means it gets version control (every change has an author, a timestamp, and a diff), pull requests as the review mechanism (no edit lands without a second pair of eyes), diffs as the audit trail (you can answer "when did the $50k threshold become $50k, and who approved it?"), and CI as the quality gate (a machine refuses to merge a doc that violates the rules).
Concretely, the catalog is plain Markdown with embedded Mermaid diagrams and committed schema files — nothing proprietary, nothing that needs a server to render. And one more thing lives in the same repo: the agent's own configuration — its skill definitions, subagent definitions, and CLAUDE.md. The agent that maintains the catalog is itself versioned alongside the catalog. Chapter 7 details those files; here we just reserve their place in the tree.
The repo layout is not arbitrary: /docs/ maps one-to-one onto the canonical entity catalog from chapter 3. Find any entity by knowing its number; find its docs by knowing its folder.
promodesk-docs/
├── docs/
│ ├── glossary.md # 1 Ubiquitous language
│ ├── data/
│ │ ├── conceptual-model.md # 2 Business entities + Mermaid ER
│ │ ├── schema.md # 3 Logical/physical model (prose)
│ │ └── schema.sql # 3 The schema of record
│ ├── rules/ # 4 Accrual rates, claim-match tolerances
│ │ ├── accrual-posting.md
│ │ └── claim-matching.md
│ ├── ui/ # 5 Screen inventory
│ │ ├── promotion-planner.md
│ │ ├── fund-dashboard.md
│ │ └── claims-workbench.md
│ ├── process/ # 6 Workflows + state machines
│ │ └── promotion-lifecycle.md
│ ├── batch/ # 7 Scheduled jobs
│ │ ├── accrual-posting-job.md
│ │ └── claims-matcher-job.md
│ ├── integrations/ # 8 ERP GL export, retailer EDI claims
│ │ ├── erp-gl-export.md
│ │ └── retailer-edi-claims.md
│ ├── security/
│ │ └── authz.md # 9 Roles + approval thresholds
│ ├── nfr.md # 10 SOX, audit, retention, SLAs
│ ├── decisions/ # 11 ADRs — institutional memory
│ │ └── 0007-vp-threshold-250k.md
│ ├── acceptance/ # +1 Test & acceptance catalog
│ │ └── PROMO-512.md
│ └── open-questions.md # +2 Ambiguity backlog
├── .claude/ # agent config — chapter 7
│ ├── CLAUDE.md
│ ├── skills/ # grill-me, draft-story, reconcile-docs ...
│ └── agents/ # analyst.md, librarian.md
├── .mcp.json # MCP server wiring — chapter 7
└── .github/
└── workflows/
└── docs-ci.yml # the quality gate (below)
One folder per entity, the optional +1/+2 alongside, and the agent's own brain in .claude/. The catalog and the agent that tends it ship in the same repo.
MCP — the Model Context Protocol — is a standard way to give the agent tools it can call to act on an external system, so the agent does not hand-roll API clients for every backend. In this design the Analyst is given a Jira/Atlassian MCP server (create and update stories, read the backlog, set status); the Librarian is given the GitHub MCP server (read files, open branches, raise pull requests, comment on reviews). Each subagent is granted only the server it needs — the Analyst cannot push to the docs repo, the Librarian cannot move a story to Done — which keeps each one's blast radius small.
.mcp.json.
Knowledge is only trustworthy if you can navigate between intent and reality in both directions. The wiring makes that a two-way street. The Jira story carries links to the doc paths it touches, so from a story you can jump straight to the authoritative entity. And the docs pull request and every commit carry the Jira key (`PROMO-512`), so from a doc change you can jump straight back to the work that motivated it. Story→docs and docs→story, always.
Here is a sample story the Analyst writes — note the entity links in its body:
PROMO-512 · Story · Status: Refinement
Title: Show committed-vs-actual fund balance on the Fund Dashboard
As a key-account manager
I want the Fund Dashboard to show committed accruals separately from posted actuals
So that I can see remaining spendable budget before a promotion locks it up.
Acceptance criteria (INVEST-checked):
• Dashboard shows three figures per fund: committed, posted, remaining.
• "Committed" = sum of accruals on promotions in Planned or Approved state.
• Remaining = fund cap − committed − posted; never negative (clamp + warn).
• Figures refresh after the nightly accrual-posting job completes.
Impacted catalog entities:
• docs/ui/fund-dashboard.md (screen change)
• docs/rules/accrual-posting.md (committed vs posted definition)
• docs/batch/accrual-posting-job.md (refresh dependency)
When that story ships, the Librarian opens the matching docs pull request — and its title and body carry the key back:
PR title: PROMO-512 reconcile fund-dashboard + accrual rules for committed balance
Body:
Reconciles the catalog after PROMO-512 shipped.
- docs/ui/fund-dashboard.md: add committed/posted/remaining fields,
bump last_verified to 2026-06-26.
- docs/rules/accrual-posting.md: define "committed" (Planned|Approved
accruals) vs "posted" (after nightly job).
- docs/batch/accrual-posting-job.md: note dashboard refresh dependency.
Closes-knowledge-gap-for: PROMO-512
PROMO-512 appears in the story's links, in the docs commit messages, in the PR title, and in each touched entity's related_stories front-matter. That single shared token is what makes trace (chapter 5) able to answer "where is committed-balance documented, and which story put it there?" in one hop. No key, no round-trip.
Docs-as-code only beats doc-rot if a machine enforces the rules. The docs repo runs CI on every pull request, and the checks exist specifically to catch the failure modes that let documentation drift out of truth.
| CI check | What it enforces | What it prevents |
|---|---|---|
| Front-matter linter | Required fields present; status in {draft, reviewed, authoritative}; last_verified is a real date | Untracked, ownerless, or undated entities sneaking in |
| Link checker | Every cross-link between entities resolves | Broken references after a file is renamed or moved |
| Freshness gate | Warn when last_verified is older than the threshold | Silent staleness — docs that look authoritative but aren't |
| Schema validation | schema.sql parses and matches the migrations | The schema-of-record drifting from the real database |
A trimmed GitHub Actions workflow showing two of these gates:
name: docs-ci
on:
pull_request:
paths: ["docs/**"]
jobs:
catalog-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Front-matter lint
run: |
# required fields + valid status enum + last_verified is a date
python scripts/lint_frontmatter.py docs/ \
--require owner,status,last_verified,source_of_truth \
--enum status=draft,reviewed,authoritative \
--date last_verified
- name: Cross-link check
run: python scripts/check_links.py docs/ # fails on any broken entity link
- name: Freshness audit (warn-only)
continue-on-error: true
run: python scripts/freshness.py docs/ --max-age-days 180
- name: Schema parses & matches migrations
run: |
sqlfluff parse docs/data/schema.sql
python scripts/schema_matches_migrations.py \
docs/data/schema.sql db/migrations/
last_verified is a signal for the Librarian's freshness-audit (chapter 4), not a reason to reject an unrelated PR. Hard-failing on staleness would punish whoever touches a file near a stale neighbor. Make freshness loud and visible, but keep the merge-blockers to objective facts: required fields, valid enums, resolvable links, a schema that parses.
Nothing in the catalog auto-merges. The docs repo's default branch is protected: a change lands only through a pull request that passes the blocking CI checks and carries at least one human review approval. The Librarian's job ends at opening a well-formed PR — with the right entity edits, bumped freshness dates, and the Jira key in the title. A person presses merge.
This is the deliberate seam where human judgment sits. The agent can draft, reconcile, and propose at machine speed; it cannot quietly rewrite the system of record. Every fact in the catalog has, somewhere in its history, a human who approved the PR that put it there — which is exactly what makes the catalog something the Analyst, and the team, can believe.
The backend is now concrete: Jira holds the transient work and GitHub holds the durable knowledge; the Analyst reaches Jira and the Librarian reaches GitHub, each through its own MCP server; the Jira key threads through stories, commits, PRs, and front-matter to make the round-trip navigable both ways; CI stops the catalog from rotting; and branch protection keeps a human on the merge button. Chapter 7 turns this wiring into the actual files — the subagent definitions, the skills, the soul.md judgment file, the CLAUDE.md, and the .mcp.json that names the servers we kept deliberately light here.