Files
2026-09-29 07:07:56 +02:00

11 KiB

Delta 0.0.2 rel.1 — promotion stable pré-tag

Baseline et version

Baseline : 0.0.2-rc.1, avec history/0.0.2/rc.1.md appliqué et committé.

Ce delta MUST NOT être appliqué avant ce commit.

Version avant application :

0.0.2-rc.1

Version après application :

0.0.2

rel.1 identifie la tranche de clôture opérationnelle ; il ne fait pas partie de la version SemVer du produit.

Objet

Promouvoir mécaniquement la candidate validée vers la stable 0.0.2 sans rouvrir le scope.

La tranche finalise uniquement la version stable, la présentation stable du README, la roadmap, le changelog, l'index documentaire, le plan 0.0.2 et la matrice de validation.

Aucun fichier Rust, frontend, dépendance fonctionnelle, contrat Git/sync, règle normative, étude ou prompt de reprise n'est modifié.

Scope

REPLACE :

VERSION
Cargo.toml
README.md
ROADMAP.md
CHANGELOG.md
docs/000-README.md
docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md
docs/validation/001-VALIDATION_MATRIX.md

ADD :

deltas/0.0.2/rel.1.md

DELETE : aucun.

Le history rc.1 est livré séparément. Le history rel.1 sera produit uniquement après la gate opérateur.

Présentation stable attendue

Après application :

VERSION                         0.0.2
Cargo workspace version         0.0.2
ROADMAP                         (x) 0.0.2
CHANGELOG                       ## 0.0.2
README                          0.0.2 présentée comme stable
plan 002                        plan historique de la stable 0.0.2
aucun prompt 0.0.3              oui

Aucune release postérieure à 0.0.2 n'est engagée ni numérotée.

Gate pré-tag

Exécuter depuis la racine du dépôt :

test "$(cat VERSION)" = '0.0.2'
grep -F 'version = "0.0.2"' Cargo.toml
grep -A2 '^## Statut$' history/0.0.2/rc.1.md | grep -Fx 'PASS'

git diff --check

python3 -B scripts/audit_markdown_headers.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history
python3 -B scripts/audit_markdown_links.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history
python3 -B scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts deltas history

cargo fmt --all
cargo fmt --all -- --check

python3 -B scripts/audit_rust_test_layout.py crates
python3 -B scripts/audit_rust_blank_lines.py crates
python3 -B scripts/audit_rust_item_spacing.py crates
python3 -B scripts/audit_rust_module_order.py crates
python3 -B scripts/audit_rust_trait_import_blocks.py crates
python3 -B scripts/audit_rust_reexport_blocks.py crates
python3 -B scripts/audit_rust_crate_root_facades.py crates
python3 -B scripts/audit_rust_export_completeness.py --root . --summary-only

cargo metadata --format-version 1 --no-deps > /tmp/sasedev-knowledge-0.0.2-rel.1-metadata.json
cargo check --workspace
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p knowledge-git-lib --test git_read_path
cargo test --workspace --all-targets --all-features

(
  cd Web/knowledge-template-lab
  npm ci
  npm run typecheck
  npm run build
)

python3 - <<'PYGATE'
import json
from pathlib import Path

metadata = json.loads(Path('/tmp/sasedev-knowledge-0.0.2-rel.1-metadata.json').read_text(encoding='utf-8'))
expected = {
    'knowledge-app-lib',
    'knowledge-domain-lib',
    'knowledge-git-api',
    'knowledge-git-lib',
    'knowledge-sync-api',
    'knowledge-web-lib',
    'knowledge-pgr-server',
    'knowledge-howtos-server',
    'knowledge-snippets-server',
}
assert {package['name'] for package in metadata['packages']} == expected
assert len(metadata['workspace_members']) == 9

