Skip to content

docs(kickoff): python-delivery-v0 umbrella — consumer install lane for the Python backends (S1-S3) - #967

Merged
artyhoo merged 1 commit into
stagingfrom
docs/kickoff-python-delivery-v0
Jul 11, 2026
Merged

docs(kickoff): python-delivery-v0 umbrella — consumer install lane for the Python backends (S1-S3)#967
artyhoo merged 1 commit into
stagingfrom
docs/kickoff-python-delivery-v0

Conversation

@artyhoo

@artyhoo artyhoo commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

Kickoff for the python-delivery-v0 umbrella — the «per-toolchain rule-pack + installer» milestone named by python-backend-v0's closure (#962/#963): a consumer Python project runs ./setup python and gets WORKING, FIRING enforcement (astgrep primary + ruff fast-path), CI-wired, with a post-install firing proof.

Doc-only PR (one file). Grounded in a fresh installer-architecture sweep (installer is npm-only by construction: install.sh:173-181 hard-requires package.json; renderers are pure functions with zero fs writes). Key fixed decisions: Model A commit-time pre-render (Python consumers have no Node — templates ship pre-rendered with a byte-drift gate), pure-bash delivery layer, collision policy with loud degrade (never silent clobber), python row in the install fingerprint matrix.

Phase -1 cold-review: 1 BLOCKER (render execution model) + 2 MAJOR fixed in one REVISE round → CONFIRMED-GO. Principle 12 satisfied (§4: T-enumeration + T-PD-A…E domain traps). Scope: Python lane only — cargo delivery stays behind its recorded trigger (its live-fire is unreachable even in our own CI); owner may override.

Placement per kickoff-staging-placement.md §1 — merge before any dispatch.

…r the Python backends, S1-S3

Kickoff for the python-delivery-v0 umbrella: './setup python' delivers
pre-rendered astgrep/ruff enforcement into a consumer Python project
(Model A commit-time pre-render + byte-drift gate), consumer CI template,
post-install firing proof. Doc-only; Phase -1 cold-review passed
(1 BLOCKER + 2 MAJOR fixed in one REVISE round, CONFIRMED-GO).
@artyhoo
artyhoo merged commit a003d0e into staging Jul 11, 2026
36 checks passed
artyhoo added a commit that referenced this pull request Jul 11, 2026
…yte-drift gate, pure-bash delivery layer (python-delivery-v0) (#991)

* docs(orchestrator): python-delivery-v0 meta-launch kickoff (launch-table + stage gates)

Generated by /pipeline python-delivery-v0 (plan-currency: kickoff on staging #967,
no in-flight S1, umbrella open). Orchestration record for the S1-S3 dispatch.

Prior-art: skipped — orchestration bookkeeping doc, no new capability

* feat(python-starter): curated Python starter ConventionNode[] set (S1 Task 3)

Adds packages/core/python-starter/starter-nodes.ts — a curated Python
starter node set authored in the frozen neutral IR (ir/types.ts, no field
added) and rendered through the existing ADOPTED backends: renderAstgrep
(#212, syntax-class default) and renderRuff (#215, TID251/TID253
fast-path). Colocated vitest unit tests (RED-first) assert: grammar-gate
pass, getff-* id namespacing (probe-proven: duplicate ids = ast-grep
exit 8), render-through-both-backends without error, the probe-proven
firing shape, and the datetime caveat — the rendered YAML covers BOTH
`datetime.now()` and `datetime.datetime.now()` via two literal-pattern
nodes (a single pattern cannot match both; a `$MOD.now()` metavariable
over-fires on `foo.now()` — live-verified against @ast-grep/cli@0.44.1).

Set content (all severity error): ban datetime.now()/
datetime.datetime.now() calls (flagship P5), ban eval()/os.system()
calls (bandit ADOPT-VOCABULARY; ruff refuses FF7001 — ast-grep
catch-all), ban datetime.datetime.utcnow (TID251), ban module-level
import tensorflow (TID253).

SSOT row 217 appended to docs/meta-factory/prior-art-evaluations.md in
this same commit (register tail verified: last row was 215; 216 is
reserved for the delivery-layer task). vitest.config.ts registers the
new python-starter/ include glob.

Verified: python-starter 12/12, test:backends 154/154, test:principles
303/303, tsc --noEmit clean.

Prior-art: prior-art-evaluations.md#217 (curated Python starter node set — BUILD own content in the neutral IR; ruff has no canonical "recommended" starter preset, bandit's catalog is engine-coupled with no neutral-IR export, Factory plugin #4 is JS/ESLint-coupled — ADOPT-VOCABULARY the ban semantics, author the set ourselves).

* feat(python-delivery): commit-time render script + pre-rendered templates + byte-drift gate (S1 T4)

Render the curated Python starter node set (packages/core/python-starter/starter-nodes.ts,
#217) through the pure ADOPTED backends into static shipped templates under
packages/core/templates/python/ (Model A). The renderers stay pure; all fs writes live in
packages/core/python-starter/render-python-templates.ts (planPythonTemplates() is pure and is
what the drift gate imports).

Single-owner lane per rule (coordinator Decision #5), partitioned by node kind — a rule ships
in exactly ONE lane so a consumer never gets a duplicate report:
  - ast-grep lane (call-kind): getff-no-datetime-now, getff-no-datetime-datetime-now,
    getff-no-eval, getff-no-os-system — one single-doc YAML file per rule under
    .getff/astgrep-rules/ (idiomatic ast-grep layout; a multi-doc stream fails the repo's own
    single-document pre-commit yaml.safe_load gate — empirically verified).
  - ruff lane (attribute/import-kind): getff-no-utcnow (TID251), getff-no-tensorflow-module-import
    (TID253) — one ruff.toml for the whole lane.

Our own sgconfig.yml template (Decision #4, no `ast-grep new` shellout) references the rules dir
from the consumer's perspective (ruleDirs: [.getff/astgrep-rules]). Every generated file carries a
framework-generated header comment.

Byte-drift gate: packages/core/backends/python-templates-drift.test.ts asserts committed templates
== fresh render, byte-for-byte, at the pre-push channel (test:backends / hooks/pre-push.ts §5c).
Idempotent: re-running the render script produces zero diff. Live-fired end-to-end against the
pinned @ast-grep/cli@0.44.1 + ruff==0.15.21 on the exact consumer tree layout (both lanes fire;
no rule double-reports).

Templates land under a NEW packages/core/templates/python/ dir NOT in install.sh's enumerated
copy-list — no install-fingerprint / shipped-doc / principle-test surface is touched.

Prior-art: prior-art-evaluations.md#212 (ast-grep render backend, ADOPT) and prior-art-evaluations.md#215 (ruff fast-path render backend, ADOPT) — this commit renders the #217 curated node set through the already-ADOPTED backends; no new render target, the emission verdicts stand.

* feat(python-delivery): pure-bash delivery layer + augment-first collision policy + tmp-dir tests (S1 T5)

setup.d/45-python.sh ships the pre-rendered Python lint bundle (ast-grep rules +
sgconfig.yml + ruff config, S1 Task 4) into a consumer Python repo with an augment-first
collision policy — merge into what the consumer authored, never silently clobber. INERT
on the npm flow (gated on GETFF_TOOLCHAIN=python; S2 wires the ./setup python entry) —
install.sh:564 auto-sources it as a guarded no-op, so byte-identical.test.sh stays 8/8.

Collision matrix (probe-decided, .superpowers/sdd/task-2-report.md):
- fresh dir             -> whole-file copy
- sgconfig.yml exists   -> structural merge into the block-list ruleDirs (idempotent);
                           unproven shapes (flow-list / multi-key) REFUSE-LOUDLY (a naive
                           second ruleDirs: key = duplicate-field exit 8; a clever-but-
                           unsafe merge is a STOP-line violation)
- ruff.toml exists      -> REFUSE (a sibling ruff.toml wins entirely + silently disables
                           theirs); ship getff-ruff.toml + `extend` instructions (scalar
                           caveat surfaced if they already use extend)
- pyproject [tool.ruff] -> REFUSE (a sibling ruff.toml SILENTLY overrides [tool.ruff]);
                           ship getff-ruff.toml + [tool.ruff.lint] merge steps
- re-run                -> delivered config artefacts byte-identical (idempotent)
Every degrade path is printed AND appended to <consumer>/.getff-python-install.log. A
pre-existing .prettierignore gets `.getff/` appended idempotently; none is created.

tests/install-sh/python-delivery.test.sh covers every cell + npm-inertness/activation as
first-class fixtures (RED-before-GREEN on the merge/refuse logic), plus GATED live-fire:
all 4 shipped ast-grep rules + both ruff TID bans fire RED on planted violations (pinned
@ast-grep/cli@0.44.1 + ruff==0.15.21; SKIP when unobtainable, like cargo firing.test.ts).
Wired into audit-self.yml (meta-all-wired). refresh-covers-full-delivery.test.sh made
lane-aware — the Python lane is inert on npm and has its own S2 refresh semantics, out of
the npm-refresh-parity gate's population (same rationale as its skill/merge/yq exclusions).

SSOT row 216 appended (delivery-mechanism BUILD verdict).

Prior-art: prior-art-evaluations.md#216 (thin pure-bash delivery writer — BUILD; no headless tool copies a pre-rendered multi-tool lint bundle into an existing repo with augment-first collision. ADAPT the skip/version-stamp concepts from #124/#125, REFERENCE the `ast-grep new project` #212/#185 scaffold precedent; reuses install.sh copy_safe/merge machinery, zero new deps).

* fix(python-delivery): sgconfig idempotency survives trailing-comment edits + .ruff.toml fixture + empty-array guard

Review-round-1 findings addressed (commit 9e9b5bac9, setup.d/45-python.sh + tests):
- Important: _py_sgconfig_merge's idempotency grep required an exact-match line, so a
  consumer-added trailing comment on our entry (`- .getff/astgrep-rules  # our rules`)
  was not recognised as already-present — a re-run inserted a DUPLICATE entry, tripping
  ast-grep exit 8 on the consumer's next scan. Fixed by stripping trailing #-comments
  before the compare (same shape as the existing rest="${rest%%#*}" ruleDirs: value
  check two lines away), bash 3.2 compatible. TDD: added a failing cell-(ii) fixture
  first (RED: 2 entries instead of 1), watched it fail against the unfixed code, then
  fixed and watched it pass.
- Important: setup.d/45-python.sh:153's `.ruff.toml` (dotfile) collision branch had no
  fixture. Added cell-(iii-dotfile): consumer has .ruff.toml only → layer REFUSEs
  loudly, ships getff-ruff.toml, prints extend instructions, logs the refusal. Passed
  immediately (branch already existed and worked).
- Minor: tests/install-sh/refresh-covers-full-delivery.test.sh's NPM_LANE_LAYERS array
  expansion under `set -u` would crash on bash 3.2 if the array were ever empty. Added
  the repo-precedent length guard (setup.d/lib.sh:281-283 shape) before expansion.

Prior-art: skipped — review-fix round, no new capability

---------

Co-authored-by: t <t@t.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants