Skip to content

Add the PyO3 Python API over grund-core and prove binding parity #470

Description

@vjovanov

Roadmap: §RM-distribution.

Discussion status: This is a scoped proposal. Keep human-discussion until the owner resolves the open choices and releases it for implementation. Dependencies describe sequencing, not approval. Membership in the 1.0 scope is undecided; this is not a sub-issue of #347.

Outcome

Python applications can embed the existing Rust engine through import grund, with idiomatic Python functions and inspectable results that mean the same thing as the Cargo and Node surfaces. A locally built Python distribution is ready to hand to the separate packaging workstream. Completion of this issue does not require or authorize any package upload, release dispatch, tag, or actual release.

The repository already has grund-core, grund-cli, and grund-lsp; the workspace extraction is shipped. This issue adds the missing Python frontend, not another engine split.

Scope and architecture

  1. Specify the Python surface before implementing it, extending §FS-distribution.3.3 and §AR-bindings.6. Add crates/grund-py using PyO3 and a maturin build backend. Both distribution and import module remain grund, as already decided; the Rust binding crate is not a separately promised Python package name.
  2. Build an explicit operation inventory against the current supported core API and CLI capabilities. Expose the same conceptual operations promised by §FS-distribution.3: checking and scanning/iteration, show, refs, list, cover, formatting, ID proposal, init, and configuration inspection/validation. Account for operations added since the historical bindings list and document any operation that is intentionally a process-only concern. Options and defaults must be specified; an unimplemented operation cannot silently be presented as supported.
  3. Implement a thin marshalling layer over supported data-returning grund-core APIs. No CLI subprocess, CLI dependency, parser, walker, resolver, checker, or rule duplication. The extension owns Python conversion and exception policy; the engine continues to own all behavior. Follow §AR-bindings.placement and §AR-bindings.2.
  4. Define public Python options/results, keyword defaults, root/config resolution, path inputs, and typed exceptions. Provide type information and runnable documentation for check, report iteration, and show. Preserve the distinction between a completed check containing findings and an operation that cannot run. Retain structured query-failure details, run-level cautions, nullable locations, multi-site data, suggestions, and rule authority; do not replace structured diagnostics with only a string exception.
  5. Keep API calls silent on stdout/stderr and independent of process argv, process exit, and global working-directory changes. Resolve supplied roots per call. Specify explicit write opt-ins for operations that modify files, carrying through the engine's existing dry-run/default and data-preservation contracts.
  6. Add local package metadata and source inclusion needed to build/install the extension from a clean checkout and to hand it to wheel/sdist assembly. Declare CPython 3.10+ as the existing support floor. Coordinate native-library naming, source layout, metadata, and CLI placement with the packaging issue so import grund and the future grund command coexist without a naming collision.

Acceptance criteria

  • A clean local build/install produces an importable grund module. The documented examples run against a fixture, and the supported Python operation inventory has executable coverage, including malformed inputs and unsuccessful queries.
  • Rust core and Python process the same fixture corpus. The shared parity harness compares complete report data and an explicitly specified canonical JSON representation byte for byte, including ordering, nulls, escaping, warnings, suggestions, sites, and authority. Extend that same harness to Node once its adapter is available; Python landing must not falsely claim all three bindings are already covered. Existing CLI JSON goldens remain authoritative where they define the public wire shape.
  • Corpus cases include a clean tree, erroneous citations, config-load failures, warnings without ordinary findings, suggestions enabled/disabled, ambiguous or missing queries, workspace resolution, Unicode paths/content, and logical path separators. Mutation operations exercise the same no-write/write behavior and resulting bytes as their engine counterparts.
  • API calls neither print nor terminate the interpreter; repeated calls against different roots do not inherit accidental state. Failures cross the Rust/Python boundary as documented Python outcomes rather than interpreter termination or lost diagnostic fields.
  • The resolved dependency graph proves grund-py depends on the engine and no other frontend, while existing CLI/LSP/core dependency isolation remains true. Python-only build dependencies do not become requirements for ordinary Cargo CLI builds.
  • The local Python package and type information describe the actual callable signatures. Its artifact/source layout is consumable by the separately owned packaging jobs; no registry credentials or publication are needed to establish readiness.
  • Specs distinguish implemented Python API support from still-unpublished registry availability. No change to existing Cargo CLI behavior, output, or supported Rust entry points is required merely to introduce the binding.

Compatibility and sequencing

