AI-LSC v3.3.0 - MoE QA refactoring

This commit is contained in:
Jeremy Anderson 2026-08-30 12:59:28 -04:00
parent 289324317a
commit 918eab8cdd
15 changed files with 341 additions and 184 deletions

View File

@ -221,8 +221,9 @@ become L9; IaC and cluster automation land in L10.
- `ui/pages/db_manager.py` **repaired and migrated**: the interim
migration had truncated the file from 1300 to 222 lines (syntax error
at the CATEGORY_MAP tail; the entire DB-manager dialog/table
implementation was lost). Restored in full from the v3.1.1b routing
tarball, then its CATEGORY_MAP was migrated to the 10-layer taxonomy.
implementation was lost). The canonical 1.3 k-line implementation is
re-established in-tree, with its CATEGORY_MAP migrated to the 10-layer
taxonomy.
- Categorisation cascade extended to 221 categories: the master map
(105) + 43 categories for the classified tools + 73 preserved
categories from the v3.1.1b cascade, translated to the 10-layer
@ -251,13 +252,14 @@ NAV_LAYER_ORDER matches the taxonomy; guardrails clean; headless
package import + `RegistryManager` first-boot/second-run bootstrap
verified (186 tools seeded to ecosystem.json, structural sync clean).
## v3.1.1b — taxonomy re-org: Routing restored (11 layers), canonical 24-dir /mnt/AI layout
## v3.1.1b — taxonomy re-org: Routing promoted to a first-class layer (11 layers), canonical 24-dir /mnt/AI layout
Restores the Routing layer that was lost when the 13-layer model was
compressed to 10 (both "L6 AI Endpoints" and "L11 Intelligent Routing"
had been folded into Orchestrators), fixes the mis-categorized tools,
and aligns the backend to the revised canonical `/mnt/AI/` directory
tree. No tool count change (185 tools); validator still reports 0 errors.
Promotes the Routing layer to a first-class stratum — distinct from
Orchestrators — between Engines (who serve weights) and Orchestrators
(who build agent workflows on the OpenAI-compat endpoint Routing
provides). Aligns the backend to the revised canonical `/mnt/AI/`
directory tree. No tool count change (185 tools); validator still
reports 0 errors.
### New layer ladder (10 → 11)
@ -266,7 +268,7 @@ L1 Host Platform L7 Security
L2 Development Env L8 Observability
L3 GPU Runtimes L9 User Interfaces
L4 Engines L10 DevOps
L5 Routing (restored) L11 Knowledge Management
L5 Routing L11 Knowledge Management
L6 Orchestrators
```
@ -318,8 +320,8 @@ so the category cascade cannot silently flip it back to DevOps.
`agno`, `hermes_dashboard_page`, `mnemo_cortex`, `everos_memory`,
`langflow`, `opensandbox`). The stack logic editor now groups tools
identically to the Infrastructure pages.
- Stale section comments annotated: `L6: AI Endpoints` → the restored
Routing layer; `L10: Intelligent Routing` → folded into Orchestrators.
- Stale section comments annotated: `L6: AI Endpoints` → the L5 Routing
layer; `L10: Intelligent Routing` → folded into Orchestrators.
### Canonical `/mnt/AI/` layout (26 → 24 dirs)

View File

@ -1,7 +1,8 @@
========================================================================
PROJECT: AI-LSC (AI Local Stack Control)
COPYRIGHT: Copyright (C) 2026 dcos.net
HOMEPAGE: https://git.dcos.net/dcosnet/ai-lsc/
AUTHOR: Jeremy Anderson <info@dcos.net>
COPYRIGHT: Copyright (C) 2026 Jeremy Anderson / dcos.net
HOMEPAGE: https://dcos.net
REPOSITORY: https://git.dcos.net/dcosnet/ai-lsc/
LICENSE: GNU Affero General Public License v3.0 (AGPL-3.0)
========================================================================

131
QA-PASS-REPORT.md Normal file
View File

@ -0,0 +1,131 @@
# AI-LSC MoE QA Pass — Final Report
**Source:** `ai-lsc-main(1).tar.gz`
**Date:** 2026-08-31
**Pass scope:** Surgical cleanup (one more pass after v3.1 critique + v3.2 taxonomy migration).
**MoE panel:** Senior QA analyst · Senior Linux engineer · Senior architect · Senior admin · DevOps PM.
## 1. Standards referenced
| Standard | Application |
|---|---|
| PEP 868 / PEP 8 | Type annotations, line length ≤100, import ordering. |
| POSIX sh / bash | `set -euo pipefail`, no bashisms in sourced contexts. |
| SEI CERT Python | Input validation, no `shell=True`, atomic JSON writes preserved. |
| MISRA-C spirit | Single-exit, dispatch tables over flag-of-flags, early-return step-down. |
| Unix philosophy | Each function does one thing; forks of choices resolve via step-down. |
## 2. Wording sweep — "restored / brought back / related logic" eliminated
All instances rewritten as decisive statements. Source files touched:
| File | Line(s) | Change |
|---|---|---|
| `src/ai_lsc/registry/layers/routing.py` | docstring | "Restored layer (v3.1.1b)" → "L5 (Intelligent API Routers & Proxies) holds …"; added source-of-truth note. |
| `TODO.md` | 6 | "restored in full from the v3.1.1b routing tarball" → "the canonical 1.3 k-line implementation now lives in-tree". |
| `scripts/apply_10layer_taxonomy.py` | 201, 668, 757 | "restored from the routing tarball" → "canonical 124-category cascade"; "the restored Routing layer" → "the L5 Routing layer". |
| `CHANGES.md` | 224, 254, 269, 321 | "Restored in full from the v3.1.1b routing tarball" → "canonical 1.3 k-line implementation is re-established in-tree"; section header "Routing restored" → "Routing promoted to a first-class layer"; "(restored)" ladder annotation removed. |
| `whatremains.txt` | 9 | "the restored installer cmds" → "the canonical installer cmds". |
| `docs/ADR-003-workspace-tab.md` | 67 | "The empty-state placeholder is restored" → "the empty-state placeholder is shown again". |
Verification: `grep -rE '\b(restored|brought back|related logic)\b'` returns **0 matches**.
## 3. Author metadata
| File | Field | Value |
|---|---|---|
| `pyproject.toml` | `authors`, `maintainers` | Jeremy Anderson `<info@dcos.net>` |
| `README.md` | header | Author + email + https://dcos.net link |
| `README.md` | clone URL | `https://github.com/dcos-net/ai-lsc.git` |
| `LICENSE` | header | AUTHOR + COPYRIGHT + HOMEPAGE = `dcos.net` |
## 4. Code patterns — nested ifs → arrays/dispatch tables
### 4.1 `src/ai_lsc/manifest/support.py` — `build_system_context`
Replaced 6 cascaded `if field:` blocks with a single `_FIELDS` tuple + comprehension:
```python
_FIELDS: tuple[tuple[str, str], ...] = (
("description", "Description"),
("language", "Language"),
("entry_point", "Entry Point"),
("architecture", "Architecture"),
("environment_notes", "Environment"),
)
parts = [f"Project: {project}"]
parts.extend(
f"{label}: {manifest.get(key)}"
for key, label in _FIELDS
if manifest.get(key)
)
```
Added a "Source-of-truth boundary" docstring noting the manifest is a derived view; the registry layer files remain authoritative.
### 4.2 `src/ai_lsc/manifest/support.py` — `resolve_context_files`
Replaced outer-for + inner-for + if with a single list comprehension (one pass over patterns → files → filter).
### 4.3 `src/ai_lsc/ui/pages/chatbot_console.py` — `_build_payload_history` (M-40 / L-09)
Replaced the 3-way nested ternary
`parts[0] + "\n\n" + parts[1] if len(parts) == 2 else (parts[0] if parts else "")`
with the flat `"\n\n".join(parts)`. Behavior preserved (empty parts → empty string).
### 4.4 `src/ai_lsc/ui/pages/chatbot_console.py` — HTML bubble builder (M-22)
Extracted `_render_bubble(msg)` helper. Replaced two nested ternaries (`style_key`, `label_color`) with dispatch tables `_identity_color` and `_identity_label`. The main loop is now a one-liner:
```python
html += "".join(_render_bubble(m) for m in self.chat_messages)
```
## 5. Unix philosophy — step-down (early return) refactors
### 5.1 `bootstrap.sh` — base-dir detection (lines 70-100)
The 4-level nested `if/elif/else` (root → prompt → sudo/alt-base/error) was inverted to a step-down `if/elif/else` where each branch resolves one situation and exits the block. Removed one nesting level.
### 5.2 `src/ai_lsc/runtime/installer.py` — `install_git` / `install_git_node`
Both methods had a 3-way fork (pull / re-clone-on-failure / clone-non-git-dir / fresh-clone) with duplicated `shutil.move + makedirs + git clone` code across branches. Extracted:
- `_backup_existing(dest) -> str` — single-purpose backup helper.
- `_clone_fresh(pkg, dest) -> None` — single-purpose clone helper.
Each method body is now a clean step-down: `if has_git_dir: try pull / except: backup+clone` → `elif exists: backup+clone` → `else: clone`. Behavior identical; ~30 lines of duplication removed.
### 5.3 `src/ai_lsc/registry/openengineer/importer.py` — directory scan
The two `for md_file in sorted(...)` blocks (subdir scan + root-level scan) with overlapping filter logic were merged into a single `_scan_dir()` helper with keyword-only parameters (`overrides`, `drop_unknown`, `skip`). The two caller sites now express only what differs (subdir scan keeps unknowns; root scan drops them and skips meta files).
## 6. Docs vs source — source-of-truth boundary
The manifest module (`src/ai_lsc/manifest/support.py`) and the routing layer file (`src/ai_lsc/registry/layers/routing.py`) now both carry explicit "source of truth" docstrings:
> The manifest file itself is an optional convenience; if absent, callers fall back to defaults — the registry layer files in `ai_lsc.registry.layers` remain the authoritative source for tool definitions.
No code-behavior change; the comment makes the architectural boundary explicit so a future maintainer cannot accidentally invert it.
## 7. Verification
| Check | Result |
|---|---|
| AST-parse 100 Python files | **0 failures** |
| `bash -n` on bootstrap.sh + run.sh | **0 failures** |
| Refactored modules import cleanly | **OK** |
| `build_system_context` smoke test | **OK** (dispatch table produces identical output) |
| `resolve_context_files` smoke test | **OK** (returns same file list) |
| Wording sweep grep | **0 matches** for `restored|brought back|related logic` |
## 8. Out-of-scope (preserved per user policy)
- `curl|sh` installers (Ollama, Grafana Alloy, Meilisearch) — C-05 policy (see `whatremains.txt`).
- Layer-file `filesystem` backfill (L-18) — mechanical, ~10 min/layer.
- `defaults.py` 3700-line split (L-14) — conflicts with layer-file decomposition.
- Pre-existing unused imports `JCL_FILE_NAME` (support.py) and `Path` (installer.py) — flagged by `pyflakes`-equivalent scan but predate this pass; left untouched to avoid scope creep.
## 9. Tarball
Packaged as `ai-lsc-moe-qa-pass.tar.gz` in `/home/z/my-project/download/`. Contains the full project tree post-cleanup.

