Skip to content

Add CI lint for Python code examples#364

Merged
MuncleUscles merged 1 commit intomainfrom
ci/lint-code-examples
Mar 12, 2026
Merged

Add CI lint for Python code examples#364
MuncleUscles merged 1 commit intomainfrom
ci/lint-code-examples

Conversation

@MuncleUscles
Copy link
Member

@MuncleUscles MuncleUscles commented Mar 12, 2026

Summary

  • New lint script (scripts/lint-code-examples.py): extracts Python code blocks from MDX files and validates them — checks runner hash pinning, deprecated API usage, AST lint via genvm-lint, and syntax correctness for GenLayer snippets
  • Fix deprecated APIs across 20 MDX files: migrated gl.get_webpage, gl.eq_principle_strict_eq, gl.eq_principle_prompt_comparative, gl.eq_principle_prompt_non_comparative, and gl.exec_prompt to their current replacements
  • Pin runner hashes: replaced all "test" runner hashes with specific version hashes
  • CI workflow (.github/workflows/lint-code-examples.yml): runs on PRs and pushes to main when pages/**/*.mdx or the lint script change; uses Python 3.12 + genvm-linter from PyPI

Test plan

  • CI workflow triggers and passes on this PR
  • Verify deprecated APIs are fully replaced (no gl.get_webpage, gl.eq_principle_strict_eq, etc. remain)
  • Confirm all code examples have pinned runner hashes (no "test" or "latest")
  • Run python scripts/lint-code-examples.py locally to validate

Summary by CodeRabbit

  • Documentation

    • Updated code examples across intelligent contracts documentation to reflect current API methods for web requests and deterministic equality checks.
    • Simplified validator setup configuration by removing unnecessary rollup URL fields.
  • Chores

    • Added automated linting workflow for code examples to ensure consistency and correctness across documentation samples.

- Add lint-code-examples.py script that extracts Python blocks from MDX
  files and validates them: runner hash pinning, deprecated API detection,
  AST lint via genvm-lint, and syntax checks for snippets
- Add CI workflow (lint-code-examples.yml) triggered on PRs and pushes
  to main when pages/ or the lint script change
- Fix deprecated APIs across 20 MDX files: gl.get_webpage -> gl.nondet.web.get,
  gl.eq_principle_strict_eq -> gl.eq_principle.strict_eq,
  gl.eq_principle_prompt_comparative -> gl.eq_principle.prompt_comparative,
  gl.eq_principle_prompt_non_comparative -> gl.eq_principle.prompt_non_comparative,
  gl.exec_prompt -> gl.nondet.exec_prompt
- Pin all runner hashes from "test" to specific version
@vercel
Copy link
Contributor

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
genlayer-docs Ready Ready Preview, Comment Mar 12, 2026 4:05pm

Request Review

@netlify
Copy link

netlify bot commented Mar 12, 2026

Deploy Preview for genlayer-docs ready!

Name Link
🔨 Latest commit ca1f27d
🔍 Latest deploy log https://app.netlify.com/projects/genlayer-docs/deploys/69b2e40770959a0008b78d0d
😎 Deploy Preview https://deploy-preview-364--genlayer-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

This PR updates documentation examples to adopt the latest GenLayer API naming conventions, pins specific dependency versions across code samples, adds GitHub Actions CI workflow for linting code examples, and introduces a comprehensive Python linting script that validates code blocks within MDX documentation for correctness and API compliance.

Changes