Decisions before implementation

  1. Select and document the first supported Python options/result representation: Python classes/dataclasses, mappings, or a deliberate combination, including typing and null/optional-field behavior.
  2. Specify exception classes and structured payloads for configuration, filesystem, and query failures; decide whether unsuccessful queries return a typed outcome or raise a documented exception while retaining all engine details.
  3. Choose Python path input handling (str and os.PathLike), invalid/non-Unicode paths, and explicit-root behavior. Preserve logical report paths independently of host filesystem paths.
  4. Decide the GIL-release and concurrency contract for long-running Rust operations, with cancellation/interrupt expectations stated honestly. Do not accidentally promise async Python APIs because Node uses Promises.
  5. Agree the extension ABI policy (per-interpreter wheels or PyO3's compatible ABI where viable) with packaging. CPython 3.10+ is the existing floor; PyPy, free-threaded Python, and additional platform support need explicit decisions rather than implied coverage.
  6. Confirm who owns the shared parity harness between the two bindings, and which exact APIs form the initial supported Python contract. Any proposed reduction from the same-conceptual-operations specification needs an explicit spec decision.
  7. Decide whether this distribution work belongs before or after 1.0. This issue assigns no milestone or release date.

Activity

  1. added
    enhancementNew feature or request
    human-discussionPeople are still discussing this; no machine lays work on it until the label comes off
    and removed
    human-discussionPeople are still discussing this; no machine lays work on it until the label comes off
    on Oct 5, 2026
  2. self-assigned this
    on Oct 6, 2026
  3. vjovanov commented on Oct 6, 2026

    @vjovanov
    CollaboratorAuthor

    Claimed by vjovanov@kung-fu-workstation on 2026-10-06 00:11Z · released on 2026-10-06 14:07Z (completed).

  4. vjovanov commented on Oct 6, 2026

    @vjovanov
    CollaboratorAuthor

    Record · 50 transitions · now completed · last 2026-10-06 13:35Z
    Holder vjovanov@kung-fu-workstation · since 2026-10-06 00:11Z

    1 · supervising → supervising 2026-10-06 00:12Z · `PATH: planned`

    plan runtime/supervision/plan.md (not shown; this comment is at its size limit)

    decision runtime/supervision/decision.md (not shown; this comment is at its size limit)

    2 · ticket.triage · intake → triage 2026-10-06 00:12Z

    ticket runtime/ticket/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage.json (not shown; this comment is at its size limit)

    3 · ticket.triage · triage → assessing 2026-10-06 00:12Z

    candidates runtime/triage/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage.candidates.md (not shown; this comment is at its size limit)

    4 · ticket.triage · assessing → route-assessment 2026-10-06 00:14Z · `VERDICT: none`

    verdict runtime/triage/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage.verdict.md (not shown; this comment is at its size limit)

    5 · ticket.triage · route-assessment → validating 2026-10-06 00:14Z
    6 · ticket.triage · validating → fitting 2026-10-06 00:14Z
    7 · ticket.triage · fitting → route-fit 2026-10-06 00:21Z · `FIT: fits` · `SIZE: complex`

    fit runtime/triage/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage.fit.md (not shown; this comment is at its size limit)

    8 · ticket.triage · route-fit → validated 2026-10-06 00:21Z
    9 · ticket.triage · validated → completed 2026-10-06 00:21Z

    validation runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage/validation.md (not shown; this comment is at its size limit)

    10 · ticket.agora · agora → cancelled
    11 · supervising → supervising 2026-10-06 00:12Z
    12 · ticket.clarify · clarifying → asking 2026-10-06 00:26Z · `QUESTIONS: ready`

    questions runtime/clarify/questions-1.md (not shown; this comment is at its size limit)

    questions runtime/clarify/questions.md (not shown; this comment is at its size limit)

    13 · ticket.clarify · asking → completed 2026-10-06 00:26Z · `CLARIFICATION: answered`

    answers runtime/clarify/answers.md

    Questions and answers on #470

    Round 1 of 2

    Asked at: #470 (comment)

    Today, python3 -I -c 'from grund import check, show' fails with ModuleNotFoundError: No module named 'grund'. After this work, a local build/install would make that import succeed and let Python inspect the same findings as Rust, including the dangling citation in tests/e2e/cases/json-report/repo. This issue delivers that frontend and its parity evidence; package publication remains separately authorized.

    Your discussion boundary remains:

    This is a scoped proposal. Keep human-discussion until the owner resolves the open choices and releases it for implementation. Dependencies describe sequencing, not approval. Membership in the 1.0 scope is undecided; this is not a sub-issue of #347.

    1. Can this frontend land independently of the core API transition and 1.0, or should either gate it? For example, answering “plan independently; leave release scheduling open” allows a proposal using currently supported engine APIs, with a named adaptation task if Python lands before Complete the grund-core public API transition and retire only properly deprecated surfaces #466/Engine: introduce Project, Run, Compiled, and Catalog with lossless v1 lowering #453/Engine: separate schema conformance, relational checks, and rendered expectations #454. Answering “wait for Complete the grund-core public API transition and retire only properly deprecated surfaces #466” makes the completed replacement API a landing prerequisite; “after 1.0” instead defers landing until that release. The existing workspace extraction is already shipped; this question concerns the remaining public API transition and release placement.

      Why needed: The answer determines whether the proposal targets today's supported APIs with later adaptation or requires an upstream transition/release first.

      Assumption without an answer: Plan independently, assign no milestone or release date, and carry a named adaptation task if Python lands first. Release scheduling may stay outside this implementation; this does not add Python to the 1.0 gate. These assumptions still require your acceptance before planning proceeds.

    2. May Add the PyO3 Python API over grund-core and prove binding parity #470 own the initial shared parity harness, or should a peer-owned contract land first? For example, answering “Add the PyO3 Python API over grund-core and prove binding parity #470 starts it” puts the shared fixture corpus, complete-data comparator and canonical JSON contract here: Rust and Python would process the json-report fixture and compare every field and the specified wire bytes, with Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 later adding Node to that same harness. Answering “Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 owns it first” puts the shared contract/infrastructure there and makes this proposal consume it, while Add the PyO3 Python API over grund-core and prove binding parity #470 still owns the Python adapter. If another ticket owns it, please name that ticket. In either case, Prepare cross-registry CLI/LSP packages, PGO artifacts, and gated publication workflows #471 consumes the extension's source/build contract, with ABI and layout coordinated in the proposal.

      Why needed: Both binding tickets reserve shared-harness ownership, and the answer changes which shared artifacts this proposal creates and which it must wait for.

    … cut at 3000 characters; the whole file is runtime/clarify/answers.md

    clarification runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.clarify/clarification.md

    Clarification of #470

    CLARIFICATION: answered

    Questions and answers on #470

    Round 1 of 2

    Asked at: #470 (comment)

    Today, python3 -I -c 'from grund import check, show' fails with ModuleNotFoundError: No module named 'grund'. After this work, a local build/install would make that import succeed and let Python inspect the same findings as Rust, including the dangling citation in tests/e2e/cases/json-report/repo. This issue delivers that frontend and its parity evidence; package publication remains separately authorized.

    Your discussion boundary remains:

    This is a scoped proposal. Keep human-discussion until the owner resolves the open choices and releases it for implementation. Dependencies describe sequencing, not approval. Membership in the 1.0 scope is undecided; this is not a sub-issue of #347.

    1. Can this frontend land independently of the core API transition and 1.0, or should either gate it? For example, answering “plan independently; leave release scheduling open” allows a proposal using currently supported engine APIs, with a named adaptation task if Python lands before Complete the grund-core public API transition and retire only properly deprecated surfaces #466/Engine: introduce Project, Run, Compiled, and Catalog with lossless v1 lowering #453/Engine: separate schema conformance, relational checks, and rendered expectations #454. Answering “wait for Complete the grund-core public API transition and retire only properly deprecated surfaces #466” makes the completed replacement API a landing prerequisite; “after 1.0” instead defers landing until that release. The existing workspace extraction is already shipped; this question concerns the remaining public API transition and release placement.

      Why needed: The answer determines whether the proposal targets today's supported APIs with later adaptation or requires an upstream transition/release first.

      Assumption without an answer: Plan independently, assign no milestone or release date, and carry a named adaptation task if Python lands first. Release scheduling may stay outside this implementation; this does not add Python to the 1.0 gate. These assumptions still require your acceptance before planning proceeds.

    2. May Add the PyO3 Python API over grund-core and prove binding parity #470 own the initial shared parity harness, or should a peer-owned contract land first? For example, answering “Add the PyO3 Python API over grund-core and prove binding parity #470 starts it” puts the shared fixture corpus, complete-data comparator and canonical JSON contract here: Rust and Python would process the json-report fixture and compare every field and the specified wire bytes, with Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 later adding Node to that same harness. Answering “Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 owns it first” puts the shared contract/infrastructure there and makes this proposal consume it, while Add the PyO3 Python API over grund-core and prove binding parity #470 still owns the Python adapter. If another ticket owns it, please name that ticket. In either case, Prepare cross-registry CLI/LSP packages, PGO artifacts, and gated publication workflows #471 consumes the extension's source/build contract, with ABI and layout coordinated in the proposal.

      Why needed: Both binding tickets reserve shared-harness ownership, and the answer changes which shared artifacts this proposal creates and which it must wait for.

    … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.clarify/clarification.md

    14 · supervising → supervising 2026-10-06 00:12Z
    15 · ticket.plan · planning → discussing 2026-10-06 01:08Z

    proposal runtime/plan/proposal-1.md

    1. What changes

    from grund import check, show
    repo = "tests/e2e/cases/json-report/repo"
    result = check(repo)
    for finding in result.report:
        print(finding.code, finding.line)  # dangling 3
    assert "FS-999-missing" in show("FS-001-alpha", root=repo, mode="brief").body

    Today the checkout has no Python frontend; the recorded import attempt fails with ModuleNotFoundError. After local build/install, Python can inspect that fixture's findings and body through the Rust engine. Findings return normally; operations that cannot run raise structured exceptions. PyPI availability remains pending.

    2. The design

    crates/grund-py carries the PyO3 conversion layer over grund-core, realizing §AR-bindings.6. Reuse warning-preserving APIs, selectors, queries and writers. Missing warning/failure carriers and integration-install orchestration become additive core adapters; CLI bytes stay unchanged. No frontend dependency, CLI subprocess or duplicate engine logic.

    Choose frozen Python dataclasses, tuples for collections, and a read-only schema-keyed mapping for effective config. Optional values are always present as None; empty collections stay empty. Preserve columns, sites, authority and separate run_cautions. CheckResult contains complete report, selected_report, had_scan_errors and output_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal Rust Config/Findings layouts.

    Under GrundError, choose ConfigError, FilesystemError, QueryError, and fallback OperationError. Each has a typed .failure payload: code/message, nullable location, sites/authority, causes, cautions, partial output and details such as candidates or OS error codes. Add missing classification at its engine source, preserving Display text; never parse locations from messages. Single unsuccessful queries raise QueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raise TypeError; invalid option values raise ValueError.

    Tree calls accept str/os.PathLike[str]. Optional root=None snapshots cwd at entry and means omitted scope; explicit files/directories retain explicit-path semantics. No cwd changes, argv reads, printing or exit. Reuse config discovery, without config injection; result paths retain engine / spelling. Reject bytes, surrogate-containing paths and NUL; PathEncodingError(ValueError) identifies encoding rejection. A core preflight reuses workspace member discovery to refuse the known unnamed non-Unicode member case before alias derivation; the CLI's recorded deviation remains unchanged. This is bounded input handling, not a promise to recover arbitrary Rust panics.

    … cut at 3000 characters; the whole file is runtime/plan/proposal-1.md

    proposal runtime/plan/proposal.md

    1. What changes

    from grund import check, show
    repo = "tests/e2e/cases/json-report/repo"
    result = check(repo)
    for finding in result.report:
        print(finding.code, finding.line)  # dangling 3
    assert "FS-999-missing" in show("FS-001-alpha", root=repo, mode="brief").body

    Today the checkout has no Python frontend; the recorded import attempt fails with ModuleNotFoundError. After local build/install, Python can inspect that fixture's findings and body through the Rust engine. Findings return normally; operations that cannot run raise structured exceptions. PyPI availability remains pending.

    2. The design

    crates/grund-py carries the PyO3 conversion layer over grund-core, realizing §AR-bindings.6. Reuse warning-preserving APIs, selectors, queries and writers. Missing warning/failure carriers and integration-install orchestration become additive core adapters; CLI bytes stay unchanged. No frontend dependency, CLI subprocess or duplicate engine logic.

    Choose frozen Python dataclasses, tuples for collections, and a read-only schema-keyed mapping for effective config. Optional values are always present as None; empty collections stay empty. Preserve columns, sites, authority and separate run_cautions. CheckResult contains complete report, selected_report, had_scan_errors and output_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal Rust Config/Findings layouts.

    Under GrundError, choose ConfigError, FilesystemError, QueryError, and fallback OperationError. Each has a typed .failure payload: code/message, nullable location, sites/authority, causes, cautions, partial output and details such as candidates or OS error codes. Add missing classification at its engine source, preserving Display text; never parse locations from messages. Single unsuccessful queries raise QueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raise TypeError; invalid option values raise ValueError.

    Tree calls accept str/os.PathLike[str]. Optional root=None snapshots cwd at entry and means omitted scope; explicit files/directories retain explicit-path semantics. No cwd changes, argv reads, printing or exit. Reuse config discovery, without config injection; result paths retain engine / spelling. Reject bytes, surrogate-containing paths and NUL; PathEncodingError(ValueError) identifies encoding rejection. A core preflight reuses workspace member discovery to refuse the known unnamed non-Unicode member case before alias derivation; the CLI's recorded deviation remains unchanged. This is bounded input handling, not a promise to recover arbitrary Rust panics.

    … cut at 3000 characters; the whole file is runtime/plan/proposal.md

    16 · ticket.plan · discussing → completed 2026-10-06 01:09Z

    response runtime/plan/response.md

    The author approved the plan by reaction

    Round 1 of 3, posted at: #470 (comment)

    vjovanov reacted 👍 to round 1 and wrote nothing further.
    That is approval of the plan as proposed: implement it as written.
    No part of it was amended, and nothing was added - where the plan left
    something to your judgement, it is still yours.

    17 · supervising → supervising 2026-10-06 00:12Z
    18 · ticket.dependencies · dependencies → completed 2026-10-06 01:20Z · `DEPENDENCIES: clear`

    dependencies runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.dependencies/dependencies.md

    Dependencies for #470

    No prerequisite ticket was declared by the approved proposal.

    DEPENDENCIES: clear

    19 · supervising → supervising 2026-10-06 00:12Z
    20 · ticket.specify · specify → opening 2026-10-06 01:46Z · `PR: https://github.com//pull/480`

    contract runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.specify/contract.md

    Specification and failing acceptance contract for #470

    The approved Python frontend must satisfy the public API, local build, complete-data/canonical-byte parity and host-isolation contract below; the draft PR for #470 is pending the following opening program.

    Spec/test commit: 820b289.
    Acceptance command (from the repository checkout, with this checkout's local Python distribution installed and the core-only oracle built):

    python tests/bindings/run.py

    Before any implementation this command exits 1. Exact terminating failure:

    ModuleNotFoundError: No module named 'grund'
    

    Full traceback and exit are preserved in runtime/spec/acceptance-failure.log. This is the missing capability established in triage, not a syntax error, an unrelated registry installation, or a skip. The entrypoint verifies local-source provenance once import exists. No task-specific reproducer existed to port.

    Specification IDs written or changed

    • §FS-distribution.1 docs/functional-spec/FS-distribution.md:15
    • §FS-distribution.3 docs/functional-spec/FS-distribution.md:60
    • §FS-distribution.3.0 docs/functional-spec/FS-distribution.md:68
    • §FS-distribution.3.0.1 docs/functional-spec/FS-distribution.md:78
    • §FS-distribution.3.0.2 docs/functional-spec/FS-distribution.md:116
    • §FS-distribution.3.0.3 docs/functional-spec/FS-distribution.md:135
    • §FS-distribution.3.1 docs/functional-spec/FS-distribution.md:165
    • §FS-distribution.3.3 docs/functional-spec/FS-distribution.md:198
    • §FS-distribution.3.3.1 docs/functional-spec/FS-distribution.md:216
    • §FS-distribution.3.3.2 docs/functional-spec/FS-distribution.md:231
    • §FS-distribution.3.3.3 docs/functional-spec/FS-distribution.md:244
    • §FS-distribution.3.3.4 docs/functional-spec/FS-distribution.md:256
    • §FS-distribution.3.3.5 docs/functional-spec/FS-distribution.md:264
    • §FS-distribution.3.3.6 docs/functional-spec/FS-distribution.md:301
    • §FS-distribution.3.3.7 docs/functional-spec/FS-distribution.md:310
    • §AR-bindings.placement docs/architecture/AR-bindings.md:5
    • §AR-bindings.1 docs/architecture/AR-bindings.md:27
    • §AR-bindings.2 docs/architecture/AR-bindings.md:52
    • §AR-bindings.6 docs/architecture/AR-bindings.md:96
    • §AR-goal-measurement.2 docs/architecture/AR-goal-measurement.md:47

    All FS detail remains below the existing distribution declaration with its single Terms chapter. Python local support is a required target, not a claim of current implementation or published PyPI availability. Node coverage remains pending.

    Every added test and today's evidence

    • tests/bindings/test_api.py:43 — test_exact_callable_defaults_keyword_policy_and_types: BLOCKED by the entrypoint's ModuleNotFoundError; not individually exercised.
    • tests/bindings/test_api.py:63 — test_check_is_immutable_complete_and_iterable: BLOCKED by the entrypoint's ModuleNotFoundError; not individually exercised.
      … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.specify/contract.md

    pr runtime/spec/pr.md

    PR: #480

    21 · ticket.specify · opening → completed 2026-10-06 01:46Z
    22 · supervising → supervising 2026-10-06 00:12Z
    23 · ticket.implement · implement → completed 2026-10-06 02:25Z

    report runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.implement/report.md

    {
      "summary": "Adds the complete synchronous disk-backed Python operation inventory through import grund, with frozen typed records, structured operational/query failures, complete and selected reports, retained run cautions and explicit write opt-ins. The private abi3 CPython extension delegates to additive core adapters, releases the GIL and checks interrupts on return. Per-call scope includes a zero-config fallback without changing process cwd. Local wheel/sdist metadata, typing, runnable guides/examples and a core-only parity oracle with independent canonical encoding and frozen CLI projection are supplied.",
      "root_cause": "The Python frontend and parity adapter were absent, matching the approved diagnosis; the supported engine also needed additive carriers for structured source failures, cautions, batch data and integration orchestration.",
      "commits": [
        "17e6647c84eaee77a409594b8a61193fc987d82d"
      ],
      "files": [
        "Cargo.lock",
        "Cargo.toml",
        "README.md",
        "crates/grund-core/Cargo.toml",
        "crates/grund-core/examples/binding_oracle/canonical.rs",
        "crates/grund-core/examples/binding_oracle/cli.rs",
        "crates/grund-core/examples/grund-binding-oracle.rs",
        "crates/grund-core/src/api/cover.rs",
        "crates/grund-core/src/api/embedding.rs",
        "crates/grund-core/src/api/embedding_config.rs",
        "crates/grund-core/src/api/embedding_data.rs",
        "crates/grund-core/src/api/embedding_failure.rs",
        "crates/grund-core/src/api/embedding_queries.rs",
        "crates/grund-core/src/api/embedding_writers.rs",
        "crates/grund-core/src/api/fmt.rs",
        "crates/grund-core/src/api/mod.rs",
        "crates/grund-core/src/api/refs_query.rs",
        "crates/grund-core/src/api/show.rs",
        "crates/grund-core/src/config/call_scope.rs",
        "crates/grund-core/src/config/discovery.rs",
        "crates/grund-core/src/config/mod.rs",
        "crates/grund-core/src/config/parse.rs",
        "crates/grund-core/src/grammar/shorthand.rs",
        "crates/grund-core/src/lib.rs",
        "crates/grund-core/src/model/failure.rs",
        "crates/grund-core/src/model/mod.rs",
        "crates/grund-core/src/queries/batch.rs",
        "crates/grund-core/src/queries/mod.rs",
        "crates/grund-core/src/queries/show.rs",
        "crates/grund-core/src/queries/size_output.rs",
        "crates/grund-core/src/queries/sizes.rs",
        "crates/grund-core/src/resolver/id_candidates.rs",
        "crates/grund-core/src/scanner/legacy.rs",
        "crates/grund-core/src/workspace/id_arg.rs",
        "crates/grund-core/src/workspace/mod.rs",
        "crates/grund-core/src/workspace/preflight.rs",
        "crates/grund-core/src/workspace/scope.rs",
        "crates/grund-core/src/writers/fetch.rs",
        "crates/grund-core/src/writers/init.rs",
        "crates/grund-core/src/writers/init_output.rs",
        "crates/grund-core/src/writers/integrations_api.rs",
        "crates/grund-core/src/writers/integrations_guidance.rs",
        "crates/grund-core/src/writers/mod.rs",
        "crates/grund-py/Cargo.toml",
        "crates/grund-py/README.md",
    … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.implement/report.md
    
    </details>
    
    <details><summary><b>24 · supervising → supervising</b> 2026-10-06 00:12Z</summary>
    
    </details>
    
    <details><summary><b>25 · ticket.review-1 · review → completed</b> 2026-10-06 02:34Z</summary>
    
    _findings_ `runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.review-1/findings.md`
    
    ```json
    {
      "round": 1,
      "verdict": "changes-requested",
      "findings": [
        {
          "id": "R1-01",
          "severity": "major",
          "category": "test-contract",
          "file": "tests/bindings/test_parity.py",
          "line": 50,
          "summary": "The frozen CLI assertion compares stderr findings against stdout and makes valid empty-scan parity fail.",
          "repro": "env TMPDIR=\"$HOME/ag/tmp\" PYTHONPATH=tests/bindings \"$HOME/ag/tmp/grund-470-check/bin/python\" -m unittest -v test_parity test_isolation: exit 1; test_shared_read_corpus_complete_data_and_bytes fails for check-empty-json and check-empty-scan-warning with 'Lists differ: [{'severity': 'warning', 'path': None, 'line': None, 'code': 'empty-scan', ...}] != []'. Complete Rust/Python fields and canonical bytes compare successfully before this assertion. cli_check.rs:267 sends absent-line findings to stderr, and the oracle retains them there. Full output: runtime/review-focused.log.",
          "fix": "Compare the independently constructed, ordered JSON records and exact bytes separately for stdout and stderr, retaining null-line findings and separate run-caution rendering; preserve the existing CLI/oracle streams.",
          "criterion": "Approved complete-data/canonical-byte parity and authoritative CLI streams; §FS-distribution.3.0.3 docs/functional-spec/FS-distribution.md:135."
        },
        {
          "id": "R1-02",
          "severity": "major",
          "category": "test-contract",
          "file": "tests/integration/test_frontend_isolation.py",
          "line": 17,
          "summary": "The existing workspace frontend inventory rejects the newly added grund-py member.",
          "repro": "python tests/integration/test_frontend_isolation.py: exit 1; test_every_frontend_depends_directly_on_the_engine_and_on_no_other_member fails at line 82: 'Items in the second set but not the first: grund-py : a new frontend crate joins the isolation contract'. The remaining four tests pass. Full output: runtime/review-isolation.log.",
          "fix": "Add grund-py to FRONTENDS and retain every existing direct-dependency, closure and transport isolation assertion.",
          "criterion": "Ticket dependency-isolation acceptance and the supervisor's required fixed-inventory maintenance."
        },
        {
          "id": "R1-03",
          "severity": "major",
          "category": "test-contract",
          "file": "tests/bindings/test_parity.py",
          "line": 64,
          "summary": "The new golden comparison treats the existing lone-newline empty-stream sentinel as actual CLI output.",
    … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.review-1/findings.md
    
    </details>
    
    <details><summary><b>26 · supervising → supervising</b> 2026-10-06 00:12Z</summary>
    
    </details>
    
    <details><summary><b>27 · ticket.fix-1 · fix → completed</b> 2026-10-06 02:41Z</summary>
    
    _resolutions_ `runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-1/resolutions.md`
    
    ```json
    {
      "round": 1,
      "resolutions": [
        {
          "id": "R1-01",
          "status": "fixed",
          "commit": "705e6b192b7577c8c321279baf2fbe4c728f5416",
          "note": "tests/bindings/test_parity.py constructs ordered finding records independently from host fields, partitions them by nullable line into stdout/stderr, checks parsed records/key order and exact UTF-8 bytes, and renders run cautions separately before stderr findings. Stable channel ties, sites/authority null conversion, authority-last and absent columns remain asserted. CLI and oracle sources are unchanged. Syntax checked; behavioral tests unrun as required by the brief."
        },
        {
          "id": "R1-02",
          "status": "fixed",
          "commit": "705e6b192b7577c8c321279baf2fbe4c728f5416",
          "note": "tests/integration/test_frontend_isolation.py adds grund-py to the fixed FRONTENDS set. Inventory equality and all direct-dependency, closure and transport assertions remain intact; the separate CLI-only build guard is unchanged. Syntax checked; isolation tests unrun."
        },
        {
          "id": "R1-03",
          "status": "fixed",
          "commit": "705e6b192b7577c8c321279baf2fbe4c728f5416",
          "note": "tests/bindings/test_parity.py reads golden bytes and maps only exact b'\\n' to an empty stream, matching the existing CLI golden reader. Every other byte is compared exactly with UTF-8 oracle streams; fixtures and production output are unchanged. Syntax checked; golden comparisons unrun."
        },
        {
          "id": "R1-04",
          "status": "fixed",
          "commit": "705e6b192b7577c8c321279baf2fbe4c728f5416",
          "note": "crates/grund-core/src/writers/fetch.rs and fetch_write.rs retain original execution/read/metadata/install I/O errors through the private additive diagnostic carrier. Filesystem classification, located executable or snapshot target, source chain and raw OS code reach the existing Python FilesystemError converter, with run cautions preserved. Public FetchFailure and Rust signatures, CLI refusal text, write opt-in and rollback behavior remain unchanged. tests/bindings/test_regressions.py asserts class, required fields, nonempty causes/cautions, ENOENT/EACCES, full envelope/canonical parity and unchanged bytes. Core/native/oracle compiled; regressions unrun and review-2 is required. Permission coverage requires an unprivileged POSIX user; unexpected success fails, while root and non-POSIX constraints are explicit."
        },
        {
          "id": "R1-05",
          "status": "fixed",
          "commit": "705e6b192b7577c8c321279baf2fbe4c728f5416",
    … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-1/resolutions.md
    
    </details>
    
    <details><summary><b>28 · supervising → supervising</b> 2026-10-06 00:12Z</summary>
    
    </details>
    
    <details><summary><b>29 · ticket.review-2 · review → completed</b> 2026-10-06 02:47Z</summary>
    
    _findings_ `runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.review-2/findings.md`
    
    ```json
    {
      "round": 2,
      "verdict": "changes-requested",
      "findings": [
        {
          "id": "R2-01",
          "severity": "major",
          "category": "correctness",
          "file": "crates/grund-core/src/writers/fetch_write.rs",
          "line": 198,
          "summary": "Snapshot-home traversal still reduces filesystem failures to message-only OperationError payloads, leaving a path of R1-04 unresolved.",
          "repro": "env TMPDIR=\"$HOME/ag/tmp\" \"$HOME/ag/tmp/grund-470-check/bin/python\" panta/github-issues-agent-grounds-grund-470-implement-379c3f6c/runtime/review2-probes.py (exit 0, assertions reproduce the defect). With freshly installed HEAD 705e6b192b, uid=1000 on POSIX, the script copies fetch-workspace-folder and chmods packages/alpha/docs/tickets to 000. Independent os.scandir prints \"PermissionError errno=13\". grund.fetch(\"alpha/TICKET-1234\", root=root, write=True) prints \"denied folder traversal exception: OperationError\" with kind=\"operation\", code=\"fetch\", path=null, causes=[], details={}, and message \"cannot read snapshot folder .../docs/tickets at .../docs/tickets: permission denied\". The core oracle agrees on the incomplete payload. Permissions are restored in finally and fixture bytes are unchanged. Exact setup/output: runtime/review2-probes.py and runtime/review2-probes.log.",
          "fix": "Preserve source-classified traversal failures and their locations/cause chains/OS codes through an additive engine diagnostic carrier into FilesystemError; pin denied snapshot-folder traversal directly while preserving existing scanner and CLI output/signatures.",
          "criterion": "Remaining R1-04 structured-filesystem failure obligation; approved proposal design and ticket scope point 4 require source classification, locations, original causes and OS codes. §FS-distribution.3.3.2 docs/functional-spec/FS-distribution.md:231."
        }
      ]
    }
    30 · supervising → supervising 2026-10-06 00:12Z
    31 · ticket.fix-2 · fix → completed 2026-10-06 02:57Z

    resolutions runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-2/resolutions.md

    {
      "round": 2,
      "resolutions": [
        {
          "id": "R2-01",
          "status": "fixed",
          "commit": "fa57931b179e99f09c2aae356b597433f4906f68",
          "note": "Implemented the remaining R1-04 snapshot-home traversal repair required by §FS-distribution.3.3.2 docs/functional-spec/FS-distribution.md:231; runtime verification remains unrun for green. Changed crates/grund-core/src/scanner/{mod.rs,walk.rs,walk_reporting.rs}, crates/grund-core/src/writers/fetch_write.rs and tests/bindings/test_regressions.py. The existing reporting walk now hands off the owned original ignore::Error only after its existing reporting gates admit a record. Fetch matches the source by the complete path/message record after unchanged sorting/deduplication, preserves the original ignore context, and exposes its original embedded std::io::Error through a private source adapter. Classification and raw OS code come from that actual source, without Display parsing, recreated I/O errors, or hardcoded EACCES. The walk's fallible Result also retains its original error and folder location in the additive diagnostic carrier. Public Rust signatures/records, scanner records/filter semantics, CLI refusal text, write/refusal ordering, rollback, and the Python schema remain unchanged. The new discoverable test independently requires os.scandir PermissionError/EACCES on a chmod-000 snapshot home, directly asserts FilesystemError/filesystem/io, the directory path, original I/O cause, OS detail and cautions, compares complete envelopes/canonical bytes, and restores permissions before comparing fixture bytes. Existing missing-executable/denied-write assertions remain intact. This requires an unprivileged POSIX user: non-POSIX/root skips supply no permission-case evidence; unexpected traversal success fails. The initial staged-size check failed on walk.rs soft-edit promotion; moving its unchanged reporting pass/identity helpers into a sibling scanner module made the final check pass, with only a nonblocking fetch_write.rs size advisory. All ci entries are final successful verification results; no tests, acceptance, pre-commit gate or hosted CI were run, and no push/lifecycle transition was performed. Exact checking commands and platform requirements are in runtime/results/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-2.md."
        }
      ],
      "ci": {
        "cargo fmt --all": "pass",
        "cargo build --locked -p grund-py -p grund-core --target-dir target": "pass",
        "env RUSTFLAGS=-Dwarnings cargo build --workspace --all-targets --locked --target-dir target": "pass",
        "python -c 'import ast; from pathlib import Path; ast.parse(Path(\"tests/bindings/test_regressions.py\").read_text())'": "pass",
        "cargo fmt --all -- --check": "pass",
        "git diff --check": "pass",
        "git diff --cached --check": "pass",
        "fissile check --staged": "pass"
      }
    }
    32 · supervising → supervising 2026-10-06 00:12Z
    33 · ticket.green · green → green-fix 2026-10-06 03:10Z · `READY: yes`

    green runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.green/green.md

    The gate, run 3

    Checkout /home/vjovanov/ag/grund/fix/issue-470 on fix/issue-470 at bba278fc06; 3 command(s), 8m42s in all.

    grund: the installed grund 0.15.1-dev, since the checkout pins none.

    check command exit time
    1 mkdir -p "$HOME/ag/tmp" && python3 -m venv "$HOME/ag/tmp/grund-470-green" && env TMPDIR="$HOME/ag/tmp" CARGO_TARGET_DIR="$PWD/target" "$HOME/ag/tmp/grund-470-green/bin/python" -m pip install --no-cache-dir --force-reinstall . && cargo build --locked -p grund-core --example grund-binding-oracle --target-dir target && cp target/debug/examples/grund-binding-oracle target/debug/grund-binding-oracle 0 0m02s
    2 env TMPDIR="$HOME/ag/tmp" "$HOME/ag/tmp/grund-470-green/bin/python" -c 'import os; assert os.name == "posix" and os.geteuid() != 0, "Permission regressions require an unprivileged POSIX account"' && env TMPDIR="$HOME/ag/tmp" "$HOME/ag/tmp/grund-470-green/bin/python" tests/bindings/run.py 0 1m43s
    3 env TMPDIR=/dev/shm pre-commit run --all-files 0 6m57s

    1. mkdir -p "$HOME/ag/tmp" && python3 -m venv "$HOME/ag/tmp/grund-470-green" && env TMPDIR="$HOME/ag/tmp" CARGO_TARGET_DIR="$PWD/target" "$HOME/ag/tmp/grund-470-green/bin/python" -m pip install --no-cache-dir --force-reinstall . && cargo build --locked -p grund-core --example grund-binding-oracle --target-dir target && cp target/debug/examples/grund-binding-oracle target/debug/grund-binding-oracle - passed

    Verdict lines:

          Successfully uninstalled grund-0.16.2.dev0
    Successfully installed grund-0.16.2.dev0
    [notice] A new release of pip is available: 23.2.1 -> 26.2.1
    [notice] To update, run: /home/vjovanov/ag/tmp/grund-470-green/bin/python -m pip install --upgrade pip
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s
    

    2. env TMPDIR="$HOME/ag/tmp" "$HOME/ag/tmp/grund-470-green/bin/python" -c 'import os; assert os.name == "posix" and os.geteuid() != 0, "Permission regressions require an unprivileged POSIX account"' && env TMPDIR="$HOME/ag/tmp" "$HOME/ag/tmp/grund-470-green/bin/python" tests/bindings/run.py - passed

    Verdict lines:

    test_explicit_symlink_parent_root_matches_engine (test_regressions.RegressionTests.test_explicit_symlink_parent_root_matches_engine) ... ok
    test_missing_fetch_executable_retains_io_source (test_regressions.RegressionTests.test_missing_fetch_executable_retains_io_source) ... ok
    ----------------------------------------------------------------------
    Ran 33 tests in 103.158s
    OK
    

    3. env TMPDIR=/dev/shm pre-commit run --all-files - passed

    Verdict lines:

    grund fmt................................................................Passed
    grund init --check (managed block is current)............................Passed
    lychee...................................................................Passed
    fissile (file size budgets)..............................................Passed
    … cut at 3000 characters; the whole file is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.green/green.md
    
    </details>
    
    <details><summary><b>34 · ticket.green · green-fix → green</b> 2026-10-06 03:13Z</summary>
    
    _fix_ `runtime/green/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.green/fix-1.md`
    
    - `env TMPDIR="$HOME/ag/tmp" "$HOME/ag/tmp/grund-470-green/bin/python" tests/bindings/run.py`: `test_denied_fetch_snapshot_traversal_retains_io_source` failed with `AssertionError: 13 != None`; embedding failure conversion now finds the original OS-backed I/O cause through the retained source chain before falling back to an I/O wrapper, without parsing messages or synthesizing errno; after rebuilding/reinstalling the native package and rebuilding/copying the oracle, the unchanged focused regression passed as an unprivileged POSIX user, including complete/canonical parity, original causes, cautions and unchanged bytes.
    - `cargo test --workspace --all-targets --locked --features grund/test-workspace-load-count` (pre-commit `cargo-test`): `a_zero_config_folder_uses_its_anchor_for_hover_reads` failed with `hover must rescan the second zero-config anchor, not the server cwd: null`; reproduced under `TMPDIR="$HOME/ag/tmp"`, whose ancestor workspace config contaminates this zero-config fixture; the unchanged focused test passed with `TMPDIR=/dev/shm`, and the orchestration gate command now uses that permitted temporary storage for pre-commit while scratch builds/installations remain under `~/ag/tmp`; no LSP code or test changed.
    - `python -m unittest discover -s tests/integration -p test_*.py` (pre-commit `python-test`): `test_every_public_root_name_has_an_inventory_row` named seven missing additive exports; added their classified rows and corresponding counts to the public-surface inventory, including Python consumer evidence; the failed test and all five inventory checks passed without changing tests or specifications.
    - `cargo run --quiet -- fmt --write` (pre-commit `grund-fmt`): the hook rewrote one citation link in `crates/grund-py/README.md`; retained its relative declaration link, and `env TMPDIR=/dev/shm pre-commit run grund-fmt --all-files` passed without further changes.
    
    _fix_ `runtime/green/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.green/fix-2.md`
    
    - `env TMPDIR=/dev/shm pre-commit run --all-files` (lychee): GitHub returned HTTP 500 for `https://github.com/agent-grounds/grund/pull/389` in `docs/changelog/0.16.0.md`; reran only `env TMPDIR=/dev/shm pre-commit run lychee --all-files`, which exited 0 (`Passed`); transient remote failure cleared without repository changes.
    
    </details>
    
    <details><summary><b>35 · ticket.green · green → green-fix</b> 2026-10-06 03:10Z</summary>
    
    </details>
    
    <details><summary><b>36 · ticket.green · green-fix → green</b> 2026-10-06 03:13Z</summary>
    
    </details>
    
    <details><summary><b>37 · ticket.green · green → completed</b> 2026-10-06 03:10Z</summary>
    
    </details>
    
    <details><summary><b>38 · supervising → supervising</b> 2026-10-06 00:12Z</summary>
    
    </details>
    
    <details><summary><b>39 · ticket.describe · describe → completed</b> 2026-10-06 12:36Z</summary>
    
    _description_ `runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.describe/description.md`
    
    ```json
    {
      "title": "Add the Python API over grund-core and prove binding parity",
      "body_path": "runtime/supervision/pr-description.md",
      "ticket": "#470",
      "steps_summarized": 24,
      "accounting": "measured",
      "notes": "Set PR #480's title to the exported suggestion when shipping; its current title is still 'Specify the Python API and pin complete binding parity'. This gh client's pr edit fails on deprecated projectCards; the body was successfully applied through gh api PATCH and verified byte-for-byte. PR #480 remains draft at bba278fc068ff8594e39dd29ddf3f29a8cac9a8b; hosted CI and merge remain for ship."
    }
    

    pr-description runtime/supervision/pr-description.md

    Closes #470

    Before this change, the checkout had no Python frontend: import grund failed with ModuleNotFoundError. After a local install with CPython 3.10+ and Rust (python -m pip install . from the checkout), an application can check a tree, iterate its findings and read a declaration:

    from grund import check, show
    
    repo = "tests/e2e/cases/json-report/repo"
    result = check(repo)
    for finding in result.report:
        print(finding.code, finding.line)  # dangling 3
    assert "FS-999-missing" in show("FS-001-alpha", root=repo, mode="brief").body

    The output above comes from the caller's print; the API itself is silent. The Python guide and runnable example use this fixture.

    The complete initial disk-backed inventory includes check/selectors and report iteration, scan, single/batch show, refs, ID lists and sizes, coverage/text, formatting, ID proposal, init, effective configuration and validation, fetch, managed integrations, ID completion, reference style and agent setup instructions. Required operands are positional and other options are keywords with documented defaults. Process transport, watch/exit lifecycle and LSP editor utilities retain their stated frontend scope. §FS-distribution.3.3.5

    Results are frozen dataclasses with tuple collections and explicit None for optional fields; effective configuration is recursively read-only. CheckResult keeps both the complete report and selected_report, plus separate run_cautions. Findings preserve locations, all sites, suggestions and rule authority. A completed check with findings returns normally. §FS-distribution.3.3.1

    Operations that cannot run raise ConfigError, FilesystemError, QueryError or fallback OperationError, each carrying a structured .failure with causes, details, cautions and partial output. Single query failures raise; batch queries retain individual failures in ordered records. Fetch execution, snapshot writes and snapshot traversal retain filesystem locations and original I/O causes. The final conversion fix extracts OS error codes from the retained source chain, without parsing messages or synthesizing codes. §FS-distribution.3.3.2

    … cut at 3000 characters; the whole file is runtime/supervision/pr-description.md

    40 · supervising → supervising 2026-10-06 00:12Z
    41 · ticket.ship · ship → ship-fix 2026-10-06 12:38Z

    shipped runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/shipped.md

    {
      "pr": 480,
      "url": "https://github.com/agent-grounds/grund/pull/480",
      "branch": "fix/issue-470",
      "merged": true,
      "merge_commit": "dbc2d00362777ca051c5b68fe7531cc6c5c2ea5b",
      "ci": {
        "cargo test (ubuntu-latest)": "pass",
        "cargo test (macos-latest)": "pass",
        "cargo test (windows-latest)": "pass",
        "commit message attribution gate": "pass",
        "cargo bench (instruction counts)": "pass"
      },
      "fixups": [
        "Set and verified PR #480's title to “Add the Python API over grund-core and prove binding parity”, preserving the accepted body and generated workflow summary.",
        "Preserve all upstream LSP completion exports alongside the Python adapter and warning-carrier exports in the core root export list.",
        "Preserve main's check-spec size exception and the Python parser allowance; retire the README exception because main completed its documented overview split.",
        "Combine completion and Python API inventory rows and audit provenance, recalculating all totals from the 193 retained rows.",
        "Add an exact-head reviewed-rebase handoff to this ticket's generated shipping script, preserving divergence rejection and the explicit force-with-lease.",
        "Record approval of fork bba278fc068ff8594e39dd29ddf3f29a8cac9a8b to local 984baf6ca391af56ee67f40bf60e658ee1818cd2 after reviewing all five replayed commits."
      ],
      "main": {
        "commit": "dbc2d00362777ca051c5b68fe7531cc6c5c2ea5b",
        "runs": {
          "CI": "success"
        },
        "status": "green"
      },
      "notes": "Merged as dbc2d00362777ca051c5b68fe7531cc6c5c2ea5b; main is green on it (1 run(s))."
    }
    42 · ticket.ship · ship-fix → ship 2026-10-06 12:39Z

    fix runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-1.md

    The prepare program called ship-fix for the supervisor's title-only correction. PR #480 still had the provisional title “Specify the Python API and pin complete binding parity”.

    Updated only the title through a structured gh api PATCH to repos/agent-grounds/grund/pulls/480. A subsequent forge read verified the exact requested title. The published body remains byte for byte identical to both its previous value and runtime/supervision/pr-description.md (SHA256 ae1030dd666d16b1a49e0ae1b3eeb78e9b8e5f7db357c9e55785755ad024d115). The draft status and head bba278f are unchanged.

    No repository source change, commit or gate run was needed for this metadata-only correction. No push, CI wait, ready action or merge was performed; the ship program owns those next steps and the shipped export.

    FIXUP: Set and verified PR #480's title to “Add the Python API over grund-core and prove binding parity”, preserving the accepted body and generated workflow summary.
    SHIP: retry

    fix runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-2.md

    The prepare program called ship-fix because rebasing fix/issue-470 onto origin/main conflicted in crates/grund-core/src/lib.rs and docs/file-size-agent-exceptions.toml. After git fetch origin, the rebase reproduced those conflicts and later also conflicted in docs/discussions/proposals/2026-09-22-grund-core-public-surface-inventory.md.

    Completed the rebase onto origin/main at 8455c37. The rebased branch head is 984baf6. The range comparison preserves all five original change commits, with only the conflict resolutions described below. Resolutions are committed in the replayed implementation and inventory commits; no additional fix commit is needed.

    Rebuilt/reinstalled the native package with TMPDIR under ~/ag/tmp and CARGO_TARGET_DIR set to the checkout target, then rebuilt/copied the current core-only oracle with --target-dir target. All 33 binding tests passed in 107.133s, including clean-checkout/unpacked-sdist installs, complete/canonical and CLI-golden parity, mutation bytes, host boundaries, isolation and both denied-permission regressions under UID 1000. Five existing public-surface inventory tests passed. Logs are bindings-2.log and pre-commit-2.log beside this report.

    env TMPDIR=/dev/shm pre-commit run --all-files exited 0 with all ten hooks Passed: cargo fmt, warnings-as-errors build, Rust tests, Python integration tests, grund check, grund fmt, managed init check, lychee, fissile and attribution checks. fissile check --staged and git diff --check also passed. The tracked worktree is clean; .pre-commit-config.yaml is unchanged.

    Verified PR #480 retains the final title and the accepted body byte for byte (SHA256 ae1030dd666d16b1a49e0ae1b3eeb78e9b8e5f7db357c9e55785755ad024d115). No PR metadata/body changes, push, hosted-CI wait, ready action or merge were performed. The ship program continues to own those actions and the shipped export.

    FIXUP: Preserve all upstream LSP completion exports alongside the Python adapter and warning-carrier exports in the core root export list.
    FIXUP: Preserve main's check-spec size exception and the Python parser allowance; retire the README exception because main completed its documented overview split.
    FIXUP: Combine completion and Python API inventory rows and audit provenance, recalculating all totals from the 193 retained rows.
    SHIP: retry

    fix runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-3.md

    Prepare rejected the previous visit's completed conflict-resolved rebase as remote-only work. The fork still has the original reviewed head bba278f; this checkout has its five replayed commits at 984baf6, based on origin/main at 8455c37. git cherry reports two original patches as unmatched because conflict resolution changed their patch IDs. This is a generated shipping-script handoff defect, not new work pushed by someone else.

    Fetched fork/fix/issue-470 and reviewed all five pairs with git range-diff. Three patches are identical. The implementation differences preserve main's completion exports and completed README split; the inventory differences preserve both sets of additions and their counts. These match fix-2.md's recorded resolutions. No original change was discarded. Evidence is range-diff-3.log alongside this report.

    Updated only this execution root's generated scripts/ship-pr.sh to accept a reviewed rebase record matching the exact remote, branch, old remote head and new local head. Missing, malformed or mismatched approval still calls need_agent. The existing force-with-lease remains bound to the fetched remote head, a refused push remains a failure, and approval is consumed only after a successful push. Recorded this verified pair in approved-rebase.json. No shared template, state machine, task state or ship round limit was changed.

    bash -n passed. Eight focused cases exercised the actual sync_branch function: absent/malformed approval, each of the four mismatched fields, exact-head success and refused leased push. Network fetch/push were mocked; rejection never pushed, success retained the explicit lease and consumed approval, and refusal retained approval. Test source is ~/ag/tmp/grund-470-ship-guard/verify.py; output is guard-3.log.

    env TMPDIR=/dev/shm pre-commit run --all-files exited 0; all ten hooks Passed. Full output is pre-commit-3.log. fissile check --staged and git diff --check passed. Repository source remains clean and HEAD is unchanged; the generated script and handoff record are ignored orchestration files, so no repository commit is needed. The unchanged package/core remain covered by fix-2's successful post-rebase 33-test binding run.

    Verified PR #480's exact final title, original fork head and accepted body byte for byte (SHA256 ae1030dd666d16b1a49e0ae1b3eeb78e9b8e5f7db357c9e55785755ad024d115). No push, CI wait, ready action, merge or PR metadata change was performed. The ship program owns those actions and the shipped export. The script belongs to the custom agent-grounds workspace template rather than rhei's own repository; rhei behaved correctly, so no tool-defect ticket was filed.

    FIXUP: Add an exact-head reviewed-rebase handoff to this ticket's generated shipping script, preserving divergence rejection and the explicit force-with-lease.
    … cut at 3000 characters; the whole file is runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-3.md

    43 · ticket.ship · ship → ship-fix 2026-10-06 12:38Z
    44 · ticket.ship · ship-fix → ship 2026-10-06 12:39Z
    45 · ticket.ship · ship → ship-fix 2026-10-06 12:38Z
    46 · ticket.ship · ship-fix → ship 2026-10-06 12:39Z
    47 · ticket.ship · ship → awaiting-checks 2026-10-06 12:38Z
    48 · ticket.ship · awaiting-checks → merging 2026-10-06 13:03Z
    49 · ticket.ship · merging → watching-main 2026-10-06 13:35Z
    50 · ticket.ship · watching-main → completed 2026-10-06 13:35Z

    Also on disk: runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.plan/proposal.md

    Outcome completed · Verdict: shipped - #480 merged as dbc2d00.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage · Validated: kind feature, verdict none, fit fits, size complex. The export is runtime/exports/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.triage/validation.md.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.agora · Planned feature aligned with existing goals and requirements; no foundational disagreement requires an agora.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.clarify · The author accepted every assumption on #470 by reaction. The answers are in runtime/clarify/answers.md.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.plan · The author approved the plan on #470 by reaction. Their answer is in runtime/plan/response.md.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.dependencies · Every prerequisite declared for #470 is closed.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.specify · Draft pull request #480 opened from fix/issue-470 with the specification change and the failing test.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.implement · Implemented and committed the complete Python frontend, additive core adapters, local source/build handoff and shared parity oracle.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.review-1 · Changes requested: 5 major, 1 minor, 0 blocker, 0 nit and 0 adjacent findings.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-1 · Fixed all six scoped findings in commit 705e6b1; no finding was rejected or deferred, and tests and the full gate remain unrun for review-2 and green.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.review-2 · Changes requested: 1 major, 0 blocker, 0 minor, 0 nit and 0 adjacent findings.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.fix-2 · Implemented the R2-01 snapshot traversal source-retention fix in commit fa57931, with no rejected or deferred findings and behavioral verification left to green.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.green · The gate is green: 3 command(s) passed on bba278f in 8m42s.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.describe · Suggested title: “Add the Python API over grund-core and prove binding parity”.

    Outcome github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship · Pull request #480 merged as dbc2d00.

    Rendered from github-issues-agent-grounds-grund-470-implement-379c3f6c/tasks/01-ticket.md by scripts/lifecycle-sync.sh.

  5. added
    state:triageFiled; being checked against what is already known
    state:validatingReproducer being built, or fit against the ground being established
    state:decidingThe supervisor is choosing the path
    path:plannedA written plan, discussed on the ticket, before the fix
    state:clarifyingQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptions
    and removed
    state:triageFiled; being checked against what is already known
    state:validatingReproducer being built, or fit against the ground being established
    state:decidingThe supervisor is choosing the path
    on Oct 6, 2026
  6. vjovanov commented on Oct 6, 2026

    @vjovanov
    CollaboratorAuthor

    Questions before the plan, round 1 of 2 - please answer on this issue

    This ticket is about to be planned, or argued out in an agora. Before
    either, the few things it leaves open are put to you here. Reply on
    this issue
    with an answer to each, in any form; the work is on hold
    until then.

    Each question states the assumption it would otherwise proceed under,
    so a 👍 on this comment is a complete answer: it accepts every
    assumption as written. A 👎 stops the work and hands the ticket to a
    person. Words beat a reaction where both are present.

    Today, python3 -I -c 'from grund import check, show' fails with ModuleNotFoundError: No module named 'grund'. After this work, a local build/install would make that import succeed and let Python inspect the same findings as Rust, including the dangling citation in tests/e2e/cases/json-report/repo. This issue delivers that frontend and its parity evidence; package publication remains separately authorized.

    Your discussion boundary remains:

    This is a scoped proposal. Keep human-discussion until the owner resolves the open choices and releases it for implementation. Dependencies describe sequencing, not approval. Membership in the 1.0 scope is undecided; this is not a sub-issue of #347.

    1. Can this frontend land independently of the core API transition and 1.0, or should either gate it? For example, answering “plan independently; leave release scheduling open” allows a proposal using currently supported engine APIs, with a named adaptation task if Python lands before Complete the grund-core public API transition and retire only properly deprecated surfaces #466/Engine: introduce Project, Run, Compiled, and Catalog with lossless v1 lowering #453/Engine: separate schema conformance, relational checks, and rendered expectations #454. Answering “wait for Complete the grund-core public API transition and retire only properly deprecated surfaces #466” makes the completed replacement API a landing prerequisite; “after 1.0” instead defers landing until that release. The existing workspace extraction is already shipped; this question concerns the remaining public API transition and release placement.

      Why needed: The answer determines whether the proposal targets today's supported APIs with later adaptation or requires an upstream transition/release first.

      Assumption without an answer: Plan independently, assign no milestone or release date, and carry a named adaptation task if Python lands first. Release scheduling may stay outside this implementation; this does not add Python to the 1.0 gate. These assumptions still require your acceptance before planning proceeds.

    2. May Add the PyO3 Python API over grund-core and prove binding parity #470 own the initial shared parity harness, or should a peer-owned contract land first? For example, answering “Add the PyO3 Python API over grund-core and prove binding parity #470 starts it” puts the shared fixture corpus, complete-data comparator and canonical JSON contract here: Rust and Python would process the json-report fixture and compare every field and the specified wire bytes, with Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 later adding Node to that same harness. Answering “Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 owns it first” puts the shared contract/infrastructure there and makes this proposal consume it, while Add the PyO3 Python API over grund-core and prove binding parity #470 still owns the Python adapter. If another ticket owns it, please name that ticket. In either case, Prepare cross-registry CLI/LSP packages, PGO artifacts, and gated publication workflows #471 consumes the extension's source/build contract, with ABI and layout coordinated in the proposal.

      Why needed: Both binding tickets reserve shared-harness ownership, and the answer changes which shared artifacts this proposal creates and which it must wait for.

      Assumption without an answer: Add the PyO3 Python API over grund-core and prove binding parity #470 owns the initial shared infrastructure and Python adapter, Add the napi-rs Node binding and typed Promise API for npm grund-cli #469 extends it with Node, and Prepare cross-registry CLI/LSP packages, PGO artifacts, and gated publication workflows #471 consumes the agreed source/build handoff. Full conceptual operation coverage remains the baseline; Node implementation and release-package assembly stay with their respective tickets. These assumptions still require your acceptance before planning proceeds.

    A 👍 accepts both assumptions for planning. The resulting Python API and build proposal will still need your explicit approval before implementation.


    Posted by an agent working this ticket. Round 1 of 2; after the last round the answers stand as given.

  7. added
    state:proposedA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejects
    and removed
    state:clarifyingQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptions
    on Oct 6, 2026
  8. vjovanov commented on Oct 6, 2026

    @vjovanov
    CollaboratorAuthor

    Proposed plan, round 1 of 3 - please confirm before I implement

    This ticket looks like a change to the API or the workflow, so the plan
    goes to you before any code is written. Reply on this issue and the
    fix will continue from what you say; it is on hold until then.

    If the plan is right as it stands, a 👍 on this comment is enough -
    it is read as "as proposed" and releases the step. A 👎 rejects it
    and hands the ticket to a human. Anything else you write is read as a
    revision: the planner rewrites the plan from your words and posts the
    next round here. Words beat a reaction where both are present.

    1. What changes

    from grund import check, show
    repo = "tests/e2e/cases/json-report/repo"
    result = check(repo)
    for finding in result.report:
        print(finding.code, finding.line)  # dangling 3
    assert "FS-999-missing" in show("FS-001-alpha", root=repo, mode="brief").body

    Today the checkout has no Python frontend; the recorded import attempt fails with ModuleNotFoundError. After local build/install, Python can inspect that fixture's findings and body through the Rust engine. Findings return normally; operations that cannot run raise structured exceptions. PyPI availability remains pending.

    2. The design

    crates/grund-py carries the PyO3 conversion layer over grund-core, realizing §AR-bindings.6. Reuse warning-preserving APIs, selectors, queries and writers. Missing warning/failure carriers and integration-install orchestration become additive core adapters; CLI bytes stay unchanged. No frontend dependency, CLI subprocess or duplicate engine logic.

    Choose frozen Python dataclasses, tuples for collections, and a read-only schema-keyed mapping for effective config. Optional values are always present as None; empty collections stay empty. Preserve columns, sites, authority and separate run_cautions. CheckResult contains complete report, selected_report, had_scan_errors and output_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal Rust Config/Findings layouts.

    Under GrundError, choose ConfigError, FilesystemError, QueryError, and fallback OperationError. Each has a typed .failure payload: code/message, nullable location, sites/authority, causes, cautions, partial output and details such as candidates or OS error codes. Add missing classification at its engine source, preserving Display text; never parse locations from messages. Single unsuccessful queries raise QueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raise TypeError; invalid option values raise ValueError.

    Tree calls accept str/os.PathLike[str]. Optional root=None snapshots cwd at entry and means omitted scope; explicit files/directories retain explicit-path semantics. No cwd changes, argv reads, printing or exit. Reuse config discovery, without config injection; result paths retain engine / spelling. Reject bytes, surrogate-containing paths and NUL; PathEncodingError(ValueError) identifies encoding rejection. A core preflight reuses workspace member discovery to refuse the known unnamed non-Unicode member case before alias derivation; the CLI's recorded deviation remains unchanged. This is bounded input handling, not a promise to recover arbitrary Rust panics.

    Calls are synchronous and release the GIL during Rust work, following PyO3's threading model. Independent reads may overlap; callers serialize writers to the same files. Interrupts are checked on return; operations can finish before KeyboardInterrupt. No mid-call cancellation or added rollback.

    Choose CPython 3.10+ with GIL and abi3-py310, rather than per-interpreter wheels; this surface needs no version-specific native API (PyO3 ABI policy). PyPy and free-threaded builds are unsupported. Root pyproject.toml uses maturin, python/grund supplies public types/functions and py.typed, and private grund._native holds the extension (maturin layout). Distribution/import names remain grund. Include required workspace manifests/lockfile, Rust sources/assets, Python/types and licence in the sdist. Prove an unpacked-sdist install. #471 owns CLI installation; add no competing console entrypoint.

    3. The ground it moves

    • Extended §FS-distribution.1: distinguish implemented local API from unpublished registry distribution.
    • Extended §FS-distribution.3: require the complete operation inventory below and explicit transport exclusions.
    • Extended §FS-distribution.3.0.1: retain complete host data and run cautions separately from the frozen CLI projection.
    • Extended §FS-distribution.3.0.2: specify Python roots, keywords and query outcomes.
    • Extended §FS-distribution.3.1: additive structured-failure, path-validation and integration adapters; existing entry points remain.
    • Extended §FS-distribution.3.3: require the approved signatures, types, exceptions, paths, execution, writes and local build contract.
    • New FS-distribution.3.0.3: require complete-data and canonical-byte parity, as described below.
    • Extended §AR-bindings.placement: mark Python as an implemented independent frontend.
    • Extended §AR-bindings.1: add Python to workspace/isolation evidence.
    • Extended §AR-bindings.2: document the reused and additive data adapters.
    • Extended §AR-bindings.6: record concrete conversion/build placement.
    • Extended §AR-goal-measurement.2: record Rust/Python coverage and pending Node coverage.

    Requirements are served without amendment or contradiction:

    Requirement Obligation here
    §REQ-deterministic-output.1 Preserve ordering and exact bytes.
    §REQ-never-crashes.1 Return documented failures; bound the existing deviation.
    §REQ-no-data-loss.1 Reads never write.
    §REQ-no-data-loss.2 Writers preserve engine ownership.
    §REQ-no-data-loss.3 Force remains explicit and bounded.
    §REQ-runs-offline.1 Reads execute no fetcher.
    §REQ-runs-offline.2 Only explicit fetch materializes.
    §REQ-backwards-compatibility.2 Retire no existing surface.
    §REQ-no-missed-citation.1 Preserve incomplete-scan diagnostics.
    §REQ-no-wrong-citation.1 Keep ambiguous-query sites and refusals.
    §REQ-no-wrong-citation.3 Delegate rewrite completeness/refusals.
    §REQ-readme.2 Capture runnable examples.
    §REQ-shipped-surfaces.1 Ship portable documentation addresses.

    4. Existing seams and defaults

    Required operands are positional; other options are keywords. Tree operations also take root=None (check accepts it positionally); raw scan(root) requires an explicit root. Each returns a typed dataclass. Filters/selectors take string sequences; size units are lines/words/bytes, top is positive, and width is non-negative.

    Python operation/options Reused core operation
    check(require_grounding=False, suggestions=False, full=False, rule=None, only=(), ignore=(), only_rule=False) check_with_run_warnings + CheckFindingSelection
    scan(root) scan; catalog/citations/scan-diagnostic snapshot
    show(id, section=None, mode="lead", format="text") show_with_scope
    show_batch(queries=None, mode="lead") show_batch_with_scope; None means all; ordered strings/typed ShowQuery inputs
    refs(id, section=None, descendants=False) refs_with_metadata; result exposes file summaries and site/file totals
    list_ids(kinds=(), projects=(), unused=False, selector=None) list_with_run_warnings; includes summaries
    list_sizes(kinds=(), projects=(), unused=False, selector=None, units=("lines","words","bytes"), top=None) list_sizes
    cover(text=False) cover / cover_text
    fmt(write=False, marker=False, cross_refs=False) format_references; preview by default
    propose_id(kind, title, width=3) warning-preserving ID proposal; rejected queries raise QueryError, unknown kind raises OperationError
    init(target=None, name=None, description=None, docs=False, force=False, write=False, check=False, no_vcs=False, agents=None) init; target replaces root; None agents means automatic selection
    effective_config() / validate_config() corresponding core APIs + config/run cautions
    fetch(id, write=False) fetch_snapshot_with_run_warnings
    integrations(client=None, write=False, conversation=None, conversation_target=None, agent=None) client artifacts/detection and extracted managed-install adapter; user-global, no root
    complete_ids(prefix="", sections=False) / reference_style() corresponding core APIs
    agent_setup_instructions() canonical setup payload; no root

    mode accepts lead/brief/toc/full; format accepts text/md/json. Batch failures follow existing batch semantics, including empty-input success without config loading. Selectors retain the complete report and filter only its selected view: ignore wins, rule selection requires a rule, and safety io findings remain. Scope, kind overrides, workspace aliases and scan exclusions remain engine decisions. Config-enabled cross-reference formatting still applies when cross_refs=False.

    Init maps write=False to dry-run; check=True suppresses writes and reports pending changes. Force never replaces config. Fetch requires write=True, otherwise raises ValueError before execution; no preview is promised. Integration reads return artifacts/detection; writes retain existing preference validation, agent gates, ownership and manual steps. CLI defaults stay intact.

    Shell scripts, stdin/NDJSON, process/watch lifecycle, exit codes and LSP transport stay frontend concerns. Optional overlay/on-type/hover utilities are excluded from this initial disk-backed API; no CLI conceptual operation is dropped.

    5. How it is pinned

    Your accepted ownership assumption:

    #470 owns the initial shared infrastructure and Python adapter, #469 extends it with Node, and #471 consumes the agreed source/build handoff.

    tests/bindings/ compares complete Rust/Python result/failure/caution data, with a later Node adapter seam. Canonical UTF-8 JSON has envelope keys failure,result,run_cautions, recursively byte-sorted object keys, engine-ordered arrays, explicit nulls, compact separators and one final LF. Preserve Unicode and slashes; escape quote, backslash, LF, CR and tab, with other control characters as lowercase \\u00xx. Empty collections remain arrays. Separately compare existing CLI stdout/stderr JSON goldens through their frozen projection: global stable path/line/message ordering (null path first, absent line as zero, warning/error/suggestion tie order), severity versus suggestion channel, empty sites/authority as null, authority last, and no added column key. Run cautions remain separate from report warnings; raw Rust serialization is insufficient.

    Fixture coverage includes clean/error trees, config failures, caution-only runs and cautions before failure, suggestions on/off, selector authority/safety, missing/ambiguous single and batch queries, workspaces, Unicode/control characters/logical separators, malformed Python inputs and the unsafe alias case. Every operation gets success/refusal coverage; mutation copies compare preview/no-execution behavior and exact resulting bytes against core, including integration user-home fixtures.

    An end-to-end clean-checkout and unpacked-sdist install test runs the example above. Boundary tests assert no streams/exits/cwd changes, repeated/concurrent independent roots, GIL release and documented interrupt behavior. Unit tests pin conversions, nulls, options and failures. Extend resolved dependency isolation; CLI-only builds require no Python build dependencies. The recorded import failure becomes acceptance coverage; no task-specific reproducer exists.

    6. Cost and compatibility

    This is a sequence: approved spec/tests, additive core seams, binding/types/build metadata, then shared parity and local handoff. Direct citations: Python FS/AR one file each; shared report six files/ten sites; engine architecture 38 files/59 sites. These bound review, not required edits.

    You accepted:

    Plan independently, assign no milestone or release date, and carry a named adaptation task if Python lands first.

    The recorded answer was:

    Reacted 👍 to round 1 and wrote nothing further. That accepts every assumption stated above as written; where a question left something to the plan's judgement, it is still the plan's.

    Carry “Adapt Python marshalling to #466/#453/#454”: replace internal adapters after that transition, rerun parity, preserve Python's approved schema. Existing Rust/CLI/LSP behavior stays compatible; Python has its stated write defaults. No prerequisite, milestone or 1.0 gate is added. Node implementation, #471 release assembly, uploads/tags/releases remain outside scope. Keep human discussion until explicit implementation approval.

    7. Documentation

    • docs/user-facing/python-api.md: local install, runnable check/iteration/show, full signatures/results/errors, paths, threading and write examples.
    • README.md, user-guide/example indexes and examples/python-api/: link the tested workflow and distinguish local support from PyPI availability.
    • crates/grund-py/README.md and typed package files: source/sdist/ABI handoff to Prepare cross-registry CLI/LSP packages, PGO artifacts, and gated publication workflows #471, reserved CLI placement and actual signatures.
    • Distribution/bindings specs and architecture meter: approved contract and actual Rust/Python coverage.
    • The eventual PR title is the release line; no PR changelog entry.

    PROPOSAL: ready


    Posted by an agent working this ticket. Round 1 of 3; after the last round it waits for a person.

  9. added
    state:approvedProposal approved; the spec PR is being opened
    state:implementingPR open; implement, review, fix, green
    state:shippingPR ready; CI watched; merging
    and removed
    state:proposedA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejects
    state:approvedProposal approved; the spec PR is being opened
    state:implementingPR open; implement, review, fix, green
    on Oct 6, 2026
  10. removed their assignment
    on Oct 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpath:plannedA written plan, discussed on the ticket, before the fix

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions