feat(system): DPLAN-0172 windows_compat — standard + 39 file fixes across 9 branches
Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
# Windows Compatibility
|
||||
**Status:** Draft v1
|
||||
**Date:** 2026-05-10
|
||||
|
||||
---
|
||||
|
||||
## What It Is
|
||||
|
||||
A standard that ensures Python code runs on both Linux and Windows by detecting
|
||||
POSIX-only APIs used without platform guards. Windows CI (GitHub Actions on
|
||||
Windows Server 2022) validates compliance end-to-end.
|
||||
|
||||
---
|
||||
|
||||
## Why It Matters
|
||||
|
||||
AIPass is pip-installable and must work cross-platform. POSIX-only patterns
|
||||
like bare `import fcntl` or `os.waitpid(-1, os.WNOHANG)` crash immediately on
|
||||
Windows with `ImportError` or `WinError 87`. The Windows CI caught 71 failures
|
||||
from these systemic patterns. This standard prevents regressions.
|
||||
|
||||
---
|
||||
|
||||
## What the Checker Scans For
|
||||
|
||||
AST-parses every `.py` file and flags POSIX-only usage outside platform guards.
|
||||
|
||||
### Rule 1 — POSIX-only imports
|
||||
`import fcntl`, `import pwd`, `import grp`, `import termios`, `import resource`
|
||||
without `if sys.platform` or `try/except ImportError`.
|
||||
|
||||
### Rule 2 — POSIX-only constants
|
||||
`os.WNOHANG`, `signal.SIGPIPE` without platform guard or `hasattr()` check.
|
||||
|
||||
### Rule 3 — POSIX-only calls
|
||||
`os.fork()`, `os.setpgid()`, `os.killpg()`, `os.getpgid()`, `os.waitpid()`
|
||||
without platform guard.
|
||||
|
||||
### Rule 4 — os.kill without exception handling
|
||||
`os.kill(pid, signal)` without `try/except OSError`. Windows raises
|
||||
`OSError: [WinError 87] The parameter is incorrect` for invalid PIDs.
|
||||
|
||||
### Valid Guards (recognized by checker)
|
||||
- `if sys.platform != "win32":` / `if sys.platform == "linux":`
|
||||
- `if os.name != "nt":` / `if os.name == "posix":`
|
||||
- `try: ... except ImportError:`
|
||||
- `try: ... except OSError:`
|
||||
- `if hasattr(signal, "SIGPIPE"):`
|
||||
|
||||
### Skips
|
||||
- `__init__.py` files
|
||||
- Non-`.py` files
|
||||
|
||||
---
|
||||
|
||||
## Code Examples
|
||||
|
||||
### Violation — bare POSIX import
|
||||
```python
|
||||
import fcntl
|
||||
fcntl.flock(fd, fcntl.LOCK_EX)
|
||||
```
|
||||
|
||||
### Fix 1 — platform guard with Windows fallback
|
||||
```python
|
||||
if sys.platform == "win32":
|
||||
import msvcrt
|
||||
else:
|
||||
import fcntl
|
||||
|
||||
def lock_file(fd):
|
||||
if sys.platform == "win32":
|
||||
msvcrt.locking(fd, msvcrt.LK_LOCK, 1)
|
||||
else:
|
||||
fcntl.flock(fd, fcntl.LOCK_EX)
|
||||
```
|
||||
|
||||
### Fix 2 — try/except import
|
||||
```python
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None
|
||||
```
|
||||
|
||||
### Violation — unguarded os.WNOHANG
|
||||
```python
|
||||
pid, _ = os.waitpid(-1, os.WNOHANG)
|
||||
```
|
||||
|
||||
### Fix — platform guard
|
||||
```python
|
||||
if sys.platform != "win32":
|
||||
pid, _ = os.waitpid(-1, os.WNOHANG)
|
||||
```
|
||||
|
||||
### Violation — os.kill without exception handling
|
||||
```python
|
||||
os.kill(pid, 0) # Crashes on Windows with WinError 87
|
||||
```
|
||||
|
||||
### Fix — wrap in try/except
|
||||
```python
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError:
|
||||
return False
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scoring
|
||||
- **Scope:** AUDIT_SCOPE = "all_files"
|
||||
- **Checks per file:** 1 (Windows compat)
|
||||
- **Score 100:** No unguarded POSIX-only patterns found
|
||||
- **Score 0:** One or more unguarded patterns found
|
||||
- **Failure message:** "N unguarded POSIX pattern(s): L42: import fcntl; ..."
|
||||
- **Overall pass threshold:** 75%
|
||||
|
||||
---
|
||||
|
||||
## Bypass
|
||||
|
||||
File-level bypass (entire file is Linux-only):
|
||||
```json
|
||||
{"file": "apps/handlers/dispatch/daemon.py", "standard": "windows_compat",
|
||||
"reason": "Daemon process management is Linux-only by design"}
|
||||
```
|
||||
|
||||
Line-level bypass (specific line has a valid reason):
|
||||
```json
|
||||
{"file": "apps/config.py", "standard": "windows_compat", "lines": [89],
|
||||
"reason": "fcntl used only in POSIX lock path, Windows path above"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
- **Checker:** windows_compat_check.py
|
||||
- **Scope:** all_files
|
||||
- **Entry point:** check_module()
|
||||
- **Standard label:** WINDOWS_COMPAT
|
||||
- **Windows CI:** .github/workflows/windows-test.yml
|
||||
- **Issue:** #326 (Input-X Windows platform report)
|
||||
@@ -0,0 +1,305 @@
|
||||
# =================== AIPass ====================
|
||||
# Name: windows_compat_check.py
|
||||
# Description: Windows Compatibility Standards Checker Handler
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-05-10
|
||||
# Modified: 2026-05-10
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Windows Compatibility Standards Checker Handler
|
||||
|
||||
Detects POSIX-only patterns that will crash or behave incorrectly on
|
||||
Windows. Scans for:
|
||||
|
||||
1. Unguarded POSIX-only imports (fcntl, pwd, grp, termios, resource)
|
||||
2. Unguarded POSIX-only constants (os.WNOHANG, signal.SIGPIPE)
|
||||
3. Unguarded POSIX-only calls (os.fork, os.setpgid, os.killpg, os.waitpid)
|
||||
4. os.kill() without try/except catching OSError
|
||||
|
||||
Detection uses AST parsing to identify violations and check whether they
|
||||
sit inside a platform guard (if sys.platform / if os.name / try-except
|
||||
ImportError).
|
||||
"""
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from aipass.prax import logger
|
||||
from aipass.seedgo.apps.handlers.bypass.utils import is_bypassed
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
|
||||
AUDIT_SCOPE = "all_files"
|
||||
|
||||
_POSIX_ONLY_MODULES = frozenset({"fcntl", "pwd", "grp", "termios", "resource"})
|
||||
|
||||
_POSIX_ONLY_OS_ATTRS = frozenset({"WNOHANG"})
|
||||
|
||||
_POSIX_ONLY_OS_CALLS = frozenset({"fork", "setpgid", "killpg", "getpgid", "waitpid"})
|
||||
|
||||
_GUARDED_EXCEPT_TYPES = frozenset(
|
||||
{
|
||||
"ImportError",
|
||||
"ModuleNotFoundError",
|
||||
"OSError",
|
||||
"PermissionError",
|
||||
"ProcessLookupError",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _is_platform_guard(node: ast.expr) -> bool:
|
||||
"""Return True if the test expression is a sys.platform or os.name comparison."""
|
||||
if isinstance(node, ast.Compare):
|
||||
left = node.left
|
||||
if isinstance(left, ast.Attribute) and isinstance(left.value, ast.Name):
|
||||
if left.value.id == "sys" and left.attr == "platform":
|
||||
return True
|
||||
if left.value.id == "os" and left.attr == "name":
|
||||
return True
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
|
||||
if node.func.id == "hasattr":
|
||||
return True
|
||||
if isinstance(node, ast.BoolOp):
|
||||
return any(_is_platform_guard(v) for v in node.values)
|
||||
return False
|
||||
|
||||
|
||||
def _collect_child_linenos(node: ast.AST) -> set[int]:
|
||||
"""Walk all descendants and return line numbers where present."""
|
||||
lines: set[int] = set()
|
||||
for child in ast.walk(node):
|
||||
lineno = getattr(child, "lineno", None)
|
||||
if lineno is not None:
|
||||
lines.add(lineno)
|
||||
return lines
|
||||
|
||||
|
||||
def _handler_catches_guarded_type(handler: ast.ExceptHandler) -> bool:
|
||||
"""Return True if the except handler catches an import/OS error type."""
|
||||
if handler.type is None:
|
||||
return True
|
||||
if isinstance(handler.type, ast.Name):
|
||||
return handler.type.id in _GUARDED_EXCEPT_TYPES
|
||||
if isinstance(handler.type, ast.Tuple):
|
||||
return any(isinstance(elt, ast.Name) and elt.id in _GUARDED_EXCEPT_TYPES for elt in handler.type.elts)
|
||||
return False
|
||||
|
||||
|
||||
def _lines_in_guarded_blocks(tree: ast.Module) -> set[int]:
|
||||
"""Collect line numbers inside platform guard blocks or try/except ImportError."""
|
||||
guarded: set[int] = set()
|
||||
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.If) and _is_platform_guard(node.test):
|
||||
guarded.update(_collect_child_linenos(node))
|
||||
if isinstance(node, ast.Try):
|
||||
if any(_handler_catches_guarded_type(h) for h in node.handlers):
|
||||
guarded.update(_collect_child_linenos(node))
|
||||
|
||||
return guarded
|
||||
|
||||
|
||||
def _check_import_node(node: ast.Import, guarded: set[int]) -> list[tuple[int, str]]:
|
||||
"""Check a single Import node for POSIX-only module names."""
|
||||
return [
|
||||
(node.lineno, f"import {alias.name}")
|
||||
for alias in node.names
|
||||
if alias.name in _POSIX_ONLY_MODULES and node.lineno not in guarded
|
||||
]
|
||||
|
||||
|
||||
def _find_posix_import_violations(tree: ast.Module, guarded: set[int]) -> list[tuple[int, str]]:
|
||||
"""Find unguarded imports of POSIX-only modules."""
|
||||
violations: list[tuple[int, str]] = []
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
violations.extend(_check_import_node(node, guarded))
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if not node.module:
|
||||
continue
|
||||
if node.module.split(".")[0] in _POSIX_ONLY_MODULES and node.lineno not in guarded:
|
||||
violations.append((node.lineno, f"from {node.module} import ..."))
|
||||
return violations
|
||||
|
||||
|
||||
def _find_posix_constant_violations(tree: ast.Module, guarded: set[int]) -> list[tuple[int, str]]:
|
||||
"""Find unguarded references to POSIX-only constants like os.WNOHANG."""
|
||||
violations: list[tuple[int, str]] = []
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.Attribute) or not isinstance(node.value, ast.Name):
|
||||
continue
|
||||
if node.value.id == "os" and node.attr in _POSIX_ONLY_OS_ATTRS:
|
||||
if node.lineno not in guarded:
|
||||
violations.append((node.lineno, f"os.{node.attr}"))
|
||||
if node.value.id == "signal" and node.attr == "SIGPIPE":
|
||||
if node.lineno not in guarded:
|
||||
violations.append((node.lineno, "signal.SIGPIPE"))
|
||||
return violations
|
||||
|
||||
|
||||
def _find_posix_call_violations(tree: ast.Module, guarded: set[int]) -> list[tuple[int, str]]:
|
||||
"""Find unguarded calls to POSIX-only os functions."""
|
||||
violations: list[tuple[int, str]] = []
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.Call):
|
||||
continue
|
||||
func = node.func
|
||||
if not isinstance(func, ast.Attribute) or not isinstance(func.value, ast.Name):
|
||||
continue
|
||||
if func.value.id == "os" and func.attr in _POSIX_ONLY_OS_CALLS:
|
||||
if node.lineno not in guarded:
|
||||
violations.append((node.lineno, f"os.{func.attr}()"))
|
||||
return violations
|
||||
|
||||
|
||||
def _find_os_kill_violations(tree: ast.Module, guarded: set[int]) -> list[tuple[int, str]]:
|
||||
"""Find os.kill() calls not inside a try/except that catches OSError."""
|
||||
violations: list[tuple[int, str]] = []
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, ast.Call):
|
||||
continue
|
||||
func = node.func
|
||||
if (
|
||||
isinstance(func, ast.Attribute)
|
||||
and isinstance(func.value, ast.Name)
|
||||
and func.value.id == "os"
|
||||
and func.attr == "kill"
|
||||
and node.lineno not in guarded
|
||||
):
|
||||
violations.append((node.lineno, "os.kill() without OSError handling"))
|
||||
return violations
|
||||
|
||||
|
||||
def check_module(module_path: str, bypass_rules: list | None = None) -> Dict:
|
||||
"""Check a Python file for Windows-incompatible patterns."""
|
||||
path = Path(module_path)
|
||||
|
||||
if is_bypassed(module_path, "windows_compat", bypass_rules=bypass_rules):
|
||||
return {
|
||||
"passed": True,
|
||||
"checks": [
|
||||
{
|
||||
"name": "Bypassed",
|
||||
"passed": True,
|
||||
"message": "Standard bypassed via .seedgo/bypass.json",
|
||||
}
|
||||
],
|
||||
"score": 100,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
|
||||
if path.suffix != ".py" or path.name == "__init__.py":
|
||||
return {
|
||||
"passed": True,
|
||||
"checks": [
|
||||
{
|
||||
"name": "Windows compat",
|
||||
"passed": True,
|
||||
"message": "File skipped (non-target)",
|
||||
}
|
||||
],
|
||||
"score": 100,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
|
||||
if not path.exists():
|
||||
return {
|
||||
"passed": False,
|
||||
"checks": [
|
||||
{
|
||||
"name": "File exists",
|
||||
"passed": False,
|
||||
"message": f"File not found: {module_path}",
|
||||
}
|
||||
],
|
||||
"score": 0,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
|
||||
try:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
except Exception as e:
|
||||
logger.info("Cannot read %s: %s", path, e)
|
||||
return {
|
||||
"passed": False,
|
||||
"checks": [
|
||||
{
|
||||
"name": "File readable",
|
||||
"passed": False,
|
||||
"message": f"Error reading file: {e}",
|
||||
}
|
||||
],
|
||||
"score": 0,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
|
||||
try:
|
||||
tree = ast.parse(source, filename=str(path))
|
||||
except SyntaxError as e:
|
||||
logger.info("Skipped %s: SyntaxError during parse", path)
|
||||
return {
|
||||
"passed": False,
|
||||
"checks": [
|
||||
{
|
||||
"name": "File parseable",
|
||||
"passed": False,
|
||||
"message": f"Syntax error: {e}",
|
||||
}
|
||||
],
|
||||
"score": 0,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
|
||||
guarded = _lines_in_guarded_blocks(tree)
|
||||
|
||||
all_violations: list[tuple[int, str]] = []
|
||||
all_violations.extend(_find_posix_import_violations(tree, guarded))
|
||||
all_violations.extend(_find_posix_constant_violations(tree, guarded))
|
||||
all_violations.extend(_find_posix_call_violations(tree, guarded))
|
||||
all_violations.extend(_find_os_kill_violations(tree, guarded))
|
||||
|
||||
non_bypassed = [
|
||||
(ln, desc) for ln, desc in all_violations if not is_bypassed(module_path, "windows_compat", ln, bypass_rules)
|
||||
]
|
||||
non_bypassed.sort(key=lambda x: x[0])
|
||||
|
||||
checks = []
|
||||
violation_count = len(non_bypassed)
|
||||
|
||||
if violation_count == 0:
|
||||
checks.append(
|
||||
{
|
||||
"name": "Windows compat",
|
||||
"passed": True,
|
||||
"message": "No unguarded POSIX-only patterns found",
|
||||
}
|
||||
)
|
||||
else:
|
||||
previews = [f"L{ln}: {desc}" for ln, desc in non_bypassed[:3]]
|
||||
preview_str = "; ".join(previews)
|
||||
suffix = f" (and {violation_count - 3} more)" if violation_count > 3 else ""
|
||||
checks.append(
|
||||
{
|
||||
"name": "Windows compat",
|
||||
"passed": False,
|
||||
"message": f"{violation_count} unguarded POSIX pattern(s): {preview_str}{suffix}",
|
||||
}
|
||||
)
|
||||
|
||||
passed_checks = sum(1 for c in checks if c["passed"])
|
||||
total_checks = len(checks)
|
||||
score = int((passed_checks / total_checks) * 100) if total_checks > 0 else 0
|
||||
overall_passed = score >= 75
|
||||
|
||||
json_handler.log_operation(
|
||||
"check_completed",
|
||||
{"file": str(module_path), "score": score, "standard": "windows_compat"},
|
||||
)
|
||||
return {
|
||||
"passed": overall_passed,
|
||||
"checks": checks,
|
||||
"score": score,
|
||||
"standard": "WINDOWS_COMPAT",
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
# =================== AIPass ====================
|
||||
# Name: windows_compat_content.py
|
||||
# Description: Windows Compatibility Standards Content Handler
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-05-10
|
||||
# Modified: 2026-05-10
|
||||
# =============================================
|
||||
|
||||
"""
|
||||
Windows Compatibility Standards Content Handler
|
||||
|
||||
Provides formatted Windows Compatibility standards content.
|
||||
Module orchestrates, handler implements.
|
||||
"""
|
||||
|
||||
from aipass.seedgo.apps.handlers.json import json_handler
|
||||
|
||||
|
||||
def get_windows_compat_standards() -> str:
|
||||
"""Return formatted windows_compat standards content with Rich markup.
|
||||
|
||||
Returns:
|
||||
str: Formatted standards text with Rich styling
|
||||
"""
|
||||
lines = [
|
||||
"[bold cyan]CORE PRINCIPLE:[/bold cyan]",
|
||||
" Code must run on both Linux and Windows. POSIX-only APIs must be",
|
||||
" guarded with [yellow]if sys.platform != 'win32'[/yellow] or wrapped in",
|
||||
" [yellow]try/except ImportError[/yellow]. We ADD Windows code paths alongside",
|
||||
" existing Linux code -- we NEVER change the Linux path.",
|
||||
"",
|
||||
"[bold cyan]WHAT IT CHECKS:[/bold cyan]",
|
||||
" Parses Python files with [dim]ast.parse()[/dim] and detects:",
|
||||
"",
|
||||
" 1. [red]Unguarded POSIX-only imports[/red]: fcntl, pwd, grp, termios, resource",
|
||||
" 2. [red]Unguarded POSIX-only constants[/red]: os.WNOHANG, signal.SIGPIPE",
|
||||
" 3. [red]Unguarded POSIX-only calls[/red]: os.fork, os.setpgid, os.killpg,",
|
||||
" os.getpgid, os.waitpid",
|
||||
" 4. [red]os.kill() without OSError handling[/red]: Windows raises WinError 87",
|
||||
"",
|
||||
" [yellow]Valid guards (checker recognizes):[/yellow]",
|
||||
" - [dim]if sys.platform != 'win32':[/dim]",
|
||||
" - [dim]if sys.platform == 'linux':[/dim]",
|
||||
" - [dim]if os.name != 'nt':[/dim] / [dim]if os.name == 'posix':[/dim]",
|
||||
" - [dim]try: import fcntl / except ImportError:[/dim]",
|
||||
" - [dim]if hasattr(signal, 'SIGPIPE'):[/dim]",
|
||||
" - [dim]try: os.kill(...) / except OSError:[/dim]",
|
||||
"",
|
||||
" [yellow]Skips:[/yellow]",
|
||||
" - [dim]__init__.py[/dim] files",
|
||||
" - Non-.py files",
|
||||
"",
|
||||
"[bold cyan]VIOLATIONS:[/bold cyan]",
|
||||
"",
|
||||
" [red]Bad -- bare POSIX import:[/red]",
|
||||
" [dim]import fcntl[/dim]",
|
||||
" [dim]fcntl.flock(fd, fcntl.LOCK_EX)[/dim]",
|
||||
"",
|
||||
" [red]Bad -- unguarded os.WNOHANG:[/red]",
|
||||
" [dim]pid, _ = os.waitpid(-1, os.WNOHANG)[/dim]",
|
||||
"",
|
||||
" [red]Bad -- os.kill without exception handling:[/red]",
|
||||
" [dim]os.kill(pid, 0) # WinError 87 on Windows[/dim]",
|
||||
"",
|
||||
"[bold cyan]HOW TO FIX:[/bold cyan]",
|
||||
"",
|
||||
" [green]Good -- platform-guarded import:[/green]",
|
||||
' [dim]if sys.platform == "win32":[/dim]',
|
||||
" [dim] import msvcrt[/dim]",
|
||||
" [dim]else:[/dim]",
|
||||
" [dim] import fcntl[/dim]",
|
||||
"",
|
||||
" [green]Good -- try/except import:[/green]",
|
||||
" [dim]try:[/dim]",
|
||||
" [dim] import fcntl[/dim]",
|
||||
" [dim]except ImportError:[/dim]",
|
||||
" [dim] fcntl = None[/dim]",
|
||||
"",
|
||||
" [green]Good -- guarded POSIX constant:[/green]",
|
||||
' [dim]if sys.platform != "win32":[/dim]',
|
||||
" [dim] pid, _ = os.waitpid(-1, os.WNOHANG)[/dim]",
|
||||
"",
|
||||
" [green]Good -- os.kill with OSError catch:[/green]",
|
||||
" [dim]try:[/dim]",
|
||||
" [dim] os.kill(pid, 0)[/dim]",
|
||||
" [dim]except OSError:[/dim]",
|
||||
" [dim] return False[/dim]",
|
||||
"",
|
||||
" [green]Good -- hasattr guard for signal constants:[/green]",
|
||||
' [dim]if hasattr(signal, "SIGPIPE"):[/dim]',
|
||||
" [dim] signal.signal(signal.SIGPIPE, signal.SIG_DFL)[/dim]",
|
||||
"",
|
||||
"[yellow]SCOPE:[/yellow]",
|
||||
" AUDIT_SCOPE = [bold]all_files[/bold]",
|
||||
" Checks every .py file in the branch individually via AST parsing",
|
||||
"",
|
||||
"[bold cyan]SCORING:[/bold cyan]",
|
||||
" One check per file (Windows compat)",
|
||||
" [green]100[/green] = no unguarded POSIX-only patterns found",
|
||||
" [red]0[/red] = one or more unguarded patterns found",
|
||||
" Reports up to 3 offending line numbers with descriptions",
|
||||
" Overall pass threshold: [yellow]75%[/yellow]",
|
||||
"",
|
||||
"[bold cyan]BYPASS:[/bold cyan]",
|
||||
" Via [dim].seedgo/bypass.json[/dim] -- supports standard, file-level,",
|
||||
" and line-level bypass rules",
|
||||
"",
|
||||
" [dim]Example bypass entry:[/dim]",
|
||||
' [dim]{{"file": "apps/daemon.py", "standard": "windows_compat",[/dim]',
|
||||
' [dim] "reason": "Daemon is Linux-only by design"}}[/dim]',
|
||||
"",
|
||||
"[bold cyan]REFERENCE:[/bold cyan]",
|
||||
" [dim]See: seedgo standards pack (windows_compat)[/dim]",
|
||||
" [dim]Checker: windows_compat_check.py[/dim]",
|
||||
" [dim]Windows CI: .github/workflows/windows-test.yml[/dim]",
|
||||
" [dim]Issue: #326 (Input-X Windows platform report)[/dim]",
|
||||
]
|
||||
|
||||
json_handler.log_operation("standard_content_queried", {"standard": "windows_compat"})
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user