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.