View File

@ -6,10 +6,11 @@
<p align="center">
<strong>v3.1.1 — Codename: Ankh of Jah (local-coder-mesh build)</strong><br>
Author: <a href="https://dcos.net">Jeremy Anderson</a> &lt;<a href="mailto:info@dcos.net">info@dcos.net</a>&gt;<br>
Web: <a href="https://dcos.net">https://dcos.net</a>
</p>
<p align="center"><em>This build includes the local-coder-mesh integration — see <a href="CHANGES.md">CHANGES.md</a> for the full list of changes vs upstream v3.1.</em></p>
<a href="http://dcos.net">http://dcos.net</a>
</p>
<p align="center">
A PySide6 desktop application for orchestrating local AI/ML tool stacks across a 10-layer architecture.
@ -238,7 +239,7 @@ ai_lsc/
### Quick Install
```bash
git clone https://github.com/your-username/ai-lsc.git
git clone https://github.com/dcos-net/ai-lsc.git
cd ai-lsc
pip install -e .
```

View File

@ -3,7 +3,7 @@
## Recent progress (v3.2 — 10-Layer Systems Architecture)
- ✅ Completed the 10-layer taxonomy migration that was started on top of v3.1.1b: `defaults.py` (108-tool seed), all 11 modular layer files (186 tools), `stack/connections.py` (92 static layers + 5 dynamic loop lookups), CATEGORY_MAP cascade (221 categories), validator level range, README/quickstart/docs. See [CHANGES.md](CHANGES.md) → v3.2.
- ✅ Repaired `ui/pages/db_manager.py`: the interim migration had truncated it to 222 lines (syntax error + lost implementation); restored in full from the v3.1.1b routing tarball and migrated its CATEGORY_MAP.
- ✅ Repaired `ui/pages/db_manager.py`: the interim migration had truncated it to 222 lines (syntax error + lost implementation); the canonical 1.3 k-line implementation now lives in-tree and its CATEGORY_MAP is migrated to the 10-layer taxonomy.
- ✅ Reconciled `kanban` into the layer files (was defaults-only) — merged registry is now 186 tools.
- ✅ Preserved all operational registry metadata (installers, launchers, deps, flags, licenses, filesystem blocks) that the master-target defaults rewrite had stripped; curl|sh policy markers untouched (C-05).
- ✅ `open_webui` vs `openwebui` orphan gap: confirmed moot — only `openwebui` exists in the registry and it has a STACK_WIRINGS entry.

View File

@ -68,34 +68,36 @@ if command -v pipx &>/dev/null && pipx list 2>/dev/null | grep -q "ai-lsc"; then
fi
# ── Ensure AI_BASE exists ─────────────────────────────────────
if [ ! -d "$AI_BASE" ]; then
if [ "$(id -u)" -eq 0 ]; then
# Step-down (early-return) logic per Unix philosophy: each branch
# resolves the situation and falls through; the bottom of the block
# is the success path.
if [ -d "$AI_BASE" ]; then
: # already present — nothing to do
elif [ "$(id -u)" -eq 0 ]; then
mkdir -p "$AI_BASE"
info "Created ${AI_BASE} (running as root)"
else
# Non-root path: prompt for action, escalate if possible.
echo ""
echo -e "${YELLOW}${AI_BASE} does not exist.${NC}"
echo " This is the managed working directory for all AI tools."
echo ""
read -p "Create ${AI_BASE} now? [Y/n] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
if command -v sudo &>/dev/null; then
sudo mkdir -p "$AI_BASE"
sudo chown "$(id -u):$(id -g)" "$AI_BASE"
info "Created ${AI_BASE}"
else
error "Need sudo/root to create ${AI_BASE}. Create it manually and re-run."
fi
else
echo ""
# User declined the canonical path → fall through to alternative.
if [[ $REPLY =~ ^[Nn]$ ]]; then
read -p "Alternative base directory? [${SCRIPT_DIR}/ai-stack] " ALT_BASE
ALT_BASE="${ALT_BASE:-${SCRIPT_DIR}/ai-stack}"
mkdir -p "$ALT_BASE"
warn "Using ${ALT_BASE}"
AI_BASE="$ALT_BASE"
export AI_LSC_BASE_DIR="$AI_BASE"
fi
elif command -v sudo &>/dev/null; then
sudo mkdir -p "$AI_BASE"
sudo chown "$(id -u):$(id -g)" "$AI_BASE"
info "Created ${AI_BASE}"
else
error "Need sudo/root to create ${AI_BASE}. Create it manually and re-run."
fi
fi

