@dx-do/cli 6.0.1 → 6.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +12 -6
  2. package/dist-node/{03-discover-sources.nassql.data-store-by6sqk23.json5 → 03-discover-sources.nassql.data-store-d6hb6wf7.json5} +4 -3
  3. package/dist-node/{20-nassql-from-metadata-basic.nassql.data-store-zdf1gp1v.json5 → 20-nassql-from-metadata-basic.nassql.data-store-mq1hn0qz.json5} +4 -2
  4. package/dist-node/34-nassql-entity-to-metric-ids.nassql-jn6t8zxs.md +76 -0
  5. package/dist-node/34-nassql-entity-to-metric-ids.nassql.data-store-t8ecm336.json5 +69 -0
  6. package/dist-node/34-nassql-entity-to-metric-ids.nassql.data-store-tmd-9vbg6p76.json +4 -0
  7. package/dist-node/40-metadata-filter-by-typeenum.metadata-kmcc85ac.md +66 -0
  8. package/dist-node/40-metadata-filter-by-typeenum.metadata.data-store-b4erpx6h.json5 +52 -0
  9. package/dist-node/40-metadata-filter-by-typeenum.metadata.data-store-tmd-3zg3n0j1.json +4 -0
  10. package/dist-node/41-metadata-frontend-durations.metadata-n2ba62j7.md +50 -0
  11. package/dist-node/41-metadata-frontend-durations.metadata.data-store-kpkhaf6c.json5 +48 -0
  12. package/dist-node/41-metadata-frontend-durations.metadata.data-store-tmd-c685fc9v.json +4 -0
  13. package/dist-node/42-metadata-metric-id-to-entities.metadata-qg0p7y71.md +67 -0
  14. package/dist-node/42-metadata-metric-id-to-entities.metadata.data-store-tmd-zd99v6pz.json +4 -0
  15. package/dist-node/42-metadata-metric-id-to-entities.metadata.data-store-vwv8dq0e.json5 +52 -0
  16. package/dist-node/{SKILL-1xn7r9nt.md → SKILL-86r2cm61.md} +26 -2
  17. package/dist-node/{agentic-mcp-rycd2gh8.md → agentic-mcp-9nz7zh8d.md} +2 -1
  18. package/dist-node/alarms-cookbook-xrwbrdte.md +160 -0
  19. package/dist-node/alarms-quickstart-fhg59mr2.md +100 -0
  20. package/dist-node/{chunk-RNMHSXZF-pdwasrg7.js → chunk-4L5DIG2E-9p3rrkp9.js} +1 -1
  21. package/dist-node/{chunk-VV2FJEMA-3rvtkmga.js → chunk-4Q347M53-e6wyjnpc.js} +2 -2
  22. package/dist-node/{chunk-Q2JA73UH-akkb8bh3.js → chunk-6TL6OWJA-jrr526kw.js} +1 -1
  23. package/dist-node/chunk-7G4RSOZU-nnmp25yx.js +3753 -0
  24. package/dist-node/{chunk-YVD3UK5I-9pxr1jka.js → chunk-JYKZ5EMU-6jq5egbw.js} +3 -3
  25. package/dist-node/{chunk-5VSFINOX-ewzpx7wh.js → chunk-OV4PNSIK-pqs8y3nm.js} +3 -3
  26. package/dist-node/{chunk-JRM4BLOM-rg32z8w4.js → chunk-P6TRLBVU-rxz4tacs.js} +1 -1
  27. package/dist-node/{chunk-4I3HBO6U-2ebgf7kh.js → chunk-TFFMCZCS-rhchxvk7.js} +1 -1
  28. package/dist-node/{chunk-4PMCLJMS-0mqvr4m4.js → chunk-W5FHICA2-x50sz7wk.js} +1 -1
  29. package/dist-node/{discovery-flow-fw79kbx4.md → discovery-flow-m0zp07e3.md} +13 -0
  30. package/dist-node/{gotchas-8ab64kcd.md → gotchas-p696dmam.md} +131 -0
  31. package/dist-node/{index-104hyb1m.html → index-dfkxebky.html} +1 -1
  32. package/dist-node/{index-mbzg9rhc.json → index-mf7znaf7.json} +30 -0
  33. package/dist-node/{index-g3hh5wez.json → index-x07qhy6g.json} +53 -0
  34. package/dist-node/{investigator-flow-jc2s0n46.md → investigator-flow-dcyk8v51.md} +36 -2
  35. package/dist-node/main-4VTFKCWX-6x4n4v8m.js +13 -0
  36. package/dist-node/main.js +26106 -18435
  37. package/dist-node/{metrics-grounding-2h4kkbe3.md → metrics-grounding-8vhfqya9.md} +59 -4
  38. package/dist-node/{mm-cookbook-23jpw721.md → mm-cookbook-fdwbt989.md} +1 -1
  39. package/dist-node/{nassql-cookbook-n8kc0mff.md → nassql-cookbook-kp525xra.md} +41 -1
  40. package/dist-node/{nassql-quickstart-090e0yex.md → nassql-quickstart-mth22qd3.md} +3 -1
  41. package/package.json +1 -1
  42. package/dist-node/chunk-72HYG3XZ-kf7hy4vs.js +0 -3625
  43. package/dist-node/main-SGLYO5YX-ht69eb0y.js +0 -13
