Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MANIFEST.sha256
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ b9687f0785ccd4f186cd27ab4e58eaddf37abc43da3d3eb6f905a3d1d860db33 schemas/resolv
d55c4d1eb6edc1ecccbee5e6db1e0bea371919ad956cbc861caa30f32228e9a3 tools/distribution.py
f12e52837e8b71dbc4436a2cf0a3a234f2c15823909b6c787d383451e454dcad tools/evaluate.py
f90fefe7940fb9438cfec6e163724407da5166e9826f54e57c8d67aaba01e7ba tools/generate_compendium.py
889d27abdf88b2695b57e864ad17de94376f2facfd03eb46ef27b5975fd52488 tools/generate_site_docs.py
0c062fd50ccf31fdcbbd37538570cca4b85bd4026f6be6909454c7dc9f1c8753 tools/generate_site_docs.py
504558dd715c337641e86c194869d9d80f120183e9f0210043bb8385e89747ef tools/orchestrate.py
6b9ba027c38c88d567467bbbd52af1ec350cfa75a8825e798b0c9e6e2d771b89 tools/package.py
0acadfba1a6044eabd532f285d7e673fba395537075c6468dc1ebb66493da10d tools/update_manifest.py
Expand Down
3 changes: 3 additions & 0 deletions docs-site/stylesheets/extra.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
:root {
--cp-radius: 0.75rem;
}

:root > * {
--cp-border: color-mix(in srgb, var(--md-default-fg-color) 16%, transparent);
--cp-surface: color-mix(in srgb, var(--md-default-bg-color) 94%, var(--md-primary-fg-color) 6%);
--cp-surface-hover: color-mix(in srgb, var(--md-default-bg-color) 86%, var(--md-primary-fg-color) 14%);
Expand Down
137 changes: 129 additions & 8 deletions tools/generate_site_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
"evaluations",
]

COMPONENT_INDEX_SOURCE = {
"core": "SKILL.md",
"profiles": "PROFILE.md",
"modifiers": "MODIFIER.md",
"languages": "SKILL.md",
"frameworks": "SKILL.md",
}


def load_yaml(path: Path):
return yaml.safe_load(path.read_text(encoding="utf-8"))
Expand All @@ -46,11 +54,18 @@ def copy_markdown_tree(relative: str) -> None:
source = ROOT / relative
if not source.is_dir():
return

index_source = COMPONENT_INDEX_SOURCE.get(relative)
for path in source.rglob("*.md"):
destination = OUT / path.relative_to(ROOT)
destination.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(path, destination)

# Component cards link to /<collection>/<id>/. Publish the canonical
# component document at that directory index as well as its source name.
if index_source and path.name == index_source and path.parent.parent == source:
shutil.copy2(path, destination.parent / "index.md")


def write(relative: str, content: str) -> None:
path = OUT / relative
Expand Down Expand Up @@ -214,6 +229,112 @@ def generate_component_hubs() -> None:
)


def inline_code(value) -> str:
text = str(value).replace("`", "\\`")
return f"`{text}`"


def render_value(value) -> str:
if value is None:
return "—"
if isinstance(value, list):
return ", ".join(inline_code(item) for item in value) if value else "—"
if isinstance(value, dict):
return "; ".join(f"{inline_code(key)}: {inline_code(item)}" for key, item in value.items()) or "—"
return inline_code(value)


def markdown_table(mapping: dict) -> str:
if not mapping:
return "_None._"
rows = ["| Item | Expected |", "| --- | --- |"]
for key, value in mapping.items():
rendered = render_value(value).replace("|", "\\|")
rows.append(f"| {inline_code(key)} | {rendered} |")
return "\n".join(rows)


def yaml_block(value) -> str:
dumped = yaml.safe_dump(value, sort_keys=False, allow_unicode=True).rstrip()
return f"```yaml\n{dumped}\n```"


