@zio.dev/zio-blocks 0.0.32 → 0.0.33
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.
- package/guides/query-dsl-extending.md +1 -1
- package/guides/query-dsl-fluent-builder.md +1 -1
- package/guides/query-dsl-reified-optics.md +1 -1
- package/guides/query-dsl-sql.md +1 -1
- package/guides/zio-schema-migration.md +6 -6
- package/index.md +13 -13
- package/package.json +1 -1
- package/reference/codec.md +10 -10
- package/reference/context.md +1 -1
- package/reference/docs.md +1 -1
- package/reference/dynamic-schema.md +7 -7
- package/reference/json-patch.md +2 -2
- package/reference/media-type.md +2 -2
- package/reference/resource-management/resource.md +2 -2
- package/reference/resource-management/scope.md +1 -1
- package/reference/resource-management/wire.md +2 -2
- package/reference/schema-evolution/as.md +7 -7
- package/reference/schema-evolution/into.md +7 -7
- package/reference/schema-expr.md +2 -2
- package/reference/schema.md +1 -1
- package/reference/type-class-derivation.md +1 -1
- package/reference/typeid.md +2936 -583
- package/reference/xml.md +21 -183
- package/ringbuffer.md +1 -1
- package/superpowers/plans/2026-03-19-docs-critique-subagent.md +407 -0
- package/superpowers/specs/2026-03-19-docs-critique-subagent-design.md +222 -0
package/reference/xml.md
CHANGED
|
@@ -466,7 +466,7 @@ val xml = XmlReader.read("""
|
|
|
466
466
|
</book>
|
|
467
467
|
</books>
|
|
468
468
|
</library>
|
|
469
|
-
""")
|
|
469
|
+
""")
|
|
470
470
|
|
|
471
471
|
// Navigate to child elements
|
|
472
472
|
val books = xml.select.get("library").get("books")
|
|
@@ -478,9 +478,16 @@ val firstBook = books.get("book")(0)
|
|
|
478
478
|
Extract text content from the first book:
|
|
479
479
|
|
|
480
480
|
```scala
|
|
481
|
-
val title: Either[
|
|
482
|
-
// title: Either[
|
|
483
|
-
//
|
|
481
|
+
val title: Either[SchemaError, String] = firstBook.get("title").text
|
|
482
|
+
// title: Either[SchemaError, String] = Left(
|
|
483
|
+
// SchemaError(
|
|
484
|
+
// List(
|
|
485
|
+
// Message(
|
|
486
|
+
// source = DynamicOptic(IndexedSeq()),
|
|
487
|
+
// details = "Expected single value but got 0"
|
|
488
|
+
// )
|
|
489
|
+
// )
|
|
490
|
+
// )
|
|
484
491
|
// )
|
|
485
492
|
```
|
|
486
493
|
|
|
@@ -534,19 +541,19 @@ import zio.blocks.schema.xml._
|
|
|
534
541
|
val selection: XmlSelection = ???
|
|
535
542
|
|
|
536
543
|
// Get single value (fails if not exactly one)
|
|
537
|
-
val one: Either[
|
|
544
|
+
val one: Either[SchemaError, Xml] = selection.one
|
|
538
545
|
|
|
539
546
|
// Get any value (first of many)
|
|
540
|
-
val any: Either[
|
|
547
|
+
val any: Either[SchemaError, Xml] = selection.any
|
|
541
548
|
|
|
542
549
|
// Get all values as a single XML element
|
|
543
|
-
val all: Either[
|
|
550
|
+
val all: Either[SchemaError, Xml] = selection.all
|
|
544
551
|
|
|
545
552
|
// Convert to chunk
|
|
546
553
|
val chunk = selection.toChunk
|
|
547
554
|
|
|
548
555
|
// Extract text content
|
|
549
|
-
val text: Either[
|
|
556
|
+
val text: Either[SchemaError, String] = selection.text
|
|
550
557
|
val allText: String = selection.textContent
|
|
551
558
|
```
|
|
552
559
|
|
|
@@ -635,7 +642,7 @@ val xml: Xml = ???
|
|
|
635
642
|
val patch = XmlPatch.setAttribute(p".person", "active", "true")
|
|
636
643
|
|
|
637
644
|
// Apply the patch
|
|
638
|
-
val result: Either[
|
|
645
|
+
val result: Either[SchemaError, Xml] = patch(xml)
|
|
639
646
|
```
|
|
640
647
|
|
|
641
648
|
### Composing Patches
|
|
@@ -657,175 +664,6 @@ val patch2 = XmlPatch.add(
|
|
|
657
664
|
val combined = patch1 ++ patch2
|
|
658
665
|
```
|
|
659
666
|
|
|
660
|
-
## XmlEncoder and XmlDecoder
|
|
661
|
-
|
|
662
|
-
For more fine-grained control over XML serialization, use the separate `XmlEncoder` and `XmlDecoder` traits:
|
|
663
|
-
|
|
664
|
-
### XmlEncoder
|
|
665
|
-
|
|
666
|
-
`XmlEncoder[A]` provides type-safe XML encoding:
|
|
667
|
-
|
|
668
|
-
```scala
|
|
669
|
-
import zio.blocks.schema._
|
|
670
|
-
import zio.blocks.schema.xml._
|
|
671
|
-
|
|
672
|
-
// Automatic derivation from Schema
|
|
673
|
-
case class Person(name: String, age: Int)
|
|
674
|
-
object Person {
|
|
675
|
-
implicit val schema: Schema[Person] = Schema.derived
|
|
676
|
-
implicit val encoder: XmlEncoder[Person] = XmlEncoder.fromSchema
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
val person = Person("Alice", 30)
|
|
680
|
-
val xml: Xml = XmlEncoder[Person].encode(person)
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
#### Creating custom encoders
|
|
684
|
-
|
|
685
|
-
Create custom encoders from functions or using contravariance:
|
|
686
|
-
|
|
687
|
-
```scala
|
|
688
|
-
import zio.blocks.schema.xml._
|
|
689
|
-
|
|
690
|
-
// Create from a function
|
|
691
|
-
val customEncoder: XmlEncoder[Int] = XmlEncoder.instance(n =>
|
|
692
|
-
Xml.Element("number", Xml.Text(n.toString))
|
|
693
|
-
)
|
|
694
|
-
|
|
695
|
-
// Map with contravariance - encode a wrapper type
|
|
696
|
-
case class UserId(value: Int)
|
|
697
|
-
|
|
698
|
-
val userIdEncoder: XmlEncoder[UserId] =
|
|
699
|
-
customEncoder.contramap[UserId](_.value)
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
#### Using implicit resolution
|
|
703
|
-
|
|
704
|
-
Leverage implicit resolution for automatic encoder derivation:
|
|
705
|
-
|
|
706
|
-
```scala
|
|
707
|
-
import zio.blocks.schema._
|
|
708
|
-
import zio.blocks.schema.xml._
|
|
709
|
-
|
|
710
|
-
case class Product(id: String, price: Double)
|
|
711
|
-
object Product {
|
|
712
|
-
implicit val schema: Schema[Product] = Schema.derived
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
// No explicit encoder needed - derives automatically
|
|
716
|
-
def encodeProduct[A](value: A)(implicit encoder: XmlEncoder[A]): Xml =
|
|
717
|
-
encoder.encode(value)
|
|
718
|
-
|
|
719
|
-
val result = encodeProduct(Product("item-1", 99.99))
|
|
720
|
-
```
|
|
721
|
-
|
|
722
|
-
### XmlDecoder
|
|
723
|
-
|
|
724
|
-
`XmlDecoder[A]` provides type-safe XML decoding with error handling:
|
|
725
|
-
|
|
726
|
-
```scala
|
|
727
|
-
import zio.blocks.schema._
|
|
728
|
-
import zio.blocks.schema.xml._
|
|
729
|
-
|
|
730
|
-
// Automatic derivation from Schema
|
|
731
|
-
case class Person(name: String, age: Int)
|
|
732
|
-
object Person {
|
|
733
|
-
implicit val schema: Schema[Person] = Schema.derived
|
|
734
|
-
implicit val decoder: XmlDecoder[Person] = XmlDecoder.fromSchema
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
val xml = Xml.Element("Person",
|
|
738
|
-
Xml.Element("name", Xml.Text("Alice")),
|
|
739
|
-
Xml.Element("age", Xml.Text("30"))
|
|
740
|
-
)
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
Decode the XML:
|
|
744
|
-
|
|
745
|
-
```scala
|
|
746
|
-
val result: Either[XmlError, Person] = XmlDecoder[Person].decode(xml)
|
|
747
|
-
// result: Either[XmlError, Person] = Right(Person(name = "Alice", age = 30))
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
#### Creating custom decoders
|
|
751
|
-
|
|
752
|
-
Create custom decoders from functions or using covariance:
|
|
753
|
-
|
|
754
|
-
```scala
|
|
755
|
-
import zio.blocks.schema.xml._
|
|
756
|
-
import zio.blocks.chunk.Chunk
|
|
757
|
-
|
|
758
|
-
// Create from a function
|
|
759
|
-
val numberDecoder: XmlDecoder[Int] = XmlDecoder.instance { xml =>
|
|
760
|
-
xml match {
|
|
761
|
-
case Xml.Element(_, _, Chunk(Xml.Text(text), _*)) =>
|
|
762
|
-
text.toIntOption.toRight(XmlError("Invalid number"))
|
|
763
|
-
case _ => Left(XmlError("Expected number element"))
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
// Map for covariance - decode to a wrapper type
|
|
768
|
-
case class UserId(value: Int)
|
|
769
|
-
|
|
770
|
-
val userIdDecoder: XmlDecoder[UserId] =
|
|
771
|
-
numberDecoder.map(UserId(_))
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
#### Error handling with decoders
|
|
775
|
-
|
|
776
|
-
Handle decoding errors gracefully with fallback strategies:
|
|
777
|
-
|
|
778
|
-
```scala
|
|
779
|
-
import zio.blocks.schema._
|
|
780
|
-
import zio.blocks.schema.xml._
|
|
781
|
-
|
|
782
|
-
case class Person(name: String, age: Int)
|
|
783
|
-
object Person {
|
|
784
|
-
implicit val schema: Schema[Person] = Schema.derived
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
def decodeWithFallback[A](
|
|
788
|
-
xml: Xml,
|
|
789
|
-
fallback: A
|
|
790
|
-
)(implicit decoder: XmlDecoder[A]): A = {
|
|
791
|
-
decoder.decode(xml).getOrElse(fallback)
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
val invalidXml = Xml.Element("Empty")
|
|
795
|
-
val defaultPerson = Person("Unknown", 0)
|
|
796
|
-
val result = decodeWithFallback(invalidXml, defaultPerson)
|
|
797
|
-
// Person("Unknown", 0)
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
#### Combining encoders and decoders
|
|
801
|
-
|
|
802
|
-
Round-trip values by encoding and decoding:
|
|
803
|
-
|
|
804
|
-
```scala
|
|
805
|
-
import zio.blocks.schema._
|
|
806
|
-
import zio.blocks.schema.xml._
|
|
807
|
-
import zio.blocks.schema.xml.syntax._
|
|
808
|
-
|
|
809
|
-
case class Message(id: String, text: String)
|
|
810
|
-
object Message {
|
|
811
|
-
implicit val schema: Schema[Message] = Schema.derived
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Round-trip: encode then decode
|
|
815
|
-
val message = Message("msg-1", "Hello")
|
|
816
|
-
val encoded: Xml = message.toXml
|
|
817
|
-
```
|
|
818
|
-
|
|
819
|
-
Decode the encoded value:
|
|
820
|
-
|
|
821
|
-
```scala
|
|
822
|
-
val result: Either[XmlError, Message] =
|
|
823
|
-
implicitly[XmlDecoder[Message]].decode(encoded)
|
|
824
|
-
// result: Either[XmlError, Message] = Right(
|
|
825
|
-
// Message(id = "msg-1", text = "Hello")
|
|
826
|
-
// )
|
|
827
|
-
```
|
|
828
|
-
|
|
829
667
|
## Extension Syntax
|
|
830
668
|
|
|
831
669
|
When a `Schema` is in scope, use convenient extension methods:
|
|
@@ -963,7 +801,7 @@ val person = Person("Alice", 30)
|
|
|
963
801
|
val xml: Xml = codec.encodeValue(person)
|
|
964
802
|
|
|
965
803
|
// Decode from Xml directly
|
|
966
|
-
val decoded:
|
|
804
|
+
val decoded: Person = codec.decodeValue(xml)
|
|
967
805
|
```
|
|
968
806
|
|
|
969
807
|
**XmlCodec supports all Schema types:**
|
|
@@ -977,7 +815,7 @@ val decoded: Either[XmlError, Person] = codec.decodeValue(xml)
|
|
|
977
815
|
|
|
978
816
|
## Error Handling
|
|
979
817
|
|
|
980
|
-
All decoding operations return `Either[SchemaError, A]` or `Either[
|
|
818
|
+
All decoding operations return `Either[SchemaError, A]` or `Either[SchemaError, A]`. The `SchemaError` type provides detailed error information:
|
|
981
819
|
|
|
982
820
|
```scala
|
|
983
821
|
import zio.blocks.schema._
|
|
@@ -1002,12 +840,12 @@ result match {
|
|
|
1002
840
|
}
|
|
1003
841
|
```
|
|
1004
842
|
|
|
1005
|
-
`
|
|
843
|
+
`SchemaError` provides detailed error information for debugging:
|
|
1006
844
|
|
|
1007
845
|
```scala
|
|
1008
846
|
import zio.blocks.schema.xml._
|
|
1009
847
|
|
|
1010
|
-
val error =
|
|
848
|
+
val error = SchemaError("Parse failed")
|
|
1011
849
|
|
|
1012
850
|
// Error message
|
|
1013
851
|
val message: String = error.getMessage
|
|
@@ -1133,7 +971,7 @@ val xmlString = """
|
|
|
1133
971
|
</library>
|
|
1134
972
|
"""
|
|
1135
973
|
|
|
1136
|
-
val xml = XmlReader.read(xmlString)
|
|
974
|
+
val xml = XmlReader.read(xmlString)
|
|
1137
975
|
|
|
1138
976
|
// Find all books
|
|
1139
977
|
val books = xml.select.get("library").get("books").get("book")
|
package/ringbuffer.md
CHANGED
|
@@ -25,7 +25,7 @@ All variants expose `offer` (returns `false` if full) and `take` (returns `null`
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
27
27
|
```scala
|
|
28
|
-
libraryDependencies += "dev.zio" %% "zio-blocks-ringbuffer" % "0.0.
|
|
28
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-ringbuffer" % "0.0.33"
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
---
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
# Docs Critique Subagent Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Add a maker-critic agent workflow that automatically reviews documentation for content quality, technical accuracy, completeness, and consistency.
|
|
6
|
+
|
|
7
|
+
**Architecture:** A pure-coordinator skill (`docs-critique`) spawns a maker agent to run a doc creation skill, then spawns a fresh critic agent to review the output. The orchestrator passes critique back to the maker via `SendMessage`. The maker fixes its own work. Critic is freshly spawned each review round.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Claude Code skills, Claude Code agent definitions, Agent tool, SendMessage
|
|
10
|
+
|
|
11
|
+
**Spec:** `docs/superpowers/specs/2026-03-19-docs-critique-subagent-design.md`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## File Structure
|
|
16
|
+
|
|
17
|
+
| File | Responsibility |
|
|
18
|
+
|------|---------------|
|
|
19
|
+
| `.claude/agents/docs-critic.md` | Critic agent definition — persona, review dimensions, severity rubric, report format. Read-only tools. |
|
|
20
|
+
| `.claude/skills/docs-critique/SKILL.md` | Orchestrating skill — pure coordinator that spawns maker, spawns critic, passes messages, manages iteration loop. Never edits files. |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
### Task 1: Create the Critic Agent Definition
|
|
25
|
+
|
|
26
|
+
**Files:**
|
|
27
|
+
- Create: `.claude/agents/docs-critic.md`
|
|
28
|
+
|
|
29
|
+
- [ ] **Step 1: Create the `.claude/agents/` directory**
|
|
30
|
+
|
|
31
|
+
Run: `mkdir -p /home/milad/sources/scala/zio-blocks-new/.claude/agents`
|
|
32
|
+
|
|
33
|
+
- [ ] **Step 2: Write the critic agent definition**
|
|
34
|
+
|
|
35
|
+
Create `.claude/agents/docs-critic.md` with this exact content:
|
|
36
|
+
|
|
37
|
+
```markdown
|
|
38
|
+
---
|
|
39
|
+
name: docs-critic
|
|
40
|
+
description: Reviews ZIO Blocks documentation for content quality, technical accuracy, completeness, and consistency. Returns a structured report with severity-rated findings. Read-only — never modifies files.
|
|
41
|
+
tools: Read, Glob, Grep
|
|
42
|
+
model: sonnet
|
|
43
|
+
color: purple
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
You are a senior technical writer and Scala developer reviewing ZIO Blocks documentation. You are skeptical by default — assume the document has problems and find them.
|
|
47
|
+
|
|
48
|
+
## Inputs
|
|
49
|
+
|
|
50
|
+
You will receive:
|
|
51
|
+
1. A documentation file path to review
|
|
52
|
+
2. A list of relevant Scala source file paths (read them yourself)
|
|
53
|
+
3. A list of related documentation file paths (read them yourself)
|
|
54
|
+
4. (Optional) Results from mechanical checks already performed — skip those areas
|
|
55
|
+
|
|
56
|
+
## Review Dimensions
|
|
57
|
+
|
|
58
|
+
Evaluate the document across four dimensions:
|
|
59
|
+
|
|
60
|
+
### Content Quality
|
|
61
|
+
- Is there motivation before code? Does the reader understand *why* before *how*?
|
|
62
|
+
- Are examples realistic (not toy `foo`/`bar` examples)?
|
|
63
|
+
- Is the narrative arc logical — does each section build on the previous?
|
|
64
|
+
- Is the writing appropriate for the target audience?
|
|
65
|
+
- Is the prose clear and concise?
|
|
66
|
+
|
|
67
|
+
### Technical Accuracy
|
|
68
|
+
- Do API signatures in the doc match the actual source code? (Read the source files to verify.)
|
|
69
|
+
- Are code examples correct beyond just compiling? Would they produce the described output?
|
|
70
|
+
- Does the described behavior match the actual implementation?
|
|
71
|
+
- Are type parameters, return types, and method names accurate?
|
|
72
|
+
|
|
73
|
+
**Note:** You cannot compile code. Your accuracy checks are static text comparisons against source files. Flag anything you cannot verify with certainty.
|
|
74
|
+
|
|
75
|
+
### Completeness
|
|
76
|
+
- Are all required sections present for this doc type?
|
|
77
|
+
- **Reference pages** (`docs/reference/`): Overview, Construction, Predefined Instances, Operators, Comparison, Advanced Usage
|
|
78
|
+
- **How-to guides** (`docs/guides/`): Prerequisites, Steps, Verification, Troubleshooting
|
|
79
|
+
- **Tutorials** (`docs/tutorials/`): Introduction, Prerequisites, Steps, Summary, Next Steps
|
|
80
|
+
- If the doc type cannot be determined from its path, skip required-sections check and note this in your report.
|
|
81
|
+
- Are edge cases and error scenarios mentioned?
|
|
82
|
+
- Are cross-references to related types/pages adequate?
|
|
83
|
+
|
|
84
|
+
### Consistency
|
|
85
|
+
- Does terminology match related documentation pages? (Read the related docs to verify.)
|
|
86
|
+
- Are there contradictions with other pages?
|
|
87
|
+
- Is the tone consistent with the rest of the documentation?
|
|
88
|
+
|
|
89
|
+
## Severity Rubric
|
|
90
|
+
|
|
91
|
+
Rate each finding:
|
|
92
|
+
|
|
93
|
+
- **HIGH**: Factually wrong, misleading, or missing critical content. A reader following this doc would be confused or write buggy code.
|
|
94
|
+
- **MEDIUM**: Incomplete, unclear, or inconsistent. A reader could figure it out but shouldn't have to.
|
|
95
|
+
- **LOW**: Stylistic nit or minor improvement. A reader wouldn't notice.
|
|
96
|
+
|
|
97
|
+
## Report Format
|
|
98
|
+
|
|
99
|
+
You MUST structure your response exactly like this:
|
|
100
|
+
|
|
101
|
+
## Docs Critic Report: <filename>
|
|
102
|
+
|
|
103
|
+
### Summary
|
|
104
|
+
<1-2 sentence overall assessment>
|
|
105
|
+
|
|
106
|
+
### Findings
|
|
107
|
+
|
|
108
|
+
#### [HIGH/dimension] <title>
|
|
109
|
+
**Location:** <section name or line range>
|
|
110
|
+
**Issue:** <what's wrong>
|
|
111
|
+
**Evidence:** <quote from source code or related doc that proves it>
|
|
112
|
+
**Suggested fix:** <concrete suggestion>
|
|
113
|
+
|
|
114
|
+
#### [MEDIUM/dimension] <title>
|
|
115
|
+
**Location:** <section name or line range>
|
|
116
|
+
**Issue:** <what's wrong>
|
|
117
|
+
**Evidence:** <supporting evidence>
|
|
118
|
+
**Suggested fix:** <concrete suggestion>
|
|
119
|
+
|
|
120
|
+
#### [LOW/dimension] <title>
|
|
121
|
+
**Location:** <section name or line range>
|
|
122
|
+
**Issue:** <what's wrong>
|
|
123
|
+
**Suggested fix:** <concrete suggestion>
|
|
124
|
+
|
|
125
|
+
### Verdict
|
|
126
|
+
<APPROVED | ITERATE — N high, M medium issues remain>
|
|
127
|
+
|
|
128
|
+
## Rules
|
|
129
|
+
|
|
130
|
+
- Always read the source files before making accuracy claims. Never guess.
|
|
131
|
+
- Always read related docs before making consistency claims.
|
|
132
|
+
- If you find no issues, return APPROVED with an empty Findings section.
|
|
133
|
+
- Never suggest fixes that require information you don't have.
|
|
134
|
+
- Never modify any files. You are read-only.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- [ ] **Step 3: Verify the file was created correctly**
|
|
138
|
+
|
|
139
|
+
Run: `head -5 /home/milad/sources/scala/zio-blocks-new/.claude/agents/docs-critic.md`
|
|
140
|
+
Expected: The YAML frontmatter starting with `---` and `name: docs-critic`
|
|
141
|
+
|
|
142
|
+
- [ ] **Step 4: Commit**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
git add .claude/agents/docs-critic.md
|
|
146
|
+
git commit -m "feat: add docs-critic agent definition
|
|
147
|
+
|
|
148
|
+
Read-only agent that reviews documentation for content quality,
|
|
149
|
+
technical accuracy, completeness, and consistency. Returns structured
|
|
150
|
+
reports with severity-rated findings."
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Task 2: Create the Orchestrating Skill
|
|
156
|
+
|
|
157
|
+
**Files:**
|
|
158
|
+
- Create: `.claude/skills/docs-critique/SKILL.md`
|
|
159
|
+
|
|
160
|
+
- [ ] **Step 1: Create the skill directory**
|
|
161
|
+
|
|
162
|
+
Run: `mkdir -p /home/milad/sources/scala/zio-blocks-new/.claude/skills/docs-critique`
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 2: Write the orchestrating skill**
|
|
165
|
+
|
|
166
|
+
Create `.claude/skills/docs-critique/SKILL.md` with this exact content:
|
|
167
|
+
|
|
168
|
+
````markdown
|
|
169
|
+
---
|
|
170
|
+
name: docs-critique
|
|
171
|
+
description: >
|
|
172
|
+
Run a documentation creation skill with automatic maker-critic review loop.
|
|
173
|
+
Spawns a maker agent to run the skill, then a critic agent to review the output.
|
|
174
|
+
The maker receives critique and fixes its own work. Iterates until approved or
|
|
175
|
+
max 3 rounds. Pure coordinator — never edits files itself.
|
|
176
|
+
argument-hint: "<skill-name> <skill-args>"
|
|
177
|
+
allowed-tools: Agent, Glob, Grep, Read, SendMessage
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
# Documentation Critique Loop
|
|
181
|
+
|
|
182
|
+
## Arguments
|
|
183
|
+
|
|
184
|
+
1. **skill-name** — The documentation skill to run (e.g., `docs-data-type-ref`, `docs-how-to-guide`, `docs-tutorial`, `docs-document-pr`, `docs-enrich-section`, `docs-add-missing-section`)
|
|
185
|
+
2. **skill-args** — Arguments to pass to the skill (e.g., `Schema`, `TypeId`)
|
|
186
|
+
|
|
187
|
+
Example invocation: `/docs-critique docs-data-type-ref Schema`
|
|
188
|
+
|
|
189
|
+
## Role
|
|
190
|
+
|
|
191
|
+
You are a **pure coordinator**. You NEVER read, write, or edit documentation files yourself. You ONLY:
|
|
192
|
+
1. Spawn agents
|
|
193
|
+
2. Pass messages between agents
|
|
194
|
+
3. Parse critic reports to decide next action
|
|
195
|
+
4. Report final status to the user
|
|
196
|
+
|
|
197
|
+
## Phase 1: Spawn Maker Agent
|
|
198
|
+
|
|
199
|
+
Spawn a general-purpose agent via the `Agent` tool:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
Agent(
|
|
203
|
+
description: "Run doc creation skill",
|
|
204
|
+
prompt: "Run /<skill-name> <skill-args>. Complete all steps of the skill.
|
|
205
|
+
When done, report the absolute path of the generated/modified
|
|
206
|
+
documentation file as the LAST line of your response, in the format:
|
|
207
|
+
DOC_PATH: <absolute-path>"
|
|
208
|
+
)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Parse the maker's response to extract the doc file path from the `DOC_PATH:` line.
|
|
212
|
+
|
|
213
|
+
**Error handling:** If the maker does not return a `DOC_PATH:` line, ask the user which file was generated and use that path.
|
|
214
|
+
|
|
215
|
+
Save the maker's agent ID for later `SendMessage` calls. The `Agent` tool returns an `agentId` in its result — store this value. You will use it as the `to` field in `SendMessage` to route critique back to the maker.
|
|
216
|
+
|
|
217
|
+
## Phase 2: Gather Critic Context
|
|
218
|
+
|
|
219
|
+
Using the doc file path from Phase 1, gather context for the critic. You MAY use `Glob` and `Grep` for this phase only — this is the one exception to the "never read files" rule, because you need file paths (not content) to pass to the critic.
|
|
220
|
+
|
|
221
|
+
1. **Source files** — Extract the type name from the doc path (e.g., `docs/reference/schema.md` → `Schema`). Find source files:
|
|
222
|
+
```
|
|
223
|
+
Glob: **/<TypeName>.scala
|
|
224
|
+
Grep: "class <TypeName>" or "trait <TypeName>" or "object <TypeName>"
|
|
225
|
+
```
|
|
226
|
+
Also find test files:
|
|
227
|
+
```
|
|
228
|
+
Glob: **/<TypeName>Spec.scala or **/<TypeName>Test.scala
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
2. **Related docs** — Find sibling pages using two methods:
|
|
232
|
+
- **sidebars.js** (preferred): Read `sidebars.js` and find the array containing the doc's ID. Extract sibling page IDs from the same array. Map IDs to file paths.
|
|
233
|
+
- **Fallback glob** (if sidebars.js parsing fails): Glob the parent directory:
|
|
234
|
+
```
|
|
235
|
+
Glob: docs/reference/*.md (for reference pages)
|
|
236
|
+
Glob: docs/guides/*.md (for guides)
|
|
237
|
+
Glob: docs/tutorials/*.md (for tutorials)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
3. Collect all found paths into two lists: `source_files` and `related_docs`.
|
|
241
|
+
|
|
242
|
+
## Phase 3: Spawn Critic Agent
|
|
243
|
+
|
|
244
|
+
Spawn the `docs-critic` agent:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
Agent(
|
|
248
|
+
description: "Review documentation",
|
|
249
|
+
subagent_type: "docs-critic",
|
|
250
|
+
prompt: "Review the following documentation file for content quality,
|
|
251
|
+
technical accuracy, completeness, and consistency.
|
|
252
|
+
|
|
253
|
+
Documentation file: <doc-path>
|
|
254
|
+
|
|
255
|
+
Source files to check accuracy against:
|
|
256
|
+
<list of source_files, one per line>
|
|
257
|
+
|
|
258
|
+
Related documentation to check consistency against:
|
|
259
|
+
<list of related_docs, one per line>
|
|
260
|
+
|
|
261
|
+
Read each file yourself using the Read tool. Return your
|
|
262
|
+
structured report."
|
|
263
|
+
)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Error handling:** If the critic's response does not contain a `### Findings` section or a `### Verdict` line, treat it as an agent failure. Retry by spawning a fresh critic with the same prompt. If the second attempt also fails, report the raw response to the user and stop.
|
|
267
|
+
|
|
268
|
+
## Phase 4: Triage
|
|
269
|
+
|
|
270
|
+
Parse the critic's `### Verdict` line:
|
|
271
|
+
|
|
272
|
+
- **`APPROVED`** → Report success to user. Done.
|
|
273
|
+
- **`ITERATE`** with HIGH or MEDIUM findings → Enter Phase 5.
|
|
274
|
+
- Only LOW findings → Send LOWs to maker for a single-pass fix:
|
|
275
|
+
```
|
|
276
|
+
SendMessage(
|
|
277
|
+
to: <maker-agent-id>,
|
|
278
|
+
message: "The documentation critic found minor issues. Fix them if easy,
|
|
279
|
+
skip if not. One commit per fix.
|
|
280
|
+
Commit format: docs(<file-stem>): fix LOW/<dimension> — <description>
|
|
281
|
+
|
|
282
|
+
<paste LOW findings here>"
|
|
283
|
+
)
|
|
284
|
+
```
|
|
285
|
+
Done after maker responds.
|
|
286
|
+
|
|
287
|
+
## Phase 5: Fix Loop
|
|
288
|
+
|
|
289
|
+
**Maximum 3 rounds.** Track the current round number.
|
|
290
|
+
|
|
291
|
+
**Severity-based iteration rules:**
|
|
292
|
+
- **HIGH** findings: iterate until fixed (up to round 3)
|
|
293
|
+
- **MEDIUM** findings: iterate at most once — if a MEDIUM finding persists after round 1, do not iterate further for it
|
|
294
|
+
- After round 1, only HIGH findings drive further iteration
|
|
295
|
+
|
|
296
|
+
### Each Round:
|
|
297
|
+
|
|
298
|
+
**Step A — Send critique to maker:**
|
|
299
|
+
|
|
300
|
+
For round 1, send all HIGH and MEDIUM findings:
|
|
301
|
+
```
|
|
302
|
+
SendMessage(
|
|
303
|
+
to: <maker-agent-id>,
|
|
304
|
+
message: "The documentation critic found issues that need fixing.
|
|
305
|
+
Fix ALL HIGH and MEDIUM findings below. For each fix:
|
|
306
|
+
- Make a separate git commit
|
|
307
|
+
- Commit format: docs(<file-stem>): fix <SEVERITY>/<dimension> — <description>
|
|
308
|
+
- If multiple findings target the same paragraph, combine into one commit
|
|
309
|
+
using the highest severity level
|
|
310
|
+
|
|
311
|
+
<paste HIGH and MEDIUM findings here>"
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
For rounds 2+, send only HIGH findings (MEDIUM issues have had their one iteration).
|
|
316
|
+
|
|
317
|
+
Wait for the maker to respond confirming fixes are done.
|
|
318
|
+
|
|
319
|
+
**Step B — Spawn fresh critic:**
|
|
320
|
+
|
|
321
|
+
Spawn a NEW `docs-critic` agent (do NOT reuse the previous one — fresh eyes each round):
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
Agent(
|
|
325
|
+
description: "Re-review documentation round N",
|
|
326
|
+
subagent_type: "docs-critic",
|
|
327
|
+
prompt: <same prompt as Phase 3, identical>
|
|
328
|
+
)
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Step C — Check verdict:**
|
|
332
|
+
|
|
333
|
+
- `APPROVED` → Report success to user. Done.
|
|
334
|
+
- `ITERATE` with only MEDIUM findings remaining (no HIGH) → Done. MEDIUM had its one iteration.
|
|
335
|
+
- `ITERATE` with HIGH findings and round < 3 → Go to next round.
|
|
336
|
+
- `ITERATE` and round = 3 → Report remaining issues to user:
|
|
337
|
+
"The documentation was reviewed 3 times. These issues remain unresolved:
|
|
338
|
+
<paste remaining findings>
|
|
339
|
+
Please review manually."
|
|
340
|
+
|
|
341
|
+
## Output
|
|
342
|
+
|
|
343
|
+
When done, report to the user:
|
|
344
|
+
- Whether the doc was APPROVED or has remaining issues
|
|
345
|
+
- How many rounds were needed
|
|
346
|
+
- Summary of findings fixed (count by severity)
|
|
347
|
+
````
|
|
348
|
+
|
|
349
|
+
- [ ] **Step 3: Verify the file was created correctly**
|
|
350
|
+
|
|
351
|
+
Run: `head -10 /home/milad/sources/scala/zio-blocks-new/.claude/skills/docs-critique/SKILL.md`
|
|
352
|
+
Expected: The YAML frontmatter with `name: docs-critique`
|
|
353
|
+
|
|
354
|
+
- [ ] **Step 4: Commit**
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
git add .claude/skills/docs-critique/SKILL.md
|
|
358
|
+
git commit -m "feat: add docs-critique orchestrating skill
|
|
359
|
+
|
|
360
|
+
Pure coordinator that spawns a maker agent to run any doc creation
|
|
361
|
+
skill, then spawns a fresh critic agent each round to review. Passes
|
|
362
|
+
critique back to maker via SendMessage. Severity-gated iteration
|
|
363
|
+
with max 3 rounds."
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
### Task 3: Manual Smoke Test
|
|
369
|
+
|
|
370
|
+
**Files:**
|
|
371
|
+
- None (testing only)
|
|
372
|
+
|
|
373
|
+
- [ ] **Step 1: Verify the critic agent is recognized**
|
|
374
|
+
|
|
375
|
+
Run: `ls -la /home/milad/sources/scala/zio-blocks-new/.claude/agents/docs-critic.md`
|
|
376
|
+
Expected: File exists with correct permissions
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 2: Verify the skill is recognized**
|
|
379
|
+
|
|
380
|
+
Run: `ls -la /home/milad/sources/scala/zio-blocks-new/.claude/skills/docs-critique/SKILL.md`
|
|
381
|
+
Expected: File exists with correct permissions
|
|
382
|
+
|
|
383
|
+
- [ ] **Step 3: Test invocation with an existing doc**
|
|
384
|
+
|
|
385
|
+
In a new Claude Code session, run:
|
|
386
|
+
```
|
|
387
|
+
/docs-critique docs-data-type-ref TypeId
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Verify:
|
|
391
|
+
- The orchestrator spawns a maker agent that runs `/docs-data-type-ref TypeId`
|
|
392
|
+
- After the maker finishes, the orchestrator gathers source file and related doc paths
|
|
393
|
+
- The orchestrator spawns a `docs-critic` agent with the gathered context
|
|
394
|
+
- The critic returns a structured report with `### Findings` and `### Verdict`
|
|
395
|
+
- If ITERATE, the orchestrator sends findings to the maker via SendMessage
|
|
396
|
+
- The maker fixes issues and commits
|
|
397
|
+
- The loop repeats until APPROVED or 3 rounds
|
|
398
|
+
|
|
399
|
+
- [ ] **Step 4: Verify error handling — malformed critic report**
|
|
400
|
+
|
|
401
|
+
If the critic returns a malformed report (no `### Verdict`), verify:
|
|
402
|
+
- The orchestrator retries once
|
|
403
|
+
- If still malformed, it reports the raw response and stops
|
|
404
|
+
|
|
405
|
+
- [ ] **Step 5: Commit any fixes discovered during smoke test**
|
|
406
|
+
|
|
407
|
+
If any issues are found in the agent definition or skill during testing, fix them and commit each fix separately.
|