Repository navigation
Add the PyO3 Python API over grund-core and prove binding parity #470
Description
Activity
- addedenhancementNew feature or requestNew feature or requesthuman-discussionPeople are still discussing this; no machine lays work on it until the label comes offPeople are still discussing this; no machine lays work on it until the label comes offand removedhuman-discussionPeople are still discussing this; no machine lays work on it until the label comes offPeople are still discussing this; no machine lays work on it until the label comes off
on Oct 5, 2026 Claimed by
vjovanov@kung-fu-workstationon 2026-10-06 00:11Z · released on 2026-10-06 14:07Z (completed).Record · 50 transitions · now
completed· last 2026-10-06 13:35Z
Holdervjovanov@kung-fu-workstation· since 2026-10-06 00:11Z1 · 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.mdQuestions and answers on #470
Round 1 of 2
Asked at: #470 (comment)
Today,
python3 -I -c 'from grund import check, show'fails withModuleNotFoundError: 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 intests/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-discussionuntil 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.-
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.
-
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-reportfixture 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.mdClarification 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 withModuleNotFoundError: 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 intests/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-discussionuntil 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.-
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.
-
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-reportfixture 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.md1. 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-pycarries the PyO3 conversion layer overgrund-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 separaterun_cautions.CheckResultcontains completereport,selected_report,had_scan_errorsandoutput_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal RustConfig/Findingslayouts.Under
GrundError, chooseConfigError,FilesystemError,QueryError, and fallbackOperationError. Each has a typed.failurepayload: 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 raiseQueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raiseTypeError; invalid option values raiseValueError.Tree calls accept
str/os.PathLike[str]. Optionalroot=Nonesnapshots 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.md1. 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-pycarries the PyO3 conversion layer overgrund-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 separaterun_cautions.CheckResultcontains completereport,selected_report,had_scan_errorsandoutput_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal RustConfig/Findingslayouts.Under
GrundError, chooseConfigError,FilesystemError,QueryError, and fallbackOperationError. Each has a typed.failurepayload: 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 raiseQueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raiseTypeError; invalid option values raiseValueError.Tree calls accept
str/os.PathLike[str]. Optionalroot=Nonesnapshots 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.mdThe 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.mdDependencies 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.mdSpecification 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.mdPR: #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.mdThe gate, run 3
Checkout
/home/vjovanov/ag/grund/fix/issue-470onfix/issue-470atbba278fc06; 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-oracle0 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.py0 1m43s 3 env TMPDIR=/dev/shm pre-commit run --all-files0 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- passedVerdict 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.02s2.
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- passedVerdict 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 OK3.
env TMPDIR=/dev/shm pre-commit run --all-files- passedVerdict 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.mdCloses #470
Before this change, the checkout had no Python frontend:
import grundfailed withModuleNotFoundError. 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
Nonefor optional fields; effective configuration is recursively read-only.CheckResultkeeps both the completereportandselected_report, plus separaterun_cautions. Findings preserve locations, all sites, suggestions and rule authority. A completed check with findings returns normally. §FS-distribution.3.3.1Operations that cannot run raise
ConfigError,FilesystemError,QueryErroror fallbackOperationError, each carrying a structured.failurewith 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.mdThe 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: retryfix
runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-2.mdThe 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: retryfix
runtime/ship/github-issues-agent-grounds-grund-470-implement-379c3f6c.ticket.ship/fix-3.mdPrepare 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.md43 · 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.mdOutcome
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.mdbyscripts/lifecycle-sync.sh.-
- addedstate:triageFiled; being checked against what is already knownFiled; being checked against what is already knownstate:validatingReproducer being built, or fit against the ground being establishedReproducer being built, or fit against the ground being establishedstate:decidingThe supervisor is choosing the pathThe supervisor is choosing the pathpath:plannedA written plan, discussed on the ticket, before the fixA written plan, discussed on the ticket, before the fixstate:clarifyingQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptionsQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptionsand removedstate:triageFiled; being checked against what is already knownFiled; being checked against what is already knownstate:validatingReproducer being built, or fit against the ground being establishedReproducer being built, or fit against the ground being establishedstate:decidingThe supervisor is choosing the pathThe supervisor is choosing the path
on Oct 6, 2026 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 withModuleNotFoundError: 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 intests/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-discussionuntil 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.-
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.
-
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-reportfixture 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.
Reacted by Vojin Jovanovic-
- addedstate:proposedA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejectsA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejectsand removedstate:clarifyingQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptionsQuestions posted on this ticket wait for its author: words answer them, +1 accepts the assumptions
on Oct 6, 2026 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-pycarries the PyO3 conversion layer overgrund-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 separaterun_cautions.CheckResultcontains completereport,selected_report,had_scan_errorsandoutput_format; report iteration yields errors/warnings/suggestions in engine order. This provides discoverable types without freezing internal RustConfig/Findingslayouts.Under
GrundError, chooseConfigError,FilesystemError,QueryError, and fallbackOperationError. Each has a typed.failurepayload: 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 raiseQueryError; batch records contain either a typed result or that same failure and continue. Batch setup failure raises once. Wrong types raiseTypeError; invalid option values raiseValueError.Tree calls accept
str/os.PathLike[str]. Optionalroot=Nonesnapshots 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. Rootpyproject.tomluses maturin,python/grundsupplies public types/functions andpy.typed, and privategrund._nativeholds the extension (maturin layout). Distribution/import names remaingrund. 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(checkaccepts it positionally); rawscan(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+CheckFindingSelectionscan(root)scan; catalog/citations/scan-diagnostic snapshotshow(id, section=None, mode="lead", format="text")show_with_scopeshow_batch(queries=None, mode="lead")show_batch_with_scope; None means all; ordered strings/typed ShowQuery inputsrefs(id, section=None, descendants=False)refs_with_metadata; result exposes file summaries and site/file totalslist_ids(kinds=(), projects=(), unused=False, selector=None)list_with_run_warnings; includes summarieslist_sizes(kinds=(), projects=(), unused=False, selector=None, units=("lines","words","bytes"), top=None)list_sizescover(text=False)cover/cover_textfmt(write=False, marker=False, cross_refs=False)format_references; preview by defaultpropose_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 selectioneffective_config()/validate_config()corresponding core APIs + config/run cautions fetch(id, write=False)fetch_snapshot_with_run_warningsintegrations(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 modeaccepts lead/brief/toc/full;formataccepts 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 safetyiofindings remain. Scope, kind overrides, workspace aliases and scan exclusions remain engine decisions. Config-enabled cross-reference formatting still applies whencross_refs=False.Init maps
write=Falseto dry-run;check=Truesuppresses writes and reports pending changes. Force never replaces config. Fetch requireswrite=True, otherwise raisesValueErrorbefore 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 keysfailure,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 andexamples/python-api/: link the tested workflow and distinguish local support from PyPI availability.crates/grund-py/README.mdand 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.
Reacted by Vojin Jovanovic- addedstate:approvedProposal approved; the spec PR is being openedProposal approved; the spec PR is being openedstate:implementingPR open; implement, review, fix, greenPR open; implement, review, fix, greenstate:shippingPR ready; CI watched; mergingPR ready; CI watched; mergingand removedstate:proposedA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejectsA plan is posted on this ticket and waits for its author: +1 approves, words revise, -1 rejectsstate:approvedProposal approved; the spec PR is being openedProposal approved; the spec PR is being openedstate:implementingPR open; implement, review, fix, greenPR open; implement, review, fix, green
on Oct 6, 2026 - removedstate:shippingPR ready; CI watched; mergingPR ready; CI watched; merging
on Oct 6, 2026 - added 2 commits that reference this issue
on Oct 7, 2026
Roadmap: §RM-distribution.
Discussion status: This is a scoped proposal. Keep
human-discussionuntil 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, andgrund-lsp; the workspace extraction is shipped. This issue adds the missing Python frontend, not another engine split.Scope and architecture
crates/grund-pyusing PyO3 and amaturinbuild backend. Both distribution and import module remaingrund, as already decided; the Rust binding crate is not a separately promised Python package name.grund-coreAPIs. 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.check, report iteration, andshow. 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.import grundand the futuregrundcommand coexist without a naming collision.Acceptance criteria
grundmodule. The documented examples run against a fixture, and the supported Python operation inventory has executable coverage, including malformed inputs and unsuccessful queries.grund-pydepends 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.Compatibility and sequencing
grund-corepublic API transition; Engine: introduce Project, Run, Compiled, and Catalog with lossless v1 lowering #453 and Engine: separate schema conformance, relational checks, and rendered expectations #454 own its concern split. Coordinate the binding contract with those changes. Do not freeze obsoleteConfig/Findingslayouts into Python merely because the historical architecture sketch lists them, and do not duplicate or accelerate their removal here. If Python work lands first, use supported APIs and carry an explicit adaptation task; public Python shape approval must account for the transition.cibuildwheel/maturintools, the prebuiltgrundCLI payload, separately installedgrund-lsp, PGO binary provenance, registry checks, and gated publication workflow preparation. This issue supplies the Python extension and source/build contract those jobs need.Decisions before implementation
strandos.PathLike), invalid/non-Unicode paths, and explicit-root behavior. Preserve logical report paths independently of host filesystem paths.