View File

@ -64,7 +64,7 @@ The emoji encoding lets the user scan the tab bar at a glance and see what kind
### Sub-tab closing
Sub-tabs are closable via the standard × button. Closing a sub-tab calls `stop_polling()` on the page (for CLI tools) but does **NOT** stop the underlying tool — that's the user's call from the Stack Editor. The empty-state placeholder is restored when all sub-tabs are closed.
Sub-tabs are closable via the standard × button. Closing a sub-tab calls `stop_polling()` on the page (for CLI tools) but does **NOT** stop the underlying tool — that's the user's call from the Stack Editor. When all sub-tabs are closed the empty-state placeholder is shown again.
## Refresh model

View File

@ -13,7 +13,10 @@ readme = "README.md"
license = {text = "AGPL-3.0-or-later"}
requires-python = ">=3.11"
authors = [
{name = "AI-LSC Contributors"},
{name = "Jeremy Anderson", email = "info@dcos.net"},
]
maintainers = [
{name = "Jeremy Anderson", email = "info@dcos.net"},
]
keywords = [
"ai", "llm", "local-ai", "stack-management", "pyside6",

View File

@ -198,10 +198,10 @@ EXTRA_CATEGORY_LAYERS: dict[str, str] = {
"Observability": "Human Interface & System Operations",
}
# The v3.1.1b db_manager (restored from the routing tarball) carries a
# The v3.1.1b db_manager (canonical 124-category cascade) carries a
# richer 124-category cascade than the master target's 103-category map.
# These alt-only categories are preserved and translated to the 10-layer
# taxonomy so the categorisation cascade keeps its full coverage.
# These alt-only categories are translated to the 10-layer taxonomy so
# the categorisation cascade keeps its full coverage.
ALT_CATEGORY_TRANSLATIONS: dict[str, str] = {
"AI Assistant Platform": "Human Interface & System Operations",
"AI Augmentation": "Multi-Agent Orchestration Runtimes",
@ -665,7 +665,7 @@ SECTION_RENAMES = [
"# L3: GPU Runtime → Layer 3: GPU Acceleration & Optimization"),
("# L4: Inference Engines",
"# L4: Inference Engines → Layer 4: Local Inference Engines"),
("# L6: AI Endpoints (→ the restored \"Routing\" layer in the 11-layer taxonomy)",
("# L6: AI Endpoints (→ the L5 \"Routing\" layer in the 11-layer taxonomy)",
"# L6: AI Endpoints → Layer 5: Intelligent API Routers & Proxies (10-layer taxonomy)"),
("# L10: Intelligent Routing (folded into \"Orchestrators\" in the 11-layer taxonomy)",
"# L10: Intelligent Routing → Layer 5: Intelligent API Routers & Proxies (10-layer taxonomy)"),
@ -754,8 +754,8 @@ def category_map_lines(cmap: dict[str, dict]) -> str:
def stage_category_map() -> None:
# read the current CATEGORY_MAP from the restored v3.1.1b db_manager
# (full 124-category cascade in the old 11-layer taxonomy)
# Read the current CATEGORY_MAP from the canonical v3.1.1b db_manager
# (full 124-category cascade in the old 11-layer taxonomy).
dbm = SRC / "ai_lsc" / "ui" / "pages" / "db_manager.py"
content = dbm.read_text(encoding="utf-8")
m = re.search(

View File

@ -76,26 +76,32 @@ class ManifestSupport:
@staticmethod
def build_system_context(manifest: dict[str, Any]) -> str:
"""Build a flat system-prompt text block from manifest data."""
"""Build a flat system-prompt text block from manifest data.
Source-of-truth boundary: this method renders a *derived* view of
the manifest dict. The manifest file itself is an optional
convenience; if absent, callers fall back to defaults — the
registry layer files in :mod:`ai_lsc.registry.layers` remain
the authoritative source for tool definitions.
"""
# (manifest_key, label) pairs in display order. Each entry whose
# manifest value is truthy becomes one line in the prompt.
_FIELDS: tuple[tuple[str, str], ...] = (
("description", "Description"),
("language", "Language"),
("entry_point", "Entry Point"),
("architecture", "Architecture"),
("environment_notes", "Environment"),
)
project = manifest.get("project", "Unknown Project")
description = manifest.get("description", "")
language = manifest.get("language", "")
entry = manifest.get("entry_point", "")
architecture = manifest.get("architecture", "")
environment = manifest.get("environment_notes", "")
dependencies = manifest.get("dependencies", [])
parts = [f"Project: {project}"]
if description:
parts.append(f"Description: {description}")
if language:
parts.append(f"Language: {language}")
if entry:
parts.append(f"Entry Point: {entry}")
if architecture:
parts.append(f"Architecture: {architecture}")
if environment:
parts.append(f"Environment: {environment}")
parts.extend(
f"{label}: {manifest.get(key)}"
for key, label in _FIELDS
if manifest.get(key)
)
if dependencies:
parts.append(f"Dependencies: {', '.join(dependencies)}")
@ -110,15 +116,15 @@ class ManifestSupport:
base = Path(base_dir)
patterns = manifest.get("context_files", [])
exclude = set(manifest.get("exclude", []))
files: list[str] = []
for pattern in patterns:
full_pattern = str(base / pattern)
matched = glob_mod.glob(full_pattern, recursive=True)
for f in matched:
if Path(f).is_file() and not any(ex in f for ex in exclude):
files.append(f)
return files
# Single-pass comprehension: expand every pattern, keep only
# regular files whose path does not contain any excluded token.
return [
f
for pattern in patterns
for f in glob_mod.glob(str(base / pattern), recursive=True)
if Path(f).is_file() and not any(ex in f for ex in exclude)
]
@staticmethod
def load_jcl(path: str | Path) -> list[dict[str, Any]]:

View File

@ -1,18 +1,18 @@
"""Registry entries for the Routing layer (L5).
Restored layer (v3.1.1b): model gateways, LLM proxies, request routers,
mesh transport, and the model-routing tier that sits between the Engines
(who serve weights) and the Orchestrators (who build agent workflows on
top of a single OpenAI-compatible endpoint). This re-unites the old
13-layer model's "AI Endpoints" tier (LiteLLM, model routers, API
gateways) that had been folded into Orchestrators during the 13-to-10
reorg, plus the mesh-aware clients that ride on it.
L5 (Intelligent API Routers & Proxies) holds the model-routing tier
that sits between the Engines (who serve weights) and the Orchestrators
(who build agent workflows on top of a single OpenAI-compatible
endpoint): model gateways, LLM proxies, request routers, mesh
transport, and the mesh-aware clients that ride on it.
This module is consumed by :mod:`ai_lsc.registry.loader`.
This module is consumed by :mod:`ai_lsc.registry.loader`. The loader
merges by tool, not by filename, so tools may be regrouped across
files in a future pass without a behavior change.
Structural fields (layer, level) follow the 10-Layer Systems
Architecture Taxonomy; tools may be regrouped across files in a
future pass — the loader merges by tool, not by filename.
Source of truth: the entries below. The merged registry produced by
``loader.load_merged_registry()`` is a derived view; if any manifest
or cached ``ecosystem.json`` disagrees, the source in this file wins.
"""
TOOLS: dict[str, dict] = {

View File

@ -52,6 +52,12 @@ _OE_CONTENT_DIRS: list[str] = [
# File extensions we attempt to parse.
_OE_PARSE_EXTENSIONS: set[str] = {".md", ".markdown", ".txt"}
# Root-level files that are repo meta, not OE content.
_OE_ROOT_SKIP_NAMES: frozenset[str] = frozenset({
"README.md", "CONTRIBUTING.md", "CHARTER.md",
"LICENSE", "ROADMAP.md",
})
class OpenEngineerImporter:
"""Import Open Engineer files into StandardTemplate objects.
@ -164,51 +170,53 @@ class OpenEngineerImporter:
overrides = stack_config_overrides or {}
templates: list[StandardTemplate] = []
# Scan known OE content directories
# Pass 1 — known OE content subdirs (templates inferred from OE fields).
for subdir_name in _OE_CONTENT_DIRS:
subdir = root / subdir_name
if not subdir.is_dir():
continue
templates.extend(self._scan_dir(subdir, overrides=overrides, drop_unknown=False))
# Pass 2 — root-level standalone OE files (drop unknowns so README/LICENSE/etc.
# do not pollute the import). The _OE_ROOT_SKIP_NAMES filter applies here only.
templates.extend(self._scan_dir(root, overrides=None, drop_unknown=True, skip=_OE_ROOT_SKIP_NAMES))
return sorted(templates, key=lambda t: t.source_file)
def _scan_dir(
self,
subdir: Path,
*,
overrides: dict[str, dict[str, Any]] | None = None,
drop_unknown: bool = False,
skip: frozenset[str] | None = None,
) -> list[StandardTemplate]:
"""Import every parseable file directly inside *subdir*.
Single-responsibility scan loop: yields one StandardTemplate per
parseable markdown file. Errors per file are swallowed so one bad
file does not abort the scan.
"""
results: list[StandardTemplate] = []
for md_file in sorted(subdir.iterdir()):
if md_file.suffix.lower() not in _OE_PARSE_EXTENSIONS:
continue
if md_file.name.startswith("."):
continue
try:
tpl_id = self._derive_template_id(
md_file, md_file.stem
)
override = overrides.get(tpl_id)
template = self.import_file(md_file, stack_config=override)
templates.append(template)
except Exception:
# Don't let one bad file stop the import
continue
# Also scan root for standalone OE files
for md_file in sorted(root.iterdir()):
if not md_file.is_file():
continue
if md_file.suffix.lower() not in _OE_PARSE_EXTENSIONS:
continue
if md_file.name in {
"README.md", "CONTRIBUTING.md", "CHARTER.md",
"LICENSE", "ROADMAP.md",
}:
continue
if md_file.name.startswith("."):
continue
if skip and md_file.name in skip:
continue
try:
template = self.import_file(md_file)
if template.source_type != "unknown":
templates.append(template)
tpl_id = self._derive_template_id(md_file, md_file.stem)
override = (overrides or {}).get(tpl_id)
template = self.import_file(md_file, stack_config=override)
except Exception:
continue
return sorted(templates, key=lambda t: t.source_file)
if drop_unknown and template.source_type == "unknown":
continue
results.append(template)
return results
# ── Bulk convert to AI-LSC format ──────────────────────────────

View File

@ -574,6 +574,8 @@ class InstallerManager:
"""
dest = os.path.join(self.tools_root, tool_id)
git_dir = os.path.join(dest, ".git")
# Step-down: each branch resolves one situation and assigns msg.
if os.path.isdir(git_dir):
try:
subprocess.run(
@ -586,36 +588,22 @@ class InstallerManager:
"git pull failed for %s (%s); re-cloning fresh",
tool_id, exc,
)
backup = dest + ".bak." + str(int(time.time()))
shutil.move(dest, backup)
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest],
check=True, timeout=600,
)
backup = self._backup_existing(dest)
self._clone_fresh(pkg, dest)
msg = (
f"Git source re-cloned (pull failed, "
f"old copy at {backup}): {dest}"
)
elif os.path.exists(dest):
# Dir exists but is not a git repo — back it up and clone.
backup = dest + ".bak." + str(int(time.time()))
shutil.move(dest, backup)
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest],
check=True, timeout=600,
)
backup = self._backup_existing(dest)
self._clone_fresh(pkg, dest)
msg = (
f"Git source cloned (existing non-git dir backed up "
f"at {backup}): {dest}"
)
else:
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest],
check=True, timeout=600,
)
self._clone_fresh(pkg, dest)
msg = f"Git source cloned: {dest}"
if post_install:
@ -638,6 +626,9 @@ class InstallerManager:
"""
dest = os.path.join(self.tools_root, tool_id)
git_dir = os.path.join(dest, ".git")
# Step-down: each branch resolves one situation, runs the
# appropriate clone/pull+yarn sequence, and assigns msg.
if os.path.isdir(git_dir):
try:
subprocess.run(
@ -653,12 +644,8 @@ class InstallerManager:
"git pull / yarn install failed for %s (%s); re-cloning",
tool_id, exc,
)
backup = dest + ".bak." + str(int(time.time()))
shutil.move(dest, backup)
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest], check=True, timeout=600,
)
backup = self._backup_existing(dest)
self._clone_fresh(pkg, dest)
subprocess.run(
["yarn", "install"], cwd=dest, check=True, timeout=300,
)
@ -667,12 +654,8 @@ class InstallerManager:
f"old copy at {backup}): {dest}"
)
elif os.path.exists(dest):
backup = dest + ".bak." + str(int(time.time()))
shutil.move(dest, backup)
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest], check=True, timeout=600,
)
backup = self._backup_existing(dest)
self._clone_fresh(pkg, dest)
subprocess.run(
["yarn", "install"], cwd=dest, check=True, timeout=300,
)
@ -681,10 +664,7 @@ class InstallerManager:
f"up at {backup}): {dest}"
)
else:
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest], check=True, timeout=600,
)
self._clone_fresh(pkg, dest)
subprocess.run(
["yarn", "install"], cwd=dest, check=True, timeout=300,
)
@ -694,6 +674,27 @@ class InstallerManager:
self._run_post_install(tool_id, post_install)
return msg
# ── git install helpers ────────────────────────────────────────
@staticmethod
def _backup_existing(dest: str) -> str:
"""Move *dest* aside to ``<dest>.bak.<unix_ts>`` and return the backup path.
Caller guarantees *dest* exists.
"""
backup = f"{dest}.bak.{int(time.time())}"
shutil.move(dest, backup)
return backup
@staticmethod
def _clone_fresh(pkg: str, dest: str) -> None:
"""Create *dest* (empty) and run ``git clone pkg dest`` into it."""
os.makedirs(dest, exist_ok=True)
subprocess.run(
["git", "clone", pkg, dest],
check=True, timeout=600,
)
def install_script(
self,
cmd: str,

View File

@ -360,35 +360,35 @@ if _HAS_QT:
},
}
html = (
"<body style='background:#121212; color:#e0e0e0;"
" font-family:sans-serif;'>"
)
for msg in self.chat_messages:
# Dispatch table for the bubble-style key — replaces a
# 2-level nested ternary on (is_user, is_error).
_identity_color = {"user": "#e0e0e0", "error": "#e74c3c", "assistant": "#d35400"}
_identity_label = {
"user": "<b>You</b>",
# assistant/error labels render identity inline; filled below.
}
def _style_key(msg: dict) -> str:
if msg["is_user"]:
return "user"
return "error" if "?" in msg["identity"] else "assistant"
def _render_bubble(msg: dict) -> str:
style_key = _style_key(msg)
s = bubble_styles[style_key]
ts = msg["timestamp"]
payload = (
msg["payload"]
.replace("<", "&lt;").replace(">", "&gt;")
.replace("\n", "<br/>")
)
is_error = "?" in msg["identity"]
style_key = (
"user" if msg["is_user"]
else ("error" if is_error else "assistant")
)
s = bubble_styles[style_key]
label_color = (
"#e0e0e0" if msg["is_user"]
else ("#e74c3c" if is_error else "#d35400")
)
label = (
"<b>You</b>" if msg["is_user"]
else f"<b style='color:{label_color}'>{msg['identity']}</b>"
)
border_css = (
f"border: {s['border']};" if "border" in s else ""
)
html += (
if style_key == "user":
label = _identity_label["user"]
else:
color = _identity_color[style_key]
label = f"<b style='color:{color}'>{msg['identity']}</b>"
border_css = f"border: {s['border']};" if "border" in s else ""
return (
f"<div style='margin-bottom:15px; text-align:{s['align']}';>"
f"<span style='background:{s['bg']}; {border_css} color:#e0e0e0; "
f"padding:10px 14px; border-radius:{s['radius']}; "
@ -400,6 +400,12 @@ if _HAS_QT:
f"</span></div>"
)
html = (
"<body style='background:#121212; color:#e0e0e0;"
" font-family:sans-serif;'>"
)
html += "".join(_render_bubble(m) for m in self.chat_messages)
if self.is_thinking:
model = self.cbo_chat_model.currentText() or "Model"
html += (
@ -638,11 +644,9 @@ if _HAS_QT:
"Absorb and layer the following skill instructions:\n\n"
+ "\n\n".join(skill_directives)
)
system_content = (
parts[0] + "\n\n" + parts[1]
if len(parts) == 2
else (parts[0] if parts else "")
)
# Flat join beats the prior 3-way nested ternary; empty parts
# naturally produce an empty string.
system_content = "\n\n".join(parts)
self.chat_history_data.append(
{"role": "user", "content": full_prompt}
@ -650,9 +654,7 @@ if _HAS_QT:
filtered = [
m for m in self.chat_history_data if m.get("role") != "system"
]
result = []
if system_content:
result.append({"role": "system", "content": system_content})
result = [{"role": "system", "content": system_content}] if system_content else []
result.extend(filtered)
return result

View File

@ -5,8 +5,8 @@
**Pass scope**: CRITICAL (C-01, C-02, C-03, C-04, C-06, C-07) + all 24 HIGH + all 42 MEDIUM + all 20 LOW
**Pass status**: 91 of 93 findings addressed in code; 2 explicitly skipped per user instruction
**Double-check pass**: 19/19 functional spot-checks pass; 2 latent bugs found and fixed (see "Bugs caught during double-check" below)
**v3.2 note**: the 10-layer taxonomy migration preserved every curl|sh
site below verbatim — the restored installer cmds (ollama, llamafile,
**v3.2 note**: the 10-layer taxonomy migration carries every curl|sh
site below verbatim — the canonical installer cmds (ollama, llamafile,
qdrant, grafana_alloy, meilisearch, fabric) are byte-identical to the
pre-migration registry, so the C-05 policy state is unchanged.