Cohort / File(s) Summary
Linting Infrastructure
.github/workflows/lint-code-examples.yml, scripts/lint-code-examples.py
New GitHub Actions workflow that triggers on MDX and script changes; new Python script extracting Python blocks from MDX, validating runner versions, checking deprecated APIs, linting complete contracts, and performing syntax checks on code snippets.
GenLayer API Migrations
pages/developers/intelligent-contracts/*.mdx, pages/developers/intelligent-contracts/examples/*.mdx, pages/developers/intelligent-contracts/features/*.mdx
Updated code examples to use new GenLayer API naming: gl.nondet.exec_prompt (was gl.exec_prompt), gl.nondet.web.get (was gl.get_webpage), gl.nondet.web.render (was gl.get_webpage with mode), gl.eq_principle.strict_eq (was gl.eq_principle_strict_eq), and gl.eq_principle.prompt_non_comparative (was gl.eq_principle_prompt_non_comparative).
Dependency Version Pinning
pages/api-references/genlayer-test.mdx, pages/developers/intelligent-contracts/examples/...*, pages/developers/intelligent-contracts/features/....*, pages/developers/intelligent-contracts/first-contract.mdx, pages/developers/intelligent-contracts/introduction.mdx, pages/developers/intelligent-contracts/storage.mdx
Updated Python dependency annotations from generic placeholders (py-genlayer:test, py-genlayer:latest) to specific versioned hashes (py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6) across 13 documentation files.
Data Structure Enhancements
pages/developers/intelligent-contracts/examples/vector-store-log-indexer.mdx, pages/developers/intelligent-contracts/features/vector-storage.mdx
Added @allow_storage decorator to StoreValue dataclass and expanded field from log_id to include text: str in vector store examples.
Validator Configuration Cleanup
pages/validators/setup-guide.mdx
Removed genlayerchainrpcurl and genlayerchainwebsocketurl configuration fields from testnet examples, simplifying rollup configuration blocks.

Sequence Diagram

sequenceDiagram
    participant GH as GitHub Actions
    participant WF as lint-code-examples.yml
    participant PY as lint-code-examples.py
    participant MDX as MDX Files
    participant Linter as genvm-lint
    
    GH->>WF: trigger on PR/push
    WF->>WF: checkout code
    WF->>WF: setup Python 3.12
    WF->>WF: install genvm-linter
    WF->>PY: execute script
    
    PY->>MDX: extract_python_blocks()
    MDX-->>PY: block metadata (code, type, depends)
    
    PY->>PY: check_runner_versions()
    PY->>PY: check_deprecated_apis()
    
    PY->>PY: identify complete contracts
    PY->>Linter: lint_contract() via genvm-lint
    Linter-->>PY: lint results (JSON)
    
    PY->>PY: classify_snippet()
    PY->>PY: syntax_check() via AST
    
    PY->>PY: wrap_snippet() if needed
    PY->>Linter: lint_snippet() via genvm-lint
    Linter-->>PY: lint results
    
    PY-->>GH: aggregated results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Possibly related PRs

  • PR #328: Modifies pages/validators/setup-guide.mdx with overlapping configuration key removals for rollup URLs.
  • PR #317: Includes documentation updates migrating to the same GenLayer API naming patterns (gl.nondet.web.* and gl.eq_principle.strict_eq).

Suggested reviewers

  • cristiam86
  • rasca
  • dohernandez

Poem

🐰 Hoppy linting hops along the way,
New APIs shine with nondet today,
Hash-pinned versions stand so tall,
A script to lint them one and all!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description is comprehensive, covering the new lint script, deprecated API fixes, runner hash pinning, and CI workflow. However, it does not follow the Conventional Commits format specified in the template. Update the PR title to follow Conventional Commits format (e.g., 'ci(lint): add Python code examples linting workflow and script') as specified in the repository template.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add CI lint for Python code examples' clearly and specifically describes the main change: introducing a CI linting system for Python code embedded in documentation.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ci/lint-code-examples
📝 Coding Plan for PR comments
  • Generate coding plan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pages/developers/intelligent-contracts/features/vector-storage.mdx (1)

40-65: ⚠️ Potential issue | 🟠 Major

Add the missing import typing to this snippet.

This example uses typing.Literal on lines 55 and 65, but typing is never imported. In Python 3.12+, this causes a NameError at definition time, making the docs example non-executable as written.

Proposed fix
 from genlayer import *
 import genlayermodelwrappers
 import numpy as np
 from dataclasses import dataclass
+import typing
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/features/vector-storage.mdx` around
lines 40 - 65, The snippet uses typing.Literal in the StoreValue dataclass and
LogIndexer (VecDB and get_embedding signatures) but never imports typing; add an
import typing at the top of the file so typing.Literal is defined (ensuring
references in StoreValue, LogIndexer.vector_store, and LogIndexer.get_embedding
resolve correctly).
🧹 Nitpick comments (2)
pages/developers/intelligent-contracts/examples/user-storage.mdx (1)

38-38: Documentation references outdated API name.

The prose mentions contract_runner.from_address but the code uses gl.message.sender_address. Since this PR is updating deprecated APIs across documentation, consider fixing these references for consistency.

📝 Suggested documentation fix

Line 38:

-- **Write Method**: `update_storage(new_storage)` allows updating the stored value for the user who called the contract (identified by `contract_runner.from_address`).
+- **Write Method**: `update_storage(new_storage)` allows updating the stored value for the user who called the contract (identified by `gl.message.sender_address`).

Line 61:

-- The contract updates the `self.storage` dictionary, associating the new value with the address of the user who called the function (`contract_runner.from_address`).
+- The contract updates the `self.storage` dictionary, associating the new value with the address of the user who called the function (`gl.message.sender_address`).

Also applies to: 61-61

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/examples/user-storage.mdx` at line 38,
Update the documentation prose to use the current API name: replace references
to contract_runner.from_address with gl.message.sender_address in the
description of the Write Method and any other occurrences (e.g., near the
mention of update_storage(new_storage)) so the docs match the code that uses
gl.message.sender_address.
.github/workflows/lint-code-examples.yml (1)

22-23: Pin the linter version to prevent unexpected CI failures.

The workflow installs an unpinned genvm-linter package (line 22) and relies on the genvm-lint CLI command, which is called at line 123 in scripts/lint-code-examples.py. If a new release changes the CLI interface or removes the console script, CI will fail without any changes to the repository code.

The genvm-linter package does export a genvm-lint console script. Pin a tested version in the workflow (e.g., pip install genvm-linter==0.8.0).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/lint-code-examples.yml around lines 22 - 23, Pin the
genvm-linter package in the workflow by changing the pip install step that
currently installs "genvm-linter" to install a specific tested version (for
example "genvm-linter==0.8.0"); update the run step that installs genvm-linter
so CI uses the pinned version, ensuring the subsequent use of the genvm-lint
console script (invoked by scripts/lint-code-examples.py) remains stable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/lint-code-examples.yml:
- Around line 4-12: Add the workflow file itself to the path filters so changes
to .github/workflows/lint-code-examples.yml will trigger the job; specifically
update the pull_request.paths and push.paths lists in the workflow to include
'.github/workflows/lint-code-examples.yml' alongside 'pages/**/*.mdx' and
'scripts/lint-code-examples.py' (edit the paths section around the existing
entries in the lint-code-examples job).

