Files
chemenu/tools/chemenu/commands/log_append.py
T
torben 24cd221b21
CI / verify (push) Successful in 55s
Release / release (push) Successful in 39s
fix: stale wiki/ path literals nach kb/ nachgezogen, mit Test-Guard gegen die naechste Umbenennung
Files changed:
- CHANGES.md
- VERSION
- kb/entities/projects/Chemenu.md
- kb/log.md
- tools/chemenu/commands/_util.py
- tools/chemenu/commands/cite_cmd.py
- tools/chemenu/commands/git_publish.py
- tools/chemenu/commands/log_append.py
- tools/chemenu/commands/page_ops.py
- tools/chemenu/commands/provenance_cmd.py
- tools/chemenu/commands/raw_cmd.py
- tools/chemenu/commands/run_budget.py
- tools/chemenu/commands/touch.py
- tools/chemenu/commands/xref.py
- tools/chemenu/frontmatter_io.py
- tools/chemenu/lint_core.py
- tools/chemenu/tests/test_log_append.py
- tools/chemenu/tests/test_source_hygiene.py
- tools/chemenu/tests/test_touch.py
- tools/chemenu/tests/test_type_resolver.py
- tools/chemenu/type_resolver.py
- tools/wikitool
- types/type-spec.md
- types/type-spec.schema.yaml
2026-09-17 08:59:25 +02:00

85 lines
3.2 KiB
Python

"""Append correctly-formatted entries to kb/log.md."""
from __future__ import annotations
import re
from pathlib import Path
from typing import Optional
import typer
from chemenu import config
from chemenu.commands._util import fail, rel_path, success, today_iso
app = typer.Typer(help="Manage kb/log.md.")
VALID_OPS = ["ingest", "query", "lint", "create", "update", "delete", "rename", "move"]
# Matches the "## [YYYY-MM-DD] op | title" heading `format_log_entry` writes,
# in file order (oldest first, since entries are appended).
LOG_ENTRY_RE = re.compile(r"^## \[(\d{4}-\d{2}-\d{2})\] (\S+) \| (.+)$", re.MULTILINE)
def format_log_entry(op: str, title: str, body: str = "", today: Optional[str] = None) -> str:
today = today or today_iso()
entry = f"## [{today}] {op} | {title}\n"
if body.strip():
entry += f"\n{body.strip()}\n"
entry += "\n---\n"
return entry
def parse_log_entries(text: str) -> list[tuple[str, str, str]]:
"""Return every logged (date, op, title) entry in file order."""
return [(m.group(1), m.group(2), m.group(3)) for m in LOG_ENTRY_RE.finditer(text)]
def ingests_since_last_lint(entries: list[tuple[str, str, str]]) -> int:
"""Count `ingest` entries logged after the most recent `lint` entry (or
since the start of the log, if it has never been linted).
This is the deterministic count behind the Maintenance Schedule's "every
10 sources" full-lint cadence: nothing else in the system tracks it, so
without this the claim was prose with no enforcement - an agent (or user)
had to remember to count."""
count = 0
for _date, op, _title in entries:
if op == "lint":
count = 0
elif op == "ingest":
count += 1
return count
@app.command("append")
def log_append(
op: str = typer.Option(..., "--op", help="|".join(VALID_OPS)),
title: str = typer.Option(..., "--title", help="Brief description, e.g. a source path"),
body: str = typer.Option("", "--body", help="Optional multi-line details"),
body_file: Optional[Path] = typer.Option(None, "--body-file", help="Read the body from a file instead of --body"),
):
if op not in VALID_OPS:
fail(f"--op must be one of {VALID_OPS}")
text = body
if body_file:
text = body_file.read_text(encoding="utf-8")
entry = format_log_entry(op, title, text)
with config.LOG_FILE.open("a", encoding="utf-8") as f:
f.write("\n" + entry)
success(f"Appended log entry to {rel_path(config.LOG_FILE)}")
@app.command("status")
def log_status():
"""Report how many `ingest` operations have been logged since the last
`lint` - the deterministic trigger for the Maintenance Schedule's "every
10 sources" full-lint cadence. Read-only."""
if not config.LOG_FILE.exists():
success("No kb/log.md yet; nothing logged.")
return
entries = parse_log_entries(config.LOG_FILE.read_text(encoding="utf-8"))
count = ingests_since_last_lint(entries)
typer.echo(f"Ingests since last lint: {count}")
if count >= 10:
typer.echo("Threshold reached (>=10) - run the wiki-lint skill (`tools/wikitool lint ...`) next.")
success(f"{len(entries)} total log entries in {rel_path(config.LOG_FILE)}.")