statuses = {
    'alpha.1': 'FAIL',
    'alpha.1.fix.1': 'PASS',
    'alpha.2': 'FAIL',
    'alpha.2.fix.1': 'FAIL',
    'alpha.2.fix.2': 'PASS',
    'alpha.3': 'FAIL',
    'alpha.3.fix.1': 'PASS',
    'alpha.4': 'FAIL',
    'alpha.4.fix.1': 'PASS',
    'alpha.5': 'PASS',
    'beta.1': 'PARTIAL',
    'beta.1.fix.1': 'FAIL',
    'beta.1.fix.2': 'PASS',
    'rc.1': 'PASS',
}
for tranche, status in statuses.items():
    path = Path(f'history/0.0.2/{tranche}.md')
    assert path.is_file(), path
    text = path.read_text(encoding='utf-8')
    assert f'## Statut\n\n{status}' in text, (tranche, status)

history_names = {path.stem for path in Path('history/0.0.2').glob('*.md')}
assert history_names == set(statuses), history_names

delta_names = {path.stem for path in Path('deltas/0.0.2').glob('*.md')}
assert delta_names == set(statuses) | {'rel.1'}, delta_names
assert not Path('history/0.0.2/rel.1.md').exists()

assert '## 0.0.2\n' in Path('CHANGELOG.md').read_text(encoding='utf-8')
assert '## 0.0.2 (candidate)' not in Path('CHANGELOG.md').read_text(encoding='utf-8')
roadmap = Path('ROADMAP.md').read_text(encoding='utf-8')
assert '- `(x) 0.0.2`' in roadmap
assert '0.0.3' not in roadmap
readme = Path('README.md').read_text(encoding='utf-8')
assert '`0.0.2` est la stable du chemin Git générique de lecture' in readme
index = Path('docs/000-README.md').read_text(encoding='utf-8')
assert 'plan historique de la stable Git générique' in index
plan = Path('docs/plans/002-V0_0_2_GIT_SYNC_PLAN.md').read_text(encoding='utf-8')
assert 'Plan historique de la stable `0.0.2`' in plan
assert '`rc.1` est `PASS`' in plan
assert not Path('prompts/003-V0_0_3_START_PROMPT.md').exists()

for forbidden in (
    Path('crates/knowledge-forge-gitea-lib'),
    Path('crates/knowledge-store-postgres-lib'),
):
    assert not forbidden.exists(), forbidden

print('Knowledge 0.0.2-rel.1 stable audit: clean')
PYGATE

python3 - <<'PYTEXT'
from pathlib import Path

roots = [Path('README.md'), Path('RULES.md'), Path('ROADMAP.md'), Path('CHANGELOG.md'), Path('docs'), Path('prompts'), Path('deltas'), Path('history')]
paths = []
for root in roots:
    if root.is_file():
        paths.append(root)
    elif root.is_dir():
        paths.extend(path for path in root.rglob('*') if path.is_file())
for path in paths:
    data = path.read_bytes()
    assert b'\r' not in data, path
    assert data.endswith(b'\n'), path
    assert not data.endswith(b'\n\n'), path
    text = data.decode('utf-8')
    for number, line in enumerate(text.splitlines(), start=1):
        assert line == line.rstrip(' \t'), (path, number)
print(f'Knowledge 0.0.2-rel.1 text hygiene: clean ({len(paths)} file(s))')
PYTEXT

python3 - <<'PYMANIFEST'
import json
from pathlib import Path

manifest_path = Path('../builds/sasedev-knowledge/web/template-lab/.vite/manifest.json')
assert manifest_path.is_file(), manifest_path
manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
assert 'src/main.ts' in manifest, manifest.keys()
entry = manifest['src/main.ts']
assert entry.get('file'), entry
assert entry.get('css'), entry
print('Knowledge 0.0.2-rel.1 Vite manifest audit: clean')
PYMANIFEST

python3 - <<'PYSMOKE'
import json
import os
from pathlib import Path
import signal
import subprocess
import time
import urllib.request

