Agent Skills Series (5): An admin query page case study—turning implicit conventions into an executable Skill

About this series

Earlier posts covered why Skills matter, how to write them, how to ship them, and what deserves to be a Skill. This one does a single thing: a concrete landing example—how rules that never appear in the spec but the team has always assumed get frozen into a project-level Skill.


Why “looks fine” code fails in QA

The scenario is familiar: you ask the model to generate an admin query page. It runs; the page renders. Then QA finds issues:

  • Pagination only wires pageSize and omits showSizeChanger, so users cannot change page size
  • The spec says the query window must not exceed one year; humans follow habit—but how should the model enforce “not more than a year”?
  • Only one or two rows of filters, yet the panel defaults to collapsed—an extra click every time
  • Org filtering ignores permission differences, so normal users can still change org criteria
  • Export returns error JSON but the UI shows nothing beyond “download failed”

These are not “deep domain logic”—they are implicit project conventions. Specs usually describe this change, not every historical habit. The model keeps writing the generic pattern; you keep filing the same corrections.


The core takeaway from this case

Split what you repeatedly correct in the model into a project Skill instead of oral add-ons.

The bar is blunt:

  • Repeats: you corrected the same thing more than three times in two weeks
  • Stable: it is unlikely to churn every week for the next six months
  • Executable: it can be written as a checklist, not a slogan

If all three hold, stop leaving it in chat—freeze it as a Skill.


Data first: define the constraint objects, then write rules

State the data relationships first. In this case, five bundles matter most:

  1. Query params: QueryParams (form fields + pagination fields)
  2. Backend request params: ApiParams (map pageSize/current to count/page)
  3. Date range: dateRange -> startTime/endTime/durationDays
  4. Org permission state: canSelectOrg and fixedOrgCode
  5. Export state: loading, blob, errorJson

Invariants should be explicit:

  • Pagination mapping is always: pageSize -> count, current -> page
  • Do not fetch the list before a query is triggered (optional policy)
  • Date-range validation must be unified (pick 31 days or 365 days per scenario)
  • Users without org-switch permission cannot change org; they only use their own org
  • For export responses, try to detect error JSON first, then treat as a file stream

Once this is written, the Skill has an executable skeleton, not slogans.


A redacted project Skill example (ready to reuse)

Below is a redacted layout: generic project names and paths; code stays pseudocode.

Directory layout:

admin-pc-query-guidelines/
├── SKILL.md
└── references/
    ├── query-page.md
    ├── query-form-fields.md
    └── export-download.md

1) SKILL.md (root)

---
name: admin-pc-query-guidelines
description: >-
  Use when building or changing admin-pc query pages, detail-page query blocks,
  or export features. Covers pagination mapping, org-permission queries,
  date-range constraints, query fold behavior, and export error handling.
tags:
  - admin-pc
  - admin-console
  - query-page
---

# Admin console development guidelines

## When to use
- New or changed query pages (ProTable)
- New or changed query modules inside detail pages
- New report export flows
- Org filters and date-range filters

## Reference docs

While developing, follow these reference files:

### Query page development

- [ProTable query page guidelines](references/query-page.md) — full template, required settings, column examples, checklist

### Query field configuration

- [Query field configuration](references/query-form-fields.md) — projects, branch / mid-level org selection, date-range rules (default one month, max one year), etc.

### Report download

- [Report download](references/export-download.md) — download API shape, error handling, etc.

2) references/query-page.md (template and checks)

# Query page guidelines

## Required setup
- Define `actionRef` / `formRef`
- Set `rowKey`
- Set full `pagination` (including `showSizeChanger`)
- Map pagination fields inside `request`

## Example
```tsx
const pagination = {
  defaultPageSize: 10,
  pageSizeOptions: ['10', '20', '50', '100'],
  showTotal: (total, range) =>
    `${range[0]}-${range[1]} of ${total} items`,
  showSizeChanger: true,
};

function requestList(params) {
  if (!shouldRequest) return emptyResult();

  const apiParams = {
    ...params,
    count: params.pageSize ?? 10,
    page: params.current ?? 1,
  };

  const res = await queryList(apiParams);
  return normalizeTableResult(res);
}
```

## Fold behavior
- If rendered filters fit within two rows: `defaultCollapsed = false`
- Otherwise default collapse is allowed

3) references/query-form-fields.md (org and date range)

# Query field configuration

## Org filter
- Load the current user’s permissions first
- If org cannot be switched: hide the org control and use a fixed org value
- If allowed: show the org control

## Date range
- Default: current month (1st of this month through yesterday)
- Convert to `startTime` / `endTime` (00:00:00 ~ 23:59:59)
- Validate `durationDays`; if invalid, show a message and block submit
- Default `MAX_DAYS` is 30 days

## Pseudocode
```ts
if (durationDays > MAX_DAYS) {
  showError(`Please select a range within ${MAX_DAYS} days`);
  return false;
}
```

4) references/export-download.md (export flow)

# Export guidelines

## Request constraints
- `responseType = blob`
- Export button uses the same params as the query

## Error handling
- Try parsing the response body as JSON text first
- If JSON parses and `code != 0`, show the business error
- If parsing fails, continue as a file download

## Pseudocode
```ts
async function handleExport(params) {
  const hide = showLoading('Exporting...');
  try {
    const blob = await exportApi(params);
    await tryParseBusinessError(blob);
    triggerFileDownload(blob);
    return true;
  } catch {
    showError('Export failed. Please try again.');
    return false;
  } finally {
    hide();
  }
}
```

Why this shape works

The win is not “how complete the template is”—it is turning leak-prone details into checkable items:

  • Round one already carries the project’s real constraints, not generic guesses
  • Review shifts from “spot by experience” to “tick a checklist”
  • Incremental specs no longer drop historical habits
  • New teammates align via the Skill instead of oral tradition

One line: move tacit knowledge from brains into versioned files.


Rollout order (do not boil the ocean)

A stable sequence:

  1. Start with one high-frequency surface (e.g., query pages)
  2. Freeze five to eight “most often missed” hard rules first
  3. For two weeks, only watch first-pass pass rate and rework rounds
  4. Then extend export, permissions, and date checks to similar pages

Do not start with a “one Skill for the entire repo”—that always spins out of control.


Non-engineering migration hints

The same pattern applies to marketing, content, and ops writing:

  • Marketing: brand banned words, compliance tone, campaign copy structure as a Skill
  • Content: pitch structure, headline rules, citation rules, pre-publish checks as a Skill
  • Ops: SOPs, script boundaries, escalation paths as a Skill

Same substrate: repeat, stable, executable.


Series map (sibling posts)

PartPostWhat it covers
1Pain points and motivationWhy Skills
2Concepts in practiceSKILL.md structure and progressive disclosure
3Toolingskill-base / skb distribution and install
4What deserves a SkillTopic criteria and counterexamples
6Operating playbookTriggers, conflicts, and version governance
ExtraFragmentationTrigger governance when many tools coexist