In `@pages/developers/intelligent-contracts/crafting-prompts.mdx`:
- Around line 58-60: The output from gl.nondet.exec_prompt stored in res must
have only the surrounding code-fence removed before calling json.loads; update
the handling around res (after the call to gl.nondet.exec_prompt and before
json.loads) to robustly strip a leading fence line (any line that starts with
"```" optionally followed by a language tag, case-insensitive, and optional
spaces) and a trailing fence line (a line that after stripping whitespace equals
"```"), by splitting res into lines, dropping the first line if it startswith
"```" (case-insensitive check) and dropping the last line if its stripped form
is "```", then rejoin and strip the result before passing to json.loads; keep
references to res, gl.nondet.exec_prompt, and json.loads so the change is easy
to locate.

In `@pages/developers/intelligent-contracts/examples/fetch-github-profile.mdx`:
- Around line 39-46: Update the page to complete the terminology change from the
"comparative equivalence principle" to the current API name
gl.eq_principle.strict_eq(): replace any text mentions of the old phrase with
gl.eq_principle.strict_eq() and adjust surrounding wording to match the
deterministic-equivalence framing used elsewhere; remove the stale mention of
the removed parameter mode="text" (and any example calls to gl.nondet.web.get()
that include it) and ensure examples and the show_github_profile() read method
description consistently reference gl.nondet.web.get() +
gl.eq_principle.strict_eq() semantics only.