root = Path.cwd()
build_root = (root / '../builds/sasedev-knowledge').resolve()
manifest_path = build_root / 'web/template-lab/.vite/manifest.json'
manifest = json.loads(manifest_path.read_text(encoding='utf-8'))
entry = manifest['src/main.ts']
asset_paths = ['/' + entry['file'].lstrip('/')]
asset_paths.extend('/' + value.lstrip('/') for value in entry.get('css', []))

subprocess.run([
    'cargo', 'build',
    '-p', 'knowledge-pgr-server',
    '-p', 'knowledge-howtos-server',
    '-p', 'knowledge-snippets-server',
], check=True)

servers = (
    ('knowledge-pgr-server', 'KNOWLEDGE_PGR_BIND_ADDRESS', 18181),
    ('knowledge-howtos-server', 'KNOWLEDGE_HOWTOS_BIND_ADDRESS', 18182),
    ('knowledge-snippets-server', 'KNOWLEDGE_SNIPPETS_BIND_ADDRESS', 18183),
)

for binary_name, bind_variable, port in servers:
    binary = build_root / 'target/debug' / binary_name
    assert binary.is_file(), binary
    env = os.environ.copy()
    env.pop('KNOWLEDGE_VITE_DEV_ORIGIN', None)
    env['KNOWLEDGE_VITE_MANIFEST_PATH'] = str(manifest_path)
    env[bind_variable] = f'127.0.0.1:{port}'
    process = subprocess.Popen(
        [str(binary)], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        start_new_session=True, text=True,
    )
    try:
        health_url = f'http://127.0.0.1:{port}/healthz'
        for attempt in range(50):
            try:
                with urllib.request.urlopen(health_url, timeout=1) as response:
                    body = response.read().decode('utf-8')
                    assert response.status == 200
                    assert body == 'ok'
                    break
            except Exception:
                if process.poll() is not None:
                    stdout, stderr = process.communicate()
                    raise AssertionError((binary_name, process.returncode, stdout, stderr))
                if attempt == 49:
                    raise
                time.sleep(0.1)
        with urllib.request.urlopen(f'http://127.0.0.1:{port}/', timeout=2) as response:
            html = response.read().decode('utf-8')
            assert response.status == 200
        for asset_path in asset_paths:
            assert asset_path in html, (binary_name, asset_path)
            with urllib.request.urlopen(f'http://127.0.0.1:{port}{asset_path}', timeout=2) as response:
                assert response.status == 200, (binary_name, asset_path, response.status)
        print(f'{binary_name} smoke PASS')
    finally:
        if process.poll() is None:
            os.killpg(process.pid, signal.SIGTERM)
            try:
                process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                os.killpg(process.pid, signal.SIGKILL)
                process.wait(timeout=5)

print('Knowledge 0.0.2-rel.1 HTTP smoke: clean')
PYSMOKE

test -f Cargo.lock
git check-ignore -q Cargo.lock
if git ls-files --error-unmatch Cargo.lock >/dev/null 2>&1; then
    echo 'Cargo.lock must remain untracked for Knowledge' >&2
    exit 1
fi

test -z "$(git diff --name-only -- crates Web docs/architecture docs/studies docs/rules prompts)"
test ! -e crates/knowledge-forge-gitea-lib
test ! -e crates/knowledge-store-postgres-lib
test ! -e history/0.0.2/rel.1.md

git add -A
git diff --cached --check
git status --short

Transition

PASS

Produire séparément history/0.0.2/rel.1.md, l'appliquer, puis créer le commit stable avec le message exact :

0.0.2

Après push de master, créer et publier le tag annoté v0.0.2, vérifier l'objet tag local/distant et son commit déréférencé, puis télécharger l'archive source réellement produite depuis le tag et produire le rapport externe :

sasedev-knowledge-0.0.2-publish.001-report.md

Aucune prochaine release n'est engagée à ce stade. Un futur prompt n'est créé qu'après décision explicite de sa mission et s'appuie sur la stable publiée/vérifiée 0.0.2.

FAIL

Créer history/0.0.2/rel.1.md avec le diagnostic réel, ne pas créer le tag et corriger uniquement la fermeture dans rel.1.fix.1.