[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DEP8PWK8VFF271A64341D0":3,"skill-same:01M6DEP8PWK8VFF271A64341D0":80},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":30,"linkOnly":31,"markdownContent":32,"markdownBody":33,"frontMatter":34,"refName":37,"commitSha":38,"skillDirectory":39,"forksCount":14,"repositoryHtmlUrl":40,"repositoryHomepageUrl":6,"ownerHtmlUrl":41,"ownerBlogUrl":42,"ownerDescription":6,"updatedAt":43,"install":44,"resources":57},"01M6DEP8PWK8VFF271A64341D0","assessment",null,"Guided product state evaluation. Spawns parallel discovery agents, validates findings with the developer, and produces a structured ASR document.","iamsteveholmes\u002Fmomentum","iamsteveholmes","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F92353?v=4","01M6DENK56DRSF72CJ6FJQ4RT1","Steve Holmes","01M6DENK9KCZRYFFEB0M689F7D",0,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002FSKILL.md",[],[5,19,20,21,22,23,24,25,26,27,28,29],"state","product","developer","Evaluation","ASR","discovery","parallel","document","structured","with","high","2026-11-10 10:12:30",false,"---\nname: docker-audit-firewall\ndescription: Use when reviewing\u002Fwriting host firewall rules (iptables\u002Fnftables) for a Docker-published port, or when a compose change adds a `ports:` mapping that replaces app-layer auth (Traefik, reverse-proxy) with a network ACL.\n---\n# Docker-Published Port Firewall Audit\n\nDocker manages its own iptables rules. Traffic to a **published** container port (`ports:\n\"host_ip:host_port:container_port\"`) is DNAT'd in the `nat` PREROUTING\u002F`DOCKER` chains and then traverses the\n**FORWARD** chain (via the `DOCKER-USER` hook) — it does **not** traverse the **INPUT** chain. INPUT only sees traffic\nterminating on a host-local process. So an `INPUT` ACCEPT\u002FDROP rule added to \"lock down\" a published port silently does\nnothing, while printing a success message that gives false assurance.\n\n1. **Identify the exposure model.** Read the compose\u002Frun config. Is the port *published* (`ports:`) or only on an\n   internal Docker network (`expose:` \u002F shared network)? Only published ports hit the host's packet-filter path. If the\n   change removes an app-layer control (Traefik TLS + basic-auth + ipallowlist middlewares) and replaces it with a raw\n   `ports:` mapping, treat the auth surface as **regressed** until proven otherwise.\n\n2. **Check which chain the ACL targets.** Grep the deploy\u002Fprovisioning script for `iptables`\u002F`nft` rules guarding that\n   port. If the rules are `-I INPUT`\u002F`-A INPUT` (or an nft `input` hook), they are on the wrong chain for\n   Docker-published traffic. The correct hook is **`DOCKER-USER`** (traversed before Docker's own FORWARD rules):\n\n   ```text\n   # -I prepends, so insert the DROP first, then the RETURN on top of it\n   iptables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL -j DROP\n   iptables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL \\\n     -s \u003Callowed-cidr> -j RETURN\n   # Same pair for IPv6 unless the port is published IPv4-only (ports: \"0.0.0.0:\u003Chost>:\u003Ccontainer>\")\n   ip6tables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL -j DROP\n   ip6tables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL \\\n     -s \u003Callowed-cidr6> -j RETURN   # omit this RETURN if no IPv6 source is allowed\n   ```\n\n   Resulting chain order (`iptables -L DOCKER-USER -n --line-numbers`): **1 = RETURN** for the allowlisted\n   source, **2 = DROP** for everyone else. The RETURN must sit **above** the DROP. Each `-I` prepends to the top,\n   so the rule inserted *last* ends up *first* — issuing RETURN then DROP (both `-I`) leaves DROP on top and blocks\n   the allowlisted source too. Flag any script that does that. With `-A` (append) the order is the reverse:\n   RETURN first, then DROP — but `-A` lands after Docker's own trailing `-j RETURN` in DOCKER-USER, so prefer\n   `-I`. DOCKER-USER sees packets after DNAT, so a bare `--dport` matches the *container* port of **every**\n   forwarded container — a DROP on `--dport 443` also blocks unrelated containers listening on 443 internally,\n   even when the host and container port numbers are equal. Scope the rule to the published host port with\n   conntrack's pre-DNAT `--ctorigdstport \u003Chost_port>` and flag any DOCKER-USER rule that uses a bare `--dport`.\n   Pair it with `--ctstate DNAT`: DOCKER-USER also sees container egress and other routed traffic, and without the\n   DNAT discriminator a rule for published port 443 also drops containers' outbound HTTPS.\n\n   **Scope of this rule pair:** it filters only NAT-published traffic that is forwarded through DOCKER-USER.\n   It does **not** cover (a) routed\u002Fdirect-routing networks (`gateway_mode_ipv4`\u002F`gateway_mode_ipv6=routed`, or\n   clients reaching the container IP directly), which are not DNAT; or (b) IPv6 connections the default userland\n   proxy accepts in a host process when the bridge is IPv4-only, which never traverse IPv6 DOCKER-USER. An\n   `ip6tables` pair covers only IPv6 that is NAT-published on an IPv6-enabled bridge.\n\n   **Verify these preconditions before approving the ACL** (from the config\u002Fscript, or ask for the output):\n   - `\u002Fetc\u002Fdocker\u002Fdaemon.json`: `firewall-backend`, `userland-proxy`, `ipv6`, `ip6tables`, `allow-direct-routing`;\n   - `docker info --format '{{.FirewallBackend}}'`: the backend actually in use (daemon.json may omit it);\n   - `docker network inspect \u003Cnet>`: `com.docker.network.bridge.gateway_mode_ipv4` \u002F `_ipv6`,\n     `com.docker.network.bridge.trusted_host_interfaces`, and `EnableIPv6`;\n   - `ss -ltnp` on the host: no `[::]:\u003Chost_port>` listener unless the IPv6 pair is installed on an IPv6 bridge.\n\n   Approve only when Docker uses the `iptables` firewall backend (the default; with `firewall-backend: nftables`\n   there is no DOCKER-USER chain, so these rules filter nothing), the traffic is NAT-published on a bridge (no\n   routed gateway mode, no direct routing, no `trusted_host_interfaces` — that option lets those interfaces reach\n   published container ports directly, without DNAT), and IPv6 is covered according to the bridge's IPv6 setting\n   (`EnableIPv6` \u002F daemon `ipv6`): on an **IPv6-enabled bridge**, a wildcard publication is NAT-published over IPv6\n   too, so require the `ip6tables` pair (`userland-proxy: false` does not help there); on an **IPv4-only bridge**,\n   require `userland-proxy: false` or an IPv4-only publication address (`ports: \"0.0.0.0:\u003Chost>:\u003Ccontainer>\"`)\n   so the proxy cannot accept IPv6 on the host. If any precondition is non-default or unknown,\n   report the ACL as **unverified — needs manual review** and name the uncovered path; never call it complete.\n   The fix for those paths is to publish IPv4-only, disable the userland proxy, or filter on the path that actually\n   carries the traffic. See Docker's\n   [port publishing and direct routing](https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fnetwork\u002Fport-publishing\u002F).\n   Add `--ctorigdst \u003Chost_ip>` \u002F `-i \u003Cext_if>` only when the port is published on\n   that specific address (`ports: \"IP:host:container\"`) or when every published address\u002Finterface gets its own rule\n   pair: on a wildcard publication (address-unspecified, or `0.0.0.0`\u002F`::`), one interface\u002Faddress qualifier leaves\n   the other paths unfiltered and `--ctorigdst 0.0.0.0` matches nothing — both fail open, so flag them.\n   Add `--ctdir ORIGINAL`: conntrack's original-tuple match applies to both directions of a flow, so without it the\n   container's reply packets also match the DROP (and fail the `-s` RETURN), breaking allowlisted connections\n   whenever the rule is not pinned to the ingress interface. See Docker's\n   [match the original IP and ports](https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fnetwork\u002Ffirewall-iptables\u002F#match-the-original-ip-and-ports).\n\n3. **Verify the control is testable, not just present.** The primary check is behavioral: from a host that is\n   **not** on the allowlist, connect to the published port over **both IPv4 and IPv6** (e.g.\n   `nc -vz -w3 \u003Chost_ipv4> \u003Cport>` and `nc -6 -vz -w3 \u003Chost_ipv6> \u003Cport>`) and confirm both are refused, then\n   confirm an allowlisted source still connects. Rule listings (`iptables`\u002F`ip6tables -L DOCKER-USER`,\n   `docker port`, `ss -ltn`) are supporting evidence only — they cannot prove there is no unfiltered path, so never\n   report the ACL as effective from them alone; if the reachability test cannot be run, report it as unverified.\n   A single-`\u002F32` source-IP filter over plaintext HTTP is\n   defense-in-depth, not authentication — it's spoofable on a shared segment and carries no transport encryption. Flag\n   any design where the *sole* remaining control is source-IP on the wrong chain.\n\n4. **Audit the fail-open path.** If the rule-install commands suppress stderr (`2>\u002Fdev\u002Fnull`) and only warn when the\n   `iptables` binary is missing, a privilege failure (no root, no NOPASSWD sudo) leaves the port published with no\n   filter **and no warning**. Require the deploy to surface install failures and `exit 1` (or refuse to start the\n   container) rather than print a green checkmark.\n\n5. **Prefer eliminating the exposure.** When only an internal client needs the service, the strongest fix is to **not\n   publish** the port at all — put both containers on a shared internal Docker network and reach the service by\n   container name\u002FIP. Recommend this over any host-level ACL when the topology allows it.\n","# Docker-Published Port Firewall Audit\n\nDocker manages its own iptables rules. Traffic to a **published** container port (`ports:\n\"host_ip:host_port:container_port\"`) is DNAT'd in the `nat` PREROUTING\u002F`DOCKER` chains and then traverses the\n**FORWARD** chain (via the `DOCKER-USER` hook) — it does **not** traverse the **INPUT** chain. INPUT only sees traffic\nterminating on a host-local process. So an `INPUT` ACCEPT\u002FDROP rule added to \"lock down\" a published port silently does\nnothing, while printing a success message that gives false assurance.\n\n1. **Identify the exposure model.** Read the compose\u002Frun config. Is the port *published* (`ports:`) or only on an\n   internal Docker network (`expose:` \u002F shared network)? Only published ports hit the host's packet-filter path. If the\n   change removes an app-layer control (Traefik TLS + basic-auth + ipallowlist middlewares) and replaces it with a raw\n   `ports:` mapping, treat the auth surface as **regressed** until proven otherwise.\n\n2. **Check which chain the ACL targets.** Grep the deploy\u002Fprovisioning script for `iptables`\u002F`nft` rules guarding that\n   port. If the rules are `-I INPUT`\u002F`-A INPUT` (or an nft `input` hook), they are on the wrong chain for\n   Docker-published traffic. The correct hook is **`DOCKER-USER`** (traversed before Docker's own FORWARD rules):\n\n   ```text\n   # -I prepends, so insert the DROP first, then the RETURN on top of it\n   iptables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL -j DROP\n   iptables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL \\\n     -s \u003Callowed-cidr> -j RETURN\n   # Same pair for IPv6 unless the port is published IPv4-only (ports: \"0.0.0.0:\u003Chost>:\u003Ccontainer>\")\n   ip6tables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL -j DROP\n   ip6tables -I DOCKER-USER -p tcp -m conntrack --ctstate DNAT --ctorigdstport \u003Chost_port> --ctdir ORIGINAL \\\n     -s \u003Callowed-cidr6> -j RETURN   # omit this RETURN if no IPv6 source is allowed\n   ```\n\n   Resulting chain order (`iptables -L DOCKER-USER -n --line-numbers`): **1 = RETURN** for the allowlisted\n   source, **2 = DROP** for everyone else. The RETURN must sit **above** the DROP. Each `-I` prepends to the top,\n   so the rule inserted *last* ends up *first* — issuing RETURN then DROP (both `-I`) leaves DROP on top and blocks\n   the allowlisted source too. Flag any script that does that. With `-A` (append) the order is the reverse:\n   RETURN first, then DROP — but `-A` lands after Docker's own trailing `-j RETURN` in DOCKER-USER, so prefer\n   `-I`. DOCKER-USER sees packets after DNAT, so a bare `--dport` matches the *container* port of **every**\n   forwarded container — a DROP on `--dport 443` also blocks unrelated containers listening on 443 internally,\n   even when the host and container port numbers are equal. Scope the rule to the published host port with\n   conntrack's pre-DNAT `--ctorigdstport \u003Chost_port>` and flag any DOCKER-USER rule that uses a bare `--dport`.\n   Pair it with `--ctstate DNAT`: DOCKER-USER also sees container egress and other routed traffic, and without the\n   DNAT discriminator a rule for published port 443 also drops containers' outbound HTTPS.\n\n   **Scope of this rule pair:** it filters only NAT-published traffic that is forwarded through DOCKER-USER.\n   It does **not** cover (a) routed\u002Fdirect-routing networks (`gateway_mode_ipv4`\u002F`gateway_mode_ipv6=routed`, or\n   clients reaching the container IP directly), which are not DNAT; or (b) IPv6 connections the default userland\n   proxy accepts in a host process when the bridge is IPv4-only, which never traverse IPv6 DOCKER-USER. An\n   `ip6tables` pair covers only IPv6 that is NAT-published on an IPv6-enabled bridge.\n\n   **Verify these preconditions before approving the ACL** (from the config\u002Fscript, or ask for the output):\n   - `\u002Fetc\u002Fdocker\u002Fdaemon.json`: `firewall-backend`, `userland-proxy`, `ipv6`, `ip6tables`, `allow-direct-routing`;\n   - `docker info --format '{{.FirewallBackend}}'`: the backend actually in use (daemon.json may omit it);\n   - `docker network inspect \u003Cnet>`: `com.docker.network.bridge.gateway_mode_ipv4` \u002F `_ipv6`,\n     `com.docker.network.bridge.trusted_host_interfaces`, and `EnableIPv6`;\n   - `ss -ltnp` on the host: no `[::]:\u003Chost_port>` listener unless the IPv6 pair is installed on an IPv6 bridge.\n\n   Approve only when Docker uses the `iptables` firewall backend (the default; with `firewall-backend: nftables`\n   there is no DOCKER-USER chain, so these rules filter nothing), the traffic is NAT-published on a bridge (no\n   routed gateway mode, no direct routing, no `trusted_host_interfaces` — that option lets those interfaces reach\n   published container ports directly, without DNAT), and IPv6 is covered according to the bridge's IPv6 setting\n   (`EnableIPv6` \u002F daemon `ipv6`): on an **IPv6-enabled bridge**, a wildcard publication is NAT-published over IPv6\n   too, so require the `ip6tables` pair (`userland-proxy: false` does not help there); on an **IPv4-only bridge**,\n   require `userland-proxy: false` or an IPv4-only publication address (`ports: \"0.0.0.0:\u003Chost>:\u003Ccontainer>\"`)\n   so the proxy cannot accept IPv6 on the host. If any precondition is non-default or unknown,\n   report the ACL as **unverified — needs manual review** and name the uncovered path; never call it complete.\n   The fix for those paths is to publish IPv4-only, disable the userland proxy, or filter on the path that actually\n   carries the traffic. See Docker's\n   [port publishing and direct routing](https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fnetwork\u002Fport-publishing\u002F).\n   Add `--ctorigdst \u003Chost_ip>` \u002F `-i \u003Cext_if>` only when the port is published on\n   that specific address (`ports: \"IP:host:container\"`) or when every published address\u002Finterface gets its own rule\n   pair: on a wildcard publication (address-unspecified, or `0.0.0.0`\u002F`::`), one interface\u002Faddress qualifier leaves\n   the other paths unfiltered and `--ctorigdst 0.0.0.0` matches nothing — both fail open, so flag them.\n   Add `--ctdir ORIGINAL`: conntrack's original-tuple match applies to both directions of a flow, so without it the\n   container's reply packets also match the DROP (and fail the `-s` RETURN), breaking allowlisted connections\n   whenever the rule is not pinned to the ingress interface. See Docker's\n   [match the original IP and ports](https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fnetwork\u002Ffirewall-iptables\u002F#match-the-original-ip-and-ports).\n\n3. **Verify the control is testable, not just present.** The primary check is behavioral: from a host that is\n   **not** on the allowlist, connect to the published port over **both IPv4 and IPv6** (e.g.\n   `nc -vz -w3 \u003Chost_ipv4> \u003Cport>` and `nc -6 -vz -w3 \u003Chost_ipv6> \u003Cport>`) and confirm both are refused, then\n   confirm an allowlisted source still connects. Rule listings (`iptables`\u002F`ip6tables -L DOCKER-USER`,\n   `docker port`, `ss -ltn`) are supporting evidence only — they cannot prove there is no unfiltered path, so never\n   report the ACL as effective from them alone; if the reachability test cannot be run, report it as unverified.\n   A single-`\u002F32` source-IP filter over plaintext HTTP is\n   defense-in-depth, not authentication — it's spoofable on a shared segment and carries no transport encryption. Flag\n   any design where the *sole* remaining control is source-IP on the wrong chain.\n\n4. **Audit the fail-open path.** If the rule-install commands suppress stderr (`2>\u002Fdev\u002Fnull`) and only warn when the\n   `iptables` binary is missing, a privilege failure (no root, no NOPASSWD sudo) leaves the port published with no\n   filter **and no warning**. Require the deploy to surface install failures and `exit 1` (or refuse to start the\n   container) rather than print a green checkmark.\n\n5. **Prefer eliminating the exposure.** When only an internal client needs the service, the strongest fix is to **not\n   publish** the port at all — put both containers on a shared internal Docker network and reach the service by\n   container name\u002FIP. Recommend this over any host-level ACL when the topology allows it.\n",{"name":35,"description":36},"docker-audit-firewall","Use when reviewing\u002Fwriting host firewall rules (iptables\u002Fnftables) for a Docker-published port, or when a compose change adds a `ports:` mapping that replaces app-layer auth (Traefik, reverse-proxy) with a network ACL.","main","1cb705536db26c557b919c204aeea2cfd0658ac1","skills\u002Fmomentum\u002Fskills\u002Fassessment","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes","http:\u002F\u002Fcapitalcodemonkey.blogspot.com","2026-11-09 05:58:39",{"source":8,"skillName":35,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add iamsteveholmes\u002Fmomentum --skill docker-audit-firewall",{"tool":50,"command":51},"bunx","bunx skills add iamsteveholmes\u002Fmomentum --skill docker-audit-firewall",{"tool":53,"command":54},"pnpm","pnpm dlx skills add iamsteveholmes\u002Fmomentum --skill docker-audit-firewall","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002FSKILL.md",true,[58,64,68,72,76],{"path":59,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":62,"sourceHtmlUrl":63},"agents\u002Fopenai.yaml","REFERENCE","参考文档",43,"https:\u002F\u002Fgithub.com\u002FRB-chrismandich\u002FManifest\u002Fblob\u002Fmain\u002Fplugins\u002Fmanifest-security\u002Fskills\u002Fdocker-audit-firewall\u002Fagents\u002Fopenai.yaml",{"path":65,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":66,"sourceHtmlUrl":67},"evals\u002Feval-collaborative-scoping-and-validation.md",3447,"https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002Fevals\u002Feval-collaborative-scoping-and-validation.md",{"path":69,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":70,"sourceHtmlUrl":71},"evals\u002Feval-findings-cap-and-floor.md",2696,"https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002Fevals\u002Feval-findings-cap-and-floor.md",{"path":73,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":74,"sourceHtmlUrl":75},"references\u002Fasr-template.md",3601,"https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002Freferences\u002Fasr-template.md",{"path":77,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":78,"sourceHtmlUrl":79},"workflow.md",10664,"https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fassessment\u002Fworkflow.md",[81,101,119,140,156,174],{"skillNo":82,"name":83,"nameZh":6,"description":84,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":85,"categories":86,"keywords":87,"publishedAt":30},"01M6DES6J0HGT27SEJJWF316FV","upstream-fix","Traces quality failures upstream to spec, rule, or workflow root cause. Proposes fixes at the right level.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fupstream-fix\u002FSKILL.md",[],[88,89,90,91,92,93,94,95,29,96,97,98,99,100],"upstream","upstream fix","fix","to spec","root cause","traces","fixes","at","root","rule","quality","workflow","spec",{"skillNo":102,"name":103,"nameZh":6,"description":104,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":107,"publishedAt":30},"01M6DES4A08NFWQM0AJBR2RRX5","triage","Dedup gate + batch-classify observations into five classes, enrich ARTIFACTs, batch-approve, delegate to intake\u002Fdecision or queue.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Ftriage\u002FSKILL.md",[],[103,108,109,110,111,112,113,114,29,115,116,117,118],"batch","intake","artifacts","decision","queue","delegate","approve","observations","enrich","classify","gate",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":30},"01M6DERYX8W6Z9Z4RM5V6E9HED","sprint-planning","Sprint planning — story selection, team composition, Gherkin specs, and visual companion plan gate for developer sign-off.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fsprint-planning\u002FSKILL.md",[],[126,127,128,29,129,130,131,132,133,134,135,136,137,138,139,21,118],"planning","sprint","sprint planning","visual companion","plan gate","for","companion","sign","visual","selection","Composition","Story","plan","team",{"skillNo":141,"name":142,"nameZh":6,"description":143,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":144,"categories":145,"keywords":146,"publishedAt":30},"01M6DERWCP7Y0WDY5KG6109RQB","sprint-manager","Sole writer of stories\u002Findex.json and sprints\u002Findex.json — validates state transitions, activates sprints, manages epic membership.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fsprint-manager\u002FSKILL.md",[],[147,127,148,149,150,151,152,153,154,155,19],"sprint manager","manager","writer","stories","sprints","EPIC","transitions","json","index",{"skillNo":157,"name":158,"nameZh":6,"description":159,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":160,"categories":161,"keywords":162,"publishedAt":30},"01M6DERS7B4QHAH5GZHHBA4AHB","sprint-dev","Sprint execution — dependency-driven story development, post-merge AVFL, and team review.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fsprint-dev\u002FSKILL.md",[],[163,164,127,139,165,166,167,168,169,170,29,171,172,173,137],"dev","sprint dev","execution","team review","development","merge","review","dependency","post","avfl","driven",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":180,"publishedAt":30},"01M6DERP0A271FYSAXCV85GF04","retro","Sprint retrospective — transcript audit engine (dynamic Workflow), story verification, findings document, and sprint closure.","https:\u002F\u002Fgithub.com\u002Fiamsteveholmes\u002Fmomentum\u002Fblob\u002Fmain\u002Fskills\u002Fmomentum\u002Fskills\u002Fretro\u002FSKILL.md",[],[176,181,182,29,183,127,26,137,184,185,186,187,188,99],"sprint retrospective","closure","dynamic workflow","engine","audit","verification","transcript","retrospective"]