a9s-add-child-view
Blueprint for adding a new child view to a9s — 3-phase workflow (scope -> QA tests -> coder implement) with exact file manifests and hard-won lessons
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Blueprint for adding a new child view to a9s — 3-phase workflow (scope -> QA tests -> coder implement) with exact file manifests and hard-won lessons
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Recipe for adding a list-view attention column to a resource type — handles Tier A (existing field), Tier B (computed/Wave-2), and the FieldUpdates promotion path. Optimized to avoid the rediscovery overhead that plagued the first Tier A sweep.
Bug-hunting workflow for a9s using real AWS data, grounded in docs first, then live behavior, then code. Use for EC2-detail and related-view QA where existing tests may be misleading.
Shared rules for all a9s agents — shell rules, package access, build/test commands
Create or update the single-source fixture file for a resource type under `core/demo/fixtures/<shortName>.go`. Use when the coder is given a fixture-creation task during phase 6 of `a9s-implement-resource`, or any time a resource type needs new demo/test data (new states, new edge cases, new spec coverage). Takes a plain-language fixture list (typically from `docs/resources/<shortName>-impl-plan.md` §2) and produces realistic, graph-connected AWS SDK typed fakes that both `./a9s --demo` and the unit test suite import. Cross-references sibling fixture files so every related-panel pivot renders a non-zero count. Never creates orphan fixtures — a DB instance fixture without matching KMS keys, security groups, alarms, and CloudTrail events is a bug, not a feature. Adversarial / malformed fixtures (nil pointers, error cases) stay inline in tests and are explicitly out of scope for this skill.
End-to-end workflow for implementing a GitHub issue — from analysis through QA stories, design, scoped tasks, implementation, pre-release checks, and release prep. Use for any issue that is NOT a new-resource or child-view (those have their own skills).
Implement (or re-implement) an a9s resource type from its golden UX/UI spec at `docs/resources/<shortName>.md`. Use whenever the user asks to "implement", "wire up", "finish", "fix", or "rebuild" a resource that already has a spec doc — including cases where partial, stubbed, or buggy code exists and must be replaced. Treats the spec doc as the contract and the existing implementation as disposable. Reads ONLY the spec doc and four contract-surface files (`<shortName>_interfaces.go`, `<shortName>_related.go`, `<shortName>_issue_enrichment.go`, `<shortName>_detail_enrichment.go`); never reads existing tests or fetchers. Dispatches `a9s-qa` and `a9s-coder` with scoped file lists. Cleans up stubs and "pretend to work" code tied to TBDs. Trigger this for any request that names a resource shortName and asks for implementation, tests, fixtures, or cleanup — even if the user doesn't explicitly mention the spec doc.
| name | a9s-add-child-view |
| description | Blueprint for adding a new child view to a9s — 3-phase workflow (scope -> QA tests -> coder implement) with exact file manifests and hard-won lessons |
| disable-model-invocation | true |
Workflow: main session scopes -> QA + Coder execute (parallel-safe for child views).
| Phase | Owner | Writes to |
|---|---|---|
| Phase 1: Spec & scoping | Main session | Scoped task manifests only |
| Phase 2: Tests | a9s-qa | tests/unit/ only |
| Phase 3: Implementation | a9s-coder | internal/, cmd/, .a9s/ only |
Coder MUST NOT write test files. QA MUST NOT write production code. Both subagents MUST reject tasks without an exact file scope.
The main session reads the design spec and parent fetcher, then produces two scoped tasks — one for QA, one for coder. This prevents context drain — the subagents receive only the manifest, not the full design spec.
Parent analysis — read core/aws/{parent}.go:
Resource.ID? (often a name, NOT an ARN)ServiceClients field is needed? Already exists?ContextKeys mapping — the #1 source of bugs:
"ID" -> Resource.ID (often a name — verify!)"Name" -> Resource.Name"field_key" -> Resource.Fields["field_key"]"@parent.x" -> inherited from parent context (Pattern C nesting)Field formatting rules (apply to ALL fields):
*int64 epoch-ms timestamps -> use formatEpochMillis() in fetcher, key: in config*int64 byte counts -> use formatBytes() in fetcher, key: in configpath: in config (reads from RawStruct directly)key: in config (reads from Fields map)path: for timestamps or byte counts — they show raw numbersFile manifest — exact list of files to CREATE/EDIT/APPEND with specific content
CHILD VIEW SPEC: {child_shortname}
Parent: {parent_shortname} | Pattern: {A/B/C/D} | Key: {enter/e/L/r/s}
API: {service}:{APICall} with {ParentParam}
Client field: c.{ServiceField} (exists: yes/no)
Parallelization: parallel-safe
CONTEXT KEYS:
{context_key} <- Fields["{field_key}"] (verified: parent stores ARN at line N)
COLUMNS (all use key: in config):
{key} | {Title} | {width} | formatter: {none/formatEpochMillis/formatBytes}
DETAIL PATHS:
{Path1}, {Path2}, ... (longest: {N} chars -> keyW will auto-size)
### CODER TASK:
Files to create:
core/aws/{child_type}.go — child fetcher + init() with RegisterChildType/RegisterPaginatedChild/RegisterFieldKeys
Files to modify:
core/aws/{service}_interfaces.go — append {InterfaceName} narrow interface AND embed it on the aggregate {Service}API in the same file
Append point: after last narrow interface, before the aggregate {Service}API
core/resource/types.go — add Children to {parent}, add {ChildType}Columns()
Append point: grep "{parent_shortname}" in resourceTypes
core/config/defaults.go — add "{child_shortname}" entry
Append point: last entry in defaultViews.Views map
.a9s/views/{child_shortname}.yaml — regenerate via viewsgen
cmd/refgen/main.go — append entry (if SDK struct)
Append point: last entry in resources slice
core/demo/fixtures/<service>.go — add child fixture data to the parent service's fixture file
Append point: last fixture builder function in the service file
core/demo/fakes/<service>.go — extend the fake to serve the child data
Append point: last method on the fake struct
Context files (read-only):
core/aws/{parent}.go — parent fetcher for ContextKeys verification
core/aws/ec2.go — canonical example
### QA TASK:
Test files to create:
tests/unit/aws_{child_shortname}_test.go — fetcher tests
Test files to modify:
tests/unit/mocks_test.go — append mock struct
Append point: last mock in file
tests/unit/qa_detail_child_views_test.go — append 2 tests
Append point: last TestQA_Detail_ function
tests/unit/qa_yaml_child_views_test.go — append 3 tests
Append point: last TestQA_YAML_ function
tests/unit/qa_list_rawstruct_child_views_test.go — append 1 test
Append point: last TestQA_ListRawStruct_ function
Mock structure:
{exact mock struct + method signature}
Type signatures:
{interface + SDK types needed for compilable tests}
What to test:
- Happy path: {expected behavior}
- Empty response: {expected behavior}
- API error: {expected behavior}
- Pagination: {if applicable}
- Nil fields: no panic
- Parent context: verify correct key used
Context files (read-only):
core/aws/{service}_interfaces.go — interface definition (after coder adds it)
core/resource/types.go — column keys
The QA agent receives the scoped QA task and writes ALL tests.
1. Mock: tests/unit/mocks_test.go (APPEND)
outputs []*{Output} slice with callIdx counteroutput *{Output}2. Fetcher tests: tests/unit/aws_{child_shortname}_test.go (CREATE)
3. Detail tests: tests/unit/qa_detail_child_views_test.go (APPEND)
4. YAML + List tests: (APPEND to existing files)
5. Run go test — confirm tests compile (or fail with expected missing-function errors if running before coder).
The coder receives the scoped coder task and makes all tests pass.
1. Interface: core/aws/<service>_interfaces.go (APPEND to the service's per-service file; also embed on the aggregate <Service>API in the same file)
type {InterfaceName} interface {
{APICall}(ctx context.Context, params *{service}.{APICall}Input, optFns ...func(*{service}.Options)) (*{service}.{APICall}Output, error)
}
2. Client field (IF new service): core/aws/client.go
3. Child fetcher: core/aws/{child_type}.go (CREATE)
init() registers: RegisterFieldKeys, RegisterPaginatedChild, RegisterChildTypeformatEpochMillis(*field) — NEVER fmt.Sprintf("%d", *field)formatBytes(*field) — NEVER fmt.Sprintf("%d", *field)\nconst maxResults = 500)[]resource.Column with proper widths4. Parent wiring: core/resource/types.go (EDIT)
Children on parent type5. Config: core/config/defaults.go (ADD)
Key: for computed fields, Path: only for string SDK fields6. Views config: .a9s/views/{child_shortname}.yaml (REGENERATE)
go run ./cmd/viewsgen/7. Refgen: cmd/refgen/main.go (APPEND if SDK struct)
8. Demo fixtures:
Hybrid fixture pattern (014-demo-transport-mock). Demo mode has two layers: the legacy HTTP transport (core/demo/transport.go + handlers.go) is the base for all services, and per-service typed fakes (core/demo/fakes/<service>.go) override individual services. Currently only EC2 uses a typed fake.
core/demo/fixtures/<service>.go and extend the matching fake in core/demo/fakes/<service>.go.core/demo/fixtures_*.go category file and (if needed) extend handlers in core/demo/handlers.go.When adding a new child view, match the parent service's current layer. Do not mix layers for the same service.
9. Parent fetcher (IF needed): add missing Fields (e.g., ARN)
make test
make lint
make gofix
make build
go run ./cmd/viewsgen/ # always — regenerate from defaults
go run ./cmd/refgen/ > .a9s/views_reference.yaml # if SDK struct added to refgen
Enter drills in.Enter->Tasks, e->Events, L->Logs), CFN (Enter->Events, r->Resources)RegisterChildType includes Children slice.@parent. prefix in ContextKeys.app.go — generic handleEnterChildView and fetchChildResourcesmessages.go — EnterChildViewMsg handles all child navigationresourcelist.go — handleChildKey and buildChildContextkeys.go — trigger keys already definedContextKeys: ARN vs Name — Resource.ID is often a name, not an ARN. If the child API needs an ARN, verify the parent populates it in Fields. Test this explicitly.
key: vs path: in config — path: reads raw SDK struct (epoch ms, raw bytes). key: reads formatted Fields values. ALWAYS use key: for timestamps and byte counts.
Detail view Fields-first — renderFromConfig checks Fields before RawStruct. Fetchers must populate Fields with ALL formatted values needed for detail display.
Newlines in messages — PadOrTrunc strips \n/\r, but log-like messages should be cleaned in the fetcher too.
Narrow screens — fitColumns shrinks the last column to remaining space (min 10 chars). Don't assume fixed terminal width.
Detail key column — computeKeyWidth() auto-sizes from longest field name. Long dotted paths like Target.AvailabilityZone are handled.
Pagination caps — large AWS resources (log groups with 8000+ streams) need pagination limits. Add const maxResults = 500 and break when exceeded.
Deprecated AWS fields — StoredBytes on LogStream is deprecated (always 0). Check AWS docs before adding fields.
formatBytes/formatFloat are shared utilities in core/aws/log_streams.go — reuse them, never delete.
Sort by age — getAgeField matches field keys containing: time, date, launch, creation, event, start, timestamp. Name new time fields accordingly.