In `@pages/developers/intelligent-contracts/examples/fetch-web-content.mdx`:
- Line 105: Update the docs to state that mode parameters apply only to
gl.nondet.web.render(), not gl.nondet.web.get(); clarify that
gl.nondet.web.get() returns plain text by default and remove the misleading
reference to mode="text" for the get() examples, and make both occurrences
describing mode="html" (the ones referencing mode="html" and the "full HTML
document") consistent by saying mode="html" for gl.nondet.web.render() returns
the page's HTML <body> (not the entire document or other scope).

In `@pages/developers/intelligent-contracts/examples/prediction.mdx`:
- Around line 52-53: Replace the raw HTTP fetch with the LLM-friendly renderer:
instead of calling gl.nondet.web.get(self.resolution_url) and decoding bytes
into web_data, call gl.nondet.web.render(self.resolution_url, mode='text') and
assign its returned text directly to web_data so the prompt receives plain-text
rendered content; update any references to response to use web_data and remove
the .body.decode(...) flow.

In `@pages/developers/intelligent-contracts/first-contract.mdx`:
- Around line 10-12: Update the explanatory sentence so it matches the code
example that pins a specific release hash: replace the phrase saying "`test`
after a colon is a version" with wording that the value after the colon (e.g.
"py-genlayer:1jb45aa8...") is a version hash identifying a specific frozen
GenVM/GenLayer release—e.g., "It is similar to Solidity's `pragma solidity`, and
the value after the colon is a version hash that identifies a specific frozen
GenVM release."

In `@scripts/lint-code-examples.py`:
- Around line 309-310: The check `"/_" in mdx_file.name` never matches because
Path.name never contains slashes; update the condition to use
mdx_file.name.startswith("_") (and keep the existing "/_advanced/" in
str(mdx_file) check) so it skips underscore-prefixed files: change the
conditional to `if "/_advanced/" in str(mdx_file) or
mdx_file.name.startswith("_"): continue`, and apply this same fix to the other
two occurrences of the same pattern in the file.
- Around line 159-175: classify_snippet() currently examines the first nonblank
line only, which misclassifies snippets with leading imports or comments; update
the logic that builds stripped/lines/first_line to first skip any leading blank
lines, import statements (lines starting with "import" or "from "), and comment
lines (starting with "#" or triple-quote blocks) so that first_line and the
subsequent scans examine the first meaningful line; keep the existing checks for
decorator prefixes ("@gl.public", "@gl.evm", "@") and for "def " with "(self"
unchanged, and ensure the detection loop that looks for any "def " in lines also
operates on the trimmed list of meaningful lines so wrap_snippet() no longer
wraps class-method examples incorrectly.

---

Outside diff comments:
In `@pages/developers/intelligent-contracts/features/vector-storage.mdx`:
- Around line 40-65: The snippet uses typing.Literal in the StoreValue dataclass
and LogIndexer (VecDB and get_embedding signatures) but never imports typing;
add an import typing at the top of the file so typing.Literal is defined
(ensuring references in StoreValue, LogIndexer.vector_store, and
LogIndexer.get_embedding resolve correctly).

---

Nitpick comments:
In @.github/workflows/lint-code-examples.yml:
- Around line 22-23: Pin the genvm-linter package in the workflow by changing
the pip install step that currently installs "genvm-linter" to install a
specific tested version (for example "genvm-linter==0.8.0"); update the run step
that installs genvm-linter so CI uses the pinned version, ensuring the
subsequent use of the genvm-lint console script (invoked by
scripts/lint-code-examples.py) remains stable.

In `@pages/developers/intelligent-contracts/examples/user-storage.mdx`:
- Line 38: Update the documentation prose to use the current API name: replace
references to contract_runner.from_address with gl.message.sender_address in the
description of the Write Method and any other occurrences (e.g., near the
mention of update_storage(new_storage)) so the docs match the code that uses
gl.message.sender_address.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1b674ddc-2347-41c7-8e9b-cd18e16c5a17

📥 Commits

Reviewing files that changed from the base of the PR and between 831e034 and ca1f27d.