@@ -1 +1 @@
1
- import{R as n,W as r,bd as o,g as t,ka as e}from"./chunk-5VSFINOX.js";var l=class s{api=r(o);session=e(null);loading=e(!1);error=e(null);initialized=!1;inflight=null;ensureLoaded(){return this.initialized?Promise.resolve():this.inflight?this.inflight:(this.inflight=this.refresh().finally(()=>{this.initialized=!0,this.inflight=null}),this.inflight)}refresh(){return t(this,null,function*(){this.loading.set(!0),this.error.set(null);try{let i=yield this.api.getSession();this.session.set(i)}catch(i){this.error.set(i.message??"Failed to fetch session"),this.session.set(null)}finally{this.loading.set(!1)}})}set(i){this.session.set(i),this.initialized=!0}static \u0275fac=function(a){return new(a||s)};static \u0275prov=n({token:s,factory:s.\u0275fac,providedIn:"root"})};export{l as a};
1
+ import{R as n,W as r,bd as o,g as t,ka as e}from"./chunk-OV4PNSIK.js";var l=class s{api=r(o);session=e(null);loading=e(!1);error=e(null);initialized=!1;inflight=null;ensureLoaded(){return this.initialized?Promise.resolve():this.inflight?this.inflight:(this.inflight=this.refresh().finally(()=>{this.initialized=!0,this.inflight=null}),this.inflight)}refresh(){return t(this,null,function*(){this.loading.set(!0),this.error.set(null);try{let i=yield this.api.getSession();this.session.set(i)}catch(i){this.error.set(i.message??"Failed to fetch session"),this.session.set(null)}finally{this.loading.set(!1)}})}set(i){this.session.set(i),this.initialized=!0}static \u0275fac=function(a){return new(a||s)};static \u0275prov=n({token:s,factory:s.\u0275fac,providedIn:"root"})};export{l as a};
@@ -1,4 +1,4 @@
1
- import{ca as $,da as c,ga as B,ia as N,ja as x,ka as r}from"./chunk-RNMHSXZF.js";import{Ab as j,Bb as P,Cc as H,Hb as S,Ib as R,Jb as V,Kb as z,La as a,Mb as E,Nb as w,Pc as L,R as h,Rc as W,S as v,Sb as O,Tc as q,U as b,Uc as M,Vb as s,W as p,Wb as G,Xb as Q,Za as k,_a as T,ba as A,bb as C,cb as I,db as l,dc as D,kb as g,na as d,tb as o,ub as u,vb as f,wb as _}from"./chunk-5VSFINOX.js";var J=`
1
+ import{ca as $,da as c,ga as B,ia as N,ja as x,ka as r}from"./chunk-4L5DIG2E.js";import{Ab as j,Bb as P,Cc as H,Hb as S,Ib as R,Jb as V,Kb as z,La as a,Mb as E,Nb as w,Pc as L,R as h,Rc as W,S as v,Sb as O,Tc as q,U as b,Uc as M,Vb as s,W as p,Wb as G,Xb as Q,Za as k,_a as T,ba as A,bb as C,cb as I,db as l,dc as D,kb as g,na as d,tb as o,ub as u,vb as f,wb as _}from"./chunk-OV4PNSIK.js";var J=`
2
2
  .p-progressspinner {
3
3
  position: relative;
4
4
  margin: 0 auto;
@@ -1 +1 @@
1
- import{a as P,b as T,c as ee,d as ne}from"./chunk-4I3HBO6U.js";import{Ea as E,Ga as k,Ka as Q,La as Z,ua as W,va as q}from"./chunk-RNMHSXZF.js";import{$ as M,Cb as z,Db as w,Fb as D,Hb as l,La as a,R as X,Rb as j,Tb as J,Tc as U,W as x,Wb as s,Xb as u,Yb as _,Za as C,aa as N,bd as H,db as Y,fc as $,g as A,ka as O,nb as p,ob as g,oc as K,pb as G,rb as h,rc as f,sb as S,tb as c,ub as r,vb as o,wb as m}from"./chunk-5VSFINOX.js";var le=Object.freeze({error:0,warning:0,recommendation:0,info:0}),y=class t{api=x(H);report=O(null);loading=O(!1);error=O(null);selectedServiceName=O(null);hasReport=f(()=>this.report()!==null);isEmpty=f(()=>{let n=this.report();return n!==null&&n.totalDxo2Services===0});findingCountsBySeverity=f(()=>this.report()?.findingCountsBySeverity??le);selectedNode=f(()=>{let n=this.report(),e=this.selectedServiceName();return!n||!e?null:n.nodesByName[e]??null});load(){return A(this,null,function*(){if(!this.loading()){this.loading.set(!0),this.error.set(null);try{let n=yield this.api.getServiceDiagnostics();this.report.set(n);let e=this.selectedServiceName();e&&!n.nodesByName[e]&&this.selectedServiceName.set(null)}catch(n){this.error.set(n.message??"Failed to load service diagnostics"),this.report.set(null)}finally{this.loading.set(!1)}}})}select(n){this.selectedServiceName.set(n)}static \u0275fac=function(e){return new(e||t)};static \u0275prov=X({token:t,factory:t.\u0275fac,providedIn:"root"})};var I=class t{store=x(y);static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-summary-bar"]],decls:23,vars:6,consts:[[1,"dx-summary-bar"],[1,"dx-summary-cell"],[1,"dx-summary-label"],[1,"dx-summary-value"],[1,"dx-summary-cell","dx-summary-cell--findings"],[1,"dx-summary-tags"],["severity","danger",3,"value"],["severity","warn",3,"value"],["severity","info",3,"value"]],template:function(e,i){if(e&1&&(r(0,"div",0)(1,"div",1)(2,"span",2),s(3,"DXO2 services"),o(),r(4,"span",3),s(5),o()(),r(6,"div",1)(7,"span",2),s(8,"Leaf services without entities"),o(),r(9,"span",3),s(10),o()(),r(11,"div",1)(12,"span",2),s(13,"Top-level roots"),o(),r(14,"span",3),s(15),o()(),r(16,"div",4)(17,"span",2),s(18,"Findings"),o(),r(19,"div",5),m(20,"p-tag",6)(21,"p-tag",7)(22,"p-tag",8),o()()()),e&2){let d,v,b;a(5),u(((d=i.store.report())==null?null:d.totalDxo2Services)??0),a(5),_(" ",((v=i.store.report())==null?null:v.totalLeafServicesWithoutEntities)??0," "),a(5),_(" ",((b=i.store.report())==null||b.topLevelNames==null?null:b.topLevelNames.length)??0," "),a(5),c("value","errors: "+(i.store.findingCountsBySeverity().error??0)),a(),c("value","warnings: "+(i.store.findingCountsBySeverity().warning??0)),a(),c("value","recommendations: "+(i.store.findingCountsBySeverity().recommendation??0))}},dependencies:[T,P],styles:["[_nghost-%COMP%]{display:block}.dx-summary-bar[_ngcontent-%COMP%]{display:flex;gap:1.5rem;flex-wrap:wrap;align-items:center;padding:.75rem 1rem;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;background:var(--p-content-background, transparent)}.dx-summary-cell[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:.25rem}.dx-summary-cell--findings[_ngcontent-%COMP%]{margin-left:auto}.dx-summary-label[_ngcontent-%COMP%]{font-size:.7rem;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color, #888)}.dx-summary-value[_ngcontent-%COMP%]{font-size:1.25rem;font-weight:600}.dx-summary-tags[_ngcontent-%COMP%]{display:flex;gap:.4rem;flex-wrap:wrap}"],changeDetection:0})};var ie=t=>({$implicit:t}),ce=(t,n)=>n.key,me=(t,n)=>n.severity;function pe(t,n){if(t&1&&(r(0,"div",1),z(1,2),o()),t&2){let e=n.$implicit;l();let i=j(3);a(),c("ngTemplateOutlet",i)("ngTemplateOutletContext",$(2,ie,e))}}function ge(t,n){t&1&&m(0,"p-tag",7)}function ue(t,n){if(t&1&&m(0,"p-tag",8),t&2){let e=n.$implicit,i=l(2);c("severity",i.severityToTag(e.severity))("value",e.severity+": "+e.count)("pTooltip",e.ruleCodes.join(", "))}}function ve(t,n){if(t&1&&z(0,2),t&2){let e=n.$implicit;l(3);let i=j(3);c("ngTemplateOutlet",i)("ngTemplateOutletContext",$(2,ie,e))}}function fe(t,n){if(t&1&&(r(0,"div",9),h(1,ve,1,4,"ng-container",2,G),o()),t&2){let e=l().$implicit;a(),S(e.children)}}function xe(t,n){if(t&1){let e=w();r(0,"div",3),D("click",function(d){let v=M(e).$implicit,b=l();return N(b.onClick(d,v))}),r(1,"div",4)(2,"span",5),s(3),o(),m(4,"p-tag",6),p(5,ge,1,0,"p-tag",7),h(6,ue,1,3,"p-tag",8,me),o(),p(8,fe,3,0,"div",9),o()}if(t&2){let e=n.$implicit,i=l();J("dx-svc-node--selected",i.store.selectedServiceName()===e.key),a(3),u(e.name),a(),c("value",e.entityCount+" entities"),a(),g(e.multiParent?5:-1),a(),S(e.severityChips),a(2),g(e.children.length>0?8:-1)}}var ye=["error","warning","recommendation","info"],_e={error:"danger",warning:"warn",recommendation:"info",info:"secondary"},B=class t{store=x(y);trees=f(()=>{let n=this.store.report();if(!n)return[];let e=i=>{let d=n.nodesByName[i],v=d?.name??i,b=d?.entityCount??0,L=d?.findings??[],re=Ce(L),ae=(d?.parentNames.length??0)>1,se=(n.childrenByName[i]??[]).map(de=>e(de));return{key:i,name:v,entityCount:b,findings:L,severityChips:re,multiParent:ae,children:se}};return n.topLevelNames.map(i=>e(i))});severityToTag(n){return _e[n]??"secondary"}onClick(n,e){n.stopPropagation(),this.store.select(e.key)}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-tree"]],decls:4,vars:0,consts:[["nodeTemplate",""],[1,"dx-svc-tree-root"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[1,"dx-svc-node",3,"click"],[1,"dx-svc-row"],[1,"dx-svc-name"],["severity","info","styleClass","dx-svc-count",3,"value"],["severity","secondary","value","multi-parent","styleClass","dx-svc-multi"],["tooltipPosition","top","styleClass","dx-svc-finding",3,"severity","value","pTooltip"],[1,"dx-svc-children"]],template:function(e,i){e&1&&(h(0,pe,2,4,"div",1,ce),Y(2,xe,9,6,"ng-template",null,0,K)),e&2&&S(i.trees())},dependencies:[U,T,P,Z,Q],styles:["[_nghost-%COMP%]{display:block}.dx-svc-tree-root[_ngcontent-%COMP%]{margin-bottom:.75rem}.dx-svc-node[_ngcontent-%COMP%]{cursor:pointer;padding:.25rem .4rem;border-radius:4px;transition:background-color 60ms ease-out}.dx-svc-node[_ngcontent-%COMP%]:hover > .dx-svc-row[_ngcontent-%COMP%]{background:var(--p-highlight-bg, rgba(120, 160, 240, .08))}.dx-svc-node--selected[_ngcontent-%COMP%] > .dx-svc-row[_ngcontent-%COMP%]{background:var(--p-highlight-bg, rgba(120, 160, 240, .18));outline:1px solid var(--p-primary-color, #6ea0ff)}.dx-svc-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;padding:.25rem .5rem;border-radius:4px}.dx-svc-name[_ngcontent-%COMP%]{font-weight:500}.dx-svc-children[_ngcontent-%COMP%]{margin-left:1.25rem;border-left:1px dashed var(--p-content-border-color, #444);padding-left:.5rem}"],changeDetection:0})};function Ce(t){if(t.length===0)return[];let n=new Map;for(let i of t){let d=n.get(i.severity);d||(d=new Set,n.set(i.severity,d)),d.add(i.ruleCode)}let e=[];for(let i of ye){let d=n.get(i);!d||d.size===0||e.push({severity:i,count:d.size,ruleCodes:[...d].sort()})}return e}var he=(t,n)=>n.ruleCode,Se=(t,n)=>n.ruleCode+"::"+n.subjectId;function De(t,n){t&1&&s(0),t&2&&_(" ",n.name," ")}function Pe(t,n){t&1&&s(0," All findings ")}function Te(t,n){if(t&1){let e=w();r(0,"button",4),D("click",function(){M(e);let d=l();return N(d.onClearSelection())}),o()}}function Me(t,n){t&1&&(r(0,"p",8),s(1,"No findings for this service."),o())}function Ne(t,n){if(t&1&&(r(0,"details",14)(1,"summary"),s(2,"Detail"),o(),r(3,"pre"),s(4),o()()),t&2){let e=l().$implicit,i=l(3);a(4),u(i.formatDetail(e.detail))}}function Oe(t,n){if(t&1&&(r(0,"article",9)(1,"div",10),m(2,"p-tag",11),r(3,"code",12),s(4),o()(),r(5,"p",13),s(6),o(),p(7,Ne,5,1,"details",14),o()),t&2){let e=n.$implicit,i=l(3);a(2),c("severity",i.severityTag(e.severity))("value",e.severity),a(2),u(e.ruleCode),a(2),u(e.message),a(),g(e.detail?7:-1)}}function we(t,n){if(t&1&&h(0,Oe,8,5,"article",9,he),t&2){let e=l();S(e.findings)}}function Ee(t,n){if(t&1&&(r(0,"div",3)(1,"div",5)(2,"span",6),s(3,"Direct entities:"),o(),r(4,"span",7),s(5),o()(),r(6,"div",5)(7,"span",6),s(8),o(),r(9,"span",7),s(10),o()(),r(11,"div",5)(12,"span",6),s(13),o(),r(14,"span",7),s(15),o()(),m(16,"hr"),p(17,Me,2,0,"p",8)(18,we,2,0),o()),t&2){let e=n,i=l();a(5),u(e.entityCount),a(3),_("Parents (",e.parentNames.length,"):"),a(2),_(" ",i.parentDisplayNames()," "),a(3),_("Children (",e.childNames.length,"):"),a(2),_(" ",i.childDisplayNames()," "),a(2),g(e.findings.length===0?17:18)}}function ke(t,n){t&1&&(r(0,"p",8),s(1,"No findings on this tenant."),o())}function Fe(t,n){if(t&1){let e=w();r(0,"article",16),D("click",function(){let d=M(e).$implicit,v=l(3);return N(v.onJumpTo(d))}),r(1,"div",10),m(2,"p-tag",11),r(3,"code",12),s(4),o(),r(5,"span",17),s(6),o()(),r(7,"p",13),s(8),o()()}if(t&2){let e=n.$implicit,i=l(3);a(2),c("severity",i.severityTag(e.severity))("value",e.severity),a(2),u(e.ruleCode),a(2),u(e.subjectName),a(2),u(e.message)}}function Ie(t,n){if(t&1&&h(0,Fe,9,5,"article",15,Se),t&2){let e=l(2);S(e.allFindings())}}function Be(t,n){if(t&1&&(r(0,"div",3),p(1,ke,2,0,"p",8)(2,Ie,2,0),o()),t&2){let e=l();a(),g(e.allFindings().length===0?1:2)}}var Re={error:"danger",warning:"warn",recommendation:"info",info:"secondary"},R=class t{store=x(y);selectedNode=f(()=>this.store.selectedNode());allFindings=f(()=>this.store.report()?.findings??[]);parentDisplayNames=f(()=>{let n=this.store.report(),e=this.selectedNode();return!n||!e?"\u2014":e.parentNames.length===0?"(top-level)":e.parentNames.map(i=>n.nodesByName[i]?.name??i).join(", ")});childDisplayNames=f(()=>{let n=this.store.report(),e=this.selectedNode();return!n||!e?"\u2014":e.childNames.length===0?"(none)":e.childNames.map(i=>n.nodesByName[i]?.name??i).join(", ")});severityTag(n){return Re[n]??"secondary"}formatDetail(n){return JSON.stringify(n,null,2)}onClearSelection(){this.store.select(null)}onJumpTo(n){let e=this.store.report();if(!e)return;let i=null;for(let[d,v]of Object.entries(e.nodesByName))if(v.id===n.subjectId){i=d;break}this.store.select(i)}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-finding-panel"]],decls:8,vars:3,consts:[[1,"dx-finding-panel"],[1,"dx-finding-panel-header"],["pButton","","type","button","icon","pi pi-times","severity","secondary","text","","rounded","","size","small","ariaLabel","Clear selection"],[1,"dx-finding-panel-body"],["pButton","","type","button","icon","pi pi-times","severity","secondary","text","","rounded","","size","small","ariaLabel","Clear selection",3,"click"],[1,"dx-finding-meta"],[1,"dx-finding-meta-label"],[1,"dx-finding-meta-value"],[1,"dx-finding-empty"],[1,"dx-finding-card"],[1,"dx-finding-card-head"],["styleClass","dx-finding-sev",3,"severity","value"],[1,"dx-finding-code"],[1,"dx-finding-message"],[1,"dx-finding-detail"],[1,"dx-finding-card","dx-finding-card--clickable"],[1,"dx-finding-card","dx-finding-card--clickable",3,"click"],[1,"dx-finding-service"]],template:function(e,i){if(e&1&&(r(0,"div",0)(1,"header",1)(2,"h2"),p(3,De,1,1)(4,Pe,1,0),o(),p(5,Te,1,0,"button",2),o(),p(6,Ee,19,6,"div",3)(7,Be,3,1,"div",3),o()),e&2){let d,v;a(3),g((d=i.selectedNode())?3:4,d),a(2),g(i.selectedNode()?5:-1),a(),g((v=i.selectedNode())?6:7,v)}},dependencies:[T,P,k,E],styles:["[_nghost-%COMP%]{display:block;height:100%}.dx-finding-panel[_ngcontent-%COMP%]{display:flex;flex-direction:column;height:100%}.dx-finding-panel-header[_ngcontent-%COMP%]{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.75rem 1rem;border-bottom:1px solid var(--p-content-border-color, #2a2a2a)}.dx-finding-panel-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%]{margin:0;font-size:1rem;font-weight:600;word-break:break-word}.dx-finding-panel-body[_ngcontent-%COMP%]{flex:1;overflow:auto;padding:.75rem 1rem;display:flex;flex-direction:column;gap:.5rem}.dx-finding-meta[_ngcontent-%COMP%]{display:flex;gap:.5rem;font-size:.8rem;word-break:break-word}.dx-finding-meta-label[_ngcontent-%COMP%]{color:var(--p-text-muted-color, #888);flex-shrink:0}.dx-finding-meta-value[_ngcontent-%COMP%]{flex:1}.dx-finding-empty[_ngcontent-%COMP%]{color:var(--p-text-muted-color, #888);font-style:italic;margin:0}hr[_ngcontent-%COMP%]{border:none;border-top:1px solid var(--p-content-border-color, #2a2a2a);margin:.5rem 0}.dx-finding-card[_ngcontent-%COMP%]{border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:4px;padding:.5rem .75rem;display:flex;flex-direction:column;gap:.4rem}.dx-finding-card--clickable[_ngcontent-%COMP%]{cursor:pointer;transition:background-color 60ms ease-out}.dx-finding-card--clickable[_ngcontent-%COMP%]:hover{background:var(--p-highlight-bg, rgba(120, 160, 240, .08))}.dx-finding-card-head[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.dx-finding-code[_ngcontent-%COMP%]{font-family:var(--p-font-family-monospace, ui-monospace, monospace);font-size:.75rem}.dx-finding-service[_ngcontent-%COMP%]{font-weight:500;font-size:.85rem}.dx-finding-message[_ngcontent-%COMP%]{margin:0;font-size:.85rem;line-height:1.35}.dx-finding-detail[_ngcontent-%COMP%]{font-size:.75rem}.dx-finding-detail[_ngcontent-%COMP%] pre[_ngcontent-%COMP%]{background:var(--p-surface-100, rgba(255, 255, 255, .04));padding:.4rem .5rem;border-radius:3px;white-space:pre-wrap;word-break:break-word;max-height:16rem;overflow:auto}"],changeDetection:0})};function ze(t,n){t&1&&(r(0,"p-message",6),s(1),o()),t&2&&(c("closable",!1),a(),u(n))}function je(t,n){t&1&&(r(0,"div",7),m(1,"p-progressspinner",8),r(2,"span"),s(3,"Running service diagnostics audit\u2026"),o()())}function $e(t,n){t&1&&(r(0,"p-message",9),s(1," No DXO2 services found on this tenant. Diagnostics has nothing to analyze yet. "),o()),t&2&&c("closable",!1)}function Ve(t,n){t&1&&(m(0,"dx-service-summary-bar"),r(1,"div",10)(2,"div",11),m(3,"dx-service-tree"),o(),r(4,"div",12),m(5,"dx-service-finding-panel"),o()())}function Le(t,n){if(t&1&&p(0,$e,2,1,"p-message",9)(1,Ve,6,0),t&2){let e=l();g(e.store.isEmpty()?0:1)}}var oe=class t{store=x(y);ngOnInit(){this.store.hasReport()||this.store.load()}onRefresh(){this.store.load()}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-diagnostics-page"]],decls:12,vars:3,consts:[[1,"dx-diagnostics-page"],[1,"dx-diagnostics-header"],[1,"dx-diagnostics-title"],[1,"dx-diagnostics-blurb"],[1,"dx-diagnostics-actions"],["pButton","","type","button","icon","pi pi-refresh","label","Run audit",3,"click","loading"],["severity","error",3,"closable"],[1,"dx-diagnostics-loading"],["ariaLabel","Loading diagnostics"],["severity","info",3,"closable"],[1,"dx-diagnostics-body"],[1,"dx-diagnostics-trees"],[1,"dx-diagnostics-panel"]],template:function(e,i){if(e&1&&(r(0,"section",0)(1,"header",1)(2,"div",2)(3,"h1"),s(4,"DXO2 Service Diagnostics"),o(),r(5,"p",3),s(6," Audits the bound tenant's DXO2 service organization and surfaces findings per service. Multi-parent services appear under every parent. "),o()(),r(7,"div",4)(8,"button",5),D("click",function(){return i.onRefresh()}),o()()(),p(9,ze,2,2,"p-message",6),p(10,je,4,0,"div",7)(11,Le,2,1),o()),e&2){let d;a(8),c("loading",i.store.loading()),a(),g((d=i.store.error())?9:-1,d),a(),g(i.store.loading()&&!i.store.hasReport()?10:i.store.hasReport()?11:-1)}},dependencies:[k,E,q,W,ne,ee,I,B,R],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}.dx-diagnostics-page[_ngcontent-%COMP%]{display:flex;flex-direction:column;flex:1;min-height:0;padding:1.25rem 1.5rem;gap:1rem}.dx-diagnostics-header[_ngcontent-%COMP%]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem}.dx-diagnostics-title[_ngcontent-%COMP%] h1[_ngcontent-%COMP%]{margin:0 0 .25rem;font-size:1.4rem;font-weight:600}.dx-diagnostics-blurb[_ngcontent-%COMP%]{margin:0;color:var(--p-text-muted-color, #888);font-size:.85rem;max-width:60ch}.dx-diagnostics-actions[_ngcontent-%COMP%]{flex-shrink:0}.dx-diagnostics-loading[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.75rem;padding:1rem;color:var(--p-text-muted-color, #888)}.dx-diagnostics-body[_ngcontent-%COMP%]{flex:1;display:grid;grid-template-columns:minmax(0,1fr) 22rem;gap:1rem;min-height:0;overflow:hidden}.dx-diagnostics-trees[_ngcontent-%COMP%]{overflow:auto;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;padding:.75rem .5rem;background:var(--p-content-background, transparent)}.dx-diagnostics-panel[_ngcontent-%COMP%]{overflow:auto;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;background:var(--p-content-background, transparent)}"],changeDetection:0})};export{oe as ServiceDiagnosticsPageComponent};
1
+ import{a as P,b as T,c as ee,d as ne}from"./chunk-TFFMCZCS.js";import{Ea as E,Ga as k,Ka as Q,La as Z,ua as W,va as q}from"./chunk-4L5DIG2E.js";import{$ as M,Cb as z,Db as w,Fb as D,Hb as l,La as a,R as X,Rb as j,Tb as J,Tc as U,W as x,Wb as s,Xb as u,Yb as _,Za as C,aa as N,bd as H,db as Y,fc as $,g as A,ka as O,nb as p,ob as g,oc as K,pb as G,rb as h,rc as f,sb as S,tb as c,ub as r,vb as o,wb as m}from"./chunk-OV4PNSIK.js";var le=Object.freeze({error:0,warning:0,recommendation:0,info:0}),y=class t{api=x(H);report=O(null);loading=O(!1);error=O(null);selectedServiceName=O(null);hasReport=f(()=>this.report()!==null);isEmpty=f(()=>{let n=this.report();return n!==null&&n.totalDxo2Services===0});findingCountsBySeverity=f(()=>this.report()?.findingCountsBySeverity??le);selectedNode=f(()=>{let n=this.report(),e=this.selectedServiceName();return!n||!e?null:n.nodesByName[e]??null});load(){return A(this,null,function*(){if(!this.loading()){this.loading.set(!0),this.error.set(null);try{let n=yield this.api.getServiceDiagnostics();this.report.set(n);let e=this.selectedServiceName();e&&!n.nodesByName[e]&&this.selectedServiceName.set(null)}catch(n){this.error.set(n.message??"Failed to load service diagnostics"),this.report.set(null)}finally{this.loading.set(!1)}}})}select(n){this.selectedServiceName.set(n)}static \u0275fac=function(e){return new(e||t)};static \u0275prov=X({token:t,factory:t.\u0275fac,providedIn:"root"})};var I=class t{store=x(y);static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-summary-bar"]],decls:23,vars:6,consts:[[1,"dx-summary-bar"],[1,"dx-summary-cell"],[1,"dx-summary-label"],[1,"dx-summary-value"],[1,"dx-summary-cell","dx-summary-cell--findings"],[1,"dx-summary-tags"],["severity","danger",3,"value"],["severity","warn",3,"value"],["severity","info",3,"value"]],template:function(e,i){if(e&1&&(r(0,"div",0)(1,"div",1)(2,"span",2),s(3,"DXO2 services"),o(),r(4,"span",3),s(5),o()(),r(6,"div",1)(7,"span",2),s(8,"Leaf services without entities"),o(),r(9,"span",3),s(10),o()(),r(11,"div",1)(12,"span",2),s(13,"Top-level roots"),o(),r(14,"span",3),s(15),o()(),r(16,"div",4)(17,"span",2),s(18,"Findings"),o(),r(19,"div",5),m(20,"p-tag",6)(21,"p-tag",7)(22,"p-tag",8),o()()()),e&2){let d,v,b;a(5),u(((d=i.store.report())==null?null:d.totalDxo2Services)??0),a(5),_(" ",((v=i.store.report())==null?null:v.totalLeafServicesWithoutEntities)??0," "),a(5),_(" ",((b=i.store.report())==null||b.topLevelNames==null?null:b.topLevelNames.length)??0," "),a(5),c("value","errors: "+(i.store.findingCountsBySeverity().error??0)),a(),c("value","warnings: "+(i.store.findingCountsBySeverity().warning??0)),a(),c("value","recommendations: "+(i.store.findingCountsBySeverity().recommendation??0))}},dependencies:[T,P],styles:["[_nghost-%COMP%]{display:block}.dx-summary-bar[_ngcontent-%COMP%]{display:flex;gap:1.5rem;flex-wrap:wrap;align-items:center;padding:.75rem 1rem;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;background:var(--p-content-background, transparent)}.dx-summary-cell[_ngcontent-%COMP%]{display:flex;flex-direction:column;gap:.25rem}.dx-summary-cell--findings[_ngcontent-%COMP%]{margin-left:auto}.dx-summary-label[_ngcontent-%COMP%]{font-size:.7rem;text-transform:uppercase;letter-spacing:.04em;color:var(--p-text-muted-color, #888)}.dx-summary-value[_ngcontent-%COMP%]{font-size:1.25rem;font-weight:600}.dx-summary-tags[_ngcontent-%COMP%]{display:flex;gap:.4rem;flex-wrap:wrap}"],changeDetection:0})};var ie=t=>({$implicit:t}),ce=(t,n)=>n.key,me=(t,n)=>n.severity;function pe(t,n){if(t&1&&(r(0,"div",1),z(1,2),o()),t&2){let e=n.$implicit;l();let i=j(3);a(),c("ngTemplateOutlet",i)("ngTemplateOutletContext",$(2,ie,e))}}function ge(t,n){t&1&&m(0,"p-tag",7)}function ue(t,n){if(t&1&&m(0,"p-tag",8),t&2){let e=n.$implicit,i=l(2);c("severity",i.severityToTag(e.severity))("value",e.severity+": "+e.count)("pTooltip",e.ruleCodes.join(", "))}}function ve(t,n){if(t&1&&z(0,2),t&2){let e=n.$implicit;l(3);let i=j(3);c("ngTemplateOutlet",i)("ngTemplateOutletContext",$(2,ie,e))}}function fe(t,n){if(t&1&&(r(0,"div",9),h(1,ve,1,4,"ng-container",2,G),o()),t&2){let e=l().$implicit;a(),S(e.children)}}function xe(t,n){if(t&1){let e=w();r(0,"div",3),D("click",function(d){let v=M(e).$implicit,b=l();return N(b.onClick(d,v))}),r(1,"div",4)(2,"span",5),s(3),o(),m(4,"p-tag",6),p(5,ge,1,0,"p-tag",7),h(6,ue,1,3,"p-tag",8,me),o(),p(8,fe,3,0,"div",9),o()}if(t&2){let e=n.$implicit,i=l();J("dx-svc-node--selected",i.store.selectedServiceName()===e.key),a(3),u(e.name),a(),c("value",e.entityCount+" entities"),a(),g(e.multiParent?5:-1),a(),S(e.severityChips),a(2),g(e.children.length>0?8:-1)}}var ye=["error","warning","recommendation","info"],_e={error:"danger",warning:"warn",recommendation:"info",info:"secondary"},B=class t{store=x(y);trees=f(()=>{let n=this.store.report();if(!n)return[];let e=i=>{let d=n.nodesByName[i],v=d?.name??i,b=d?.entityCount??0,L=d?.findings??[],re=Ce(L),ae=(d?.parentNames.length??0)>1,se=(n.childrenByName[i]??[]).map(de=>e(de));return{key:i,name:v,entityCount:b,findings:L,severityChips:re,multiParent:ae,children:se}};return n.topLevelNames.map(i=>e(i))});severityToTag(n){return _e[n]??"secondary"}onClick(n,e){n.stopPropagation(),this.store.select(e.key)}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-tree"]],decls:4,vars:0,consts:[["nodeTemplate",""],[1,"dx-svc-tree-root"],[3,"ngTemplateOutlet","ngTemplateOutletContext"],[1,"dx-svc-node",3,"click"],[1,"dx-svc-row"],[1,"dx-svc-name"],["severity","info","styleClass","dx-svc-count",3,"value"],["severity","secondary","value","multi-parent","styleClass","dx-svc-multi"],["tooltipPosition","top","styleClass","dx-svc-finding",3,"severity","value","pTooltip"],[1,"dx-svc-children"]],template:function(e,i){e&1&&(h(0,pe,2,4,"div",1,ce),Y(2,xe,9,6,"ng-template",null,0,K)),e&2&&S(i.trees())},dependencies:[U,T,P,Z,Q],styles:["[_nghost-%COMP%]{display:block}.dx-svc-tree-root[_ngcontent-%COMP%]{margin-bottom:.75rem}.dx-svc-node[_ngcontent-%COMP%]{cursor:pointer;padding:.25rem .4rem;border-radius:4px;transition:background-color 60ms ease-out}.dx-svc-node[_ngcontent-%COMP%]:hover > .dx-svc-row[_ngcontent-%COMP%]{background:var(--p-highlight-bg, rgba(120, 160, 240, .08))}.dx-svc-node--selected[_ngcontent-%COMP%] > .dx-svc-row[_ngcontent-%COMP%]{background:var(--p-highlight-bg, rgba(120, 160, 240, .18));outline:1px solid var(--p-primary-color, #6ea0ff)}.dx-svc-row[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;padding:.25rem .5rem;border-radius:4px}.dx-svc-name[_ngcontent-%COMP%]{font-weight:500}.dx-svc-children[_ngcontent-%COMP%]{margin-left:1.25rem;border-left:1px dashed var(--p-content-border-color, #444);padding-left:.5rem}"],changeDetection:0})};function Ce(t){if(t.length===0)return[];let n=new Map;for(let i of t){let d=n.get(i.severity);d||(d=new Set,n.set(i.severity,d)),d.add(i.ruleCode)}let e=[];for(let i of ye){let d=n.get(i);!d||d.size===0||e.push({severity:i,count:d.size,ruleCodes:[...d].sort()})}return e}var he=(t,n)=>n.ruleCode,Se=(t,n)=>n.ruleCode+"::"+n.subjectId;function De(t,n){t&1&&s(0),t&2&&_(" ",n.name," ")}function Pe(t,n){t&1&&s(0," All findings ")}function Te(t,n){if(t&1){let e=w();r(0,"button",4),D("click",function(){M(e);let d=l();return N(d.onClearSelection())}),o()}}function Me(t,n){t&1&&(r(0,"p",8),s(1,"No findings for this service."),o())}function Ne(t,n){if(t&1&&(r(0,"details",14)(1,"summary"),s(2,"Detail"),o(),r(3,"pre"),s(4),o()()),t&2){let e=l().$implicit,i=l(3);a(4),u(i.formatDetail(e.detail))}}function Oe(t,n){if(t&1&&(r(0,"article",9)(1,"div",10),m(2,"p-tag",11),r(3,"code",12),s(4),o()(),r(5,"p",13),s(6),o(),p(7,Ne,5,1,"details",14),o()),t&2){let e=n.$implicit,i=l(3);a(2),c("severity",i.severityTag(e.severity))("value",e.severity),a(2),u(e.ruleCode),a(2),u(e.message),a(),g(e.detail?7:-1)}}function we(t,n){if(t&1&&h(0,Oe,8,5,"article",9,he),t&2){let e=l();S(e.findings)}}function Ee(t,n){if(t&1&&(r(0,"div",3)(1,"div",5)(2,"span",6),s(3,"Direct entities:"),o(),r(4,"span",7),s(5),o()(),r(6,"div",5)(7,"span",6),s(8),o(),r(9,"span",7),s(10),o()(),r(11,"div",5)(12,"span",6),s(13),o(),r(14,"span",7),s(15),o()(),m(16,"hr"),p(17,Me,2,0,"p",8)(18,we,2,0),o()),t&2){let e=n,i=l();a(5),u(e.entityCount),a(3),_("Parents (",e.parentNames.length,"):"),a(2),_(" ",i.parentDisplayNames()," "),a(3),_("Children (",e.childNames.length,"):"),a(2),_(" ",i.childDisplayNames()," "),a(2),g(e.findings.length===0?17:18)}}function ke(t,n){t&1&&(r(0,"p",8),s(1,"No findings on this tenant."),o())}function Fe(t,n){if(t&1){let e=w();r(0,"article",16),D("click",function(){let d=M(e).$implicit,v=l(3);return N(v.onJumpTo(d))}),r(1,"div",10),m(2,"p-tag",11),r(3,"code",12),s(4),o(),r(5,"span",17),s(6),o()(),r(7,"p",13),s(8),o()()}if(t&2){let e=n.$implicit,i=l(3);a(2),c("severity",i.severityTag(e.severity))("value",e.severity),a(2),u(e.ruleCode),a(2),u(e.subjectName),a(2),u(e.message)}}function Ie(t,n){if(t&1&&h(0,Fe,9,5,"article",15,Se),t&2){let e=l(2);S(e.allFindings())}}function Be(t,n){if(t&1&&(r(0,"div",3),p(1,ke,2,0,"p",8)(2,Ie,2,0),o()),t&2){let e=l();a(),g(e.allFindings().length===0?1:2)}}var Re={error:"danger",warning:"warn",recommendation:"info",info:"secondary"},R=class t{store=x(y);selectedNode=f(()=>this.store.selectedNode());allFindings=f(()=>this.store.report()?.findings??[]);parentDisplayNames=f(()=>{let n=this.store.report(),e=this.selectedNode();return!n||!e?"\u2014":e.parentNames.length===0?"(top-level)":e.parentNames.map(i=>n.nodesByName[i]?.name??i).join(", ")});childDisplayNames=f(()=>{let n=this.store.report(),e=this.selectedNode();return!n||!e?"\u2014":e.childNames.length===0?"(none)":e.childNames.map(i=>n.nodesByName[i]?.name??i).join(", ")});severityTag(n){return Re[n]??"secondary"}formatDetail(n){return JSON.stringify(n,null,2)}onClearSelection(){this.store.select(null)}onJumpTo(n){let e=this.store.report();if(!e)return;let i=null;for(let[d,v]of Object.entries(e.nodesByName))if(v.id===n.subjectId){i=d;break}this.store.select(i)}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-finding-panel"]],decls:8,vars:3,consts:[[1,"dx-finding-panel"],[1,"dx-finding-panel-header"],["pButton","","type","button","icon","pi pi-times","severity","secondary","text","","rounded","","size","small","ariaLabel","Clear selection"],[1,"dx-finding-panel-body"],["pButton","","type","button","icon","pi pi-times","severity","secondary","text","","rounded","","size","small","ariaLabel","Clear selection",3,"click"],[1,"dx-finding-meta"],[1,"dx-finding-meta-label"],[1,"dx-finding-meta-value"],[1,"dx-finding-empty"],[1,"dx-finding-card"],[1,"dx-finding-card-head"],["styleClass","dx-finding-sev",3,"severity","value"],[1,"dx-finding-code"],[1,"dx-finding-message"],[1,"dx-finding-detail"],[1,"dx-finding-card","dx-finding-card--clickable"],[1,"dx-finding-card","dx-finding-card--clickable",3,"click"],[1,"dx-finding-service"]],template:function(e,i){if(e&1&&(r(0,"div",0)(1,"header",1)(2,"h2"),p(3,De,1,1)(4,Pe,1,0),o(),p(5,Te,1,0,"button",2),o(),p(6,Ee,19,6,"div",3)(7,Be,3,1,"div",3),o()),e&2){let d,v;a(3),g((d=i.selectedNode())?3:4,d),a(2),g(i.selectedNode()?5:-1),a(),g((v=i.selectedNode())?6:7,v)}},dependencies:[T,P,k,E],styles:["[_nghost-%COMP%]{display:block;height:100%}.dx-finding-panel[_ngcontent-%COMP%]{display:flex;flex-direction:column;height:100%}.dx-finding-panel-header[_ngcontent-%COMP%]{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.75rem 1rem;border-bottom:1px solid var(--p-content-border-color, #2a2a2a)}.dx-finding-panel-header[_ngcontent-%COMP%] h2[_ngcontent-%COMP%]{margin:0;font-size:1rem;font-weight:600;word-break:break-word}.dx-finding-panel-body[_ngcontent-%COMP%]{flex:1;overflow:auto;padding:.75rem 1rem;display:flex;flex-direction:column;gap:.5rem}.dx-finding-meta[_ngcontent-%COMP%]{display:flex;gap:.5rem;font-size:.8rem;word-break:break-word}.dx-finding-meta-label[_ngcontent-%COMP%]{color:var(--p-text-muted-color, #888);flex-shrink:0}.dx-finding-meta-value[_ngcontent-%COMP%]{flex:1}.dx-finding-empty[_ngcontent-%COMP%]{color:var(--p-text-muted-color, #888);font-style:italic;margin:0}hr[_ngcontent-%COMP%]{border:none;border-top:1px solid var(--p-content-border-color, #2a2a2a);margin:.5rem 0}.dx-finding-card[_ngcontent-%COMP%]{border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:4px;padding:.5rem .75rem;display:flex;flex-direction:column;gap:.4rem}.dx-finding-card--clickable[_ngcontent-%COMP%]{cursor:pointer;transition:background-color 60ms ease-out}.dx-finding-card--clickable[_ngcontent-%COMP%]:hover{background:var(--p-highlight-bg, rgba(120, 160, 240, .08))}.dx-finding-card-head[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.dx-finding-code[_ngcontent-%COMP%]{font-family:var(--p-font-family-monospace, ui-monospace, monospace);font-size:.75rem}.dx-finding-service[_ngcontent-%COMP%]{font-weight:500;font-size:.85rem}.dx-finding-message[_ngcontent-%COMP%]{margin:0;font-size:.85rem;line-height:1.35}.dx-finding-detail[_ngcontent-%COMP%]{font-size:.75rem}.dx-finding-detail[_ngcontent-%COMP%] pre[_ngcontent-%COMP%]{background:var(--p-surface-100, rgba(255, 255, 255, .04));padding:.4rem .5rem;border-radius:3px;white-space:pre-wrap;word-break:break-word;max-height:16rem;overflow:auto}"],changeDetection:0})};function ze(t,n){t&1&&(r(0,"p-message",6),s(1),o()),t&2&&(c("closable",!1),a(),u(n))}function je(t,n){t&1&&(r(0,"div",7),m(1,"p-progressspinner",8),r(2,"span"),s(3,"Running service diagnostics audit\u2026"),o()())}function $e(t,n){t&1&&(r(0,"p-message",9),s(1," No DXO2 services found on this tenant. Diagnostics has nothing to analyze yet. "),o()),t&2&&c("closable",!1)}function Ve(t,n){t&1&&(m(0,"dx-service-summary-bar"),r(1,"div",10)(2,"div",11),m(3,"dx-service-tree"),o(),r(4,"div",12),m(5,"dx-service-finding-panel"),o()())}function Le(t,n){if(t&1&&p(0,$e,2,1,"p-message",9)(1,Ve,6,0),t&2){let e=l();g(e.store.isEmpty()?0:1)}}var oe=class t{store=x(y);ngOnInit(){this.store.hasReport()||this.store.load()}onRefresh(){this.store.load()}static \u0275fac=function(e){return new(e||t)};static \u0275cmp=C({type:t,selectors:[["dx-service-diagnostics-page"]],decls:12,vars:3,consts:[[1,"dx-diagnostics-page"],[1,"dx-diagnostics-header"],[1,"dx-diagnostics-title"],[1,"dx-diagnostics-blurb"],[1,"dx-diagnostics-actions"],["pButton","","type","button","icon","pi pi-refresh","label","Run audit",3,"click","loading"],["severity","error",3,"closable"],[1,"dx-diagnostics-loading"],["ariaLabel","Loading diagnostics"],["severity","info",3,"closable"],[1,"dx-diagnostics-body"],[1,"dx-diagnostics-trees"],[1,"dx-diagnostics-panel"]],template:function(e,i){if(e&1&&(r(0,"section",0)(1,"header",1)(2,"div",2)(3,"h1"),s(4,"DXO2 Service Diagnostics"),o(),r(5,"p",3),s(6," Audits the bound tenant's DXO2 service organization and surfaces findings per service. Multi-parent services appear under every parent. "),o()(),r(7,"div",4)(8,"button",5),D("click",function(){return i.onRefresh()}),o()()(),p(9,ze,2,2,"p-message",6),p(10,je,4,0,"div",7)(11,Le,2,1),o()),e&2){let d;a(8),c("loading",i.store.loading()),a(),g((d=i.store.error())?9:-1,d),a(),g(i.store.loading()&&!i.store.hasReport()?10:i.store.hasReport()?11:-1)}},dependencies:[k,E,q,W,ne,ee,I,B,R],styles:["[_nghost-%COMP%]{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}.dx-diagnostics-page[_ngcontent-%COMP%]{display:flex;flex-direction:column;flex:1;min-height:0;padding:1.25rem 1.5rem;gap:1rem}.dx-diagnostics-header[_ngcontent-%COMP%]{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem}.dx-diagnostics-title[_ngcontent-%COMP%] h1[_ngcontent-%COMP%]{margin:0 0 .25rem;font-size:1.4rem;font-weight:600}.dx-diagnostics-blurb[_ngcontent-%COMP%]{margin:0;color:var(--p-text-muted-color, #888);font-size:.85rem;max-width:60ch}.dx-diagnostics-actions[_ngcontent-%COMP%]{flex-shrink:0}.dx-diagnostics-loading[_ngcontent-%COMP%]{display:flex;align-items:center;gap:.75rem;padding:1rem;color:var(--p-text-muted-color, #888)}.dx-diagnostics-body[_ngcontent-%COMP%]{flex:1;display:grid;grid-template-columns:minmax(0,1fr) 22rem;gap:1rem;min-height:0;overflow:hidden}.dx-diagnostics-trees[_ngcontent-%COMP%]{overflow:auto;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;padding:.75rem .5rem;background:var(--p-content-background, transparent)}.dx-diagnostics-panel[_ngcontent-%COMP%]{overflow:auto;border:1px solid var(--p-content-border-color, #2a2a2a);border-radius:6px;background:var(--p-content-background, transparent)}"],changeDetection:0})};export{oe as ServiceDiagnosticsPageComponent};
@@ -21,6 +21,19 @@ Multiple surfaces overlap in what they can tell you. Use them in this order —
21
21
 
22
22
  `corpus_list("queries", {type: "<type>"})` and `corpus_get("queries", "<id>")` sit alongside this hierarchy as the *worked-example* surface — read a known-good query when you want to pattern-match against an existing one rather than author from scratch.
23
23
 
24
+ ## The canonical investigative sequence (size → attribute surface → value distribution → project)
25
+
26
+ When authoring a query against a specific entity set, run discovery in this order — each call is cheap and informs the next:
27
+
28
+ 1. **`discovery_vertex_types`** — what types exist on this tenant? Pick the one(s) the user's question is about.
29
+ 2. **`discovery_entity_count({type})`** — how many entities of that type? (Or `discovery_service_counts({serviceNames})` for service-scoped questions, or TAS `COLLECT_COUNTS` for arbitrary filters.) This is what tells you whether `projection: DETAILED` is affordable or whether you should default to `BRIEF` / NASSQL+`KEEP`.
30
+ 3. **`discovery_attributes({type})`** — what attribute keys exist on this type? (Wraps `COLLECT_ATTRIBUTE_NAMES`.) Confirms that the field you want to filter on (or project) actually exists; cheap to call before authoring an `ATTRIBUTE(name: "X", …)` filter.
31
+ 4. **`discovery_attribute_values({type, attribute})`** — what values does attribute Y take on this tenant? (Wraps `COLLECT_ATTRIBUTES`.) Tells you whether an `IN` filter against the values the user named is going to match anything, and gives you the cardinality (is this a 4-value enum or a 4000-value freeform?).
32
+
33
+ Each stage is a server-side aggregate — no row payloads, no truncation risk. By the time you hit step 4, you have empirical knowledge of size + schema + value distribution, and you can author the actual query with the right projection mode on first attempt.
34
+
35
+ `cookbooks/investigator-flow` Step 6 has the projection-mode decision table (BRIEF for listing, DETAILED for single drill, NASSQL+KEEP for tabular).
36
+
24
37
  ## TAS — layers and attributes
25
38
 
26
39
  ```
@@ -55,6 +55,45 @@ have `.min(1)`.
55
55
 
56
56
  ---
57
57
 
58
+ ## TAS — `projection: "DETAILED"` blows the inline token budget at scale
59
+
60
+ **Symptom**: a TAS `run_query` with `projection: "DETAILED"` (the
61
+ default) against a moderate result set succeeds, but the response
62
+ auto-truncates to a tool-result file rather than landing inline.
63
+ Common at ~30+ rows because DETAILED returns full vertex bodies
64
+ including all attributes and edges.
65
+ **Workaround**: for any TAS query expected to return more than a
66
+ handful of rows, prefer **NASSQL with `KEEP`** to project just the
67
+ columns you need (`vertex.attr.name`, `vertex.attr.type`,
68
+ `vertex.id`, etc.). Use TAS `projection: "BRIEF"` or `"NO_TYPE"` if
69
+ you specifically need a TAS-shaped response with less weight. The
70
+ TAS `projection` field's schema describe carries this warning too —
71
+ worth re-reading on TAS authoring.
72
+ **Schema guard**: none — DETAILED is the legitimate default for
73
+ small result sets and detail-on-demand flows.
74
+
75
+ ---
76
+
77
+ ## NASSQL — `COUNT` after `GROUP` requires explicit `as`
78
+
79
+ **Symptom**: a pipeline ending `… → {op:"GROUP", columns:[X]} → {op:"COUNT"}`
80
+ (with no `as`) returns HTTP 400. Adding `"as":"n"` (or any non-empty
81
+ string) makes the identical pipeline succeed.
82
+ **Cause**: schema-vs-server gap. The Zod schema for `COUNT` marks `as`
83
+ as optional with a default of `"count"`, but the server's NASSQL
84
+ executor rejects a bare COUNT immediately downstream of GROUP. Probably
85
+ a server-side projection check that wants an explicit output column
86
+ name when the row shape collapses to grouping-key + aggregate.
87
+ **Workaround**: always set `as` on `COUNT` when it follows `GROUP`. As
88
+ a default habit, set `as` on every `COUNT` — costs nothing and removes
89
+ a class of silent breakage.
90
+ **Schema guard**: none today — the canonical Zod schema accepts a bare
91
+ COUNT. A schema tightening (`as` required when COUNT follows GROUP)
92
+ would catch this at SPA-validation time but is a small breaking change
93
+ for persisted bare-COUNT queries.
94
+
95
+ ---
96
+
58
97
  ## MM — `ID` specifier requires non-empty `ids`
59
98
 
60
99
  **Symptom**: `400 Bad Request` from MM endpoint.
@@ -131,6 +170,26 @@ queries (cross-layer search).
131
170
 
132
171
  ---
133
172
 
173
+ ## TAS ATTRIBUTE — filter expression name is `type`, NOT `_type`
174
+
175
+ **Symptom**: a query like `{"op":"ATTRIBUTE","expressions":[{"name":"_type","values":["AGENT"]}]}`
176
+ returns **zero rows with no error**. Schema validation passes, the
177
+ server responds 200, the result is empty.
178
+ **Cause**: a TAS ATTRIBUTE expression's `name` is the user-facing
179
+ attribute key (`type`, `hostname`, `agentType`, …) — NOT the
180
+ internal vertex storage field. `_type` is a storage-side / NASSQL
181
+ specifier convention that exists elsewhere in the codebase; using it
182
+ inside a TAS ATTRIBUTE filter is accepted by the schema (the schema
183
+ allows any string) and silently matches nothing.
184
+ **Workaround**: use `"name":"type"` (no leading underscore) to filter
185
+ by vertex type. Same for `"hostname"`, `"agentType"`, etc.
186
+ **Schema guard**: none — TAS ATTRIBUTE `name` is an open string field
187
+ because attribute names are tenant-dependent. Always
188
+ `run_partial_query upToStep:0` on a freshly authored FROM_TOPOLOGY to
189
+ confirm the filter matches before building the full pipeline.
190
+
191
+ ---
192
+
134
193
  ## NASSQL — pipeline ordering matters
135
194
 
136
195
  **Symptom**: `WINDOW` / `GROUP` / aggregation sequence produces wrong
@@ -387,3 +446,75 @@ platform handler is fixed.
387
446
  **Cause**: `{}` parses as a valid TAS payload — `filter` defaulting to no-filter is equivalent to `{op:"ALL"}`, and `limit` defaults to "no limit." Returns the entire bound tenant's topology. On large tenants that's 10MB+ + may be saved to disk by ui rather than returned inline. The same trap exists in NASSQL (`FROM_METADATA / FROM_TOPOLOGY / FROM` with `op:"ALL"` and no row-reducing op + no top-level `limit`) and in Metrics Metadata (`specifier: {op:"ALL"}` or specifier missing, `clamp` unset).
388
447
  **Workaround**: NEVER author a `run_query` payload that scans everything without scoping. For TAS, pair every `op:"ALL"` filter with a sane `limit` (5–100 for sampling). For NASSQL, either narrow the source op's specifier (`SPEC` + `sourceNameSpecifier` / `attributeNameSpecifier`; or a TAS filter for `FROM_TOPOLOGY`), include an aggregation (GROUP+COUNT, TOP/BOTTOM, FILTER), or set top-level `limit`. For MM, narrow the specifier or pass a sane `clamp`. See `corpus_get('queries', '01-discover-vertices')` and `corpus_get('queries', '03-discover-sources')` for canonical bounded-discovery patterns.
389
448
  **Schema guard**: server-side preflight refuses three unbounded shapes — TAS `ALL` filter without `limit` ≤ 100; NASSQL pipeline starting with `FROM_*` + ALL-spec, no row-reducer, no `limit` ≤ 200; MM ALL specifier (or missing) without `clamp` ≤ 1000. The error message names the trap and points at the canonical fix. The tool description for `run_query` explicitly warns up front.
449
+
450
+ ---
451
+
452
+ ## MM — `TYPE` AttributeNameSpecifier requires its inner `specifier` field
453
+
454
+ **Symptom**: a Metrics Metadata query whose `attributeNameSpecifier` uses `op: "TYPE"` returns `400 Bad Request` with `"TYPE specifier - specifier field is required"`.
455
+ **Cause**: although the schema marks `specifier` as optional on the TYPE op, the server requires it on the wire. Even a request that doesn't need any further narrowing must include something — typically `{ op: "ALL" }`.
456
+ **Workaround**: always include the inner specifier:
457
+ ```json
458
+ { "op": "TYPE", "bitMask": 1024, "bitMatch": 1024, "operator": "EQ", "specifier": { "op": "ALL" } }
459
+ ```
460
+ The visual MM editor's TYPE-op chips picker auto-fills this when missing.
461
+ **Schema guard**: not yet — the schema would have to mark `specifier` required, which risks breaking existing valid clients. The compose helpers in `@dx-do/client` (`composeMetricTypeBits`) and the editor UX both write the inner specifier automatically.
462
+
463
+ ---
464
+
465
+ ## MM — undocumented `MONITOR_DOUBLE_*` variants exist on the wire
466
+
467
+ **Symptom**: a metric returned by `/metadata/queryMetric` carries a `type` value (e.g. `1028` for "Total time (ms)" / "Connection time", or `4101` for "Slow Query Threshold(Seconds)") that doesn't decompose cleanly into the `typeEnum` names listed in the platform doc's table — `decodeMetricTypeBits` reports a non-zero residual or matches an unfamiliar combination.
468
+ **Cause**: the doc's `typeEnum` table at `metadata-index.html:732-870` lists `MONITOR_INT_*` and `MONITOR_LONG_*` variants (and a few `MONITOR_DOUBLE_*` for COUNTER / INTERVAL_COUNTER) but omits combinations the server still emits — at least `MONITOR_DOUBLE_DURATION` (`0x404` = 1028) and `MONITOR_DOUBLE_PERCENTAGE` (`0x1004` = 4101) appear on real tenants. The bit composition rule (underlying-type bit OR'd with numeric-info bit) is consistent regardless.
469
+ **Workaround**: filter by the **semantic-class bit alone** when you want "any of this kind", e.g. `bitMask: 0x400, bitMatch: 0x400` to catch every duration metric (INT, LONG, AND undocumented DOUBLE). The chips picker's "has all selected bits" semantics inherently handles this — you don't need to enumerate every variant.
470
+ **Schema guard**: none — the schema accepts arbitrary `bitMask` / `bitMatch` integers by design.
471
+
472
+ ---
473
+
474
+ ## Alarms — `status` is UPPERCASE, `severity` is lowercase
475
+
476
+ **Symptom**: a status-or-severity filter built from intuition (`status: "closed"`, `severity: "INFO"`) returns no results.
477
+ **Cause**: the wire data is case-asymmetric. `status` ∈ `NEW | UPDATED | CLOSED` (UPPERCASE). `severity` ∈ `critical | major | minor | information | unknown` (lowercase). The severity value is also spelled `information`, not `info`.
478
+ **Workaround**: match the cases exactly. `list_alarms.severities` is typed as a lowercase enum (`['critical','major','minor','information','unknown']`); any post-filter on `status` should compare against UPPERCASE.
479
+
480
+ ---
481
+
482
+ ## Alarms — "open" means `status != "CLOSED"`, and `includeClosed=true` returns BOTH
483
+
484
+ **Symptom**: agent calls `list_alarms({includeClosed: true})` expecting closed-only and gets a response that includes open alarms too.
485
+ **Cause**: the API has no "closed only" mode. The `includeClosed` flag is a UNION toggle — `false` (default) excludes closed; `true` includes them alongside open. There is no "closed only".
486
+ **Workaround**: for closed-only views, set `includeClosed: true`, then post-filter by `status === "CLOSED"`. Order by `closedTime` descending to surface the most-recently-closed.
487
+
488
+ ---
489
+
490
+ ## Alarms — `services_impacted` is not on the alarm body
491
+
492
+ **Symptom**: agent reads an alarm from `list_alarms` / `get_alarm` and reports an empty `services_impacted` array, contradicting what the DXO2 web UI shows for the same alarm.
493
+ **Cause**: regular alarms do not carry a `services_impacted` field on the body. The web UI's "Services Impacted" comes from an **enrichment event** on the alarm's lifecycle. Situation-shaped alarms carry related fields (`rc_services_impacted`, `initialImpactedServices`) but the regular Alarm does not.
494
+ **Workaround**: call `get_alarm_lifecycle(alarmId)` and find the event with `name: "Services Impacted"`. Its `propValue` is the array of impacted service names.
495
+ **Schema guard**: none — the typed alarm interface (correctly) does not declare `services_impacted`. Reporting "no services impacted" because it's missing from the body is wrong.
496
+
497
+ ---
498
+
499
+ ## Alarms — `startTime` and `@timestamp` are declared but never populated on the body
500
+
501
+ **Symptom**: agent reads `alarm.startTime` to answer "when did this alarm start?" and finds it `null` or `undefined`.
502
+ **Cause**: the alarm interface declares both `startTime` and `@timestamp` as string fields, but the OI server's alarm-search response does not populate them. The actually-populated time fields are `timeOrigin`, `lastUpdateTime`, `sourceTimestamp`, and (when closed) `closedTime`. The alarm's true start time lives on the lifecycle's first event (`name: "Alarm Opened"`, `propName: "startTime"`, ISO string in `propValue[0]`).
503
+ **Workaround**: use `timeOrigin` for the user-visible event time on the body. For the precise start time, fetch the lifecycle and read the `Alarm Opened` event.
504
+ **Schema guard**: none.
505
+
506
+ ---
507
+
508
+ ## Alarms — `alarmDescription` is almost always empty; use `message`
509
+
510
+ **Symptom**: agent surfaces `alarmDescription` to the user, finds it blank, and reports "the alarm has no description".
511
+ **Cause**: in practice `alarmDescription` is populated on a small minority of alarms (mostly DXO2-native and some APM). The field that consistently carries the "what happened" text is `message`.
512
+ **Workaround**: read `message` first. Fall back to `alarmDescription` only if `message` is unusually terse.
513
+
514
+ ---
515
+
516
+ ## Alarms — `productId` is a short code, `sourceProduct` is the human name
517
+
518
+ **Symptom**: agent reports the alarm's product as `"ao"` and the user has no idea what that means.
519
+ **Cause**: alarms carry both `productId` (a short opaque code, e.g. `"ao"` for APM) and `sourceProduct` (the human-readable label, e.g. `"Application Performance Management"`). They are not redundant — `productId` identifies the emitting product internally; `sourceProduct` is the user-visible name.
520
+ **Workaround**: cite `sourceProduct` to users. Only mention `productId` when disambiguating product internals or when the user explicitly asks for the short code.
@@ -9,5 +9,5 @@
9
9
  <style>@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}:root{color-scheme:dark;--dx-accent:#86efac;--dx-accent-strong:#34d399;--dx-accent-soft:rgba(134, 239, 172, .1);--dx-accent-amber:#fbbf24;--dx-glow:0 0 12px rgba(134, 239, 172, .4);--dx-glow-amber:0 0 12px rgba(251, 191, 36, .35);--dx-bg-canvas:#0a0a0a;--dx-bg-canvas-vignette:radial-gradient( ellipse at center, #161616 0%, #0a0a0a 70% );--dx-sidebar-bg:rgba(18, 18, 20, .94);--dx-sidebar-border:rgba(255, 255, 255, .06);--dx-text:#e5e5e5;--dx-text-muted:#a3a3a3;--dx-text-subtle:#737373}html,body{margin:0;padding:0;height:100vh;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background:var(--dx-bg-canvas);color:var(--dx-text)}.dx-app-body{background:var(--dx-bg-canvas);color:var(--dx-text)}</style><link rel="stylesheet" href="styles-23VUPSCU.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-23VUPSCU.css"></noscript></head>
10
10
  <body class="dx-app-body">
11
11
  <dx-app></dx-app>
12
- <link rel="modulepreload" href="chunk-JRM4BLOM.js"><link rel="modulepreload" href="chunk-YVD3UK5I.js"><link rel="modulepreload" href="chunk-Q2JA73UH.js"><link rel="modulepreload" href="chunk-RNMHSXZF.js"><link rel="modulepreload" href="chunk-5VSFINOX.js"><script src="polyfills-A7ZF72EO.js" type="module"></script><script src="main-SGLYO5YX.js" type="module"></script></body>
12
+ <link rel="modulepreload" href="chunk-P6TRLBVU.js"><link rel="modulepreload" href="chunk-JYKZ5EMU.js"><link rel="modulepreload" href="chunk-6TL6OWJA.js"><link rel="modulepreload" href="chunk-4L5DIG2E.js"><link rel="modulepreload" href="chunk-OV4PNSIK.js"><script src="polyfills-A7ZF72EO.js" type="module"></script><script src="main-4VTFKCWX.js" type="module"></script></body>
13
13
  </html>
@@ -1,4 +1,34 @@
1
1
  [
2
+ {
3
+ "id": "alarms-cookbook",
4
+ "title": "Alarms — full reference (fields, timestamps, queues, lifecycle)",
5
+ "applies_to": "alarms",
6
+ "tags": [
7
+ "reference",
8
+ "alarms"
9
+ ],
10
+ "related": [
11
+ "alarms-quickstart",
12
+ "investigator-flow",
13
+ "gotchas"
14
+ ],
15
+ "filename": "alarms-cookbook.md"
16
+ },
17
+ {
18
+ "id": "alarms-quickstart",
19
+ "title": "Alarms quickstart — \"are there any alarms?\" and follow-ups",
20
+ "applies_to": "alarms",
21
+ "tags": [
22
+ "getting-started",
23
+ "alarms"
24
+ ],
25
+ "related": [
26
+ "alarms-cookbook",
27
+ "investigator-flow",
28
+ "gotchas"
29
+ ],
30
+ "filename": "alarms-quickstart.md"
31
+ },
2
32
  {
3
33
  "id": "discovery-flow",
4
34
  "title": "Discovery flow — figure out what's queryable on this tenant",
@@ -346,6 +346,59 @@
346
346
  "cross-domain"
347
347
  ]
348
348
  },
349
+ {
350
+ "id": "34-nassql-entity-to-metric-ids",
351
+ "type": "nassql",
352
+ "prefix": 34,
353
+ "shortName": "nassql-entity-to-metric-ids",
354
+ "description": "entity → metricIds: NASSQL FROM_TOPOLOGY (VERTEX externalId filter) + JOIN_METADATA + KEEP — list every metric.id an entity emits",
355
+ "tags": [
356
+ "nassql",
357
+ "topology",
358
+ "metadata",
359
+ "id-lookup",
360
+ "entity-to-metric",
361
+ "round-trip"
362
+ ]
363
+ },
364
+ {
365
+ "id": "40-metadata-filter-by-typeenum",
366
+ "type": "metadata",
367
+ "prefix": 40,
368
+ "shortName": "metadata-filter-by-typeenum",
369
+ "description": "Filter MM by typeEnum bitmask — every duration metric on the tenant (bit 10 = DURATION axis)",
370
+ "tags": [
371
+ "metadata",
372
+ "type-enum",
373
+ "duration"
374
+ ]
375
+ },
376
+ {
377
+ "id": "41-metadata-frontend-durations",
378
+ "type": "metadata",
379
+ "prefix": 41,
380
+ "shortName": "metadata-frontend-durations",
381
+ "description": "Filter MM for frontend-tagged duration metrics — multi-chip composition (ANY_DURATION ∧ TYPE_INFO_FRONTEND, mask 0x10000400)",
382
+ "tags": [
383
+ "metadata",
384
+ "type-enum",
385
+ "duration",
386
+ "frontend"
387
+ ]
388
+ },
389
+ {
390
+ "id": "42-metadata-metric-id-to-entities",
391
+ "type": "metadata",
392
+ "prefix": 42,
393
+ "shortName": "metadata-metric-id-to-entities",
394
+ "description": "metricId → entity(s): MM ID specifier; entity associations live in `metric.attributes.external_ids` (TAS-compatible vertex externalIds)",
395
+ "tags": [
396
+ "metadata",
397
+ "id-lookup",
398
+ "metric-to-entity",
399
+ "round-trip"
400
+ ]
401
+ },
349
402
  {
350
403
  "id": "50-discover-custom-layers",
351
404
  "type": "tas",
@@ -50,6 +50,7 @@ Before authoring anything, mentally walk this checklist:
50
50
  | **Time window** — now, last hour, last day, custom? | (whatever the user says) | When the question implies time (most do). Default to "right now" → ~1 hour. |
51
51
  | **Threshold** — what does "high" / "low" / "noisy" / "slow" mean? | (whatever the user has in mind) | When the question implies a threshold. Often best to *not* threshold in the query — see step 5. |
52
52
  | **Output shape** — list, count, ranking, time series? | (the question's grammar) | "Are there any" → list; "how many" → count; "top" → ranking; "show me the trend" → time series. |
53
+ | **Open vs closed (alarms only)** — does "any alarms?" mean open right now, or include closed history? | the alarm's `status` field — `CLOSED` vs everything else | Whenever the question is about alarms. Default to open-only — `status != "CLOSED"` — unless the user explicitly asks about closed history. |
53
54
 
54
55
  The first two are the **load-bearing** ones because they change the query's filter shape entirely. Time/threshold/output usually have safe defaults.
55
56
 
@@ -106,6 +107,8 @@ Based on the user's confirmed intent:
106
107
 
107
108
  The metadata-vs-data distinction is the most-confused one — see `cookbooks/discovery-flow` for the full hierarchy and `cookbooks/nassql-quickstart` for source-op details.
108
109
 
110
+ **Alarms are a separate axis.** If the question is about alarms (the user said "alarm", "alarms", "open alarms", "critical alarms", or asks about something that maps to OI alarms — alert breaches, host-down notifications, service-health drops), the answer is the alarms surface — `list_alarms`, `get_alarm`, `get_alarm_lifecycle`, `get_related_alarms`, `list_alarm_queues` — not a TAS / NASSQL / Metrics-Metadata query. Alarms have their own API and their own vocabulary (`status` UPPERCASE vs `severity` lowercase, multiple timestamps, `external_ids` for entity round-trip, `services_impacted` only on the lifecycle). See `cookbooks/alarms-quickstart` before answering.
111
+
109
112
  ## Step 5 — Default: produce the query, not the analysis
110
113
 
111
114
  This is the load-bearing default for investigative work:
@@ -126,7 +129,38 @@ When to threshold in the query anyway:
126
129
 
127
130
  See `cookbooks/query-vs-analysis-separation` for the pattern catalog.
128
131
 
129
- ## Step 6 — Author + verify
132
+ ## Step 6 — Size + surface attributes before authoring
133
+
134
+ Before authoring the actual payload, **stage discovery** in this order. Each stage is a cheap server-side aggregate that informs the next; together they let you commit to the right projection mode (and filter shape) on first author instead of guess-and-recover.
135
+
136
+ | Stage | Question it answers | Canonical tool / op |
137
+ |---|---|---|
138
+ | **6a. Size** | "How many entities will this match?" | `discovery_entity_count({type})` for known types; `discovery_service_counts({serviceNames})` for service-scoped; TAS `COLLECT_COUNTS` for arbitrary filters |
139
+ | **6b. Attribute surface** | "What attribute keys exist on this entity set?" | `discovery_attributes({type})` (wraps TAS `COLLECT_ATTRIBUTE_NAMES`) |
140
+ | **6c. Value distribution** | "What values does attribute Y actually take?" | `discovery_attribute_values({type, attribute})` (wraps TAS `COLLECT_ATTRIBUTES`) |
141
+ | **6d. Project for the question** | Pick the projection / payload shape that fits the sized result (see table below) | `run_query` with the right `projection` mode, or NASSQL + `KEEP` |
142
+
143
+ ### Default to `BRIEF` for investigative listing queries
144
+
145
+ `projection: DETAILED` is the *schema* default but is rarely the *right* default for investigation. It returns full vertex bodies including the per-attribute type map, and auto-truncates to a tool-result file at ~30+ rows. The agent almost never needs that detail to answer *"show me which X match Y"*. Escalate to `DETAILED` only when the user's question requires attribute payloads (e.g. *"what version are these agents running?"*).
146
+
147
+ | Sized result | Default projection |
148
+ |---|---|
149
+ | Single entity drill ("tell me about vertex X") | `DETAILED` |
150
+ | Small list (N ≤ 10), attributes wanted | `DETAILED` |
151
+ | List / enumerate / "which X match Y" (N > 10) | `BRIEF` |
152
+ | Tabular result with named columns | NASSQL `FROM_TOPOLOGY` + `KEEP(vertex.attr.name, vertex.attr.type, vertex.id, …)` |
153
+ | Mid-scale with attribute payloads needed | `NO_TYPE` (drops the attribute type maps; smaller than DETAILED, more than BRIEF) |
154
+
155
+ ### When to skip stages
156
+
157
+ - **6a alone** suffices for pure count questions (*"how many X?"*).
158
+ - **6a + 6d** suffices when the attribute surface is already known and no attribute-filtering is needed.
159
+ - **All four** apply when the user names an attribute or value (6b confirms the attribute exists; 6c confirms the values are real on this tenant).
160
+
161
+ The general principle: **don't dump `DETAILED` on day one**. Size with COUNT, surface attributes with `COLLECT_ATTRIBUTE_NAMES`, sample values with `COLLECT_ATTRIBUTES`, then pick the projection that the question actually needs. Proactive sizing replaces reactive truncation-recovery.
162
+
163
+ ## Step 7 — Author + verify
130
164
 
131
165
  Use corpus content as starting points:
132
166
 
@@ -142,7 +176,7 @@ Verify before saving:
142
176
  - `run_query` for full execution. Watch for empty-payload trap (see `gotchas`).
143
177
  - `run_partial_query` for sub-tree verification when you have nested filters or multi-step NASSQL pipelines.
144
178
 
145
- ## Step 7 — Save + hand off
179
+ ## Step 8 — Save + hand off
146
180
 
147
181
  Save with `create_query`:
148
182