Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
c5fd276
feat(workplane): implement reviewed contract and controller foundation
Rwanbt Sep 2, 2026
fe3d27d
feat(workplane): add traceability runner and convergence core
Rwanbt Sep 2, 2026
6bcede8
chore(governance): refresh measured scope after workplane core
Rwanbt Sep 2, 2026
ed1bb3b
test(workplane): harden crash and runner gates
Rwanbt Sep 2, 2026
5733d02
feat(workplane): add scoped repository snapshot guards
Rwanbt Sep 2, 2026
7718bbd
feat(workplane): persist convergence history and stall fingerprints
Rwanbt Sep 2, 2026
07ddc6d
test(workplane): add blind historical validation checkpoint
Rwanbt Sep 2, 2026
b0f33fb
fix(workplane): make historical validation runnable directly
Rwanbt Sep 2, 2026
80047fc
feat(workplane): add minimal developer facade
Rwanbt Sep 2, 2026
bd15c05
chore(governance): refresh measured scope after facade
Rwanbt Sep 2, 2026
37f8fc1
feat(workplane): add read-only integrations and pilot metrics
Rwanbt Sep 2, 2026
10bf2b4
test(workplane): execute five-item local pilot
Rwanbt Sep 2, 2026
aa2934d
test(workplane): exercise convergence in local pilot
Rwanbt Sep 2, 2026
bcc22de
test(workplane): run pilot across two local harnesses
Rwanbt Sep 2, 2026
7fbdcf8
docs(workplane): record verified definition of done
Rwanbt Sep 2, 2026
e7f8530
docs(workplane): reconcile completion gates
Rwanbt Sep 2, 2026
7c034db
test(workplane): cover runner output limit
Rwanbt Sep 2, 2026
a5f9660
docs(workplane): inventory production hardening gaps
Rwanbt Sep 2, 2026
d568529
feat(workplane): bind verification evidence
Rwanbt Sep 2, 2026
fe299cc
feat(workplane): enforce evidence trust
Rwanbt Sep 2, 2026
df9f4a9
feat(workplane): detect unverified task paths
Rwanbt Sep 2, 2026
20675e1
feat(workplane): evaluate evidence freshness
Rwanbt Sep 2, 2026
93ce2cc
feat(workplane): bound runner output capture
Rwanbt Sep 2, 2026
31ace1e
fix(workplane): prevent vacuous convergence
Rwanbt Sep 2, 2026
a1c31f4
fix(workplane): bind approval root authority
Rwanbt Sep 2, 2026
5ee7fb5
fix(workplane): terminate timed out process trees
Rwanbt Sep 2, 2026
6833756
feat(workplane): collect checkout freshness snapshots
Rwanbt Sep 2, 2026
88f6f9a
fix(workplane): classify dependency freshness drift
Rwanbt Sep 2, 2026
cf7334e
fix(workplane): recover uncommitted revisions
Rwanbt Sep 2, 2026
2e06c9e
feat(workplane): record harness qualification evidence
Rwanbt Sep 2, 2026
99b479f
docs(workplane): record dual-harness qualification evidence
Rwanbt Sep 2, 2026
ecd0394
fix(workplane): bind convergence to declared specifications
Rwanbt Sep 2, 2026
2a0d8ee
ci(workplane): run the V2 suite on Linux and Windows
Rwanbt Sep 2, 2026
0068eff
docs(workplane): requalify opencode harness at 2a0d8ee
Rwanbt Sep 2, 2026
70adfae
feat(workplane): separate INVALID and INTERNAL_ERROR verdicts
Rwanbt Sep 2, 2026
1cc4cf3
fix(workplane): recover locks left by a dead writer
Rwanbt Sep 2, 2026
59fbbb6
feat(workplane): enforce waiver and human approval authority
Rwanbt Sep 2, 2026
4958852
docs(workplane): requalify opencode harness at 59fbbb6
Rwanbt Sep 2, 2026
286fa2a
feat(workplane): measure verification substance and widen redaction
Rwanbt Sep 2, 2026
c9ea2a9
feat(workplane): detect repository and specification freshness drift
Rwanbt Sep 2, 2026
063171c
feat(workplane): validate verification scope coverage
Rwanbt Sep 2, 2026
21739b0
feat(workplane): complete the facade with verify, converge and packaging
Rwanbt Sep 2, 2026
3668aaa
test(workplane): add the A01-A53 adversarial matrix, and fix what it …
Rwanbt Sep 2, 2026
48fe6f5
docs(workplane): reconcile the documentation with the runtime
Rwanbt Sep 2, 2026
630cb8b
docs(workplane): requalify opencode harness at 48fe6f5
Rwanbt Sep 2, 2026
b9dab7a
fix(workplane): correct the isolation rationale and prove the orphan …
Rwanbt Sep 2, 2026
18223fe
test(workplane): stop a killed command's cwd from failing its own test
Rwanbt Sep 2, 2026
95ca44e
feat(workplane): make the historical and pilot gates runnable
Rwanbt Sep 3, 2026
cf3e69a
hardening: require schemas for normative artifacts and preserve the rest
Rwanbt Sep 3, 2026
015a6c7
hardening: derive provenance from observation and authorize root tran…
Rwanbt Sep 3, 2026
a7b3993
hardening: add the authoritative evaluator and qualify evidence one b…
Rwanbt Sep 3, 2026
508c0bb
docs: align the threat model and definition of done with the authorit…
Rwanbt Sep 3, 2026
d7b7a3c
docs(workplane): requalify opencode harness at 508c0bb
Rwanbt Sep 3, 2026
13d2550
fix(workplane): prove case ordering by state, not by the clock
Rwanbt Sep 3, 2026
1bbdeb4
fix(workplane): authenticate evidence by producing it
Rwanbt Sep 3, 2026
e143514
fix(workplane): separate provenance domains and drop the ranked ladder
Rwanbt Sep 3, 2026
802eda0
hardening: authorize every change to the success conditions
Rwanbt Sep 3, 2026
5bd65f6
fix(workplane): bind root transitions to content and give the registr…
Rwanbt Sep 3, 2026
4c8d1c3
docs: record the second-round findings, decisions and residual limits
Rwanbt Sep 3, 2026
b40f510
docs(workplane): add the round-2 external review packet
Rwanbt Sep 3, 2026
da99386
fix(workplane): make the mutation approval an artifact, and re-read a…
Rwanbt Sep 3, 2026
016388d
fix(workplane): resolve root chains, let human approval converge, car…
Rwanbt Sep 3, 2026
2a6f838
docs: record the third round, and name the mode that is not built
Rwanbt Sep 3, 2026
68d16d6
docs(workplane): add the round-3 review packet and requalify
Rwanbt Sep 3, 2026
d55b161
fix(workplane): make a predicate a mechanism, and trust a project bef…
Rwanbt Sep 3, 2026
2f0420b
docs: record the fourth round, and name the default that is actually …
Rwanbt Sep 3, 2026
8cb4fdb
docs(workplane): pin the round-4 packet to its commit, CI run and qua…
Rwanbt Sep 3, 2026
6e4de6f
fix(workplane): admit the initial contract, and authorize the signer
Rwanbt Sep 3, 2026
92875e5
docs: record the fifth round, and the fix that needed a fix
Rwanbt Sep 3, 2026
c4cbdc3
fix(scripts): a qualification gate that cannot finish is a failed gate
Rwanbt Sep 3, 2026
e37b3c6
fix(workplane): judge a transition by the policy that authorized it
Rwanbt Sep 3, 2026
386b326
docs: declare the bootstrap trust boundary instead of implying a guar…
Rwanbt Sep 3, 2026
1ef7993
docs(workplane): add the round-6 review packet and requalify at 386b326
Rwanbt Sep 3, 2026
f708a03
fix(workplane): bind a transition to its own evidence, and an approva…
Rwanbt Sep 3, 2026
fa01c41
docs: record the seventh round, and decide what an approval approves
Rwanbt Sep 3, 2026
68127b3
docs(workplane): add the round-7 review packet and requalify at fa01c41
Rwanbt Sep 3, 2026
69c73cb
fix(workplane): read the approval back out of the commit that carried it
Rwanbt Sep 3, 2026
b96cb5b
docs: amend ADR-0007 for the approval binding, and note a classificat…
Rwanbt Sep 3, 2026
139a64c
docs(workplane): add the round-8 review packet and requalify at b96cb5b
Rwanbt Sep 3, 2026
b84c432
fix(workplane): establish the authority before anything executes
Rwanbt Sep 3, 2026
becf737
docs: record the authority preflight, and what it deliberately does n…
Rwanbt Sep 3, 2026
0e59dea
docs(workplane): add the round-9 review packet and requalify at becf737
Rwanbt Sep 3, 2026
294d09e
fix(workplane): one authority boundary, used by every surface that ex…
Rwanbt Sep 3, 2026
2fb2154
docs: record the shared execution boundary, and one cleanup left alone
Rwanbt Sep 3, 2026
0e5a759
docs(workplane): add the round-10 review packet and requalify at 2fb2154
Rwanbt Sep 4, 2026
fe6c525
docs: record the external closure of the authority gate
Rwanbt Sep 4, 2026
91a7aac
refactor(pilot): measure real work through the production boundary
Rwanbt Sep 4, 2026
294b136
docs(pilot): record the instrument readiness, and what only real work…
Rwanbt Sep 4, 2026
1126419
fix(evidence): record the execution window the run actually had
Rwanbt Sep 4, 2026
a4a6bd6
docs: record the H01 packet and the empirical finding it produced
Rwanbt Sep 4, 2026
c9d6547
feat: land the five real pilot items, and gate the budget that let on…
Rwanbt Sep 4, 2026
d1e8431
docs: document the work plane, and report the pilot as it happened
Rwanbt Sep 4, 2026
36d89c9
docs: close the historical gate with three conclusive cases
Rwanbt Sep 4, 2026
c8878ec
chore(docs): name the sealed case files properly
Rwanbt Sep 4, 2026
27a5fd6
docs: final qualification packet for spec
Rwanbt Sep 4, 2026
2381abb
docs: pin the candidate SHA and its green CI run
Rwanbt Sep 4, 2026
205c9f8
feat(lifecycle): Distribution & Lifecycle Manager v1 — Standard and V…
Rwanbt Sep 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 170 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ jobs:
# disagree about what the limit is.
run: |
set -e
for dir in tools skills hooks scripts; do
for dir in tools skills hooks scripts ainative; do
echo "--- $dir"
(cd "$dir" && node ../hooks/pretool-loc-gate/run_gate.js --all)
done
Expand Down Expand Up @@ -91,6 +91,127 @@ jobs:
shell: bash
run: python -m unittest discover -s hooks/tests -p "test_*.py" -v

workplane-v2:
name: Verified Work Plane V2 (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.11"

# The V2 kernel shipped its whole hardening sequence with no CI job at
# all: every gate was a local run on one machine, on one OS, on one
# Python. A verdict engine that only ever ran where it was written has
# no evidence it is deterministic anywhere else.
- name: Contracts, controller and snapshot
run: python -m unittest tests.test_workplane_contracts tests.test_workplane_controller tests.test_workplane_snapshot -v

- name: Runner, trust, freshness, traceability and convergence
run: python -m unittest tests.test_workplane_runner_convergence tests.test_workplane_substance tests.test_workplane_authorization tests.test_workplane_traceability tests.test_workplane_convergence_history -v

- name: Adversarial matrix A01-A53
run: python -m unittest tests.test_workplane_adversarial -v

- name: Authority matrix A54-A70, through the production boundary
run: python -m unittest tests.test_workplane_authority tests.test_workplane_authority_origin -v

- name: CLI, integrations and local pilot harnesses
run: python -m unittest tests.test_workplane_cli tests.test_workplane_integrations_metrics tests.test_workplane_pilot tests.test_workplane_harness_matrix tests.test_workplane_historical_case -v

- name: The package installs and exposes its console entry point
shell: bash
run: |
set -e
python -m pip install --disable-pip-version-check --no-deps -e .
ainative work --help > /dev/null
ainative converge --help > /dev/null

- name: Deterministic gates must also reproduce outside the test runner
# bash, not the Windows default shell: PowerShell only propagates the
# exit code of the last line, which would hide a failure in the first
# two commands.
shell: bash
run: |
set -e
python scripts/workplane_structural_regression.py
python scripts/workplane_harness_matrix.py
python scripts/workplane_pilot.py --self-check

- name: The complexity budget AGENTS.md declares blocking
# It was published and never measured: traceability.analyze shipped at
# ~32 branches, in the module every convergence verdict passes through.
run: python scripts/check_complexity_budget.py

lifecycle:
name: Distribution lifecycle (${{ matrix.os }}, py${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.11", "3.13"]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}

# The lifecycle layer deletes files in a user's project. Every gate below
# runs on all three platforms because path handling, links, case folding
# and process liveness are exactly where they differ.
- name: Transition matrix, round trips and idempotence
run: python -m unittest tests.test_lifecycle_matrix -v

- name: Ownership, user modifications and external config
run: python -m unittest tests.test_lifecycle_ownership -v

- name: Path traversal, link escape, tampered state, archive safety
run: python -m unittest tests.test_lifecycle_security -v

- name: Interruption, rollback, recovery, locking and legacy adoption
run: python -m unittest tests.test_lifecycle_transactions -v

- name: Update check, apply, conflict, recovery and rollback
run: python -m unittest tests.test_lifecycle_update -v

- name: CLI contract, exit codes, JSON and the layer boundary
run: python -m unittest tests.test_lifecycle_cli -v

- name: Non-vacuity — each guard must actually block
# A protection nobody has seen refuse is a comment. This reverts each
# guard in a scratch copy and asserts the matching test then fails.
run: python scripts/lifecycle_non_vacuity.py

clean-install:
name: Clean install E2E (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.11"

# As a user, not as a developer: build the wheel, install it into a
# throwaway venv, and drive the console script from a directory that has
# never heard of this checkout. No PYTHONPATH, no repo on sys.path. The
# failure mode this catches is a lifecycle layer that only works from
# inside its own source tree — and a wheel whose staged payload installs
# something different from what a checkout installs.
#
# One cross-platform script rather than three dialects of shell: the
# same gate runs here and on a developer's machine.
- name: Build a wheel, install it, and use it as a user would
run: python scripts/lifecycle_clean_install.py

installers:
name: Installers (${{ matrix.os }})
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -124,7 +245,7 @@ jobs:
shell: bash
run: python scripts/install_agents.py --home "$HOME/stack-ci/fakehome" --check

- name: Per-project installerinto a throwaway git repo
- name: Per-project bootstrapthe pre-lifecycle flags still work
shell: bash
run: |
set -e
Expand All @@ -136,24 +257,62 @@ jobs:
echo "print('hi')" > src/main.py
git add -A && git commit -qm init
cd "$GITHUB_WORKSPACE"
python install.py --project-root "$HOME/stack-ci/proj" --skip-gstack
# --project-root and --skip-gstack are the names the old installer
# used; a script written against it must keep running.
python install.py --profile standard \
--project-root "$HOME/stack-ci/proj" --skip-gstack
test -f "$HOME/stack-ci/proj/AGENTS.md"
test -f "$HOME/stack-ci/proj/conventions.json"
test -d "$HOME/stack-ci/proj/.claude/skills/verify-ai-docs"
test -d "$HOME/stack-ci/proj/.agents/skills/verify-ai-docs"
test -f "$HOME/stack-ci/proj/.ai-native/lifecycle/state.json"

- name: A re-install prunes files removed upstream
- name: A re-install prunes a managed file removed upstream — and only that
shell: bash
run: |
set -e
# A plain copy leaves a file deleted upstream sitting in every
# project that installed it earlier — the drift this stack exists
# to prevent.
touch "$HOME/stack-ci/proj/.claude/skills/verify-ai-docs/OBSOLETE.md"
python install.py --project-root "$HOME/stack-ci/proj" --skip-gstack
if [ -f "$HOME/stack-ci/proj/.claude/skills/verify-ai-docs/OBSOLETE.md" ]; then
echo "a file removed upstream survived a re-install"; exit 1
PROJ="$HOME/stack-ci/proj"
SKILL="$PROJ/.claude/skills/verify-ai-docs"

# A file the stack recorded, that the distribution no longer ships,
# must not survive: that is the drift this stack exists to prevent.
# A file it never wrote must survive: deleting it is the drift the
# ownership model exists to prevent (ADR-0009 §3).
echo "# added by me" > "$SKILL/MY-NOTES.md"
MANAGED="$(python - "$PROJ" <<'PY'
import json, sys
from pathlib import Path
state = json.loads((Path(sys.argv[1]) / ".ai-native/lifecycle/state.json")
.read_text(encoding="utf-8"))
print(next(entry["path"] for entry in state["managed_files"]
if entry["path"].startswith(".claude/skills/verify-ai-docs/")))
PY
)"
echo "managed file under test: $MANAGED"

python install.py --profile standard --project-root "$PROJ" --skip-gstack
test -f "$PROJ/$MANAGED" # still shipped, still present
test -f "$SKILL/MY-NOTES.md" # never ours, never removed

# Now make it disappear from the source and re-install: it must go.
rm -rf "$GITHUB_WORKSPACE/skills/verify-ai-docs"
python install.py --profile standard --project-root "$PROJ" --skip-gstack
if [ -f "$PROJ/$MANAGED" ]; then
echo "a managed file removed upstream survived a re-install"; exit 1
fi
test -f "$SKILL/MY-NOTES.md"
git -C "$GITHUB_WORKSPACE" checkout -- skills/verify-ai-docs

- name: Uninstall leaves the user's work, then reinstall works
shell: bash
run: |
set -e
PROJ="$HOME/stack-ci/proj"
python -m ainative.cli uninstall --project "$PROJ"
test -f "$PROJ/src/main.py"
test -f "$PROJ/.claude/skills/verify-ai-docs/MY-NOTES.md"
python install.py --profile standard --project-root "$PROJ" --skip-gstack
test -f "$PROJ/AGENTS.md"

- name: v4 governance block — six harness get vault block; user content survives
# Build a throwaway v4 vault, point the installer at it via
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ calibration_report.md

# Vault sync sentinel (per-machine state, never committed)
scripts/vault_last_sync_date.txt

# Build artefacts from `pip install .`
build/
*.egg-info/

# Lifecycle payload staged at build time by _build_backend.py — never tracked.
ainative/_payload/
24 changes: 17 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,28 @@ Central unread modules (>5 incoming imports): none

### This project — ai-native-dev-stack

Measured 2026-08-30 via `git ls-files` (image excluded). Re-measure with
`python3 scripts/measure_scope.py`; CI fails when these figures drift.
Measured 2026-09-04 (distribution & lifecycle v1) via `git ls-files` (image excluded). Re-measure
with `python3 scripts/measure_scope.py`; CI fails when these figures drift.

| Scope | Tokens (÷4) | Files | Strategy |
|---|---|---|---|
| Core stack (excl. anti-debt) | ~127 000 | 75 | **Layered read** — cartography first, then targeted reads |
| Anti-debt agent | ~130 000 | 118 | Read its `AI_CONTEXT.md` and ADRs before its sources |
| Whole repo | ~246 000 | 191 | **Multi-phase workflow** — never a single direct read |
| Core stack (excl. anti-debt) | ~396 860 | 194 | **Layered read** — cartography first then targeted reads |
| Anti-debt agent | ~129 648 | 118 | Read its `AI_CONTEXT.md` and ADRs before its sources |
| Whole repo | ~526 509 | 312 | **Multi-phase workflow** — never a single direct read |

Do **not** read the whole repo in one pass: at ~246k tokens it does not fit,
Do **not** read the whole repo in one pass: at ~527k tokens it does not fit,
and the strategy table above applies in full. Pick the scope the task needs —
most work touches only one of the two halves.
most work touches only one of the three halves below.

The core stack is itself two independent halves, and almost no task needs both:

| Half | Entry points | What it decides |
|---|---|---|
| **Distribution & lifecycle** (`ainative/`) | `docs/DISTRIBUTION-LIFECYCLE.md`, ADR-0009 | what is installed, and what may be replaced or deleted |
| **Verified Work Plane** (`ainative_workplane/`) | `docs/VERIFIED-WORK-PLANE.md`, ADR-0001…0008 | whether declared work has converged |

The dependency runs one way: lifecycle may invoke the Work Plane, never the
reverse. Reading either half without the other is correct.

> This block said "~22 000 tokens, 11 files, direct read always" until
> 2026-08-27, measured ten weeks and 178 files earlier. Every agent read that
Expand Down
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,56 @@ project adheres to [Semantic Versioning](https://semver.org/).

### Added

#### Distribution & Lifecycle Manager v1

- **Two profiles, `standard` and `verified`**, declared in
`ainative/lifecycle/data/profiles.json`. `verified` extends `standard` and
lists only what it adds; the resolver computes the effective component set.
The dependency runs one way — the lifecycle layer may invoke the Verified
Work Plane, never the reverse, and installing Standard loads no authority
module. Proved by inspecting `sys.modules`, not by convention.
- **`ainative` as a top-level dispatcher** — `init`, `profile status|switch|purge`,
`status`, `doctor`, `repair`, `uninstall`, `update check|apply|rollback`, plus
the unchanged Verified surface (`trust`, `work`, `verify`, `converge`,
`debug`), handed over verbatim with their own exit codes.
- **Recorded file ownership.** Four classes (`MANAGED_IMMUTABLE`,
`MANAGED_MUTABLE`, `USER_DATA`, `EXTERNAL_CONFIG`) and a SHA-256 per managed
file taken at the moment the stack writes it. This is what makes uninstall and
update possible at all: a file the stack wrote is now distinguishable from a
file the user rewrote.
- **Transactional mutations** — backup, apply, verify, then commit the install
state *last*, with a journal at `.ai-native/lifecycle/transactions/`. An
interruption leaves the old valid state or the new one; `ainative repair`
completes the rollback. An `O_EXCL` lock with liveness-checked stale detection
keeps two mutations from interleaving.
- **Non-destructive downgrade.** `profile switch standard` deactivates Verified
governance and preserves `.ai-native/{trust,work,runs}` as dormant state.
Deleting it is a separate, explicit `ainative profile purge verified`.
- **Update lifecycle** — cached detection (24 h TTL, bounded timeout, `OFFLINE`
is not fatal), transactional application with archive digest and path-safety
verification, `.new` files instead of merges for user-modified content, and
`ainative update rollback` for the project's assets. Detection is automatic;
application never is, and no authority command ever reaches the network.
- **Legacy adoption.** A project installed before the lifecycle existed is
detected and adopted on the next `init`. A file is claimed only when its bytes
match what the distribution ships; anything else is tracked but never replaced
and never removed.
- `--dry-run` on every mutation, `--yes` on every confirmation, `--json` on
every command a script would parse, and stable exit codes (0/1/2/3).
- `docs/DISTRIBUTION-LIFECYCLE.md` and
[ADR-0009](docs/adr/0009-distribution-profiles-and-lifecycle-ownership.md).
- `scripts/lifecycle_non_vacuity.py` — reverts each guard in a scratch copy and
requires the matching test to fail. Three cases were reported VACUOUS on the
first run and were real: two guards were layered so removing one proved
nothing, and one test could not see commit ordering at all.
- `scripts/lifecycle_clean_install.py` + a CI job on all three OSes — builds the
wheel, installs it into a throwaway venv, and drives the console script from a
directory with no `PYTHONPATH` and no checkout, asserting that the staged
payload installs exactly what a checkout installs.
- An in-tree PEP 517 backend (`_build_backend.py`) that stages the installable
payload into the wheel, so the repository keeps one copy of its own method and
a user with no checkout can still install a profile.

- `conventions.json` — machine-readable twin of the size/complexity thresholds
declared in `AGENTS.md`. Every enforcement point now reads it instead of
carrying its own copy of the numbers.
Expand Down Expand Up @@ -84,6 +134,26 @@ project adheres to [Semantic Versioning](https://semver.org/).

### Changed

- **One authority for the lifecycle.** `install.py` is now a bootstrap for the
one situation `pip` cannot cover — a fresh machine — and delegates to
`ainative init`; `install.sh` and `install.ps1` find a Python and hand over.
Its pre-lifecycle flags (`--project-root`, `--skip-gstack`, `--with-gstack`,
`--gstack-ref`, `--dry-run`) still work.
- **`install.py` no longer prunes a file it did not write.** `copy_tree` deleted
anything under a managed directory that the source no longer had, including a
skill the user had edited. Pruning is now decided per file by the digest
recorded at install time.
- `scripts/stack-update-check.sh` and `scripts/stack-upgrade.sh` are documented
as what they are — *clone*-level operations. The project-level update is
`ainative update`. There is no second project updater.
- The console entry point moved from `ainative_workplane.cli:main` to
`ainative.cli:main`, which dispatches. Every Verified command keeps its
grammar, output and exit codes.
- The distribution is now `ainative-dev-stack` and ships both packages; the
lifecycle CLI requires Python 3.11+ (the AI-docs tooling it installs still
runs on 3.8+). `docs/DISTRIBUTION-LIFECYCLE.md` states the three surfaces.
- `scripts/check_complexity_budget.py` measures the lifecycle package too, and
the CI LOC gate covers `ainative/`.
- **One implementation of the LOC rule.** `scripts/loc_gate.ps1` is merged into
`hooks/pretool-loc-gate/run_gate.js`, which now offers all three modes
(single file, `--staged`, `--all`). The CI job calls that same script, so CI
Expand Down
Loading