| name | a9s-resource-spec |
| description | Generate a uniform, implementation-blind specification for a single a9s resource type. Use whenever the user asks to "spec", "document", or "describe" a resource by its shortName (e.g. "spec ec2", "write the resource doc for dbi", "generate the lambda spec", "create docs/resources/s3.md"), or asks for the contract, related panel, issues algorithm, or Wave 1/Wave 2 behavior for a named AWS resource type in the a9s codebase. Reads only the four golden docs in docs/ โ never source code โ and writes docs/resources/<shortName>.md following a strict template so all resource specs are diff-able and can drive test and fixture generation. Trigger this skill for any request that names an a9s resource shortName and asks for a doc, spec, contract, or behavior summary. |
| argument-hint | <shortName> |
| allowed-tools | ["Read","Glob","Grep","Write","Update","Edit","Bash","Bash(go doc *)","Bash(mkdir -p *)","Agent(a9s-devops)","Agent(general-purpose)","WebFetch(domain:docs.aws.amazon.com)"] |
a9s Resource Spec Generator
Generate docs/resources/<shortName>.md โ the golden UX/UI document for one a9s resource type, written from the operator's perspective. It describes how the resource should appear and behave to a human using a9s: what the row looks like in the list, what color it is, what glyph if any, what the Status column says, what the detail view reveals, what related resources are one key press away. Not how the code is organized โ how the UX is.
What this doc is for
This is the should-be, not the is. It is the contract; implementation conforms to it. When code and this doc disagree, code is wrong.
Concretely, each generated doc drives three downstream activities:
- UX/UI review โ a reader (operator, designer, reviewer) can read it and immediately agree or disagree that this is how the resource should look and feel. Disagreement is a design conversation, not an engineering bug.
- Implementation verification โ tests, consistency checkers, and code reviews compare production behavior to this doc.
- Fixture and test generation โ the exact Status column strings and detail sentences in the doc become the assertions tests run.
WHAT vs HOW โ where each source fits
This split matters and anchors everything the skill does:
- WHAT โ which resources exist, which related pivots they have, which issues to watch for, which AWS APIs return what. Lives in the four a9s golden docs and the AWS API Reference. Changes slowly. Answers "does this resource have an X issue?"
- HOW โ how each WHAT is delivered to the operator: row color, glyph, Status column wording, detail-view sentence, menu count rule. This is what the generated
docs/resources/<shortName>.md establishes. HOW is a UX/UI decision and is expected to evolve. Regenerating a spec captures the current HOW.
The four a9s golden docs are not the HOW. Passages in docs/historical/analysis/enrichment-visibility.md that look like HOW decisions (row middle-dot ยท, โ Background Check header, derived list-level banner) are stale HOW that this skill now supersedes. The current HOW lives in this skill's surface rules (S1โS5) and in the generated per-resource doc.
Ground each generated spec in:
- WHAT sources โ the four a9s golden docs (resources to show, issues to watch, API references to use) and the AWS API Reference (field names and response shapes).
- HOW sources โ this skill's S1โS5 surface rules, applied to the WHAT.
Why implementation-blind
The skill never reads a9s source. If it did, the code would silently become the spec and drift would stop being detectable โ the whole point is catching drift between should-be (this doc) and is (the code).
When a WHAT is not specified in the golden docs or the AWS Reference, the spec writes TBD โ not specified. A TBD is valuable โ it surfaces an unmade decision so the team can make it deliberately.
Handling gaps โ escalate, don't TBD
Raw TBD is a last resort. Before writing one, gaps go through two escalation steps:
Step 1 โ Ask the a9s-devops agent (mandatory for every would-be TBD)
The skill must dispatch a9s-devops (.claude/agents/a9s-devops.md) whenever it would otherwise write a TBD. The agent is a senior AWS practitioner who knows real workflows โ it fills gaps the golden docs don't cover with operator-grounded answers.
For each gap, ask two questions:
- Possible? โ Does AWS expose what we need (a field, an API, a cross-reference) to answer this gap? Cite the field or API if yes.
- Worth it? โ Does a daily-driver operator actually benefit from this being in the spec? Explain the workflow or explicitly say no.
Prompt shape to a9s-devops:
Context: generating the a9s UX/UI spec for resource <shortName>. Gap: <describe what is silent in the golden docs>. Please answer: (a) is it possible to fill this from AWS โ which field or API? (b) is it worth filling for a daily-driver operator โ what's the workflow? Return both answers in 3-5 sentences, including any field/API citation.
Interpret the agent's response:
- possible=yes, worth=yes โ write the answer into the relevant spec section (ยง2 related target, ยง3 signal, ยง4 table cell), followed by
โ a9s-devops: <one-line rationale>. The TBD is resolved.
- possible=yes, worth=no โ record in ยง5 Out of Scope with the rationale:
<what> โ a9s-devops: not worth it, <reason>. No TBD.
- possible=no โ record as
TBD โ a9s-devops: not available in AWS surface, <reason>. This is the rare case where a real TBD remains, now documented.
Batch related gaps into one agent dispatch where sensible (e.g. "how are alarm, asg, backup, logs, ng, tg discovered as related targets for ec2?" is one dispatch, not six).
Step 2 โ Ask the user (only for material HOW choices)
Use sparingly. Ask the user directly only when the call is a UX/UI choice, not an AWS-practitioner question โ the devops agent can't decide ! vs ~ severity by itself because that's a product decision. Examples that go to the user, not devops:
- Severity choice (
! vs ~) for a borderline finding when it will establish a rule for future resources.
- Which AWS field, from several equally valid candidates, to surface in the Status column when the visible text matters.
How to ask: one focused question at a time, phrased from the operator's perspective. If the user answers "decide", the skill decides and notes the call in ยง6 Citations.
Decision record
Every devops consultation and every user answer goes into ยง6 Citations on its own bullet, with this format:
<claim> โ a9s-devops agent (<date>): possible=yes|no, worth=yes|no. <rationale>.
<claim> โ user decision (<date>): <short answer>. <rationale if given>.
Remember decisions within a session so the skill doesn't re-ask.
Operator perspective โ keep asking
Every sentence you write should answer one of these, in order:
- What does the operator see on the list row for this resource? (color, glyph, identity, status text)
- When they see something concerning, what does it mean โ in their words, not AWS jargon?
- What's their next step โ stay on the list, or press detail for the full story?
- Which other resources do they care about from here, and what does each pivot tell them?
If a sentence in the generated spec doesn't serve one of those questions, cut it.
Inputs
<shortName> โ the a9s resource short name, e.g. ec2, dbi, lambda, s3, ng, sg, elb.
Sources of truth
Primary โ the four golden docs:
docs/architecture.md โ layering, read-only invariant, allowed UI surfaces.
docs/related-resources.md โ per-type contract of related targets. The AWS API Reference URL for each type lives here.
docs/attention-signals.md โ Wave 1 / Wave 2 / Wave 3 signals per type. Each row cites its AWS API.
docs/historical/analysis/enrichment-visibility.md โ historical record of which surface categories exist (menu count, row color, glyph, status text, detail line). Treat it as WHAT only; its specific HOW mechanics are superseded by this skill's S1โS5 rules (see "Superseded HOW" below).
Secondary โ the AWS Go SDK v2 types. a9s vendors github.com/aws/aws-sdk-go-v2. The skill consults the SDK directly with go doc โ authoritative, local, instant. Use it to:
- Confirm an AWS field name is spelled correctly and actually exists on the response shape.
go doc shows the Go struct with field names as returned by the SDK (same as the wire format for AWS APIs).
- Find the exact field that carries a human cause for S4 Status text โ e.g. for a stopped EC2 instance the cause lives in
StateReason.Message and StateTransitionReason; for a failed NAT gateway it is FailureMessage; for a certificate expiry the value is NotAfter. These are the fields that let the list row read stopping: Server.SpotInstanceShutdown instead of a bare state word.
- Decide whether a signal is list-response only (Wave 1) or genuinely needs a Describe call (Wave 2). Compare the List* output type against the Describe* output type โ if the field only exists on the Describe shape, the signal is Wave 2.
Typical commands โ must be run from the a9s project root (where go.mod lives) so module resolution works. Running from $HOME will fail with cannot find package ... in any of: ($GOROOT not set).
go doc github.com/aws/aws-sdk-go-v2/service/<svc>/types.<Shape>
go doc github.com/aws/aws-sdk-go-v2/service/ec2/types.Instance
go doc github.com/aws/aws-sdk-go-v2/service/acm/types.CertificateSummary
go doc github.com/aws/aws-sdk-go-v2/service/acm/types.CertificateDetail
If the Bash call is not already running in the project root, prefix with cd /Users/k2m30/projects/a9s && or invoke via the Bash tool where the working directory is the project.
Tertiary โ web docs (fallback only). When the SDK comment is sparse or the semantics of a field aren't clear from the struct, fetch the AWS API Reference HTML (docs.aws.amazon.com). Prefer the SDK; fall back to web only when you need the narrative explanation.
Citation format follows the source you used:
- SDK:
AWS SDK Go v2 โ <package>/types.<Shape> ยง <Field>
- Web (fallback):
AWS API Reference: <API page name> ยง <field> (<URL>)
When the SDK and the golden doc contradict each other on a field name, prefer the SDK โ it is what the code actually sees on the wire.
Never read a9s source. Do not open internal/**, cmd/**, tests/**, .a9s/**. If the question is about a9s behavior (how it fetches, how it displays), the answer is in the golden docs or it is TBD. If the question is about AWS surface (what field names exist, what shape a response has), the answer is in the AWS API Reference.
Procedure
-
Validate the shortName. Confirm the shortName appears in both:
- the "Per-type contract" table in
docs/related-resources.md, and
- one of the signal tables in
docs/attention-signals.md.
If either is missing, stop and report exactly which doc lacks the row. Do not proceed, do not fabricate. This is the single hard-stop in the procedure.
-
Extract the raw material. From each source, pull what you need for the shortName:
related-resources.md: the contract row (AWS API URL + expected related targets), plus any reasoning-column notes nearby.
attention-signals.md: Name, Wave 1, Wave 2, Wave 3, Source cells for the row.
enrichment-visibility.md: only the high-level fact that surface categories exist. Do not copy specific mechanics โ use the S1โS5 rules in this skill instead.
architecture.md: the read-only invariant โ cited once in Out of Scope.
- AWS Go SDK v2 (
go doc github.com/aws/aws-sdk-go-v2/service/<svc>/types.<Shape>): the exact field name(s) that carry the cause text for S4 / S5, and which fields are on the List* vs Describe* shape.
-
Fill the template in references/output-template.md exactly. Keep every section heading and ordering. Keep AWS field names verbatim (State.Name, health.issues[], StorageEncrypted, etc.) โ paraphrasing loses the precision tests rely on.
-
Handle silence. Every would-be TBD escalates. Order: (a) for material UX/UI choices (severity, which field to surface), ask the user directly; (b) for anything else โ discovery mechanisms, workflow rationale, cost/benefit โ dispatch a9s-devops for a possible/worth verdict and record it inline with a citation (see "Handling gaps โ escalate, don't TBD" above); (c) only write a raw TBD when devops confirms the gap is not fillable from AWS surface. Never infer from a9s source, never invent a mechanism.
-
Write the file. docs/resources/<shortName>.md. Create the directory if it is missing. Overwrite an existing file โ uniformity wins over preservation.
-
Print a one-line summary to the conversation (not into the file):
<shortName>: related=<N> wave1=<N> wave2=<N> wave3=<N> tbd=<N> devops=<N> superseded=<N>[ (<short list>)]
devops = number of gaps filled by a9s-devops consultation (possible=yes answers written into the spec). tbd = remaining unfillable gaps after escalation. superseded = passages in the golden docs that describe now-superseded HOW (see "Superseded HOW" below). If zero, omit the parenthetical. This one line is the user's quick eyeball check that the extraction ran.
Output shape
See references/output-template.md for the full template. Sections, always in this order:
- Frontmatter โ
shortName, name, awsApiRef, generatedFrom.
- Identity โ shortName, display name, AWS API URL, list API, describe API (or "not used").
- Related Resources Panel โ one subsection per related target (why / how discovered / count shown).
ct-events goes last, flagged as the universal pivot.
- Attention Algorithm โ 3.1 Wave 1, 3.2 Wave 2, 3.3 Wave 3 (copied verbatim, each bullet prefixed
OUT OF SCOPE:).
- Issue Visualization โ per-signal table mapping every Wave 1/2 signal to surfaces reached, exact S4 list text, and S5 detail text. Includes ยง4.1 "UX review" paragraph.
- Out of Scope โ Wave 3 signals, any UI element not in S1โS5, any write operation.
- Citations โ one bullet per claim, citing either
docs/<file>.md ยง <heading> or AWS API Reference: <page> ยง <field> (<URL>).
Allowed visualization surfaces (exactly five)
These are the only surfaces the spec may reference. Any other "surface" the golden docs describe is superseded HOW โ the skill flags it for removal (see "Superseded HOW in the golden docs" below) rather than including it.
- S1 โ Menu
issues:N count + list frame title !N suffix. On the main menu view, counts ! findings only. ~ findings are deliberately excluded โ they are informational annotations, not issues the operator needs to chase. The resource-list frame title appends a space-separated !N after the count parentheses when the current list has N > 0 issues (s3(50+) !5, ec2(17) !1), or !N+ when N is a truncated lower bound; N uses the same aggregation as the menu badge (Wave 1 issue-colored rows + Wave 2 !-severity findings). No suffix when N = 0, and omitted in attention-only mode (ctrl+z) โ the filtered count already is the issue count, so name(5 of 50+) [!] stays as-is.
- S2 โ Row color in the list view. The row is colored by its state bucket: Healthy โ green (
ColRunning #9ece6a), Warning โ yellow (ColPending #e0af68), Broken โ red (ColStopped #f7768e), Dim โ terminated/gray. Yellow/red/dim are themselves the "something is off" signal โ they need no further annotation to get the operator's attention.
- S3 โ
! / ~ glyph prefixed before the name. Glyphs attach only to green (Healthy) rows as background-check annotations meaning "no immediate action needed, but worth knowing." Examples: RDS available + ~ โ maintenance scheduled in 12 days; ACM ISSUED + ! โ certificate expires in 7 days. Glyphs never appear on yellow, red, or dim rows โ those colors are already sufficient indicators.
- S4 โ Status / description column text in the list view, carrying the condition as short human-readable text (e.g.
stopping: Server.SpotInstanceShutdown, expires in 7d). Healthy rows render this column blank โ no OK, no available, no ACTIVE, no running. Empty-ness is the signal "nothing to see here", which drastically reduces list noise. Non-healthy rows always carry cause text here (state keyword alone is not enough; pair it with the reason).
- S5 โ Detail view enrichment line โ a short operator-readable sentence shown in the detail view for a resource that has a finding. No ceremonial header, no banner ornament; just the line rendered inline with the detail fields.
Mapping rules for ยง4 of the generated spec:
- A Wave 1 Healthy row is not worth a ยง4 row at all. S4 renders blank, no glyph, no finding. Omit from the table.
- A Wave 1 Warning / Broken / Dim signal drives S2 (color โ already the attention signal) and S4 (cause text, never a bare state keyword). It does not reach S1, S3, or S5 because Wave 1 does not produce a finding object, and S3 is forbidden on non-green rows.
- A Wave 2 Broken-style background finding on a Healthy resource gets
! โ S1, S3, S4, S5. The row stays green; the ! glyph and a short S4 cause-line flag the concern; operator can press detail for the full S5 sentence. Examples: ACM certificate nearing expiry, EC2 scheduled retirement <7d.
- A Wave 2 Warning/informational background finding on a Healthy resource gets
~ โ S3, S4, S5. Does not bump S1. Examples: RDS maintenance scheduled, EBS snapshot approaching cost-threshold age.
- A Wave 2 finding that lands on a resource whose row is already yellow/red/dim is redundant visually โ the color is already the signal. The finding is still worth recording in S5 for the detail view, and it may still bump S1 (if
!), but S3 is suppressed (no glyph on non-green rows) and S4 should deduplicate with the existing cause.
A signal that cannot land on at least one of these five surfaces is a gap โ flag it in the generated spec's ยง5 Out of Scope, do not invent a new element.
UX rules the spec must enforce
The spec drives implementation, so the spec must make bad UX impossible. A real failure mode this skill is designed to prevent: a list row showing DORMANT with no reason the user can read, and a jargon-banner count is a lower bound (truncated) above it. The user walks away knowing something is wrong but not what.
Rules every generated spec must follow:
- No internal jargon in any user-visible text. In every Status column value, glyph tooltip, and detail summary the spec writes, these words are banned:
Wave 1, Wave 2, Wave 3, finding, enrichment, probe, truncated, lower bound, bucket, severity. If a phrase from the golden docs uses these words, rewrite it in plain operator language for S4/S5, and note the rewrite.
- State keywords are not explanations.
DORMANT, available, stopped, failed, ACTIVE on their own tell the user nothing. In S4 (status/description column) or S5 (detail summary), the spec must pair the state with a cause the user cares about โ e.g. stopped: Server.SpotInstanceShutdown, dormant: not accessed in 400d, failed: IAM role missing permissions. The spec's Summary column shows the exact text an operator would read.
- Every ยง3 signal has a readable reason. For Wave 1 signals, fill the Summary column with the exact S4 / S5 wording a user would read.
n/a (row color only) is not acceptable โ a red row with no words is the screenshot bug. Even a Healthy row, when its spec row appears in ยง4, gets a short descriptor like running or omits S4/S5 entirely by leaving the row out of ยง4.
- S3 glyph never appears alone. When
! or ~ is prefixed to a name, either the same row carries the cause in S4 (preferred โ no navigation needed), or the cause is in S5 and the operator reaches it via the standard detail keypress. S5 is always the fallback; the spec does not pick a different route.
- S1 is the only terse surface. The menu
issues:N count is a number; it carries no reason, and that's fine because the user drills in to the list.
- Write for the operator, not the architect. Read each Summary back aloud as if you were the on-call engineer glancing at the list. If the answer to "what do I do next?" is not obvious from that line, rewrite it.
Add a brief ยง4.1 "UX review" block to every generated spec โ a two-sentence paragraph answering, for this resource: "At 3am, glancing at the list, can the operator tell what's wrong with a problem row without opening detail?" If the answer is no for any ยง3 signal, call it out as a UX gap the implementation must fix (e.g. add the cause to the Status column).
Superseded HOW in the golden docs
The golden docs contain passages that look like HOW decisions โ because HOW used to live there. Known superseded passages: row middle-dot ยท marker, โ Background Check detail header, derived list-level banner (โ N issues detected by background checks). These are not drift in the usual sense โ they are earlier UX calls, now replaced by the S1โS5 rules above and the per-resource specs this skill generates.
When the skill encounters such a passage:
- Ignore it for generation purposes โ do not cite it, do not reuse its mechanics.
- In the one-line summary printed after writing the file, append
superseded=<count> with a short list โ e.g. superseded=2 (row dot, banner).
- Ask the user: "Found N superseded HOW passages in
<file>. Want me to propose edits that remove them so the golden docs stay WHAT-only? (y/n)". On y, produce a diff patch for user review and apply only after explicit approval. On n, leave the golden doc untouched; the spec still ignores those passages.
The skill has permission to edit golden docs only to remove superseded HOW and only after the user approves each diff. It never adds new content to golden docs โ HOW content belongs in the per-resource spec, not in the WHAT docs.
Generation quality rules
- Every claim carries a citation. If you cannot cite it to a golden doc or the AWS API Reference, you cannot write it. Better to emit a
TBD than an unsourced sentence.
- One bullet per distinct signal. Do not collapse "status checks" or "encryption checks" into a single line โ tests want to target each condition separately.
- Uniformity beats eloquence. The docs are consumed by diff tools and test generators. A boring, mechanical extraction that matches the template in every resource is the right output.
What this skill does not do
- Does not edit source code.
- Does not read source code.
- Does not run tests or verify anything. It produces the contract that other tools verify against.
- Does not invent behavior to fill gaps โ gaps become
TBD markers so the golden docs can be patched.