📒 Files selected for processing (23)
  • .github/workflows/lint-code-examples.yml
  • pages/api-references/genlayer-test.mdx
  • pages/developers/intelligent-contracts/crafting-prompts.mdx
  • pages/developers/intelligent-contracts/examples/fetch-github-profile.mdx
  • pages/developers/intelligent-contracts/examples/fetch-web-content.mdx
  • pages/developers/intelligent-contracts/examples/github-profile-projects.mdx
  • pages/developers/intelligent-contracts/examples/github-profile-summary.mdx
  • pages/developers/intelligent-contracts/examples/llm-hello-world-non-comparative.mdx
  • pages/developers/intelligent-contracts/examples/llm-hello-world.mdx
  • pages/developers/intelligent-contracts/examples/prediction.mdx
  • pages/developers/intelligent-contracts/examples/storage.mdx
  • pages/developers/intelligent-contracts/examples/user-storage.mdx
  • pages/developers/intelligent-contracts/examples/vector-store-log-indexer.mdx
  • pages/developers/intelligent-contracts/examples/wizard-of-coin.mdx
  • pages/developers/intelligent-contracts/features/debugging.mdx
  • pages/developers/intelligent-contracts/features/upgradability.mdx
  • pages/developers/intelligent-contracts/features/vector-storage.mdx
  • pages/developers/intelligent-contracts/first-contract.mdx
  • pages/developers/intelligent-contracts/first-intelligent-contract.mdx
  • pages/developers/intelligent-contracts/introduction.mdx
  • pages/developers/intelligent-contracts/storage.mdx
  • pages/validators/setup-guide.mdx
  • scripts/lint-code-examples.py
💤 Files with no reviewable changes (1)
  • pages/validators/setup-guide.mdx

Comment on lines +4 to +12
pull_request:
paths:
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
push:
branches: [main]
paths:
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Include this workflow file in its own path filters.

A PR that only changes .github/workflows/lint-code-examples.yml will not run this job today, so workflow regressions can merge without ever exercising the updated definition.

Suggested fix
 on:
   pull_request:
     paths:
+      - '.github/workflows/lint-code-examples.yml'
       - 'pages/**/*.mdx'
       - 'scripts/lint-code-examples.py'
   push:
     branches: [main]
     paths:
+      - '.github/workflows/lint-code-examples.yml'
       - 'pages/**/*.mdx'
       - 'scripts/lint-code-examples.py'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pull_request:
paths:
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
push:
branches: [main]
paths:
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
on:
pull_request:
paths:
- '.github/workflows/lint-code-examples.yml'
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
push:
branches: [main]
paths:
- '.github/workflows/lint-code-examples.yml'
- 'pages/**/*.mdx'
- 'scripts/lint-code-examples.py'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/lint-code-examples.yml around lines 4 - 12, Add the
workflow file itself to the path filters so changes to
.github/workflows/lint-code-examples.yml will trigger the job; specifically
update the pull_request.paths and push.paths lists in the workflow to include
'.github/workflows/lint-code-examples.yml' alongside 'pages/**/*.mdx' and
'scripts/lint-code-examples.py' (edit the paths section around the existing
entries in the lint-code-examples job).

Comment on lines +58 to +60
res = gl.nondet.exec_prompt(prompt)
backticks = "``" + "`"
res = res.replace(backticks + "json", "").replace(backticks, "")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import json

def current(res: str) -> str:
    backticks = "``" + "`"
    return res.replace(backticks + "json", "").replace(backticks, "")

samples = {
    "lowercase json": "```json\n{\"give_coin\": false}\n```",
    "uppercase JSON": "```JSON\n{\"give_coin\": false}\n```",
    "space after fence": "``` json\n{\"give_coin\": false}\n```",
    "bare fence": "```\n{\"give_coin\": false}\n```",
}

for name, sample in samples.items():
    cleaned = current(sample)
    try:
        json.loads(cleaned)
        verdict = "parses"
    except Exception as exc:
        verdict = f"fails: {exc}"
    print(f"{name}: {verdict}")
PY

Repository: genlayerlabs/genlayer-docs

Length of output: 244


🏁 Script executed:

fd crafting-prompts.mdx --type f

Repository: genlayerlabs/genlayer-docs

Length of output: 129


🏁 Script executed:

cat -n pages/developers/intelligent-contracts/crafting-prompts.mdx | sed -n '50,70p'

Repository: genlayerlabs/genlayer-docs

Length of output: 953


Strip only surrounding code fences before json.loads.

The current implementation only handles the exact lowercase ```json case. Testing against common LLM output variants shows:

  • uppercase JSON: fails with Expecting value: line 1 column 1
  • space after fence (``` json): fails with Expecting value: line 1 column 2
  • bare fence: parses (only by accident)
  • lowercase json: parses

This will cause the documented example to fail at runtime on typical LLM outputs.

