Skip to content

Add hidden block directive to validate partial code snippets in docs#600

Merged
patniko merged 1 commit intomainfrom
hidden-block-validation
Feb 27, 2026
Merged

Add hidden block directive to validate partial code snippets in docs#600
patniko merged 1 commit intomainfrom
hidden-block-validation

Conversation

@patniko
Copy link
Contributor

@patniko patniko commented Feb 27, 2026

Summary

Changes

New directive in extract.ts:

  • The next visible code block after the closing tag is auto-skipped (no manual skip needed)
  • Summary output now reports a Hidden count

Converted 4 samples in getting-started.md:

  • Python event subscription example
  • Go event subscription example
  • C# event subscription example
  • Go CLI server connection example

Impact

  • Validated code blocks: 182 → 186 (+4)
  • All 186 blocks pass validation locally (TS ✅ Python ✅ Go ✅ C# ✅)

Usage

\`\`\`go
package main
import copilot "github.com/github/copilot-sdk/go"
func main() {
    client := copilot.NewClient(nil)
}
\`\`\`

\`\`\`go
client := copilot.NewClient(nil)
\`\`\`

The hidden block compiles in CI; readers see only the clean snippet.

directive pair that lets authors include a full compilable code block
that is validated but not rendered in docs, while the visible snippet
that follows is automatically skipped from validation.

Convert 4 skipped samples in getting-started.md to use hidden blocks:
- Python event subscription example
- Go event subscription example
- C# event subscription example
- Go CLI server connection example

This increases validated code blocks from 182 to 186 and demonstrates
the hidden block pattern for future conversions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@patniko patniko requested a review from a team as a code owner February 27, 2026 05:43
Copilot AI review requested due to automatic review settings February 27, 2026 05:43
@patniko patniko merged commit 9d942d4 into main Feb 27, 2026
16 checks passed
@patniko patniko deleted the hidden-block-validation branch February 27, 2026 05:45
@patniko patniko changed the title feat(docs-validation): add hidden block directive and convert samples Add hidden block directive to validate partial code snippets in docs Feb 27, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new docs-validate: hidden directive to the docs-validation extractor so docs can include a “full, compilable” hidden block while keeping a shorter visible snippet, and updates getting-started.md to use this pattern in several sections.

Changes:

  • Introduce <!-- docs-validate: hidden --> ... <!-- /docs-validate: hidden --> handling in extract.ts, including reporting a Hidden count in extraction output.
  • Update validator CLI output to document the new hidden directive usage.
  • Convert 4 getting-started.md examples from skip to the new hidden+visible snippet pattern.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
scripts/docs-validation/validate.ts Documents the new docs-validate: hidden directive in validation failure help output.
scripts/docs-validation/extract.ts Implements hidden-block parsing, auto-skips the next visible code fence, and reports hidden-block counts.
docs/getting-started.md Converts several examples to hidden “full code” blocks with a visible snippet immediately after.
Comments suppressed due to low confidence (2)

scripts/docs-validation/extract.ts:117

  • skipNext is not reset when inHiddenBlock is true, which can cause skip state to leak across multiple code fences inside a hidden section (e.g., a single <!-- docs-validate: skip --> inside a hidden block would skip all subsequent hidden fences). It can also incorrectly skip the first fence of a following hidden section if hidden blocks appear back-to-back. Suggest restoring per-block reset of skipNext and using a dedicated flag for “skip next visible block after hidden closes”, or explicitly clearing skipNext on <!-- docs-validate: hidden -->.
      // Only reset skipNext when NOT in a hidden block — hidden blocks
      // can contain multiple code fences that all get validated.
      if (!inHiddenBlock) {
        skipNext = false;
      }

docs/getting-started.md:492

  • The hidden Go “full compilable code” example constructs a session via session := &copilot.Session{}. While this may compile, it isn’t a valid/representative way to obtain a usable session (sessions are created via client.CreateSession(...)). Since hidden blocks are meant to show the complete runnable version, consider creating a client, starting it, creating a session, and deferring session.Destroy()/client.Stop() in this block.
func main() {
	session := &copilot.Session{}

	// Subscribe to all events
	unsubscribe := session.On(func(event copilot.SessionEvent) {
		fmt.Println("Event:", event.Type)

Comment on lines +1341 to +1344
// Use the client normally
_, _ = client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the hidden Go external-server example, the session creation result and error are discarded (_, _ = client.CreateSession(...)). For a “complete” example, it should at least check the error and (if it returns a session) destroy it, otherwise readers may copy/paste a pattern that silently ignores failures/leaks resources.

This issue also appears on line 487 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines 67 to +85
// Check for validation directives
if (line.includes("<!-- docs-validate: skip -->")) {
skipNext = true;
continue;
}
if (line.includes("<!-- docs-validate: wrap-async -->")) {
wrapAsync = true;
continue;
}
if (line.includes("<!-- docs-validate: hidden -->")) {
inHiddenBlock = true;
continue;
}
if (line.includes("<!-- /docs-validate: hidden -->")) {
inHiddenBlock = false;
// Skip the next visible code block since the hidden one replaces it
skipNext = true;
continue;
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directive parsing runs even while inside a fenced code block. If a code snippet contains the string <!-- docs-validate: ... -->, it will toggle skip/hidden state mid-block and corrupt extraction. Consider only processing docs-validate directives when !inCodeBlock (or otherwise ensure directives are ignored while inside code fences).

This issue also appears on line 113 of the same file.

See below for a potential fix:

    // Check for validation directives (only when not inside a code block)
    if (!inCodeBlock) {
      if (line.includes("<!-- docs-validate: skip -->")) {
        skipNext = true;
        continue;
      }
      if (line.includes("<!-- docs-validate: wrap-async -->")) {
        wrapAsync = true;
        continue;
      }
      if (line.includes("<!-- docs-validate: hidden -->")) {
        inHiddenBlock = true;
        continue;
      }
      if (line.includes("<!-- /docs-validate: hidden -->")) {
        inHiddenBlock = false;
        // Skip the next visible code block since the hidden one replaces it
        skipNext = true;
        continue;
      }
    }

Copilot uses AI. Check for mistakes.
Comment on lines +431 to +451
from copilot import CopilotClient
from copilot.generated.session_events import SessionEvent, SessionEventType

client = CopilotClient()

session = client.create_session({"on_permission_request": lambda req, inv: {"kind": "approved"}})

# Subscribe to all events
unsubscribe = session.on(lambda event: print(f"Event: {event.type}"))

# Filter by event type in your handler
def handle_event(event: SessionEvent) -> None:
if event.type == SessionEventType.SESSION_IDLE:
print("Session is idle")
elif event.type == SessionEventType.ASSISTANT_MESSAGE:
print(f"Message: {event.data.content}")

unsubscribe = session.on(handle_event)

# Later, to unsubscribe:
unsubscribe()
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hidden “full compilable code” Python example calls client.create_session(...) without await, but CopilotClient.create_session is async in the SDK. To keep the hidden block accurate and actually runnable, wrap it in an async main() and use await client.start(), session = await client.create_session(...), and await client.stop() (similar to earlier Python samples in this doc).

Suggested change
from copilot import CopilotClient
from copilot.generated.session_events import SessionEvent, SessionEventType
client = CopilotClient()
session = client.create_session({"on_permission_request": lambda req, inv: {"kind": "approved"}})
# Subscribe to all events
unsubscribe = session.on(lambda event: print(f"Event: {event.type}"))
# Filter by event type in your handler
def handle_event(event: SessionEvent) -> None:
if event.type == SessionEventType.SESSION_IDLE:
print("Session is idle")
elif event.type == SessionEventType.ASSISTANT_MESSAGE:
print(f"Message: {event.data.content}")
unsubscribe = session.on(handle_event)
# Later, to unsubscribe:
unsubscribe()
import asyncio
from copilot import CopilotClient
from copilot.generated.session_events import SessionEvent, SessionEventType
async def main() -> None:
client = CopilotClient()
await client.start()
session = await client.create_session(
{"on_permission_request": lambda req, inv: {"kind": "approved"}}
)
# Subscribe to all events
unsubscribe = session.on(lambda event: print(f"Event: {event.type}"))
# Filter by event type in your handler
def handle_event(event: SessionEvent) -> None:
if event.type == SessionEventType.SESSION_IDLE:
print("Session is idle")
elif event.type == SessionEventType.ASSISTANT_MESSAGE:
print(f"Message: {event.data.content}")
unsubscribe = session.on(handle_event)
# Later, to unsubscribe:
unsubscribe()
await client.stop()
if __name__ == "__main__":
asyncio.run(main())

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants