Files
jpmschweitzerandClaude Opus 5 c924b0934e feat(config): T-1277 — detach, and a failed job that looked busy
core/process.py spawns a child that outlives its parent: its own session, so a
signal to the parent's group or a timeout kill does not take the work with it;
re-execing reach by BARE NAME, because an absolute path would freeze the child
to whichever checkout was current at spawn time and silently run the wrong
source after a repoint; and streams kept separate exactly as in the foreground,
events to <id>.jsonl and real output to <id>.out.

Testing a case the ticket did not name found a real hole. Recording completion
inside @command looked right and was wrong: a child that fails BEFORE any
command runs — bad arguments, an unknown verb, an import error — never reaches
that decorator. `reach --detach check bogus` left its metadata reading
"running" forever with the process long gone. That is the exit-0 trap wearing a
new disguise and worse than the original, because a failed job that looks busy
sits somewhere nobody is watching, and a caller polling for completion would
wait indefinitely on something that failed in milliseconds.

So completion is recorded at the PROCESS's exit instead. main.py gains main(),
wrapping cli() in a single try/finally, and the entry point moves to
main:main. Every exit path now passes through one place. Removed from @command
rather than left in both — two writers of one field is how they drift.

Verified on three paths: success records done/0, a real drift failure records
failed/1, and the parse failure that exposed the hole now records failed/2.

One narrow conformance exemption, with its reason inline so it does not read as
an oversight: the no-domain-imports-core.jobs invariant fired on main.py,
correctly by its letter and wrongly by its purpose. main.py is not a command;
it is the entry point, and it already owns --detach.

Still open, and carried to T-1278: a child killed outright cannot record
anything, so jobs list must reconcile against process liveness rather than
trusting the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 16:46:19 +02:00

82 lines
3.9 KiB
TOML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[project]
name = "settled-reach-tooling"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"PyYAML",
"jsonschema",
"numpy",
# scipy 1.17.1 — checked clean against NVD + OSV, no CVEs on record (2026-04-06)
"scipy==1.17.1",
# Pillow 12.2.0 — checked clean against NVD + OSV (2026-04-06)
# CVE-2026-25990 fixed in 12.1.1, CVE-2025-48379 fixed in 11.3.0
"Pillow==12.2.0",
# typer 0.27.1 — the CLI transport (D-263). Checked clean against NVD, OSV
# and the GitHub Advisory Database: no advisories on record for typer at all
# (2026-08-20). Its transitive set was checked too, since typer pulls in
# rich -> pygments: pygments 2.21.0 clears CVE-2026-4539 (archetype-lexer
# ReDoS, fixed in 2.20.0); rich and click have no advisories on record.
#
# Plain `typer`, not `typer-slim`: slim is deprecated as of typer 0.22.0 and
# is now a shallow wrapper that installs all of typer, so it buys nothing.
# rich therefore ships as a transitive dependency — but `rich_markup_mode=None`
# in tooling/main.py keeps it off the import path entirely (verified: `rich`
# and `pygments` are absent from sys.modules after loading the CLI).
"typer==0.27.1",
# pydantic 2.13.4 — data shapes for domain schemas (D-263). Checked clean
# against NVD + OSV (2026-08-20). PYSEC-2026-1812 / CVE-2024-3772 (email
# regex ReDoS) is fixed in 2.4.0. NOTE the 2026 SSRF advisories
# CVE-2026-25580 and CVE-2026-54249 are against *pydantic-ai*, a different
# package that is not a dependency here — do not confuse the two on the
# next sweep.
"pydantic==2.13.4",
]
[project.scripts]
# The whole point of D-263: one bare command, so one permission-rule entry
# covers every tool.
#
# `cli` is a typer.Typer instance (callable), NOT the click.Group that T-1259
# originally specified: typer vendors click as of 0.26.0, so there is no
# top-level `click` to import and no supported way to extract typer's internal
# one. Lazy domain registration therefore goes through `typer.Typer(cls=...)`
# with a TyperGroup subclass (T-1260) rather than a click Group.
reach = "tooling.main:main"
[tool.setuptools.packages.find]
# Explicit, not flat-layout auto-discovery. The repo root holds client/, server/,
# docs/, wiki/, db/ and tests/ alongside tooling/, and auto-discovery either
# errors on the ambiguity or quietly ships something unintended (T-1258).
#
# Nothing needs excluding yet: the hyphenated directories (planet-gen,
# economy-db, garment-fit, pql-migrate) are invisible to package discovery
# because a hyphen is not a valid Python identifier, and tooling/econ-sim is a
# Rust crate with no __init__.py. That changes in T-1250, which renames them —
# at which point they become real packages and this include starts matching them.
include = ["tooling*"]
[project.optional-dependencies]
dev = [
# ruff 0.15.9 — checked clean against NVD + OSV, no CVEs on record (2026-04-05)
"ruff==0.15.9",
]
[tool.ruff]
line-length = 100
target-version = "py311"
[tool.ruff.lint]
# Widened from {E9, F401, F811, F821} to the full ruff-default tiers + W (T-1066).
# E4: import placement/style
# E7: statement-level pitfalls (== None, bare except, lambda assignment, ...)
# E9: runtime syntax/encoding errors
# F: all pyflakes (unused imports/names, undefined names, f-string misuse, ...)
# W: whitespace + invalid escape sequences (zero violations at adoption)
select = ["E4", "E7", "E9", "F", "W"]
# Rules excluded at adoption because the existing violation count was not
# trivially fixable (T-1066) — re-enable per-rule as the debt is paid down:
# E402 (43×): module-import-not-at-top — script-style sys.path.insert before import
# E702 (41×): semicolon-paired assignments, deliberate style in planet-gen noise math
# F841 (21×): unused locals, mostly in numeric/diagnostic code — needs manual review
ignore = ["E402", "E702", "F841"]