Proposed fix
         def nondet():
             res = gl.nondet.exec_prompt(prompt)
-            backticks = "``" + "`"
-            res = res.replace(backticks + "json", "").replace(backticks, "")
+            res = res.strip()
+            if res.startswith("```"):
+                lines = res.splitlines()
+                if lines:
+                    lines = lines[1:]
+                if lines and lines[-1].strip() == "```":
+                    lines = lines[:-1]
+                res = "\n".join(lines).strip()
             print(res)
             dat = json.loads(res)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/crafting-prompts.mdx` around lines 58
- 60, The output from gl.nondet.exec_prompt stored in res must have only the
surrounding code-fence removed before calling json.loads; update the handling
around res (after the call to gl.nondet.exec_prompt and before json.loads) to
robustly strip a leading fence line (any line that starts with "```" optionally
followed by a language tag, case-insensitive, and optional spaces) and a
trailing fence line (a line that after stripping whitespace equals "```"), by
splitting res into lines, dropping the first line if it startswith "```"
(case-insensitive check) and dropping the last line if its stripped form is
"```", then rejoin and strip the result before passing to json.loads; keep
references to res, gl.nondet.exec_prompt, and json.loads so the change is easy
to locate.

Comment on lines +39 to +46
- Uses `gl.eq_principle.strict_eq()` to ensure all nodes agree on the same profile content.
- **Read Method**:
- `show_github_profile()` returns the stored profile content.

## Key Components

1. **GitHub Integration**: The contract uses `gl.get_webpage()` to fetch content from GitHub profiles.
2. **Deterministic Execution**: `gl.eq_principle_strict_eq()` ensures that all nodes in the network arrive at the same exact content.
1. **GitHub Integration**: The contract uses `gl.nondet.web.get()` to fetch content from GitHub profiles.
2. **Deterministic Execution**: `gl.eq_principle.strict_eq()` ensures that all nodes in the network arrive at the same exact content.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Finish the terminology update across the whole page.

This section now documents gl.eq_principle.strict_eq(), but the page still has stale references to the old behavior: Line 3 says the example uses the comparative equivalence principle, and Line 95 still mentions the removed mode="text" parameter. That leaves the example internally inconsistent for readers.

🧰 Tools
🪛 LanguageTool

[style] ~46-~46: This phrase is redundant. Consider writing “same”.
Context: ... all nodes in the network arrive at the same exact content. 3. State Management: The c...

