AI Skill Report Card
Writing Cmpnb Documents
Quick Start14 / 15
A typical result doc: heading → meta table → result table → matching chart.
Markdown### Portfolio Name: P1 ```compassBasicTable1 +++ [ {"size": 220, "grow": true}, {"size": 220, "grow": true} ] +++ |Metric|Value| |:----------|:----------| |Date Range|2024-01-01 ~ 2024-12-31| |Tau|0.0500| ``` **Black-Litterman Posterior Return** ```compassBasicTable1 +++ [ {"size": 90, "grow": false}, {"size": 130, "grow": true} ] +++ |Fund Code|Posterior Return| |:-------:|:-------:| |F1|5.00%| |F2|2.00%| ``` ```compassChart [{"chartAttributes":[{"title":"Portfolio Name (P1)","subtitle":"Black-Litterman Posterior Return"}]},{"data":[{"fundCode":"F1","posteriorReturn":0.05},{"fundCode":"F2","posteriorReturn":0.02}]},{"series":[{"type":"bar","xKey":"fundCode","yKey":"posteriorReturn","yName":"Posterior Return","cornerRadius":4,"strokeWidth":1,"label":{"enabled":true,"format":".2%"}}]},{"axes":[{},{"min":-0.05,"max":0.10,"label":{"enabled":true,"format":".2%"}}]}] ```
Recommendation▾
Name isn't gerund form ideally ('writing-cmpnb-documents' is acceptable but could be 'generating-cmpnb-documents' for consistency); minor nit.
Workflow14 / 15
Progress:
- [ ] Identify the data: what table(s) and chart(s) are needed
- [ ] Stringify all cell values for the table (e.g. 0.05 -> "5.00%")
- [ ] Write compassBasicTable1 with +++ column-size header + pipe table body
- [ ] Verify every row has exactly N cells matching N column-size entries
- [ ] Build the compassChart JSON array: chartAttributes, data, series(+legend), axes?
- [ ] Confirm series xKey/yKey/colorKey exist in every data record
- [ ] Collapse chart JSON to a single line, keep Unicode literal (no escaping)
- [ ] Separate blocks with blank lines; place chart right after its table
- [ ] Sanity-check: strip both custom fences — remainder must still be valid plain Markdown
Recommendation▾
Add a brief note on error handling/validation output format when rendering fails, since skill description mentions 'debugging existing cmpnb blocks'.
Examples17 / 20
Example 1: Sized table only
Input: Two metrics, date range and tau, equal-width growing columns.
Output:
```compassBasicTable1
+++
[
{"size": 220, "grow": true},
{"size": 220, "grow": true}
]
+++
|Metric|Value|
|:----------|:----------|
|Date Range|2024-01-01 ~ 2024-12-31|
|Tau|0.0500|
```
Example 2: Horizontal grouped bar with legend
Input: Compare implied vs. historical returns for F1/F2, show a floating legend top-right.
Output:
```compassChart
[{"chartAttributes":[{"subtitle":"Market Implied Prior Returns VS Fund Historical Return","title":"Portfolio Name (P1)"}]},{"data":[{"fundCode":"F1","impliedReturn":0.05,"historicalReturn":0.04},{"fundCode":"F2","impliedReturn":0.02,"historicalReturn":0.03}]},{"series":[{"type":"bar","direction":"horizontal","xKey":"fundCode","yKey":"impliedReturn","yName":"Implied Return","cornerRadius":20,"strokeWidth":1,"label":{"enabled":true,"format":".2%"}},{"type":"bar","direction":"horizontal","xKey":"fundCode","yKey":"historicalReturn","yName":"Historical Return","cornerRadius":20,"strokeWidth":1,"label":{"enabled":true,"format":".2%"}}],"legend":{"position":{"placement":"right-top","floating":true},"border":{"enabled":true,"strokeWidth":1},"cornerRadius":20,"padding":16}},{"axes":[{"min":-0.05,"max":0.10,"label":{"enabled":true,"format":".2%"}}]}]
```
Example 3: Heatmap (view/asset weight matrix)
Input: P-matrix of view weights per fund, 2 views x 2 funds.
Output:
```compassChart
[{"chartAttributes":[{"subtitle":"Black-Litterman P Matrix","title":"Portfolio Name (P1)"}]},{"data":[{"viewSeq":1,"fundCode":"F1","weight":1.0},{"viewSeq":1,"fundCode":"F2","weight":0.0},{"viewSeq":2,"fundCode":"F1","weight":0.0},{"viewSeq":2,"fundCode":"F2","weight":1.0}]},{"series":[{"type":"heatmap","xKey":"fundCode","xName":"Fund Code","yKey":"viewSeq","yName":"View Seq","colorKey":"weight","colorName":"Weight","label":{"enabled":true,"format":"0.0000;-0.0000;0.0000"}}],"gradientLegend":{"scale":{"label":{"enabled":true,"format":"0.0000;-0.0000;0.0000"}},"gradient":{"thickness":15,"preferredLength":400}}},{"axes":[{"type":"category","line":{"enabled":false}},{"type":"category","position":"left","tick":{"size":20}}]}]
```
Recommendation▾
Include one negative example (a broken block and why it fails) to reinforce the pitfalls section with concrete before/after.
Best Practices
- Always stringify table cells before emitting (
5.00%,0.0500), never raw numbers. - Set
grow: false+ fixedsizefor label/identifier columns;grow: truefor data columns that should fill space. - Only enable
label.enabled: trueon bar series whendata.length <= 10. - Reuse the same field names between the table and the chart's
datasegment so both represent the same underlying values. - Supply the optional
axessegment with explicitmin/maxwhen you want the chart to hug actual data range rather than auto-scale. - Place the
compassChartblock immediately after its correspondingcompassBasicTable1for readability (order doesn't affect rendering). - When unsure about an AG Charts option's behavior or accepted values, defer to AG Charts docs — this format only reshapes AG Charts config, it doesn't redefine it.
Common Pitfalls
- Omitting the
+++ ... +++column-size header — table falls back to default layout, losing intended sizing. - Row cell count not matching the number of column-size entries — renderer rejects the block.
- Escaping Unicode/CJK characters inside
compassChartJSON — they must be written literally (组合, not\u7ec4\u5408). - Pretty-printing the
compassChartJSON across multiple lines — must be a single line, no embedded newlines. - Using a
xKey/yKey/colorKeyinseriesthat doesn't exist in everydatarecord. - Forgetting segment 4 (
axes) when a specific axis range is needed — omission is valid but yields renderer-chosen defaults. - Trailing comma after the last column-size entry in the
+++array.