Skip to content

nlght-ai v0.1.1

Choose a tag to compare

@r4id4h r4id4h released this 19 Jul 16:44

Change notes — v0.1.1

Released: 2026-07-19

  • release: process the final public-mirror exclude entry even when the file
    has no trailing newline, preventing paths such as scripts/release from
    leaking into the public snapshot. (slice:
    working/public-mirror-exclude-final-line)

  • roadmap: document the planned move from blocking tool loops to asynchronous
    orchestration where reasoning can continue around in-flight calls, suspend or
    wait when necessary, and supervise subprocesses up to complete child
    workflows. (slice: working/parallel-tool-orchestration-roadmap)

  • agent skill: expand the nlght project skill with code-backed public APIs,
    complete platform/provider options, workflow/tool/playbook/session/protocol
    patterns, diagnostics, deployment, and E2E coverage guidance; correct the
    mutable step context, signal, and tool-event examples. (slice:
    working/nlght-skill-reference)

  • fix(coverage): count regular cloud provider implementations now that their
    e2e suite contributes to CI coverage; exclude instrumented *_debug.py
    duplicates and retain the ollama_local.py exclusion because its daemon-
    backed e2e suite intentionally remains local-only. (slice:
    working/provider-coverage)

  • fix(ci): make https://api.openai.com/v1 the regular/debug OpenAI provider
    default so an explicitly empty CI OPENAI_BASE_URL cannot produce relative
    request URLs; retain non-empty Azure/proxy overrides. Run architecture tests
    without starting an empty coverage process and generate the combined report
    and XML in a dedicated step after unit, integration, and cloud e2e coverage
    have been combined. (slice: working/openai-default-endpoint; ADR-0030)

  • docs(skill): validate the nlght project skill metadata and synchronize its
    model-provider guidance with the current provider kinds, defaults, debug
    clients, continuation requirements, and shared e2e conventions. (slice:
    working/update-nlght-skill)

  • fix(model): the non-streaming Ollama tool loop (call() with a catalog) never
    sent the tool schemas — both ollama_local and ollama_cloud built the
    /v1/chat/completions body as {model, messages, stream:False} with no
    tools, so the model was never told the tools existed and returned no tool
    call (streaming already sent them). Proven by an evidence log
    (tools_in_request=None); fixed by deriving OpenAI-format tools from the
    catalog and adding them to the body, mirroring stream(). Verified: the local
    Ollama e2e suite now passes 5/5. (slice: working/model-provider-e2e)

  • chore(model): preserve the fully instrumented Anthropic, OpenAI, Google,
    Ollama local/cloud clients and shared Ollama HTTP transport as *_debug
    modules, with both Ollama debug clients isolated onto _ollama_http_debug.
    Regular production clients no longer emit diagnostic INFO/DEBUG request,
    response, token, chunk, or tool-loop traces; operational warnings and all
    provider bug fixes remain unchanged. (slice: working/model-provider-e2e)

  • fix(model): Anthropic's streaming tool loop never invoked tools.
    AsyncMessageStream.get_final_message() is async but was called without
    await, so final was a coroutine, the except swallowed the resulting
    AttributeError, and tool_use blocks were never surfaced as tool_call events.
    Non-streaming worked; both streaming tool paths (native catalog loop and the
    step-driven stream() loop) silently produced no call. Added the await; a
    regression test asserts a tool_use block in the final message reaches the
    caller as a tool_call event. (slice: working/model-provider-e2e)

  • fix(model): Google/Gemini 3.x tool loops failed with 400 INVALID_ARGUMENT
    ("Function call is missing a thought_signature"). Gemini 3.x returns a
    thought_signature on function_call parts and requires it echoed back on the
    continuation; the client dropped it by rebuilding the part. The native loops
    (call() and stream() with a catalog) now re-append the model's own turn /
    function_call Parts verbatim instead of rebuilding them, preserving the
    signature. The step-driven path carries it (base64) through the canonical
    tool-turn (append_ollama_native_tool_turn) and _to_google_contents decodes
    it back onto the Google part; other providers ignore the extra key. Unit tests
    cover the canonical round-trip; confirmed against the live Gemini API (all
    Google tool-loop e2e scenarios pass). (slice: working/model-provider-e2e)

  • test(model-clients): restructure the e2e model-client suite so every client
    is exercised by one shared scenario set (tests/model_client_suite.py:
    non-stream call, streaming tokens, native tool loop in call() and stream(),
    and the step-driven append_tool_turn() loop). Cloud clients (Anthropic,
    OpenAI, Google, Ollama Cloud) stay in tests/e2e; the local Ollama client
    moves to a new tests/local_e2e package that runs the same suite and is
    deselected by default (-m "not local_e2e") — it needs a live daemon and
    never runs in CI. Dropped the misnamed "ollama proxy" tests and the
    local-Ollama e2e conftest. Env naming is now uniform and prefix-free
    (<PROVIDER>_API_KEY + <PROVIDER>_MODEL, plus OPENAI_BASE_URL); the old
    NLGHT_E2E_* model vars and the …_OLLAMA_CLOUD_URL/KEY/MODEL trio are gone.
    Ollama Cloud gates on OLLAMA_API_KEY alone (default model gpt-oss:20b, a
    free-tier/no-subscription model), needs no URL. Also fixed a real suite bug: the backend fixture was
    module-scoped, so async SDK clients outlived their event loop and the Google
    client raised "Event loop is closed" from the second test on — now
    function-scoped, one client per test. README gains an e2e-scope note under the
    badge block: the local Ollama suite is intentionally not run in CI (no daemon)
    and is verified locally. (slice: working/model-provider-e2e)

  • feat(model): Ollama Cloud base_url defaults to https://ollama.com
    (overridable) so a cloud provider needs only an API key. OllamaCloudClient
    gains the default; CompositionService picks the endpoint per kind
    (ollama-local → localhost, cloud kinds → ollama.com), any configured
    base_url still wins. (slice: working/model-provider-e2e; ADR-0029)

  • fix(model): Google's gemini-2.0-flash was decommissioned by the
    provider (HTTP 404, "no longer available"), breaking all 5 Google e2e
    scenarios. Replaced the hardcoded default everywhere it appeared
    (production wiring in composition_service.py, e2e test default, both CI
    workflow env fallbacks, both doc examples) with gemini-3.5-flash.
    Separately, GoogleModelClient.list_models() previously only ever
    returned the static _CONTEXT_WINDOWS table — it now queries the live
    Google Generative AI API first (client.aio.models.list()), falling back
    to the static table only on error, matching the pattern
    OpenAICloudModelClient.list_models() already used. Token-budget
    resolution is unchanged (still the static prefix-matched table — Google
    exposes no reliable context-window metadata endpoint). New/updated unit
    tests mirror OpenAI's existing live/fallback test pair.
    (slice: working/google-e2e-live-model-standards)

  • fix(badge): replace the live img.shields.io/pypi/v/nlght-ai PyPI badge
    with a self-generated static pypi.svg, rendered the same way as
    tests.svg/coverage.svg (anybadge). Owned by a third lifecycle:
    prepare_release.py regenerates it from the version being bumped, at
    bump-before-tag time (ADR-0025), so it travels with the normal pre-tag
    commit — no live PyPI query, no new release-workflow step (the tagged
    commit already carries the right file via git archive). Initial value on
    main: v0.0.1. release_badges.py gained generate_pypi() and a pypi
    CLI subcommand; pypi.svg joined the BADGES tuple that drives both the
    repo's own README and the tag-pinned public/PyPI-description markdown.
    (slice: working/pypi-self-generated-badge, ADR-0028)

  • docs(memory): explain the full store-to-prompt pipeline, including Weighted
    Sigmoid as the default relevance strategy, Linear and Multiplicative
    alternatives, effective thresholds and limits, cache semantics, prompt
    disclosure controls, and the boundary between Python wiring, YAML, and fixed
    internals. (slice: working/session-doc-illustrations)

  • docs(models): document context-budget resolution and unknown-model fallbacks
    for every provider; distinguish local Ollama /api/show discovery from
    static cloud tables, and correct the shared partition chart to the actual
    Store/Prompt/Response/Safety 40/20/20/20 split.
    (slice: working/model-provider-budget-docs)

  • docs(access): visualize rule-based access-policy evaluation, including the
    default-open no-rule path, priority/tie ordering, and fail-closed allowlist
    semantics when subject rules exist. (slice: working/echarts-visualizations)

  • docs(concepts,hero): keep Concepts graph nodes and legends inside their
    chart bounds across desktop and narrow layouts, and add a responsive index
    hero topology that connects an app through nlght-ai to its four core user
    benefits: explicit workflows, persistent memory, any model, and self-hosting.
    (slice: working/echarts-visualizations)

  • docs(diagrams): migrate every fixed rectangular node-link chart to the
    responsive rectangle-edge integration, preserving native labels, tooltips,
    emphasis, and edge semantics while making arrow endpoints exact; improve
    Signals Sankey labels and the final responsive hero composition.
    (slice: working/rect-graph-doc-migration)

  • docs(hero): add three temporary hero-visual preview pages
    (/hero-variant-1 platform map with request particles, /hero-variant-2
    draggable concept constellation, /hero-variant-3 timeline pulse) for the
    index-page hero decision; the chosen one moves into docs/index.mdx and
    the previews get deleted. (slice: working/echarts-visualizations)

  • docs(concepts): add in-chart legends to all five concept diagrams (graph
    categories for node classes, styled legend items for line meanings), plus
    caption lines and layout fixes for narrow-column rendering.
    (slice: working/echarts-visualizations)

  • docs(architecture): replace the request-flow mermaid sequence diagram with
    an interactive matrix+graph sequence (numbered rows, per-message payload
    tooltips, legend-toggled stream variants, call/return legend) and add a
    chord diagram of the allowed layer import directions.
    (slice: working/echarts-visualizations)

  • docs(deployment,workflows): replace the last two mermaid diagrams — the
    migrate flow becomes an interactive graph with config-key tooltips, the
    workflow DB schema becomes a collapsible instance tree with step flags.
    The docs are now mermaid-free. (slice: working/echarts-visualizations)

  • docs(configuration,roadmap,architecture): add new visualizations — context
    budget stacked bar (model-providers), atom-promotion sankey
    (session-memory), open-items-to-stable bar (roadmap), and a zoomable
    source-layout treemap (source-structure).
    (slice: working/echarts-visualizations)

  • docs(concepts): replace the workflow mermaid diagram with an interactive
    ECharts step graph (realistic five-step example, verdict-labelled edges,
    hover adjacency focus, per-node DB-row tooltips) rendered via the landing
    <EChart> component. (slice: working/echarts-visualizations)

  • docs(concepts): fix graph edge labelling — disable series-level default
    edge labels (they rendered as "source > target" on unlabelled links),
    keep explicit labels only on horizontal edges, move loop/vertical edge
    wording into tooltips, and widen the prompt-flow layout.
    (slice: working/echarts-visualizations)

  • docs(concepts): convert the remaining concepts diagrams to interactive
    ECharts — prompt-assembly flow and executor loop as fixed-layout graphs,
    session resolution as a collapsible tree carrying the four store questions,
    signal flow as a two-path sankey; removes the legacy hand-crafted executor
    SVG. (slice: working/echarts-visualizations)

  • fix(licensing): tolerate up to five minutes of positive clock skew on signed
    machine-lease issuance and log reason-specific, secret-free temporal
    validation details while keeping expiry, TTL, signature, and binding strict.

  • fix(badges): show success/skip/total for every test family and label the
    count order, so skipped E2E suites are distinguishable from failures.

  • docs(install): standardize README, Quickstart, deployment, configuration,
    tool, and project-skill examples on sync-safe uv installation and command
    execution; document the separate uv tool install path for CLI-only use.

  • fix(badges): render all test categories as adjacent coloured segments in one
    tests.svg, giving the family one shared outer shape in development and
    release READMEs. (slice: working/connected-test-badge; refs: ADR-0027)

  • ci(badges): decouple development and release badge lifecycles. Normal CI now
    updates only this repository's SVGs while its README keeps relative links;
    release CI independently generates tagged results and injects public URLs
    pinned to the immutable release tag into package metadata and the public
    export. (slice: working/decouple-dev-release-badges; refs: ADR-0027)

  • build(release): complete PyPI metadata with a summary, keywords, Python and
    typing classifiers, direct Issues/Releases URLs, and absolute public badge
    and documentation links suitable for the PyPI long description. After PyPI
    and public mirror/tag publication, the workflow now creates the public GitHub
    Release from the committed versioned change note and marks RCs as
    prereleases. (slice: working/public-release-metadata; refs: ADR-0025)

  • ci(badges): split the aggregate test badge into a compact unit,
    architecture, integration, and E2E family that reports executed/total per
    category. CI and releases now run the environment-gated E2E suite with the
    provider SDK extras installed; configured providers execute while missing
    environments remain visible as skipped tests. (slice:
    working/test-badge-family; refs: ADR-0025)

  • docs(release): define 0.1.0 as internal-only and 0.2.0 as the first
    public Developer Preview, align current roadmap/readiness references, and
    add the end-to-end maintainer runbook for RC and final PyPI delivery.
    (slice: working/release-0.2.0-runbook)

  • fix(admin): protect every mutating Admin UI request with a constant-time
    checked CSRF cookie/request token, change version forking from GET to POST,
    and serve pinned Pico CSS, htmx, and Cytoscape assets from the package
    instead of third-party CDNs. (slice: working/security-admin-hardening;
    findings: SEC-002, SEC-003)

  • docs(security): add a German decision report that triages the attached
    Bandit, Semgrep, and pip-audit results into actionable risks, false
    positives, release recommendations, and explicit owner decisions. (slice:
    working/security-findings-report)

  • docs(adr): standardize every decision-record heading on the
    ADR-NNNN — English Title format, translate ADR-0010 through ADR-0015 into
    English, and clarify Admin UI startup with separate Unix/macOS and
    PowerShell commands plus the complete browser URL. (slice:
    working/standardize-adrs-admin-ui)

  • test(runtime): raise the executable aggregate line-coverage baseline from
    90.22% to 92.19% with 22 behavioural tests for workflow concurrency,
    coordinator/runtime cleanup, Docker lifecycle and SDK failures, robots and
    HTML fetch handling, and structured HTTP probes. Unexpected step exceptions
    now correctly record failed request metering instead of success. (slice:
    working/coverage-runtime-and-workflow)

  • test(coverage): raise the executable aggregate line-coverage baseline from
    77.13% to 90.22% with 26 behavioural tests for Hive Mind extraction,
    ingestion, attachment/artifact handling, coordinator lifecycle and
    persistence, plus the memory-artifact tool. Provider adapters that require
    unavailable external API access are explicitly omitted from the baseline
    report until their separate suite is enabled; shared model HTTP, chunking,
    and tool logic remains measured. (slice: working/coverage-over-90)

  • feat(ops): add GET /readyz alongside /healthz — reports database
    readiness (a lightweight SELECT 1 via Container.engine, 503 when
    unreachable) instead of only process liveness. Model providers are
    deliberately not pinged (see ADR-0026). Unit-tested (no DB configured /
    reachable / unreachable). (slice: working/first-release-readiness;
    finding: RLS-013; refs: ADR-0026)

  • ci(security): add baseline, non-blocking dependency-vulnerability and SAST
    scanning to tests.yml — pip-audit, Bandit, and Semgrep, all
    continue-on-error and uploaded as a security-scan-results artifact
    (SARIF for Bandit/Semgrep, JSON for pip-audit); results/coverage.xml and
    the junit files are uploaded separately as test-results. Non-blocking
    because the codebase has never been scanned before — baseline: 25 Bandit
    findings (23 low, 2 high), 0 pip-audit CVEs; enforcement policy is a
    follow-up decision after triage. bandit[sarif] and pip-audit join the
    dev dependency group; semgrep is installed ad hoc in CI only (no
    native Windows wheel, would break local uv sync on this repo's Windows
    dev environment). (slice: working/first-release-readiness; finding:
    RLS-013; refs: ADR-0026)

  • ci(release): switch to a bump-before-tag release flow (ADR-0025, amends
    ADR-0021) — the version bump and change-note rotation now happen as one
    ordinary commit on main before the tag is created (via the new
    scripts/release/prepare_release.py), so the tag, wheel metadata, public
    mirror tag, and release notes all identify the exact same commit; the
    release job no longer patches pyproject.toml in-CI or commits back to
    main after publish. The release job also now runs the same gate set as
    tests.yml (ruff, mypy, unit, integration, architecture) plus a new
    install-smoke test against the actually-built wheel in a fresh venv
    (import, CLI dispatch, packaged assets, offline migration SQL,
    create_app() construction) before publishing. set_version.py gains a
    --check mode used by the release job to fail fast if a tag doesn't match
    the already-committed version. (slice: working/first-release-readiness;
    findings: RLS-004, RLS-005, RLS-012, user-cleared)

  • ci(tests): tests.yml now also generates tests/coverage badges — not only
    the release workflow's public-mirror snapshot. Coverage accumulates across
    the three existing unit/integration/architecture steps (--cov-append);
    on every push to main (not PRs) the badges are committed to
    .github/badges/ and re-injected into the repo's own README.md marker
    block, with [skip ci] to avoid a self-trigger loop.
    scripts/release/release_badges.py's generate now sums across multiple
    junit files (tests.yml runs three separate suites; release.yml runs one
    combined suite) and gains a coverage subcommand that renders the
    coverage badge from coverage.py's XML report via anybadge — replacing
    the coverage-badge package, which imports pkg_resources and no longer
    works against current setuptools. inject() now keeps the marker
    comments in place instead of consuming them, since it re-runs on every
    push rather than once at release time. anybadge moves into the dev
    dependency group so its existing unit test runs instead of skipping in
    normal PR CI. (slice: working/first-release-readiness)

  • docs(quickstart): add an explicit "Start PostgreSQL" step (docker run postgres) to Quickstart (docs site + README), between configuring
    platform.yaml and running nlght-ai migrate — previously the guide
    assumed a running, reachable PostgreSQL instance without saying how to get
    one. (slice: working/first-release-readiness)

  • fix(security): serve() logs an explicit WARNING when the Admin UI is
    enabled (NLGHT_ADMIN) on a non-loopback binding — the UI has no
    authentication, so public bindings need an authenticating proxy. Closes
    the last open public-Alpha criterion of RLS-009.
    (slice: working/first-release-readiness; finding: RLS-009, user-cleared)

  • ci(tests): integration and architecture suites are merge gates — the PR/
    push workflow runs them after the unit suite instead of only at release.
    (slice: working/first-release-readiness; finding: RLS-011, user-cleared)

  • fix(packaging): ship the PEP 561 py.typed marker in the wheel so
    downstream Mypy/Pyright consume the inline types; presence is unit-tested.
    (slice: working/first-release-readiness; finding: RLS-010, user-cleared)

  • docs(diagrams): second prototype for the mermaid replacement — the
    workflow executor flow in concepts.mdx is a hand-crafted inline SVG in
    the docs-site visual language (theme CSS variables, monospace, teal
    accent; dark/light automatic). Root cause of the first attempt's broken
    text positions fixed: Astro-MDX drops camelCase SVG attributes, so
    text-anchor/dominant-baseline never reached the DOM — now kebab-case
    with centered baselines, verified against the built site HTML.
    Remaining diagrams follow after look-and-feel sign-off.
    (slice: working/concepts-diagrams)

  • feat(licensing): pin the trusted Keygen origin/account/Ed25519 key and add
    Keygen-issued 72-hour machine leases for bounded offline startup; verify
    signature, algorithm, TTL, embedded license state, and fingerprint before
    fallback, while explicit invalidity remains fail-closed. (slice:
    working/rls-008-signed-machine-lease; finding: RLS-008; refs: ADR-0024)

  • docs(contracts): synchronize README and canonical skill with shipped provider
    kinds, the external demo repository, SessionResultStore safety semantics,
    optional LLM handling, and the complete built-in tool set. (slice:
    working/rls-007-public-contracts; finding: RLS-007)

  • refactor(knowledge): remove the unfinished Knowledge Store prototype from the
    0.1.0 core platform, including its premature ORM tables, port, adapter,
    package extra, and tests; retain the pre-removal state on
    feature/knowledge-store and keep the cohesive pipeline as planned future
    work under ADR-0023. (slice: working/rls-003-remove-knowledge; finding:
    RLS-003; refs: ADR-0023)

  • fix(security): restore certificate verification for all license validation,
    activation, and entitlement requests by removing the global verify=False
    client override; add regression coverage for secure client construction.
    (slice: working/rls-001-license-tls; finding: RLS-001)

  • fix(security): remove the unused raw /config diagnostics endpoint entirely;
    it now returns 404 in default and Admin-enabled modes. Document unauthenticated
    Admin UI as an explicitly accepted 0.1.0 Developer Preview limitation and
    retain authentication plus deployment guardrails on the stabilization
    roadmap. (slice: working/rls-002-remove-config; finding: RLS-002)

  • docs(review): record the first-release readiness assessment with three P0
    blockers (licensing TLS verification, unauthenticated raw config exposure,
    and ORM/Alembic schema drift), public-Alpha gates, verified quality baseline,
    remediation order, and checkable exit criteria. (slice:
    working/first-release-readiness)

  • docs(architecture): expand the Structurizr workspace from three broad views
    to seven focused views: System Context, Containers, Extension Points,
    Workflow Runtime components, workflow-request and tool-calling dynamics,
    and the Single Node deployment topology. The rendered workspace.json is
    synchronized. (slice: working/architecture-views-and-svg)

  • docs(architecture): audit workspace.dsl against the code and fix drift —
    Playbook Catalog is implemented (YAML loader + builder), no longer
    "[planned]", and does not persist to the DB; removed three false
    persistence relationships (workflow runtime, workspace management, and
    session memory do not use the PostgreSQL persistence service — session
    memory persists via its own FileSystemBackend); Payload Detection is
    explicitly marked PLANNED (no code exists); the built-in tool list and
    the model-provider description now match reality (provider-specific
    adapters, full tool set); new Admin UI container with operator and
    persistence relationships. Rendered workspace.json still needs a
    re-export via Structurizr. (slice: working/docs-content-pass)

  • refactor(typing): enforce ANN401 (no bare Any in signatures) and
    PLC0415 (imports at file top) via ruff. All 76 Any sites in src+tests
    replaced with precise types or object; 5 dynamic construction seams
    (tool/store constructors forwarding into typed keyword surfaces) carry a
    reasoned # noqa: ANN401. Of 59 function-level imports in src, 13 were
    plain hoists (pathlib/sys/ast/model imports moved to file top — incl.
    removing two runtime imports from hot paths in the hive-mind
    coordinators); the remaining optional-extra/lazy seams carry a reasoned
    # noqa: PLC0415. Tests keep function-level imports
    (per-file-ignore) — idiomatic for importorskip-gated modules. Side
    effects: admin _parse_json_field now rejects non-object JSON with 422
    (a JSON array can no longer land in a dict config column), and
    ToolContract.execute is typed object-in/object-out with the result
    coerced via an isinstance check instead of hasattr.
    (slice: working/docs-content-pass)

  • refactor(bootstrap): move fastapi_app.py (create_app, lifespan, route
    registration) from adapters/inbound/http/ into bootstrap/ — it is
    the composition entry (builds the container) and violated the layering
    as an adapter. The adapters must not import bootstrap pytestarch rule
    is now enforced (15 rules total). uvicorn target nlght.main:create_app
    unchanged; architecture docs updated. (slice: working/docs-content-pass)

  • test(architecture): complete the clean-architecture layering matrix in
    tests/architecture — new pytestarch rules: ports must not import
    application, adapters must not import application, application must not
    import bootstrap, extensions must not import application (14 rules
    total). Findings recorded for decision: core→ports imports are all
    TYPE_CHECKING-only (accepted pattern for now);
    adapters/inbound/http/fastapi_app.py imports bootstrap at runtime — a
    real layering violation awaiting a move-or-except decision before an
    adapters↛bootstrap rule can be added.
    (slice: working/docs-content-pass)

  • test(e2e): live-API e2e tests for all four cloud model providers
    (Anthropic, OpenAI, Google, Ollama Cloud) — per provider: non-streaming
    call, streaming tokens, the native tool loop in both call() and
    stream() form, and the step-driven tool loop via append_tool_turn(); a
    real tool must be executed and its result must reach the final answer.
    Gated on per-provider env keys (skip without secrets); the local-Ollama
    reachability skip no longer gates the whole e2e package.
    (slice: working/model-provider-e2e)

  • ci(release): static tests/coverage badges for the public snapshot. The
    release workflow now runs unit+integration+architecture in one combined
    pytest run with --cov=nlght + junit output, renders
    coverage.svg (coverage-badge) and tests.svg (anybadge via the new
    testable scripts/release/release_badges.py), copies both into the
    public mirror at .github/badges/, and replaces the README marker
    block (<!-- release-badges:start/end -->) with local badge references
    plus a PyPI badge during mirroring. The dev README carries only the
    empty marker block; the previous public-repo Actions badge was removed
    (it reflected the wrong repo's CI). Unit tests cover junit
    summarization, README injection, and SVG generation.
    (slice: working/release-badges)

  • docs(roadmap): the SystemPromptBuilder Overhaul now describes
    store-driven prompt composition as its first planned improvement — the
    four session stores (Directive/Conversation/SessionResult/WorkingMemory)
    will feed defined prompt sections per scope; today this needs manual
    step code. session-memory.mdx points to that roadmap entry from "The
    four stores". (decision: roadmap description instead of implementing
    the wiring pre-release; slice: working/hive-mind-prompt-wiring)

  • docs: document the Resource concept and the Admin UI (both previously
    missing). concepts.mdx gains a "Resource" section (resource row =
    activation of a registered implementation; restrictions live in access
    policies, not on the row) plus a "where each concept lives" mapping of
    concepts to their data structures (DB tables / playbook YAML /
    platform.yaml / code registries). New user-docs page
    deployment/admin-ui (enabling via NLGHT_ADMIN=1, workflows/versions/
    graph editor, resources, access policies, first-time setup; explicit
    no-auth warning) + sidebar entry. Tools pages and the Docker Runtime
    page now state that tools need resource rows and link Admin UI +
    access policies. docker.mdx configuration corrected to the real
    singular os_runtime block (the documented runtime_subsystems
    variant and the "both runtimes in parallel" section did not match the
    parser) incl. workspace_path/extra_hosts keys.
    (slice: working/resources-admin-ui-docs)

  • docs(config): sync all configuration docs with what the code actually
    parses. Real drift fixed: the HTTP gateway kind is http — docs and
    examples everywhere said kind: http-gateway, which only worked because
    the 0.0.0.0:8000 fallback matched the example; the stale
    runtime_subsystems: schema in configuration-model.mdx,
    database-migrations.mdx, and README replaced by the real gateways /
    os_runtime / integrations.persistence.workflows layout; the three
    Ollama provider kinds documented (ollama-local for local serve,
    ollama/ollama-cloud for the cloud client) incl. previously
    undocumented api_key/headers/timeout config keys. Quickstart (docs +
    README) gains an explicit "Initialize the database" step (nlght-ai migrate) before first start. Docker Runtime graduates out of
    Experimental (badges removed on its page and in the platform.yaml
    reference; roadmap section removed) — validated by 12h+ long-running
    production tasks; the DEV-only security caution now scopes to
    os_runtime: kind: local specifically. session-memory.mdx no longer
    mentions license tiers. nlght skill updated to match (gateway kind,
    ollama kinds). (slice: working/docs-content-pass)

  • docs(sidebar): flatten Capability Management (Built-in Tools, Custom
    Tools, Playbooks as direct items — the playbooks page is labeled
    "Playbooks" instead of "YAML Format") and move Access Policies into a new
    top-level "Authorization" group. No slugs changed.
    (slice: working/sidebar-authorization-group)

  • test: enforce the repository copyright header on every Python file under
    tests/ via a new guard test (test_copyright_headers.py); added the
    header to all 95 test files that lacked it. src/ already carried the
    header throughout.

  • feat(access-policy): replace the allowed_models mechanism with
    rule-based access policies (ADR-0022). New access_policies table
    (subject_type tool/model/playbook, glob subject, allow/deny, JSONB
    conditions on model/header/client_host, priority, enabled) evaluated by
    the built-in AccessRuleEngine through the three AccessPolicy ports —
    default-wired whenever persistence is configured, TTL-cached, no
    restriction and no meaningful overhead when no rules exist; unmatched
    subjects with rules are denied (allowlist semantics), errors fail closed.
    Tool/Playbook policy ports now receive the effective model and a
    nullable caller (Experimental ports — permitted break). allowed_models
    is fully removed: resources column dropped (migration 0002), gone from
    ResourceDef, admin UI, playbook YAML (plus the never-resolved
    access_policy hint field). Admin UI gains an Access Policies CRUD
    section (/admin/policies). Docs: ADR-0022 + amended ADR-0013 +
    workspace.dsl, rewritten access-policies page, updated tools/playbook/
    concepts/roadmap pages and the nlght skill. Tests: rule-engine semantics,
    rule repository, admin CRUD, catalog integration on real SQLite.
    (slice: working/access-policy-rules)

  • docs: point all demo references at the standalone demo repository

  • docs: point all demo references at the standalone demo repository
    (https://github.com/Amphidrom/nlght-ai-demo) — the demo is no longer a
    demo/ directory in this repo. Quickstart now clones the demo repo;
    recipes/extension-points links updated. Dropped the dead demo entry
    from pytest pythonpath. (slice: docs/demo-repo-links)

  • build: move dev dependencies from a dev extra to a PEP 735
    [dependency-groups] group. The all extra stays user-facing but now
    aggregates only runtime extras (docker … stores) — dev tooling is no
    longer installable via an extra. CI installs adapted: tests.yml uses
    uv sync --locked (dev group is uv's default group), release.yml uses
    pip install -e . + pip install --group dev (pip ≥ 25.1, guaranteed by
    the preceding pip upgrade). README dev-setup updated to match. The mypy
    overrides for optional-extra packages additionally set
    follow_imports = "skip" so typechecking results no longer depend on
    which extras happen to be installed locally (spacy → numpy PEP 695 stubs
    broke mypy src under python_version = 3.11); three now-redundant
    type: ignore comments in the docker OS-runtime adapter were removed.
    (slice: working/release-gates)

  • ci(release): the release pipeline now also runs tests/integration and
    tests/architecture before building distributions — previously only
    tests/unit gated a release. (slice: working/release-gates)

  • chore(dev): bump the local mise Python pin 3.12 → 3.13; CI still tests
    the 3.11 floor (release) and 3.12 (tests workflow), requires-python
    unchanged at >=3.11. (slice: working/release-gates)

  • docs: introduce a "Capability Management" umbrella — Tools, Playbooks, and
    Access Policies are documented as three independent sibling concerns, none
    subordinate to another. concepts.mdx gains a "Capability Management"
    overview and a dedicated "Access Policy" section (previously undocumented
    there). Moved access-policies.mdx out from under
    documentation/playbooks/ to a top-level documentation/access-policies
    page and regrouped sidebar.json accordingly. roadmap.mdx's "Access
    Policies" entry and workspace.dsl's accessPolicyExtensions node were
    already modeled correctly (independent of Playbooks) and needed no change.
    (slice: working/experimental-stabilization)

  • docs: split the Built-in Tools stability label — shell, git, and
    directive_management are now documented as stable; only web_search
    keeps the Experimental badge, scoped to its page-content ingestion
    mechanics (full-page enrichment, bot-masking, robots.txt handling),
    which is unrelated to the separate Knowledge & Ingestion Pipeline
    milestone. ToolContract/ToolCatalog port interfaces are now
    documented as frozen (parameters already lower losslessly to JSON
    Schema). Updated roadmap.mdx and
    documentation/tools/built-in-tools.mdx.
    (slice: working/experimental-stabilization)

  • test: close coverage gaps on the Experimental features flagged for
    test-driven stabilization — Access Policy denial paths (allow/deny/
    fail-closed-on-error/no-caller-skips-check) for tools and models,
    max_hops fallback-step edge cases (configured fallback, disabled
    fallback hard-stops, exact-boundary no-overrun), Prometheus metering
    (all standard metric families asserted, missing-dependency clear error,
    duplicate metric-name/label-shape conflict documented), and Directive
    prompt rendering (all three KnownDirective members — language,
    timezone, tone — verified through their real templates end-to-end via
    SystemPromptBuilder, not just raw-value substring checks).
    (slice: working/experimental-stabilization)

  • docs(i18n): translate remaining German docstrings/comments in src/
    to English — full sweep, 26 files across 3 module-grouped commits
    (ports/core; tools/playbooks/os_runtime/workspace adapters;
    sessions/trigger/config/hive_mind/wiring/executor). Comments and
    docstrings only — no code logic, identifiers, or runtime string
    literals touched. Deliberately left in German: keyword-matching data
    in layered_observe_extractor.py/artifact_extractor.py (matches
    German user input, must stay German) and a German tool-description
    string in stores/vector.py (LLM-facing, not prose documentation).
    Also translated one German ImportError message
    (layered_observe_extractor.py) that the sweep initially left as
    "runtime literal" but is actually developer-facing diagnostic text,
    not functional data — fixed for consistency.
    744 passed throughout, ruff + mypy clean.

  • refactor(typing): finish the mypy --strict cleanup — 186 remaining
    mechanical errors (bare dict/list type-args, missing return-type
    annotations, no-any-return/no-untyped-call cascades) down to
    zero, repo-wide. Notable non-mechanical fixes found along the way:
    admin/router.py had 22 errors from a single root cause (_tmpl()'s
    missing return type made every route handler's TemplateResponse(...)
    type as Any); ModelProviderBackend.bind()/token_budget()
    (ports/outbound/model_provider_backend.py) were fully untyped despite
    the matching SignalEmitter/ModelClient/TokenBudget ports already
    existing — missed by the earlier Any-vs-Protocol audit since this port
    wasn't in that grep's scope; web_search.py/keygen_license_adapter.py
    got isinstance/cast treatment for resp.json()'s Any return,
    scaled to how trusted each source is (user-configurable endpoint vs.
    known API contract).
    Added mypy src as a CI gate (.github/workflows/tests.yml) — it
    wasn't checked in CI at all before; now that it's genuinely clean,
    gating on it stops this from silently rotting again.
    744 passed throughout, ruff clean.
    (slice: working/mypy-strict-cleanup)

  • fix: second round of real bugs found via mypy --strict (207 -> 186,
    remainder fully mechanical). Investigated every remaining non-mechanical
    category (attr-defined, union-attr, override, arg-type) instead
    of moving straight to the annotation sweep:

    • layered_observe_extractor.py's __main__ smoke-test called an
      async def extract method without await/asyncio.run — same
      zero-test-coverage file as the ExtractedArtifact bug from the
      first round. Fixed.
    • PlaybookCatalog port never declared to_contract(), though
      system_prompt.py calls it and the one existing implementation
      happens to have it — a conforming custom implementation (the whole
      point of that extension point) would be missing it. Added to the
      Protocol.
    • HiveMindStoreCoordinatorFactory.__init__'s backend: object | None
      should have been SessionBackend | None — slipped past the earlier
      Any/object-vs-Protocol audit. Fixed.
    • SimpleStoreCoordinator._dump()'s debug log called
      atom.atom_type.value, but atom_type is plain str by design
      (supports custom "namespace:label" types) — would crash on any
      genuinely custom atom type. Fixed.
    • WorkflowExecutor.stream/stream_signals (ports/outbound/workflow_executor.py)
      were declared async def -> AsyncIterator[X], but the only
      implementation is an async generator (no await needed to call it)
      — the Protocol's signature actively contradicted both real call
      sites and would crash if followed literally. Fixed to match
      ModelClient.stream()'s existing correct precedent.
    • Two asserts added (passthrough.py, artifact_extractor.py)
      where a public method's None-guard wasn't visible to mypy across
      a private-helper boundary — real safety nets, not suppressions.
    • Full suite green throughout (744 passed), ruff clean.
      (slice: working/mypy-strict-cleanup)
  • fix: multiple real bugs found while triaging mypy --strict errors
    repo-wide (268 -> 207, remainder is mechanical bare-dict/missing
    type-args, tracked separately):

    • ExtractedArtifact (core/hive_mind/extraction.py) had no language
      field, but layered_observe_extractor.py constructed it with
      language=... and read .language at 3 more sites — would crash
      with TypeError/AttributeError every time that code path ran.
      Zero test coverage existed for that file. Added the field.
    • persistence/models.py: a partial-unique-index postgresql_where
      clause used the SQLAlchemy column type Text(...) instead of the
      SQL-literal function text(...). The applied Alembic migration
      already used the correct sa.text(...), so the DB schema itself
      was fine, but the ORM model would mismatch on
      alembic revision --autogenerate diffing. Fixed.
    • docker.py: except type(None) is invalid Python and would raise
      TypeError instead of the intended fallback if ever hit. Added
      self._container is not None assertions after 4 lazy-start call
      sites (real safety net, not just type-checking noise).
    • system_prompt.py: ctx: WorkflowStepContext = None (should be
      | None), plus a byte-identical duplicate _build_active_phase
      method silently shadowing itself.
    • wiring.py: Container.license had a default_factory that isn't
      a valid zero-arg callable (KeygenLicenseAdapter requires
      server_url/license_key) — latent (always passed explicitly in
      practice, confirmed across src/ and both non-unit test helpers),
      but a real footgun. Changed to LicensingPort | None = field(default=None).
    • Root-caused the missing-type-stub errors instead of suppressing
      per-line: added [[tool.mypy.overrides]] in pyproject.toml for
      optional-extra third-party packages, and types-PyYAML as a real
      dev dependency (pyyaml is a required base dep). Found along the
      way: the repo's own top-level docker/ directory (Dockerfile
      assets) shadows the docker SDK package name via Python's
      namespace-package resolution when the real package isn't installed
      — not a runtime bug (a real installed package always wins), only
      confuses mypy in the no-[docker]-extra dev environment.
    • Smaller fixes: StoreCoordinatorFactory.save() override narrowing
      documented (simple.py/coordinator.py), emitter-variable reuse
      across mutually-exclusive branches renamed
      (ollama_adapter.py/openai_adapter.py), _stream_as_ndjson
      retyped to accept AsyncIterable[Signal] (what QueuedSignalEmitter
      actually implements via __aiter__, not AsyncIterator[Signal]),
      AtomType._missing_ now consistently coerces _value_ to str.
    • Full suite green throughout (744 passed), ruff clean.
      (slice: working/mypy-strict-cleanup)
  • fix(model): append_tool_turn() was silently broken for OpenAI Cloud,
    Google, and Anthropic — none of the three BoundXModelClient classes
    overrode it, so calls fell through to the ModelClient Protocol's
    empty stub and returned None instead of the updated message list.
    Only Ollama (local + cloud) actually implemented it. Found while
    chasing mypy --strict "Cannot instantiate abstract class" errors; no
    test existed for this documented core feature on any provider before
    now. Fixed all three: OpenAI delegates to the already-used-internally
    append_openai_tool_turn; Anthropic delegates to
    append_ollama_native_tool_turn (its existing
    _convert_messages_for_anthropic already expects that canonical shape
    on input — confirmed by tracing where append_tool_turn's output
    actually goes); Google's _to_google_contents gained tool-turn
    handling (previously had none at all) and
    BoundGoogleModelClient.append_tool_turn delegates to the same
    canonical helper. 7 new tests, including 2 round-trip tests proving
    each fix's output is valid input for that same client's own converter
    on the next call.
    (slice: working/append-tool-turn-bugfix)

  • refactor(typing): replace Any/object with the matching port/core
    type where one already existed, scoped from a 108-usage sweep of
    src/. GitTool/ShellTool/HttpProbeTool._runtime() and their
    os_runtime params → OsRuntime (all three had the identical untyped
    accessor despite ToolBase.os_runtime already being typed);
    LoadMemoryArtifactTool.store_coordinatorStoreCoordinator | None;
    ToolCatalogBuilder.build()'s store_coordinator/workspace
    their real types; _tool_helpers.py's tool_catalog/contract/
    parametersToolCatalog/ToolContract/list[ToolParameter];
    artifact_ref_evaluator.stream_response()'s emitterSignalEmitter;
    FrozenStoreCoordinator.__init__'s innerStoreCoordinator.
    Left legitimately-generic cases alone (free-form config: dict[str, Any],
    **kwargs: Any, set_slot(value: Any) — value is meant to be arbitrary
    JSON, the enum _missing_ stdlib contract, MentalModel.delta's
    intentionally-open duck-typed proxy). Net effect on the touched files:
    mypy --strict errors 29 → 18 (fixing the params let mypy narrow 11
    previously-hidden Returning Any errors through the call chain).
    This closes out the ports-and-adapters cleanup slice.
    (slice: working/ports-and-adapters-cleanup)

  • refactor(ports): add ToolCatalogBuilder/PlaybookCatalogBuilder
    Protocols (ports/outbound/tool_catalog.py/playbook_catalog.py) and
    retype StepMachineWorkflowExecutor's constructor params against them
    instead of the concrete adapter classes. Not a pytestarch-caught
    violation (both sides live in adapters/), but the same smell — and it
    meant injecting a custom catalog builder required subclassing the
    concrete YAML-driven implementation instead of just conforming to a
    shape. BufferingSignalEmitter/QueuedSignalEmitter stay direct
    concrete imports in the executor — selecting buffering vs. streaming is
    its actual job, not a boundary issue.
    (slice: working/ports-and-adapters-cleanup)

  • fix(core): close the core/workflow/llm.pyadapters.outbound.signals .buffering.BufferingSignalEmitter boundary violation — the most severe
    of the 3 found (a runtime coreadapters import, done as a
    function-local import though no circular-import constraint required
    it). call_llm() now uses a minimal private _BufferingCapture class
    local to core, structurally identical (emit()/collected()) but
    dependency-free. Public call_llm() signature unchanged. Also typed
    backend: object as backend: ModelProviderBackend | None — the
    matching port already existed. All 10 architecture tests pass with no
    xfails now (739 passed, matches the pre-slice baseline).
    (slice: working/ports-and-adapters-cleanup)

  • fix(ports): close the ports/outbound/session_backend.py
    adapters.outbound.hive_mind.persistence boundary violation (caught by
    the corrected architecture test). Moved SessionSnapshot and its
    _ser_*/_de_* (de)serialisation helpers + _slots_to_json from the
    adapter into core/hive_mind/models.py — it's a plain data shape built
    from other core types (Directive/TurnSummary/SessionResult) with
    json/datetime-only serialisation, no I/O. FileSystemBackend now
    imports it from core like every other consumer
    (coordinator.py, pipeline_snapshot.py). Removed the corresponding
    xfail — 737 passed, 2 xfailed (was 3), ruff clean. No ADR: this
    enforces an already-established layering rule, not a new one (see
    working-entry decision note); same precedent as the httpx→httpx2
    migration.
    (slice: working/ports-and-adapters-cleanup)

  • refactor(config): consolidate ProtocolAdapterConfig, GatewayConfig,
    ModelProviderConfig, HiveMindProviderConfig (core/config/snapshot.py)
    and their *Model twins (adapters/outbound/config/file/models.py) onto
    shared NamedResourceConfig/NamedResourceModel base dataclasses — they
    were byte-identical (name/kind/enabled/config). Each stays a
    distinct, empty subclass for self-documenting signatures; consumers
    already duck-type on these four attributes, so nothing downstream
    changes. OsRuntimeConfig/OsRuntimeModel (no name field) and
    WorkflowsPersistenceConfig/WorkflowsPersistenceModel (unrelated
    shape) are not part of this — they don't conform.
    (slice: working/config-dataclass-consolidation)

  • refactor(config): remove dead runtime_providers/RuntimeProviderConfig.
    It was parsed from platform.yaml, mapped into PlatformConfigSnapshot,
    and never read anywhere — no registry consumed it, and the matching
    RuntimeProviderRegistry port was never implemented or constructed.
    Removed RuntimeProviderConfig (core/config/snapshot.py),
    RuntimeProviderModel and its YAML parsing
    (adapters/outbound/config/file/{models,source,mapper}.py), and the
    empty ports/outbound/runtime_provider_registry.py. First step of
    consolidating the config layer's near-identical
    name/kind/enabled/config dataclasses.
    (slice: working/config-dataclass-consolidation)

  • build: add a uv.lock for reproducible installs — no lockfile existed
    before. uv pinned in mise.toml alongside the existing Python 3.12
    pin. tests.yml CI now installs via astral-sh/setup-uv@v5 +
    uv sync --locked --extra dev (fails on lock/pyproject drift instead of
    silently re-resolving) and runs lint/tests through uv run. README gets
    a "Development" section documenting the mise install / uv sync /
    uv run workflow. release.yml deliberately left on plain pip — it
    patches pyproject.toml's version in place before installing, which
    would fight --locked's drift check; separate ADR-0021-documented
    pipeline, out of scope here.
    (slice: working/uv-lockfile)

  • test(architecture): fix tests/architecture/test_import_rules.py — the
    pytestarch fixture passed identical root_path/module_path (both
    src/), which silently breaks absolute-import resolution and made all
    10 layer rules pass regardless of the actual code (verified with a
    positive-control rule that also went undetected). Fixed to
    module_path=src/nlght. This surfaced 3 real, previously-invisible
    violations, marked xfail(strict=True) with the follow-up slice noted
    in each reason: core/workflow/llm.py importing
    adapters.outbound.signals.buffering at runtime, and
    ports/outbound/session_backend.py referencing a concrete adapter type
    under TYPE_CHECKING. Suite stays green; fixing the violations
    themselves is the next slice.
    (slice: working/architecture-test-fixture-fix)

  • docs: reframe the Hive Mind Directive Store as a foundational,
    general-purpose layer for storing LLM communication ground-rules,
    instead of reading as a language-only setting. Added a "Directive Store"
    section to Concepts (language, timezone, tone — the current fixed
    KnownDirective vocabulary) and reworded the Roadmap's "Hive Mind —
    Directives" entry to match. No code change — tone and timezone were
    already implemented, only the docs undersold them.
    (slice: working/directive-store-framing)

  • rename: nlght's "Skill" concept is renamed to Playbook throughout code
    and docs — SkillCatalogPlaybookCatalog, ActiveSkillActivePlaybook,
    SkillDefinitionPlaybookDefinition, SkillAccessPolicyPlaybookAccessPolicy,
    ctx.skillsctx.playbooks, activate_skillactivate_playbook,
    catalogs.skillscatalogs.playbooks. Pure rename, no behavior change.
    Reason: avoid the terminology collision with Anthropic Agent Skills and
    Claude Code Skills (nlght's own concept forces the LLM through a
    prescribed procedure/phase sequence to reach an outcome — unrelated to
    either of those). The Claude Code Skill mechanism itself (.claude/skills/,
    the downloadable SKILL.md) is untouched — different concept, kept as-is.
    (slice: working/playbook-extraction)

  • tests: make the Docker-runtime unit tests hermetic — the test helper now
    also forces _DOCKER_AVAILABLE = True alongside its sys.modules docker
    stub. Previously the two _sync_* tests failed in CI, where the optional
    docker package is not installed and the import-time flag stayed False.

  • ci/release: tag-triggered release pipeline (.github/workflows/release.yml).
    Pushing vX.Y.Z[-rcN] derives the PEP-440 version from the tag
    (scripts/release/set_version.py), gates on ruff + unit tests, builds
    sdist+wheel, publishes to PyPI via Trusted Publishing, pushes a stripped
    public snapshot (scripts/release/public-exclude.txt) as one commit + tag
    to github.com/Amphidrom/nlght-ai, rotates the change notes
    (scripts/release/rotate_change_notes.py
    docs/change-notes/<version>.md), and commits version bump + rotation back
    to main. Adds tests.yml CI (ruff + unit tests on push/PR) and unit tests
    for the release scripts. Requires one-time setup: PyPI trusted publisher,
    pypi environment, PUBLIC_REPO_TOKEN secret, public repo. See ADR-0021.
    (slice: working/release-pipeline)

  • docs(site): GitHub links now point at the public repo
    github.com/Amphidrom/nlght-ai (landing pages, recipes demo link, README
    badge → public tests.yml). README: stale "free/enterprise tier" session
    memory wording removed, docs pointer now targets docs/documentation/
    (internal docs are stripped from the public snapshot).
    (slice: working/release-pipeline)

  • docs(roadmap): add the "Kubernetes Integration & Decoupled Worker Layer"
    milestone — K8s deployment support, decoupling workflow execution from the
    gateway process into a worker layer built for parallel runs, and on-demand
    resource provisioning under load.
    (slice: working/docs-rebrand-and-no-gating)

  • licensing: remove capability gating. The hive_mind.filesystem and
    providers.multi license checks are gone from bootstrap/wiring.py
    coordinator kind and provider count now depend only on platform.yaml.
    Startup still requires a valid license key (keygen validation + machine
    registration; compliance only, tier-independent). The gating infrastructure
    (LicensingPort, entitlements) stays as a hook for future enterprise
    add-ons. Public docs rewritten to match: capability tier table, "silently
    downgraded" claims, and the stale license_file config removed; licensing
    documented as compliance/machine registration with every feature available
    in every tier (licensing, pricing, quickstart, platform-yaml,
    configuration-model, session-memory, source-structure, ports-and-adapters,
    roadmap). See ADR-0020 (amends ADR-0016); Structurizr workspace gains the
    external License Server system — workspace.json still needs regeneration.
    (slice: working/docs-rebrand-and-no-gating)

  • docs: rebrand all prose product-name mentions on the published site from
    "nlght" to "nlght-ai" (documentation/**, landing pages, free-license page,
    sidebar label "Extending nlght-ai"). Technical identifiers are untouched:
    the import package nlght, NLGHT_* env vars, the nlght: logger key,
    paths, DB names, and GitHub/domain URLs. Also fixes a NLGHT__CONFIG
    double-underscore typo in Quickstart.
    (slice: working/docs-rebrand-and-no-gating)

  • cli/packaging: rename the pip distribution to nlght-ai and replace the two
    console scripts nlght-serve / nlght-migrate with a single nlght-ai
    command (nlght-ai serve, nlght-ai migrate [alembic-args…]; entry point
    nlght.main:cli). Old script names are removed without aliases (pre-1.0
    clean break). The import package stays nlght. Error messages now suggest
    pip install 'nlght-ai[…]'; README and all user-docs command/pip references
    updated. See ADR-0019. (slice: working/docs-rebrand-and-no-gating)

  • docs: add a "Claude Code Skill" download + integration section to Quickstart
    (/skills/nlght.md, served by the docs site; publishing the file itself is
    tracked separately). Removed the stale ToolSampleStep / TOOL_CALL:
    text-parsing sample from Quickstart and the matching recipe — all four
    ModelClient backends (Ollama, Anthropic, OpenAI, Google) now run tool
    calls natively when bound with tool_catalog=. Rewrote the Tool Calling
    recipe, added a "Wiring a ToolCatalog into an LLM call" section to Custom
    Tools (native loop vs step-driven loop), reworked the Tool section in
    Concepts to match, and added a disambiguation note for nlght's own Skill
    concept vs. a Claude Code Skill. Updated the nlght agent skill's stale
    "Tool calling" Common Pattern to the same native-loop approach.
    (slice: working/docs-tooling-skills-cleanup)

  • deps: migrate our direct HTTP usage from the unmaintained httpx to its
    maintained successor httpx2 (2.5.0, by Pydantic). Swapped the direct
    dependency in pyproject.toml and renamed all httpx references to httpx2
    across adapters (ollama, openai, licensing), built-in tools (fetch_url,
    web_search), bootstrap/wiring, core/model/budget, and the matching
    tests. httpx remains in the environment transitively (anthropic, openai,
    google-genai, qdrant-client, weasel). Resolves the Starlette TestClient
    StarletteDeprecationWarning at the root. (slice: working/httpx2-migration)