def scenario_page(path: Path, data: dict) -> str:
scenario_id = data.get("id", path.stem)
title = data.get("name") or data.get("title") or scenario_id.replace("-", " ").title()
description = data.get("description") or "Executable policy resolution scenario."
scenario_input = data.get("input") or {}
expected = data.get("expected") or {}
request = scenario_input.get("request") or ""
repository_signals = scenario_input.get("repository_signals") or {}
forbidden = data.get("forbidden") or []
principles = data.get("principles_under_test") or []

repository_sections = []
for filename, content in repository_signals.items():
repository_sections.append(f"### `{filename}`\n\n```text\n{str(content).rstrip()}\n```")
repository_body = "\n\n".join(repository_sections) if repository_sections else "_No repository signals._"

principle_links = "\n".join(
f"- [{principle}](../../principles/compendium/{principle}.md)" for principle in principles
) or "_None._"
decision_list = "\n".join(f"- {inline_code(item)}" for item in expected.get("significant_decisions") or []) or "_None._"
forbidden_list = "\n".join(f"- {item}" for item in forbidden) or "_None._"

return f"""# {title}

{description}

!!! info "Generated example"
This page is generated from `{path.relative_to(ROOT)}`. The scenario YAML remains the executable source of truth.

## Request

> {request}

## Repository evidence

{repository_body}

## Expected resolution

| Resolution layer | Expected |
| --- | --- |
| Profile | {render_value(expected.get('profile'))} |
| Modifiers | {render_value(expected.get('modifiers'))} |
| Language adapters | {render_value(expected.get('language_adapters'))} |
| Framework adapters | {render_value(expected.get('framework_adapters'))} |

### Normalized context

{yaml_block(expected.get('context') or {})}

### Skill modes

{markdown_table(expected.get('skill_modes') or {})}

### Conflict decisions

{markdown_table(expected.get('conflicts') or {})}

### Significant decisions

{decision_list}

### Principles under test

{principle_links}

### Forbidden outcomes

{forbidden_list}

## Scenario source

{yaml_block(data)}
"""


def generate_evaluations() -> None:
scenario_dir = ROOT / "evaluations" / "scenarios"
scenarios = []
Expand All @@ -222,26 +343,26 @@ def generate_evaluations() -> None:
scenario_id = data.get("id", path.stem)
title = data.get("name") or data.get("title") or scenario_id.replace("-", " ").title()
request = ((data.get("input") or {}).get("request") or "")
scenarios.append((scenario_id, title, request, path.name))
description = data.get("description") or request or "Executable policy scenario"
scenarios.append((scenario_id, title, description, data, path))

rows = []
for scenario_id, title, request, filename in scenarios:
description = html.escape(str(request)) if request else "Executable policy scenario"
search_text = html.escape(f"{scenario_id} {title} {request}".lower(), quote=True)
for scenario_id, title, description, data, path in scenarios:
search_text = html.escape(f"{scenario_id} {title} {description}".lower(), quote=True)
rows.append(
f'<a class="catalog-card" data-catalog-item data-search="{search_text}" '
f'href="https://github.com/PeterRamotowski/code-principles/blob/main/evaluations/scenarios/{filename}">'
f'<a class="catalog-card" data-catalog-item data-search="{search_text}" href="{scenario_id}/">'
f'<span class="catalog-card__title">{html.escape(str(title))}</span>'
f'<span class="catalog-card__description">{description}</span>'
f'<span class="catalog-card__description">{html.escape(str(description))}</span>'
f'<span class="catalog-card__id">{html.escape(str(scenario_id))}</span>'
"</a>"
)
write(f"evaluations/{scenario_id}/index.md", scenario_page(path, data))

write(
"evaluations/index.md",
catalog_page(
"Evaluation scenarios",
"These executable scenarios exercise policy selection, conflict boundaries, technology refinements, and rejected overengineering. The scenario YAML remains executable test input in the repository.",
"Browse generated, human-readable views of the executable scenarios that exercise policy selection, conflict boundaries, technology refinements, and rejected overengineering.",
"\n".join(rows),
len(rows),
),
Expand Down
Loading