| name | build-app-context |
| description | Use when you need to build or refresh a context file for a FOLIO application area before writing test cases. This skill gathers live data from TestRail (existing cases), GitHub (README, docs, feature files, capability/permission definitions), and Jira (closed stories and bugs for the component), then distills everything into a structured context .md file and saves it to references/context/. Run this skill once per application area, then use write-testrail-cases normally. |
| license | Apache-2.0 |
| metadata | {"author":"folio-org","version":"1.0.0"} |
Build App Context
Builds or refreshes a references/context/<area>.md file by gathering live data from TestRail, GitHub, and Jira. The resulting file is used by the write-testrail-cases skill as its primary domain knowledge source.
Credentials
Read credentials using this fallback chain (try in order, stop at first hit):
.env file in the repository root — parse KEY=VALUE pairs
~/.folio-credentials — legacy local file
- Environment variables — for CI/Codespaces
Keys to read:
TESTRAIL_URL, TESTRAIL_USER, TESTRAIL_API_KEY
JIRA_BASE_URL, JIRA_USER_EMAIL, JIRA_API_TOKEN
GITHUB_TOKEN (optional)
Never ask the user for credentials — try all three silently. If all three fail — report which keys are missing and stop.
Phase 0 — Interactive Setup
Phase 0.5 — Auto-resolve module metadata (run before asking questions)
Before presenting questions to the user, silently fetch the FOLIO responsibility matrix:
GET https://folio-org.atlassian.net/wiki/spaces/REL/pages/5210256/
FOLIO+Module+JIRA+project-Team-PO-Dev+Lead+responsibility+matrix
Parse the table and build a lookup by area name / module name. For the area the user named (from their trigger message), extract:
| Field | Matrix column | Use for |
|---|
jira_prefix | JIRA project | Jira JQL filter, area detection table in SKILL.md |
team | Team | custom_dev_team default |
ui_repo | Module (rows where module starts with ui-) | GitHub Phase 2 |
be_repo | Module (rows where module starts with mod-) | GitHub Phase 2 |
tests_repo | Tests repository | GitHub Phase 2 (additional repo) |
ecs_scope | ECS (Central/Member/All)/NonECS/Both | Pre-fill ECS Enabled default |
eureka_app | Application Trillium | Context file header |
A single area typically has 2–3 rows (one per module). Collect all matching rows.
If the area name matches multiple distinct teams (e.g. "ERM" is K-Int), note it in the Phase 0 questions.
If the page is not publicly accessible (auth required), skip this step silently and ask the user for repos manually as before.
After auto-resolving, skip questions 3 and 4 from the standard setup and show pre-filled values instead:
I found the following metadata for "<area>" in the FOLIO module matrix:
JIRA prefix: <prefix>
Team: <team>
GitHub repos: <ui_repo>, <be_repo>
Tests repo: <tests_repo> (if present)
ECS scope: <Central/Member/All | NonECS | Both>
Eureka app: <app name>
I still need from you:
1. TestRail section ID(s) for existing manual cases
(Find it in the URL: ?/cases/view/14§ion_id=XXXXX)
2. Jira component name (if different from "<prefix>") — or leave blank
to use the JIRA project prefix directly.
3. Releases to focus on [default: Umbrellaleaf, Trillium, Sunflower]:
Confirm or correct the pre-filled values, then I'll start building.
Fallback (matrix not accessible or area not found) — ask all five questions in one message:
I'll build a context file for a FOLIO app area by pulling data from TestRail,
GitHub, and Jira. A few questions:
1. **App area name** — what should the context file be called?
(e.g. "orders", "data-export", "bulk-edit")
2. **TestRail section ID(s)** — the section(s) containing existing manual
test cases for this area. You can give one ID or a comma-separated list.
(Find it in the TestRail URL: ?/cases/view/14§ion_id=XXXXX)
3. **GitHub repositories** — which repos cover this area?
Typically: ui-<name> and mod-<name>. Paste full repo names or URLs.
(Leave blank to skip GitHub.)
4. **Jira component or epic** — component name or epic key.
Examples: component = "Orders", epic = "UXPROD-1234"
(Leave blank to skip Jira.)
5. **Releases to focus on** [default: Umbrellaleaf, Trillium, Sunflower]
After the user answers, echo back a one-line confirmation:
Building context for from: TestRail section(s) · GitHub: · Jira: <component/epic>
Then proceed through Phases 1–4 without further interruption.
Phase 1 — Gather: TestRail
1.1 Pull existing cases
For each provided section ID, fetch all cases:
GET {TESTRAIL_URL}/index.php?/api/v2/get_cases/{project_id}§ion_id={id}&limit=250&offset=0
Authorization: Basic base64(TESTRAIL_USER:TESTRAIL_API_KEY)
Paginate (increment offset by 250) until _links.next is null. Collect all cases across all provided section IDs.
Fields to extract per case:
id, title, custom_preconds, custom_steps_separated, custom_test_group
custom_release, custom_ecs_enabled, updated_on
refs (linked Jira IDs)
1.2 Determine recency weights
Assign weight to each case based on custom_release:
| Release label | Weight |
|---|
| Umbrellaleaf (R2 2026) | 1.0 |
| Trillium (R1 2026) | 0.9 |
| Sunflower (R1 2025) | 0.8 |
| Ramsons (R2 2024) | 0.6 |
| Quesnelia (R1 2024) | 0.5 |
| Older / blank | 0.3 |
Use weights when voting on whether a text string is a real current UI element (high-weight occurrences beat low-weight ones).
1.3 Extract signals from cases
Process all cases (steps + preconditions as plain text after stripping HTML tags):
A) Toast messages — lines matching: Toast message "...", "..." toast message appears, System notifies that "...", success.*"...", "...successfully...".
- Canonicalize each extracted text: resolve
<name>, <number>, <PO number> placeholders by looking at what fills the slot in the most common variant.
- Keep only strings with weighted frequency ≥ 3 (or top 30 if fewer).
B) Modal/dialog titles — phrases matching "..." modal, "..." dialog, "..." popup.
- Keep weighted frequency ≥ 2.
C) Pane names — phrases matching "..." pane.
- Keep weighted frequency ≥ 3.
D) Accordion names — phrases matching "..." accordion.
- Keep weighted frequency ≥ 3.
E) Button labels — phrases matching "..." button, click "...", select "...".
- Keep only those with weighted frequency ≥ 5 AND length ≤ 40 chars (filter out sentence-length matches).
F) Status values — phrases matching status.*"...", "..." status, = "..." adjacent to known status fields (Workflow status, Payment status, Receipt status, Request status, etc.).
- Deduplicate; keep per-field groupings.
G) Capability Sets — lines in preconditions matching the pattern Data - ..., Procedural - ..., Settings - ..., Module - ....
- Collect all unique capability set strings; deduplicate exactly.
H) Verification patterns — step sequences where action references a field name and expected result contains = or displays with a value. Capture 3–5 of the most representative multi-step sequences verbatim (these become the "Common Verification Patterns" section).
I) Navigation paths — phrases matching navigate to ... > ... > ... or Settings > ... > ....
- Collect the 10 most common.
J) ECS-specific signals — from cases where custom_ecs_enabled = true: extract tenant names used in preconditions (Central, member-1, member-2, etc.), cross-tenant step patterns, affiliation switching steps.
1.4 Summarise business rules from case titles
Group case titles by implicit category:
- Titles containing "cannot", "not able", "without permission" → negative / capability boundary rules
- Titles containing "successfully", "can create/edit/delete" → positive rules
- Titles with status words → lifecycle rules
Extract noun phrases that repeat across ≥ 3 titles in the same category — these are implicit business rules. Format as: "<subject> <verb> <object> when <condition>".
Phase 2 — Gather: GitHub
For each provided repository (owner/repo format, inferred from URL if full URL given):
2.1 Discover relevant files
GET https://api.github.com/repos/{owner}/{repo}/git/trees/HEAD?recursive=1
Authorization: Bearer {GITHUB_TOKEN} (omit header if no token)
From the file tree, select files matching these patterns:
| Priority | Pattern | What to extract |
|---|
| High | README.md, doc/*.md, docs/*.md, **/CHANGELOG.md | Feature descriptions, business rules, known limitations |
| High | **/*.feature, **/test/**/*.feature | Karate/Cucumber scenarios → implied business rules and UI flows |
| Medium | **/permissions.json, **/moduleDescriptor.json, **/descriptors/*.json | Exact capability/permission names and their descriptions |
| Medium | src/main/resources/swagger.api/*.yaml or **/*openapi*.yaml | API endpoints and schemas (record types, field names) |
| Low | translations/ui-*/en_US.json, translations/ui-*/en.json | Exact UI label strings keyed by translation key |
Fetch at most 20 files total, prioritising by the order above. Skip files > 200 KB.
2.2 Extract from each file type
README / doc / CHANGELOG:
- Pull sections describing features, workflows, limitations, and known issues.
- Note version-specific behaviour ("Since Ramsons", "Deprecated in Sunflower").
Feature files (.feature):
- Extract scenario titles (lines starting with
Scenario: or Scenario Outline:).
- Extract Given/When/Then steps that describe UI interactions or business assertions.
- Map Cucumber steps to implied UI texts (e.g.
And I should see "..." label → UI string candidate).
moduleDescriptor.json / permissions.json:
- Extract all
"permissionName" or "id" values under "provides" / "permissionSets".
- Map legacy permission names to Eureka Capability Set equivalents where the mapping is explicit in the file (look for
"replaces" or "eureka" keys).
- If no mapping found, flag as
(verify Capability Set name — legacy permission: <name>).
en_US.json / en.json (translations):
- Extract values (not keys) for entries whose keys contain:
modal, button, label, toast, message, title, header, pane, accordion, action, success, error, confirm.
- These are the exact UI strings rendered in the interface — highest reliability for UI text extraction.
2.3 Reconcile GitHub strings with TestRail strings
For each UI text extracted from translations or feature files, check if it also appeared in TestRail cases (Phase 1 extractions). If both sources agree → mark as [confirmed]. If only GitHub → mark as [from source, verify in env]. If only TestRail (weighted freq ≥ 5) → mark as [from cases].
Phase 3 — Gather: Jira
3.1 Pull closed stories
POST {JIRA_BASE_URL}/rest/api/3/issue/search
Authorization: Basic base64(JIRA_USER_EMAIL:JIRA_API_TOKEN)
Content-Type: application/json
{
"jql": "project = FOLIO AND component = \"<component>\" AND issuetype in (Story, \"New Feature\") AND status = Done ORDER BY updated DESC",
"maxResults": 100,
"fields": ["summary", "description", "customfield_10014", "fixVersions", "labels", "status", "issuetype"]
}
If the user gave an epic key instead of a component, use:
"jql": "\"Epic Link\" = <epic_key> AND issuetype in (Story) AND status = Done ORDER BY updated DESC"
Paginate with startAt until all results collected (up to 500 stories; stop earlier if results go older than 2 years).
3.2 Pull bugs
POST {JIRA_BASE_URL}/rest/api/3/issue/search
Authorization: Basic base64(JIRA_USER_EMAIL:JIRA_API_TOKEN)
Content-Type: application/json
{
"jql": "project = FOLIO AND component = \"<component>\" AND issuetype = Bug AND status = Done ORDER BY updated DESC",
"maxResults": 100,
"fields": ["summary", "description", "fixVersions", "labels"]
}
Paginate with startAt until all results collected, then keep the latest 100 bugs for distillation.
3.3 Extract from stories and bugs
From story descriptions:
- Pull Acceptance Criteria sections (look for
h3. Acceptance Criteria, *Acceptance Criteria*, or **Scenarios:** headings).
- Each acceptance criterion → candidate business rule.
From story summaries:
- Group by verb ("View", "Create", "Edit", "Delete", "Share", "Export", "Import", "Open", "Close", "Cancel", "Approve", "Reorder", etc.).
- Build a capability map:
"User can <verb> <object> [when <condition>]".
From bug summaries:
- Bugs describe what should NOT happen or edge cases that need protection.
- Format as negative/edge-case business rules:
"<object> must not <behaviour> when <condition>" or "<action> should fail when <condition>".
From fix versions:
- Note which FOLIO release (flower name) each story/bug targets — use to date-stamp business rules.
Phase 4 — Distill: Build the Context File
Assemble the collected signals into a structured markdown file. Use the template below. Fill every section; mark gaps explicitly rather than omitting them.
# FOLIO <Area Name> App — Business Logic Context
> Generated by the `build-app-context` skill on <date>.
> Sources: TestRail section(s) <IDs> (<N> cases, releases <range>)
> GitHub: <repos>
> Jira: component/epic <name> (<N> stories, <N> bugs)
> Strings marked [confirmed] appear in both GitHub translations and TestRail cases.
> Strings marked [from source] come from GitHub only — verify in env before asserting.
> Strings marked [from cases] come from TestRail cases (weighted freq shown).
---
## What is <Area>
<2–4 sentences from README / story descriptions. What problem does this app solve? Who uses it?>
---
## Key Terms
| Term | Definition |
|---|---|
| <term> | <definition> |
---
## Record Hierarchy / Architecture
<ASCII diagram or bullet tree showing the >
---
| Status | Description |
|---|---|
---
Section > Subsection`.>
---
| Event | Toast text | Source |
|---|---|---|
| | | [confirmed] / [from cases (N)] / [from source] |
| Modal | Trigger | Source |
|---|---|---|
| | | [confirmed] / [from cases (N)] |
| Condition | Message text | Source |
|---|---|---|
---
, , ...
, , ...
---
| Action | Capability Set |
|---|---|
| | |
---
Action:
Expected:
<Repeat for 3–5 patterns extracted from Phase 1H.>
---
## ECS / Multi-Tenant Notes
<Only if ECS cases found in Phase 1J. Describe tenant setup, cross-tenant flows, and which steps require affiliation switching. Otherwise write: "No ECS-specific test cases found in this section.">
---
## Key Business Rules for Test Cases
> Numbered list. Every rule is either confirmed by ≥3 test case titles, an acceptance criterion from Jira, or a finding from a bug fix story.
> Source tagged in parentheses: (cases), (jira), (github), (cases + jira).
1. <rule> (<source>)
2. <rule> (<source>)
...
---
## Known Gaps / Items to Verify
> Strings or rules that could not be confirmed from two independent sources. Verify in env before relying on them in test cases.
- [ ] <string or rule> — found only in <source>
Phase 5 — Save and Report
Save the file
Write the distilled context to:
write-testrail-cases/references/context/<area>.md
IMPORTANT: The only valid output path is inside the
write-testrail-cases skill directory. If the agent is
running from the repo root, the full relative path is:
write-testrail-cases/references/context/.md
Never write to references/context/ at the repo root.
If the file already exists, overwrite it and note the previous version date in the header.
Update the area detection table in SKILL.md
Open SKILL.md and find the area detection table. If the area already has a row, update the Jira prefixes/keywords column. If it's a new area, add a row:
| <Area Name> | `references/context/<area>.md` | <Jira prefixes>; "<keywords from story summaries>" |
Report to the user
✅ Context file built: references/context/<area>.md
Summary:
TestRail: <N> cases processed (<N> recent, <N> older)
GitHub: <N> files read from <repos>
Jira: <N> stories + <N> bugs processed
Extracted:
Toast messages: <N> (confirmed: <N>, from cases only: <N>)
Modal titles: <N>
Capability Sets: <N> unique
Business rules: <N>
Verification patterns: <N>
⚠️ Items needing env verification: <N> (listed in "Known Gaps" section)
The context file is ready. You can now run write-testrail-cases for <area>.
Error Handling
| Situation | Action |
|---|
| TestRail section returns 0 cases | Warn: "Section returned no cases — check the section ID and project access." Still proceed with GitHub and Jira. |
| GitHub rate limit hit (403) | If no token: ask user to add GITHUB_TOKEN to ~/.folio-credentials. If token present: wait 60s and retry once. |
| GitHub repo not found (404) | Skip that repo, note in report: "Repo not found or not accessible." |
| Jira component returns 0 results | Try without component filter using only the project key. If still 0, warn and skip. |
| Jira v2 returns empty results | Switch to /rest/api/3/ — v2 is deprecated in this environment. |
| Jira API returns 401 | Report credentials error for JIRA_API_TOKEN. |
| File > 200 KB on GitHub | Skip and note filename in report. |
| Translation file missing | Note "No translation file found — UI texts rely on TestRail cases and feature files only." |
| Resulting context file > 400 lines | Trim the Verification Patterns section to the 3 most representative patterns; trim Toast and Modal tables to top 20 each by weighted frequency. Context files must stay readable — quality over quantity. |
Known Jira Access Limitations
The following Jira projects are confirmed inaccessible with the
current API token (all queries return 0 results):
| Area | Jira projects | Workaround |
|---|
| OAI-PMH | MODOAIPMH, UIOAIPMH, EDGOAIPMH | Skip Phase 3 |
| MARC Authority | UIMARCAUTH, MODELINKS | Skip Phase 3 |
| Licenses | ERM | Skip Phase 3 |
| Agreements | ERM | Skip Phase 3 |
| eHoldings | ERM | Skip Phase 3 |
For these areas: run Phase 1 (TestRail) + Phase 2 (GitHub) only.
Do not retry Phase 3 — log "Jira inaccessible" in context file
header and proceed without Jira signals.
Refresh Mode
If the user says "refresh" or "update context for " and the file already exists:
- Pull only cases updated since the file's generation date (use TestRail
updated_after filter).
- Pull only Jira issues updated in the last 90 days.
- Fetch GitHub only if the user explicitly says "also refresh GitHub".
- Merge new signals with existing ones: new toasts/modals are added, old ones with no recent case support are moved to "Known Gaps".
- Update the generation date in the file header.
- Report: "Refreshed .md: + new signals, items moved to Known Gaps."
Quick Reference
Trigger: "build context for <area>" / "update context for <area>"
Inputs: Area name, TestRail section IDs, GitHub repos, Jira component/epic
Outputs: references/context/<area>.md + updated area detection row in SKILL.md
Reads: ~/.folio-credentials
Runtime: ~2–5 min per area (depends on case count and GitHub file count)