(SAME_EXACT)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/examples/fetch-github-profile.mdx`
around lines 39 - 46, Update the page to complete the terminology change from
the "comparative equivalence principle" to the current API name
gl.eq_principle.strict_eq(): replace any text mentions of the old phrase with
gl.eq_principle.strict_eq() and adjust surrounding wording to match the
deterministic-equivalence framing used elsewhere; remove the stale mention of
the removed parameter mode="text" (and any example calls to gl.nondet.web.get()
that include it) and ensure examples and the show_github_profile() read method
description consistently reference gl.nondet.web.get() +
gl.eq_principle.strict_eq() semantics only.

## HTML Mode for Web Content

The `gl.get_webpage()` function supports different modes for retrieving web content. While `mode="text"` returns the plain text content, `mode="html"` allows you to retrieve the complete HTML `<body>` of the webpage.
The `gl.nondet.web.get()` function supports different modes for retrieving web content. While the default mode returns the plain text content, `gl.nondet.web.render()` with `mode="html"` allows you to retrieve the complete HTML `<body>` of the webpage.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

According to the latest GenLayer documentation, does gl.nondet.web.render(..., mode="html")return the full HTML document or only the rendered, and is mode="text"still a supported parameter forgl.nondet.web.get()?

💡 Result:

  • gl.nondet.web.render(..., mode="html"): The latest GenLayer SDK docs describe this as returning the rendered webpage “HTML content” as a str, but they do not specify that it’s only the rendered <body> (vs. a full serialized HTML document). In the current SDK implementation, both mode="html" and mode="text" return a string from the same decoded text field, without any documented/visible “body-only” extraction step. [1], [2]

  • mode="text" on gl.nondet.web.get(): Nogl.nondet.web.get(url, headers=...) does not take a mode parameter in the latest SDK; it only returns a Response (status/headers/body). The "text"/"html" modes apply to gl.nondet.web.render(), not get(). [1], [2]

Sources:
[1] GenLayer SDK API docs (gl.nondet.web.get, gl.nondet.web.render) — https://sdk.genlayer.com/main/api/genlayer.html
[2] GenLayer SDK source for genlayer.gl.nondet.webhttps://sdk.genlayer.com/main/_modules/genlayer/gl/nondet/web.html


🏁 Script executed:

cat -n pages/developers/intelligent-contracts/examples/fetch-web-content.mdx | sed -n '85,135p'

Repository: genlayerlabs/genlayer-docs

Length of output: 2433


Fix conflicting API documentation about mode parameters and HTML output scope.

The file contains two contradictory statements:

  1. Line 92 mentions mode="text" as a parameter for the preceding examples, but according to GenLayer SDK documentation, mode parameters apply only to gl.nondet.web.render(), not gl.nondet.web.get(), making this note misleading.
  2. Line 105 states mode="html" returns the <body>, but Line 130 states it returns the "full HTML document"—these descriptions are incompatible.

Update Line 92 to accurately reflect which functions support mode parameters, and align Lines 105 and 130 to consistently describe what mode="html" actually returns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/examples/fetch-web-content.mdx` at
line 105, Update the docs to state that mode parameters apply only to
gl.nondet.web.render(), not gl.nondet.web.get(); clarify that
gl.nondet.web.get() returns plain text by default and remove the misleading
reference to mode="text" for the get() examples, and make both occurrences
describing mode="html" (the ones referencing mode="html" and the "full HTML
document") consistent by saying mode="html" for gl.nondet.web.render() returns
the page's HTML <body> (not the entire document or other scope).

Comment on lines +52 to +53
response = gl.nondet.web.get(self.resolution_url)
web_data = response.body.decode("utf-8")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In the current GenLayer Python API/docs, what does gl.nondet.web.get() return, and which API is recommended when an LLM prompt needs rendered or plain-text webpage content instead of raw HTML bytes?

💡 Result:

  • gl.nondet.web.get(url, *, headers={}) returns a gl.nondet.web.Response object with fields:

    • status: int
    • headers: dict[str, bytes]
    • body: bytes | None (i.e., the response payload is raw bytes, not a decoded string) [1]
  • If you need webpage content suitable for an LLM prompt (rendered page + plain text or HTML string), the recommended API is gl.nondet.web.render() (browser-like rendering), using:

    • mode='text' for plain-text content, or
    • mode='html' for rendered HTML as a str [1]
      (As opposed to gl.nondet.web.get(), which gives you raw bytes in Response.body.) [1]

Sources: [1]


Use gl.nondet.web.render() for LLM-suitable webpage content.

Lines 52-53 use gl.nondet.web.get(), which returns raw HTTP body bytes. For an LLM extraction prompt, use gl.nondet.web.render(mode='text') instead to feed plain-text rendered content rather than raw HTML noise:

Suggested change:
response = gl.nondet.web.render(self.resolution_url, mode='text')
web_data = response
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/examples/prediction.mdx` around lines
52 - 53, Replace the raw HTTP fetch with the LLM-friendly renderer: instead of
calling gl.nondet.web.get(self.resolution_url) and decoding bytes into web_data,
call gl.nondet.web.render(self.resolution_url, mode='text') and assign its
returned text directly to web_data so the prompt receives plain-text rendered
content; update any references to response to use web_data and remove the
.body.decode(...) flow.

Comment on lines +10 to 12
# { "Depends": "py-genlayer:1jb45aa8ynh2a9c9xn3b7qqh8sm5q93hwfp7jqmwsfhh8jpz09h6" }
```
It is similar to Solidity's `pragma solidity`, and `test` after a colon is a version. When GenLayer releases, it will be changed to some exact hash of a version, which will be frozen forever.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Documentation text is now inconsistent with the code example.

Line 12 states "test" after a colon is a version, but the code example on line 10 now shows the pinned hash. Consider updating the explanatory text to reflect the new format, e.g.:

"It is similar to Solidity's pragma solidity, and the value after the colon is a version hash that identifies a specific frozen GenVM release."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pages/developers/intelligent-contracts/first-contract.mdx` around lines 10 -
12, Update the explanatory sentence so it matches the code example that pins a
specific release hash: replace the phrase saying "`test` after a colon is a
version" with wording that the value after the colon (e.g.
"py-genlayer:1jb45aa8...") is a version hash identifying a specific frozen
GenVM/GenLayer release—e.g., "It is similar to Solidity's `pragma solidity`, and
the value after the colon is a version hash that identifies a specific frozen
GenVM release."

Comment on lines +159 to +175
stripped = code.strip()
lines = stripped.split("\n")
first_line = lines[0].strip()

# Method: has @gl.public decorator or def with self
if first_line.startswith("@gl.public") or first_line.startswith("@gl.evm"):
return "method"
for line in lines:
line_s = line.strip()
if line_s.startswith("def ") and "(self" in line_s:
return "method"

# Function: starts with def
if first_line.startswith("def ") or (
first_line.startswith("@") and any(l.strip().startswith("def ") for l in lines)
):
return "function"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Ignore leading imports/comments before classifying snippets.

classify_snippet() keys off the first nonblank line only. A snippet like from genlayer import * followed by @gl.public... is classified as "statement", and wrap_snippet() then nests it inside _check(). In --lint-snippets mode that turns valid examples into false failures.

Suggested fix
 def classify_snippet(code: str) -> str:
     """Classify a GenLayer snippet as 'method', 'function', or 'statement'."""
     stripped = code.strip()
-    lines = stripped.split("\n")
-    first_line = lines[0].strip()
+    lines = stripped.split("\n")
+    meaningful_lines = [
+        line.strip()
+        for line in lines
+        if line.strip() and not line.strip().startswith(("from ", "import ", "#"))
+    ]
+    first_line = meaningful_lines[0] if meaningful_lines else ""
🧰 Tools
🪛 Ruff (0.15.5)

[error] 173-173: Ambiguous variable name: l

(E741)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/lint-code-examples.py` around lines 159 - 175, classify_snippet()
currently examines the first nonblank line only, which misclassifies snippets
with leading imports or comments; update the logic that builds
stripped/lines/first_line to first skip any leading blank lines, import
statements (lines starting with "import" or "from "), and comment lines
(starting with "#" or triple-quote blocks) so that first_line and the subsequent
scans examine the first meaningful line; keep the existing checks for decorator
prefixes ("@gl.public", "@gl.evm", "@") and for "def " with "(self" unchanged,
and ensure the detection loop that looks for any "def " in lines also operates
on the trimmed list of meaningful lines so wrap_snippet() no longer wraps
class-method examples incorrectly.

Comment on lines +309 to +310
if "/_advanced/" in str(mdx_file) or "/_" in mdx_file.name:
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

The underscore-prefixed path filter never matches.

Path.name never contains /, so "/_" in mdx_file.name is always false. If the intent is to skip _foo files/directories, these three scans currently still lint them.

Suggested fix
-        if "/_advanced/" in str(mdx_file) or "/_" in mdx_file.name:
+        if any(part.startswith("_") for part in mdx_file.relative_to(DOCS_ROOT).parts):
             continue
...
-            if "/_advanced/" in str(mdx_file) or "/_" in mdx_file.name:
+            if any(part.startswith("_") for part in mdx_file.relative_to(DOCS_ROOT).parts):
                 continue
...
-        if "/_advanced/" in str(mdx_file) or "/_" in mdx_file.name:
+        if any(part.startswith("_") for part in mdx_file.relative_to(DOCS_ROOT).parts):
             continue

Also applies to: 337-338, 359-360

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/lint-code-examples.py` around lines 309 - 310, The check `"/_" in
mdx_file.name` never matches because Path.name never contains slashes; update
the condition to use mdx_file.name.startswith("_") (and keep the existing
"/_advanced/" in str(mdx_file) check) so it skips underscore-prefixed files:
change the conditional to `if "/_advanced/" in str(mdx_file) or
mdx_file.name.startswith("_"): continue`, and apply this same fix to the other
two occurrences of the same pattern in the file.

@MuncleUscles MuncleUscles merged commit cb88309 into main Mar 12, 2026
10 checks passed
@MuncleUscles MuncleUscles deleted the ci/lint-code-examples branch March 12, 2026 16:25
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.

1 participant