Compare commits
261 Commits
feat/mobil
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1de032b698 | |||
| 13cded83a6 | |||
| defa9ee6db | |||
| b1831c58d1 | |||
| 063c7fb4bb | |||
| 2180fcc27b | |||
| bb868aa0b0 | |||
| 769c1589c4 | |||
| 95a22ac3d1 | |||
| 622963e8e9 | |||
| a2e5fd5bae | |||
| c9ed43c9e0 | |||
| 3439e2aaf9 | |||
| 75fd6de293 | |||
| 7551c91f37 | |||
| 40ab10b8e8 | |||
| 4bab188df7 | |||
| aaed642cec | |||
| b4f2de438e | |||
| b6f9f76f8b | |||
| 5367de4e99 | |||
| c07028c052 | |||
| af0e6694e5 | |||
| c9b02a07da | |||
| 652d50a2d3 | |||
| e6c6678344 | |||
| ada2c61586 | |||
| f73ae7bd3b | |||
| 6a2bcf2a94 | |||
| 2467b334db | |||
| 0015b384ce | |||
| 25ce0409aa | |||
| eb358fb7f8 | |||
| afa4d84997 | |||
| bf0683bbb2 | |||
| c486a5c5eb | |||
| 82e0d5ba78 | |||
| 7910a3964a | |||
| d4df080f1d | |||
| d0d5e03afb | |||
| c3065e7116 | |||
| bfe8b3b45a | |||
| a5263d65ec | |||
| 9e01821278 | |||
| 51677811ab | |||
| dde3fc9a32 | |||
| c3bdde8f58 | |||
| 5858638134 | |||
| 1bec8ae9ce | |||
| abcba413f5 | |||
| 6ed2cd4284 | |||
| 404a45caa1 | |||
| c5ff82f58d | |||
| 894156c540 | |||
| 63e24020c7 | |||
| ec248e9f72 | |||
| 47836c8de2 | |||
| a66aa24b53 | |||
| 84164d0f2a | |||
| 242aeac07a | |||
| 1bdaf3e986 | |||
| 7583f89384 | |||
| 03742db4e1 | |||
| 9a922ffed3 | |||
| 02a7ea403d | |||
| 63feddb01c | |||
| 36bb47f9c6 | |||
| b2ca1f17a4 | |||
| ad545b52af | |||
| 467c07232e | |||
| 531576452d | |||
| 4235ad85b0 | |||
| fd5c979339 | |||
| 7e8c94df6e | |||
| 6b2fd25d23 | |||
| f1c5bb0d26 | |||
| 2e4bc8f583 | |||
| 8a14ea1d7a | |||
| 9f88112aca | |||
| 689619d14d | |||
| 64e56e8abc | |||
| 8605b7198f | |||
| d41aed040f | |||
| bf01a83268 | |||
| 5b427d5602 | |||
| fa46971e76 | |||
| c313587b26 | |||
| 4976ddcc16 | |||
| 17e11f0322 | |||
| 7df18adfa8 | |||
| 535c5108e2 | |||
| 20705f6c90 | |||
| eabd2f5f29 | |||
| 482151c31c | |||
| 6d9d5a460e | |||
| 89d06ade16 | |||
| 69b35df10c | |||
| b5475e9be1 | |||
| fdd3aace5a | |||
| f86704d67e | |||
| ec8e29a313 | |||
| 1dc9a60580 | |||
| ee18b26d03 | |||
| 5f4a0f74db | |||
| 6b17df7320 | |||
| 0c33495342 | |||
| d8a09b1def | |||
| 39af501ea0 | |||
| 4c821d9e83 | |||
| deb4a91348 | |||
| 5b36b8e54e | |||
| eda5a877ef | |||
| 5b615a6999 | |||
| 99cf0947da | |||
| dbd1e14e4e | |||
| 7033354011 | |||
| 7345730e3c | |||
| cea94c56db | |||
| c1e24736e3 | |||
| 24bbfbc17f | |||
| a11e2f1d18 | |||
| 630b146d02 | |||
| 551360db83 | |||
| a96c282780 | |||
| d2e0f147c2 | |||
| ad95364021 | |||
| e48e322363 | |||
| a9f2a5bbd4 | |||
| 0cba8f868e | |||
| f8e3ee4cca | |||
| 92e0ad01c6 | |||
| e6c18d6b16 | |||
| a3092f5d5b | |||
| 7187b69935 | |||
| 4ba4453661 | |||
| ee42e391e3 | |||
| f7756be59e | |||
| 4abe70e10e | |||
| b2b4ec35c0 | |||
| b255436958 | |||
| ad06b34a85 | |||
| a38f585f3d | |||
| bf0dc125d1 | |||
| f61dc15b47 | |||
| 1ac3d91a19 | |||
| 100ba10409 | |||
| 5f14785abb | |||
| c7043ae9d5 | |||
| bd1235f8c3 | |||
| 7ae7b4dca3 | |||
| f088db57d4 | |||
| 311e94ed91 | |||
| e078f3f9ab | |||
| 7c4bb5a2cf | |||
| 214e174635 | |||
| f1b2f4605f | |||
| 87955adf5d | |||
| e39a0c547d | |||
| c89ddf7237 | |||
| 615effe8bf | |||
| e952b344a0 | |||
| bb0572cc1a | |||
| f6a2347278 | |||
| d902c2bf98 | |||
| baa2082f51 | |||
| 2f249b420f | |||
| d6b4eb82f4 | |||
| bfa44fecda | |||
| e329235aa9 | |||
| 8cbaa5117c | |||
| 3086f333ed | |||
| c1c3c19d13 | |||
| ddcd0acd4d | |||
| 9bc4eeb3e7 | |||
| 7a9a5319ac | |||
| 7d75de5c9f | |||
| 69c91cb661 | |||
| a1bfbf4405 | |||
|
|
be26b425d8 | ||
|
|
abf88e7037 | ||
| 04fee32fdb | |||
|
|
fbddf5c3fc | ||
|
|
bad748df3a | ||
| 0066cf8601 | |||
| 5d3473d66c | |||
| f9d34110e4 | |||
|
|
84e9c3ead4 | ||
|
|
72e59175fc | ||
| 81b69ac6d5 | |||
| 513ae72e85 | |||
| 4c4dd1c515 | |||
| 46b15ed6ab | |||
| a015e72f7c | |||
|
|
d5df7d77a0 | ||
| ec3ceee862 | |||
| 867475c3ff | |||
| b024c5557c | |||
| 93306f360d | |||
| 52847d999d | |||
| b9629b43ff | |||
| 06482a9f8d | |||
| f2e4ae5ac2 | |||
| 71baf0f6da | |||
| d02b6fc347 | |||
| 431f1e347b | |||
| ab6f45ed5c | |||
| 02c060d239 | |||
| be2029de82 | |||
| e148eaeaf3 | |||
| c63a1e6ce4 | |||
| b3a73a7781 | |||
| 1968585d0f | |||
| b33c758411 | |||
| afa543125c | |||
|
|
0d0252cac0 | ||
|
|
a8bd9aeed5 | ||
| d277c61afd | |||
|
|
86479952d1 | ||
| c94024a8ae | |||
| 70611d16f8 | |||
| 354db231b8 | |||
| 9d8d60d00f | |||
| f5d25abbec | |||
| 8e9f7314f5 | |||
| 03b88b944d | |||
| 385c36f660 | |||
| cfa092cd38 | |||
| 1a762f8f54 | |||
| fbdaf72775 | |||
| 67128a9ca1 | |||
| 898759db3d | |||
| 4f009a9557 | |||
| 378d0981f0 | |||
| 8f3702f803 | |||
| cfd303fc85 | |||
| 0fc0976f8a | |||
| e247ea8ead | |||
| 0c57c4bc6d | |||
| 9b7998e1c3 | |||
| 8997a00413 | |||
| a2e6f6185f | |||
| c2715b01d7 | |||
| 6f09dfcd12 | |||
| bb9f55a3b5 | |||
| 298ee7492c | |||
| 37cb836246 | |||
| 19e3318125 | |||
| 683b02f4a0 | |||
| 20aecc30b1 | |||
| daf57aa152 | |||
| bfd693de92 | |||
| ea2ad0017b | |||
| 82e7473cac | |||
| 315523e80f | |||
| 569b6de154 | |||
| 95f8159554 | |||
|
|
5698c494f1 | ||
| e640e66b8d | |||
|
|
9be7d170c6 | ||
| c2c98c516b | |||
| 32554f5998 |
@@ -41,7 +41,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export EVENT_JSON="/var/run/act/workflow/event.json"
|
||||
test -f "$EVENT_JSON" || { echo "❌ Missing $EVENT_JSON"; exit 1; }
|
||||
test -f "$EVENT_JSON" || { echo "Missing $EVENT_JSON"; exit 1; }
|
||||
|
||||
node --input-type=module - <<'NODE' > /tmp/anno.env
|
||||
import fs from "node:fs";
|
||||
@@ -66,7 +66,10 @@ jobs:
|
||||
|
||||
if (!owner || !repo) {
|
||||
const m = cloneUrl.match(/[:/](?<o>[^/]+)\/(?<r>[^/]+?)(?:\.git)?$/);
|
||||
if (m?.groups) { owner = owner || m.groups.o; repo = repo || m.groups.r; }
|
||||
if (m?.groups) {
|
||||
owner = owner || m.groups.o;
|
||||
repo = repo || m.groups.r;
|
||||
}
|
||||
}
|
||||
if (!owner || !repo) throw new Error("Cannot infer owner/repo");
|
||||
|
||||
@@ -81,7 +84,6 @@ jobs:
|
||||
throw new Error("No issue number in event.json or workflow_dispatch input");
|
||||
}
|
||||
|
||||
// label name: best-effort (non-bloquant)
|
||||
let labelName = "workflow_dispatch";
|
||||
const lab = ev?.label;
|
||||
if (typeof lab === "string") labelName = lab;
|
||||
@@ -95,7 +97,7 @@ jobs:
|
||||
? String(process.env.FORGE_API).trim().replace(/\/+$/,"")
|
||||
: origin;
|
||||
|
||||
function sh(s){ return JSON.stringify(String(s)); }
|
||||
function sh(s) { return JSON.stringify(String(s)); }
|
||||
|
||||
process.stdout.write([
|
||||
`CLONE_URL=${sh(cloneUrl)}`,
|
||||
@@ -108,7 +110,7 @@ jobs:
|
||||
].join("\n") + "\n");
|
||||
NODE
|
||||
|
||||
echo "✅ context:"
|
||||
echo "context:"
|
||||
sed -n '1,120p' /tmp/anno.env
|
||||
|
||||
- name: Early gate (label event fast-skip, but tolerant)
|
||||
@@ -116,18 +118,16 @@ jobs:
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
|
||||
echo "ℹ️ event label = $LABEL_NAME"
|
||||
echo "event label = $LABEL_NAME"
|
||||
|
||||
# Fast skip on obvious non-approved label events (avoid noise),
|
||||
# BUT do NOT skip if label payload is weird/unknown.
|
||||
if [[ "$LABEL_NAME" != "state/approved" && "$LABEL_NAME" != "workflow_dispatch" && "$LABEL_NAME" != "" && "$LABEL_NAME" != "[object Object]" ]]; then
|
||||
echo "ℹ️ label=$LABEL_NAME => skip early"
|
||||
echo "label=$LABEL_NAME => skip early"
|
||||
echo "SKIP=1" >> /tmp/anno.env
|
||||
echo "SKIP_REASON=\"label_not_approved_event\"" >> /tmp/anno.env
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "✅ continue to API gating (issue=$ISSUE_NUMBER)"
|
||||
echo "continue to API gating (issue=$ISSUE_NUMBER)"
|
||||
|
||||
- name: Fetch issue + hard gate on labels + Type
|
||||
env:
|
||||
@@ -135,9 +135,9 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "❌ Missing secret FORGE_TOKEN"; exit 1; }
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "Missing secret FORGE_TOKEN"; exit 1; }
|
||||
|
||||
curl -fsS \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
@@ -148,11 +148,12 @@ jobs:
|
||||
node --input-type=module - <<'NODE' >> /tmp/anno.env
|
||||
import fs from "node:fs";
|
||||
|
||||
const issue = JSON.parse(fs.readFileSync("/tmp/issue.json","utf8"));
|
||||
const title = String(issue.title || "");
|
||||
const issue = JSON.parse(fs.readFileSync("/tmp/issue.json", "utf8"));
|
||||
const body = String(issue.body || "").replace(/\r\n/g, "\n");
|
||||
|
||||
const labels = Array.isArray(issue.labels) ? issue.labels.map(l => String(l.name || "")).filter(Boolean) : [];
|
||||
const labels = Array.isArray(issue.labels)
|
||||
? issue.labels.map(l => String(l.name || "")).filter(Boolean)
|
||||
: [];
|
||||
const hasApproved = labels.includes("state/approved");
|
||||
|
||||
function pickLine(key) {
|
||||
@@ -164,14 +165,12 @@ jobs:
|
||||
const typeRaw = pickLine("Type");
|
||||
const type = String(typeRaw || "").trim().toLowerCase();
|
||||
|
||||
const allowed = new Set(["type/media","type/reference","type/comment"]);
|
||||
const proposer = new Set(["type/correction","type/fact-check"]);
|
||||
const allowedAnno = new Set(["type/media", "type/reference", "type/comment"]);
|
||||
const proposerTypes = new Set(["type/correction", "type/fact-check"]);
|
||||
|
||||
const out = [];
|
||||
out.push(`ISSUE_TITLE=${JSON.stringify(title)}`);
|
||||
out.push(`ISSUE_TYPE=${JSON.stringify(type)}`);
|
||||
|
||||
// HARD gate: must currently have state/approved (avoids depending on event payload)
|
||||
if (!hasApproved) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("not_approved_label_present")}`);
|
||||
@@ -182,23 +181,23 @@ jobs:
|
||||
if (!type) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("missing_type")}`);
|
||||
} else if (allowed.has(type)) {
|
||||
} else if (allowedAnno.has(type)) {
|
||||
// proceed
|
||||
} else if (proposer.has(type)) {
|
||||
} else if (proposerTypes.has(type)) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("proposer_type:"+type)}`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("proposer_type:" + type)}`);
|
||||
} else {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("unsupported_type:"+type)}`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("unsupported_type:" + type)}`);
|
||||
}
|
||||
|
||||
process.stdout.write(out.join("\n") + "\n");
|
||||
NODE
|
||||
|
||||
echo "✅ gating result:"
|
||||
echo "gating result:"
|
||||
grep -E '^(ISSUE_TYPE|SKIP|SKIP_REASON)=' /tmp/anno.env || true
|
||||
|
||||
- name: Comment issue if skipped (Proposer / unsupported / missing Type)
|
||||
- name: Comment issue if skipped (unsupported / missing Type only)
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
@@ -208,9 +207,13 @@ jobs:
|
||||
|
||||
[[ "${SKIP:-0}" == "1" ]] || exit 0
|
||||
|
||||
# IMPORTANT: do NOT comment for "not_approved_label_present" (avoid spam on other label events)
|
||||
if [[ "${SKIP_REASON:-}" == "not_approved_label_present" || "${SKIP_REASON:-}" == "label_not_approved_event" ]]; then
|
||||
echo "ℹ️ skip reason=${SKIP_REASON} -> no comment"
|
||||
echo "skip reason=${SKIP_REASON} -> no comment"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_REASON:-}" == proposer_type:* ]]; then
|
||||
echo "proposer ticket detected -> anno stays silent"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -219,15 +222,13 @@ jobs:
|
||||
REASON="${SKIP_REASON:-}"
|
||||
TYPE="${ISSUE_TYPE:-}"
|
||||
|
||||
if [[ "$REASON" == proposer_type:* ]]; then
|
||||
MSG="ℹ️ Ticket #${ISSUE_NUMBER} détecté comme **Proposer** (${TYPE}).\n\n- Ce type est **traité manuellement par les editors**.\n✅ Aucun traitement automatique."
|
||||
elif [[ "$REASON" == unsupported_type:* ]]; then
|
||||
MSG="ℹ️ Ticket #${ISSUE_NUMBER} ignoré : Type non supporté par le bot (${TYPE}).\n\nTypes supportés : type/media, type/reference, type/comment."
|
||||
if [[ "$REASON" == unsupported_type:* ]]; then
|
||||
MSG="Ticket #${ISSUE_NUMBER} ignored: unsupported Type (${TYPE}). Supported types: type/media, type/reference, type/comment."
|
||||
else
|
||||
MSG="ℹ️ Ticket #${ISSUE_NUMBER} ignoré : champ 'Type:' manquant ou illisible.\n\nAjoute : Type: type/media|type/reference|type/comment"
|
||||
MSG="Ticket #${ISSUE_NUMBER} ignored: missing or unreadable 'Type:'. Expected: type/media|type/reference|type/comment"
|
||||
fi
|
||||
|
||||
PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
|
||||
PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1] || ""}))' "$MSG")"
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
@@ -239,7 +240,7 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
|
||||
rm -rf .git
|
||||
git init -q
|
||||
@@ -252,16 +253,16 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
npm ci --no-audit --no-fund
|
||||
|
||||
- name: Check apply script exists
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
test -f scripts/apply-annotation-ticket.mjs || {
|
||||
echo "❌ missing scripts/apply-annotation-ticket.mjs on $DEFAULT_BRANCH"
|
||||
echo "missing scripts/apply-annotation-ticket.mjs on $DEFAULT_BRANCH"
|
||||
ls -la scripts | sed -n '1,200p' || true
|
||||
exit 1
|
||||
}
|
||||
@@ -270,16 +271,16 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
|
||||
npm run build
|
||||
|
||||
test -f dist/para-index.json || {
|
||||
echo "❌ missing dist/para-index.json after build"
|
||||
echo "missing dist/para-index.json after build"
|
||||
ls -la dist | sed -n '1,200p' || true
|
||||
exit 1
|
||||
}
|
||||
echo "✅ dist/para-index.json present"
|
||||
echo "dist/para-index.json present"
|
||||
|
||||
- name: Apply ticket on bot branch (strict+verify, commit)
|
||||
continue-on-error: true
|
||||
@@ -290,10 +291,10 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
test -d .git || { echo "❌ not a git repo (checkout failed)"; echo "APPLY_RC=90" >> /tmp/anno.env; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
test -d .git || { echo "not a git repo (checkout failed)"; echo "APPLY_RC=90" >> /tmp/anno.env; exit 0; }
|
||||
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "❌ Missing secret FORGE_TOKEN"; exit 1; }
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "Missing secret FORGE_TOKEN"; exit 1; }
|
||||
|
||||
git config user.name "${BOT_GIT_NAME:-archicratie-bot}"
|
||||
git config user.email "${BOT_GIT_EMAIL:-bot@archicratie.local}"
|
||||
@@ -340,11 +341,11 @@ jobs:
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
|
||||
RC="${APPLY_RC:-0}"
|
||||
if [[ "$RC" == "0" ]]; then
|
||||
echo "ℹ️ no failure detected"
|
||||
echo "no failure detected"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -356,8 +357,8 @@ jobs:
|
||||
BODY="(no apply log found)"
|
||||
fi
|
||||
|
||||
MSG="❌ apply-annotation-ticket a échoué (rc=${RC}).\n\n\`\`\`\n${BODY}\n\`\`\`\n"
|
||||
PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
|
||||
MSG="apply-annotation-ticket failed (rc=${RC}).\n\n\`\`\`\n${BODY}\n\`\`\`\n"
|
||||
PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1] || ""}))' "$MSG")"
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
@@ -374,9 +375,9 @@ jobs:
|
||||
source /tmp/anno.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "ℹ️ apply failed -> skip push"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "ℹ️ no-op -> skip push"; exit 0; }
|
||||
test -d .git || { echo "ℹ️ no git repo -> skip push"; exit 0; }
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "apply failed -> skip push"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "no-op -> skip push"; exit 0; }
|
||||
test -d .git || { echo "no git repo -> skip push"; exit 0; }
|
||||
|
||||
AUTH_URL="$(node --input-type=module -e '
|
||||
const [clone, tok] = process.argv.slice(1);
|
||||
@@ -398,8 +399,8 @@ jobs:
|
||||
source /tmp/anno.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "ℹ️ apply failed -> skip PR"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "ℹ️ no-op -> skip PR"; exit 0; }
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "apply failed -> skip PR"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "no-op -> skip PR"; exit 0; }
|
||||
|
||||
PR_TITLE="anno: apply ticket #${ISSUE_NUMBER}"
|
||||
PR_BODY="PR auto depuis ticket #${ISSUE_NUMBER} (state/approved).\n\n- Branche: ${BRANCH}\n- Commit: ${END_SHA}\n\nMerge si CI OK."
|
||||
@@ -420,10 +421,10 @@ jobs:
|
||||
console.log(pr.html_url || pr.url || "");
|
||||
' "$PR_JSON")"
|
||||
|
||||
test -n "$PR_URL" || { echo "❌ PR URL missing. Raw: $PR_JSON"; exit 1; }
|
||||
test -n "$PR_URL" || { echo "PR URL missing. Raw: $PR_JSON"; exit 1; }
|
||||
|
||||
MSG="✅ PR créée pour ticket #${ISSUE_NUMBER} : ${PR_URL}"
|
||||
C_PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
|
||||
MSG="PR created for ticket #${ISSUE_NUMBER}: ${PR_URL}"
|
||||
C_PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1] || ""}))' "$MSG")"
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
@@ -431,7 +432,7 @@ jobs:
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \
|
||||
--data-binary "$C_PAYLOAD"
|
||||
|
||||
echo "✅ PR: $PR_URL"
|
||||
echo "PR: $PR_URL"
|
||||
|
||||
- name: Finalize (fail job if apply failed)
|
||||
if: ${{ always() }}
|
||||
@@ -439,11 +440,11 @@ jobs:
|
||||
set -euo pipefail
|
||||
source /tmp/anno.env || true
|
||||
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "skipped"; exit 0; }
|
||||
|
||||
RC="${APPLY_RC:-0}"
|
||||
if [[ "$RC" != "0" ]]; then
|
||||
echo "❌ apply failed (rc=$RC)"
|
||||
echo "apply failed (rc=$RC)"
|
||||
exit "$RC"
|
||||
fi
|
||||
echo "✅ apply ok"
|
||||
echo "apply ok"
|
||||
@@ -297,6 +297,19 @@ jobs:
|
||||
docker image tag archicratie-web:blue "archicratie-web:blue.BAK.${TS}" || true
|
||||
docker image tag archicratie-web:green "archicratie-web:green.BAK.${TS}" || true
|
||||
|
||||
BUILD_TIME_RAW="$(TZ=Europe/Paris date '+%Y-%m-%dT%H:%M:%S%z')"
|
||||
BUILD_TIME="${BUILD_TIME_RAW:0:${#BUILD_TIME_RAW}-2}:${BUILD_TIME_RAW:${#BUILD_TIME_RAW}-2}"
|
||||
|
||||
PUBLIC_OPS_ENV=staging \
|
||||
PUBLIC_OPS_UPSTREAM=web_blue \
|
||||
PUBLIC_BUILD_SHA="${AFTER}" \
|
||||
PUBLIC_BUILD_TIME="${BUILD_TIME}" \
|
||||
node scripts/write-ops-health.mjs
|
||||
|
||||
test -f public/__ops/health.json
|
||||
echo "=== public/__ops/health.json (blue/staging) ==="
|
||||
cat public/__ops/health.json
|
||||
|
||||
docker compose -p "$PROJ" -f docker-compose.yml build web_blue
|
||||
docker rm -f archicratie-web-blue || true
|
||||
docker compose -p "$PROJ" -f docker-compose.yml up -d --force-recreate --remove-orphans web_blue
|
||||
@@ -306,6 +319,11 @@ jobs:
|
||||
wait_url "http://127.0.0.1:8081/annotations-index.json" "blue annotations-index"
|
||||
wait_url "http://127.0.0.1:8081/pagefind/pagefind.js" "blue pagefind.js"
|
||||
|
||||
wait_url "http://127.0.0.1:8081/__ops/health.json" "blue ops health"
|
||||
|
||||
curl -fsS --max-time 6 "http://127.0.0.1:8081/__ops/health.json" \
|
||||
| python3 -c 'import sys, json; j=json.load(sys.stdin); print("env=", j.get("env")); print("upstream=", j.get("upstream")); print("buildSha=", j.get("buildSha")); print("builtAt=", j.get("builtAt"))'
|
||||
|
||||
CANON="$(curl -fsS --max-time 6 "http://127.0.0.1:8081/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)"
|
||||
echo "canonical(blue)=$CANON"
|
||||
echo "$CANON" | grep -q 'https://staging\.archicratie\.trans-hands\.synology\.me/' || {
|
||||
@@ -353,6 +371,19 @@ jobs:
|
||||
docker compose -p "$PROJ" -f docker-compose.yml up -d --force-recreate --remove-orphans web_green || true
|
||||
}
|
||||
|
||||
BUILD_TIME_RAW="$(TZ=Europe/Paris date '+%Y-%m-%dT%H:%M:%S%z')"
|
||||
BUILD_TIME="${BUILD_TIME_RAW:0:${#BUILD_TIME_RAW}-2}:${BUILD_TIME_RAW:${#BUILD_TIME_RAW}-2}"
|
||||
|
||||
PUBLIC_OPS_ENV=prod \
|
||||
PUBLIC_OPS_UPSTREAM=web_green \
|
||||
PUBLIC_BUILD_SHA="${AFTER}" \
|
||||
PUBLIC_BUILD_TIME="${BUILD_TIME}" \
|
||||
node scripts/write-ops-health.mjs
|
||||
|
||||
test -f public/__ops/health.json
|
||||
echo "=== public/__ops/health.json (green/prod) ==="
|
||||
cat public/__ops/health.json
|
||||
|
||||
# build/restart green
|
||||
if ! docker compose -p "$PROJ" -f docker-compose.yml build web_green; then
|
||||
echo "❌ build green failed"; rollback; exit 4
|
||||
@@ -366,6 +397,11 @@ jobs:
|
||||
if ! wait_url "http://127.0.0.1:8082/annotations-index.json" "green annotations-index"; then rollback; exit 4; fi
|
||||
if ! wait_url "http://127.0.0.1:8082/pagefind/pagefind.js" "green pagefind.js"; then rollback; exit 4; fi
|
||||
|
||||
if ! wait_url "http://127.0.0.1:8082/__ops/health.json" "green ops health"; then rollback; exit 4; fi
|
||||
|
||||
curl -fsS --max-time 6 "http://127.0.0.1:8082/__ops/health.json" \
|
||||
| python3 -c 'import sys, json; j=json.load(sys.stdin); print("env=", j.get("env")); print("upstream=", j.get("upstream")); print("buildSha=", j.get("buildSha")); print("builtAt=", j.get("builtAt"))'
|
||||
|
||||
CANON="$(curl -fsS --max-time 6 "http://127.0.0.1:8082/archicrat-ia/chapitre-1/" | grep -oE 'rel="canonical" href="[^"]+"' | head -n1 || true)"
|
||||
echo "canonical(green)=$CANON"
|
||||
echo "$CANON" | grep -q 'https://archicratie\.trans-hands\.synology\.me/' || {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
name: Proposer Apply (PR)
|
||||
name: Proposer Apply (Queue)
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue:
|
||||
description: "Issue number to apply (Proposer: correction/fact-check)"
|
||||
required: true
|
||||
description: "Issue number to prioritize (optional)"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
env:
|
||||
NODE_OPTIONS: --dns-result-order=ipv4first
|
||||
@@ -17,8 +20,8 @@ defaults:
|
||||
shell: bash
|
||||
|
||||
concurrency:
|
||||
group: proposer-apply-${{ github.event.issue.number || inputs.issue || 'manual' }}
|
||||
cancel-in-progress: true
|
||||
group: proposer-queue-main
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
apply-proposer:
|
||||
@@ -34,14 +37,15 @@ jobs:
|
||||
node --version
|
||||
npm --version
|
||||
|
||||
- name: Derive context (event.json / workflow_dispatch)
|
||||
- name: Derive context (event.json / workflow_dispatch / push)
|
||||
env:
|
||||
INPUT_ISSUE: ${{ inputs.issue }}
|
||||
EVENT_NAME_IN: ${{ github.event_name }}
|
||||
FORGE_API: ${{ vars.FORGE_API || vars.FORGE_BASE }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export EVENT_JSON="/var/run/act/workflow/event.json"
|
||||
test -f "$EVENT_JSON" || { echo "❌ Missing $EVENT_JSON"; exit 1; }
|
||||
test -f "$EVENT_JSON" || { echo "Missing $EVENT_JSON"; exit 1; }
|
||||
|
||||
node --input-type=module - <<'NODE' > /tmp/proposer.env
|
||||
import fs from "node:fs";
|
||||
@@ -51,7 +55,7 @@ jobs:
|
||||
|
||||
const cloneUrl =
|
||||
repoObj?.clone_url ||
|
||||
(repoObj?.html_url ? (repoObj.html_url.replace(/\/$/,"") + ".git") : "");
|
||||
(repoObj?.html_url ? (repoObj.html_url.replace(/\/$/, "") + ".git") : "");
|
||||
|
||||
if (!cloneUrl) throw new Error("No repository clone_url/html_url in event.json");
|
||||
|
||||
@@ -66,8 +70,12 @@ jobs:
|
||||
|
||||
if (!owner || !repo) {
|
||||
const m = cloneUrl.match(/[:/](?<o>[^/]+)\/(?<r>[^/]+?)(?:\.git)?$/);
|
||||
if (m?.groups) { owner = owner || m.groups.o; repo = repo || m.groups.r; }
|
||||
if (m?.groups) {
|
||||
owner = owner || m.groups.o;
|
||||
repo = repo || m.groups.r;
|
||||
}
|
||||
}
|
||||
|
||||
if (!owner || !repo) throw new Error("Cannot infer owner/repo");
|
||||
|
||||
const defaultBranch = repoObj?.default_branch || "main";
|
||||
@@ -75,25 +83,30 @@ jobs:
|
||||
const issueNumber =
|
||||
ev?.issue?.number ||
|
||||
ev?.issue?.index ||
|
||||
(process.env.INPUT_ISSUE ? Number(process.env.INPUT_ISSUE) : 0);
|
||||
|
||||
if (!issueNumber || !Number.isFinite(Number(issueNumber))) {
|
||||
throw new Error("No issue number in event.json or workflow_dispatch input");
|
||||
}
|
||||
(process.env.INPUT_ISSUE ? Number(process.env.INPUT_ISSUE) : 0) ||
|
||||
0;
|
||||
|
||||
const labelName =
|
||||
ev?.label?.name ||
|
||||
ev?.label ||
|
||||
"workflow_dispatch";
|
||||
(typeof ev?.label === "string" ? ev.label : "") ||
|
||||
"";
|
||||
|
||||
const eventName =
|
||||
String(process.env.EVENT_NAME_IN || "").trim() ||
|
||||
(ev?.issue ? "issues" : (ev?.before || ev?.after ? "push" : "workflow_dispatch"));
|
||||
|
||||
const u = new URL(cloneUrl);
|
||||
const origin = u.origin;
|
||||
|
||||
const apiBase = (process.env.FORGE_API && String(process.env.FORGE_API).trim())
|
||||
? String(process.env.FORGE_API).trim().replace(/\/+$/,"")
|
||||
: origin;
|
||||
const apiBase =
|
||||
(process.env.FORGE_API && String(process.env.FORGE_API).trim())
|
||||
? String(process.env.FORGE_API).trim().replace(/\/+$/, "")
|
||||
: origin;
|
||||
|
||||
function sh(s) {
|
||||
return JSON.stringify(String(s));
|
||||
}
|
||||
|
||||
function sh(s){ return JSON.stringify(String(s)); }
|
||||
process.stdout.write([
|
||||
`CLONE_URL=${sh(cloneUrl)}`,
|
||||
`OWNER=${sh(owner)}`,
|
||||
@@ -101,82 +114,230 @@ jobs:
|
||||
`DEFAULT_BRANCH=${sh(defaultBranch)}`,
|
||||
`ISSUE_NUMBER=${sh(issueNumber)}`,
|
||||
`LABEL_NAME=${sh(labelName)}`,
|
||||
`EVENT_NAME=${sh(eventName)}`,
|
||||
`API_BASE=${sh(apiBase)}`
|
||||
].join("\n") + "\n");
|
||||
NODE
|
||||
|
||||
echo "✅ context:"
|
||||
sed -n '1,120p' /tmp/proposer.env
|
||||
echo "Context:"
|
||||
sed -n '1,200p' /tmp/proposer.env
|
||||
|
||||
- name: Gate on label state/approved
|
||||
- name: Early gate (tolerant on empty issue label payload)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
|
||||
if [[ "$LABEL_NAME" != "state/approved" && "$LABEL_NAME" != "workflow_dispatch" ]]; then
|
||||
echo "ℹ️ label=$LABEL_NAME => skip"
|
||||
echo "SKIP=1" >> /tmp/proposer.env
|
||||
exit 0
|
||||
fi
|
||||
echo "✅ proceed (issue=$ISSUE_NUMBER)"
|
||||
echo "event=$EVENT_NAME label=${LABEL_NAME:-<empty>}"
|
||||
|
||||
- name: Fetch issue + API-hard gate on (state/approved present + proposer type)
|
||||
if [[ "$EVENT_NAME" == "issues" ]]; then
|
||||
if [[ -n "${LABEL_NAME:-}" && "$LABEL_NAME" != "state/approved" ]]; then
|
||||
echo "issues/labeled with explicit non-approved label=$LABEL_NAME -> skip"
|
||||
echo 'SKIP=1' >> /tmp/proposer.env
|
||||
echo 'SKIP_REASON="label_not_state_approved_event"' >> /tmp/proposer.env
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Proceed to API-based selection/gating"
|
||||
|
||||
- name: Checkout default branch
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
rm -rf .git
|
||||
git init -q
|
||||
git remote add origin "$CLONE_URL"
|
||||
git fetch --depth 1 origin "$DEFAULT_BRANCH"
|
||||
git -c advice.detachedHead=false checkout -q FETCH_HEAD
|
||||
git log -1 --oneline
|
||||
|
||||
- name: Detect app dir (repo-root vs ./site)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
APP_DIR="."
|
||||
if [[ -d "site" && -f "site/package.json" ]]; then
|
||||
APP_DIR="site"
|
||||
fi
|
||||
|
||||
echo "APP_DIR=$APP_DIR" >> /tmp/proposer.env
|
||||
echo "APP_DIR=$APP_DIR"
|
||||
|
||||
test -f "$APP_DIR/package.json" || {
|
||||
echo "package.json missing in APP_DIR=$APP_DIR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
test -d "$APP_DIR/scripts" || {
|
||||
echo "scripts/ missing in APP_DIR=$APP_DIR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Select next proposer batch (by path)
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "❌ Missing secret FORGE_TOKEN"; exit 1; }
|
||||
test -n "${FORGE_TOKEN:-}" || {
|
||||
echo "Missing secret FORGE_TOKEN"
|
||||
exit 1
|
||||
}
|
||||
|
||||
export GITEA_OWNER="$OWNER"
|
||||
export GITEA_REPO="$REPO"
|
||||
export FORGE_API="$API_BASE"
|
||||
|
||||
cd "$APP_DIR"
|
||||
|
||||
test -f scripts/pick-proposer-issue.mjs || {
|
||||
echo "missing scripts/pick-proposer-issue.mjs in APP_DIR=$APP_DIR"
|
||||
ls -la scripts | sed -n '1,200p' || true
|
||||
exit 1
|
||||
}
|
||||
|
||||
node scripts/pick-proposer-issue.mjs "${ISSUE_NUMBER:-0}" > /tmp/proposer.pick.env
|
||||
cat /tmp/proposer.pick.env >> /tmp/proposer.env
|
||||
source /tmp/proposer.pick.env
|
||||
|
||||
if [[ "${TARGET_FOUND:-0}" != "1" ]]; then
|
||||
echo 'SKIP=1' >> /tmp/proposer.env
|
||||
echo "SKIP_REASON=${TARGET_REASON:-no_target}" >> /tmp/proposer.env
|
||||
echo "No target batch"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Target batch:"
|
||||
grep -E '^(TARGET_PRIMARY_ISSUE|TARGET_ISSUES|TARGET_COUNT|TARGET_CHEMIN)=' /tmp/proposer.env
|
||||
|
||||
- name: Derive deterministic batch identity
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
export TARGET_ISSUES TARGET_CHEMIN
|
||||
|
||||
node --input-type=module - <<'NODE'
|
||||
import fs from "node:fs";
|
||||
import crypto from "node:crypto";
|
||||
|
||||
const issues = String(process.env.TARGET_ISSUES || "")
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => Number(a) - Number(b));
|
||||
|
||||
const chemin = String(process.env.TARGET_CHEMIN || "").trim();
|
||||
const keySource = `${chemin}::${issues.join(",")}`;
|
||||
const hash = crypto.createHash("sha1").update(keySource).digest("hex").slice(0, 12);
|
||||
const primary = issues[0] || "0";
|
||||
const batchBranch = `bot/proposer-${primary}-${hash}`;
|
||||
|
||||
fs.appendFileSync(
|
||||
"/tmp/proposer.env",
|
||||
[
|
||||
`BATCH_KEY=${JSON.stringify(keySource)}`,
|
||||
`BATCH_HASH=${JSON.stringify(hash)}`,
|
||||
`BATCH_BRANCH=${JSON.stringify(batchBranch)}`
|
||||
].join("\n") + "\n"
|
||||
);
|
||||
NODE
|
||||
|
||||
echo "Batch identity:"
|
||||
grep -E '^(BATCH_KEY|BATCH_HASH|BATCH_BRANCH)=' /tmp/proposer.env
|
||||
|
||||
- name: Inspect open proposer PRs
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
curl -fsS \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Accept: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER" \
|
||||
-o /tmp/issue.json
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls?state=open&limit=100" \
|
||||
-o /tmp/open_pulls.json
|
||||
|
||||
export TARGET_ISSUES="${TARGET_ISSUES:-}"
|
||||
export BATCH_BRANCH="${BATCH_BRANCH:-}"
|
||||
export BATCH_KEY="${BATCH_KEY:-}"
|
||||
|
||||
node --input-type=module - <<'NODE' >> /tmp/proposer.env
|
||||
import fs from "node:fs";
|
||||
const issue = JSON.parse(fs.readFileSync("/tmp/issue.json","utf8"));
|
||||
const title = String(issue.title || "");
|
||||
const body = String(issue.body || "").replace(/\r\n/g, "\n");
|
||||
const labels = Array.isArray(issue.labels) ? issue.labels.map(l => String(l.name||"")).filter(Boolean) : [];
|
||||
|
||||
function pickLine(key) {
|
||||
const re = new RegExp(`^\\s*${key}\\s*:\\s*([^\\n\\r]+)`, "mi");
|
||||
const m = body.match(re);
|
||||
return m ? m[1].trim() : "";
|
||||
}
|
||||
const pulls = JSON.parse(fs.readFileSync("/tmp/open_pulls.json", "utf8"));
|
||||
const issues = String(process.env.TARGET_ISSUES || "")
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean);
|
||||
|
||||
const typeRaw = pickLine("Type");
|
||||
const type = String(typeRaw || "").trim().toLowerCase();
|
||||
const batchBranch = String(process.env.BATCH_BRANCH || "");
|
||||
const batchKey = String(process.env.BATCH_KEY || "");
|
||||
|
||||
const hasApproved = labels.includes("state/approved");
|
||||
const proposer = new Set(["type/correction","type/fact-check"]);
|
||||
const proposerOpen = Array.isArray(pulls)
|
||||
? pulls.filter((pr) => String(pr?.head?.ref || "").startsWith("bot/proposer-"))
|
||||
: [];
|
||||
|
||||
const sameBatch = proposerOpen.find((pr) => {
|
||||
const ref = String(pr?.head?.ref || "");
|
||||
const title = String(pr?.title || "");
|
||||
const body = String(pr?.body || "");
|
||||
|
||||
if (batchBranch && ref === batchBranch) return true;
|
||||
if (batchKey && body.includes(`Batch-Key: ${batchKey}`)) return true;
|
||||
|
||||
return issues.some((n) =>
|
||||
ref.startsWith(`bot/proposer-${n}-`) ||
|
||||
title.includes(`#${n}`) ||
|
||||
body.includes(`#${n}`) ||
|
||||
body.includes(`ticket #${n}`)
|
||||
);
|
||||
});
|
||||
|
||||
const out = [];
|
||||
out.push(`ISSUE_TITLE=${JSON.stringify(title)}`);
|
||||
out.push(`ISSUE_TYPE=${JSON.stringify(type)}`);
|
||||
out.push(`HAS_APPROVED=${hasApproved ? "1":"0"}`);
|
||||
|
||||
if (!hasApproved) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("approved_not_present")}`);
|
||||
} else if (!type) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("missing_type")}`);
|
||||
} else if (!proposer.has(type)) {
|
||||
out.push(`SKIP=1`);
|
||||
out.push(`SKIP_REASON=${JSON.stringify("not_proposer:"+type)}`);
|
||||
if (sameBatch) {
|
||||
out.push("SKIP=1");
|
||||
out.push(`SKIP_REASON=${JSON.stringify("issue_already_has_open_pr")}`);
|
||||
out.push(`OPEN_PR_URL=${JSON.stringify(String(sameBatch.html_url || sameBatch.url || ""))}`);
|
||||
out.push(`OPEN_PR_BRANCH=${JSON.stringify(String(sameBatch?.head?.ref || ""))}`);
|
||||
} else if (proposerOpen.length > 0) {
|
||||
const first = proposerOpen[0];
|
||||
out.push("SKIP=1");
|
||||
out.push(`SKIP_REASON=${JSON.stringify("queue_busy_open_proposer_pr")}`);
|
||||
out.push(`OPEN_PR_URL=${JSON.stringify(String(first.html_url || first.url || ""))}`);
|
||||
out.push(`OPEN_PR_BRANCH=${JSON.stringify(String(first?.head?.ref || ""))}`);
|
||||
}
|
||||
process.stdout.write(out.join("\n") + "\n");
|
||||
|
||||
process.stdout.write(out.join("\n") + (out.length ? "\n" : ""));
|
||||
NODE
|
||||
|
||||
echo "✅ proposer gating:"
|
||||
grep -E '^(ISSUE_TYPE|HAS_APPROVED|SKIP|SKIP_REASON)=' /tmp/proposer.env || true
|
||||
- name: Guard on remote batch branch before heavy work
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
- name: Comment issue if skipped
|
||||
if git ls-remote --exit-code --heads origin "$BATCH_BRANCH" >/dev/null 2>&1; then
|
||||
echo 'SKIP=1' >> /tmp/proposer.env
|
||||
echo 'SKIP_REASON="batch_branch_exists_without_pr"' >> /tmp/proposer.env
|
||||
echo "OPEN_PR_BRANCH=${BATCH_BRANCH}" >> /tmp/proposer.env
|
||||
echo "Remote batch branch already exists -> skip duplicate materialization"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Remote batch branch is free"
|
||||
|
||||
- name: Comment issue if queued / skipped
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
@@ -185,122 +346,149 @@ jobs:
|
||||
source /tmp/proposer.env || true
|
||||
|
||||
[[ "${SKIP:-0}" == "1" ]] || exit 0
|
||||
[[ "$LABEL_NAME" == "state/approved" || "$LABEL_NAME" == "workflow_dispatch" ]] || exit 0
|
||||
[[ "${EVENT_NAME:-}" != "push" ]] || exit 0
|
||||
|
||||
REASON="${SKIP_REASON:-}"
|
||||
TYPE="${ISSUE_TYPE:-}"
|
||||
|
||||
if [[ "$REASON" == "approved_not_present" ]]; then
|
||||
MSG="ℹ️ Proposer Apply: skip — le label **state/approved** n'est pas présent sur le ticket au moment du run (gate API-hard)."
|
||||
elif [[ "$REASON" == "missing_type" ]]; then
|
||||
MSG="ℹ️ Proposer Apply: skip — champ **Type:** manquant/illisible. Attendu: type/correction ou type/fact-check."
|
||||
else
|
||||
MSG="ℹ️ Proposer Apply: skip — Type non-Proposer (${TYPE}). (Ce workflow ne traite que correction/fact-check.)"
|
||||
if [[ "${SKIP_REASON:-}" == "label_not_state_approved_event" || "${SKIP_REASON:-}" == "label_not_state_approved" ]]; then
|
||||
echo "Skip reason=${SKIP_REASON} -> no comment"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
|
||||
test -n "${FORGE_TOKEN:-}" || exit 0
|
||||
|
||||
ISSUE_TO_COMMENT="${ISSUE_NUMBER:-0}"
|
||||
if [[ "$ISSUE_TO_COMMENT" == "0" || -z "$ISSUE_TO_COMMENT" ]]; then
|
||||
ISSUE_TO_COMMENT="${TARGET_PRIMARY_ISSUE:-0}"
|
||||
fi
|
||||
[[ "$ISSUE_TO_COMMENT" != "0" ]] || exit 0
|
||||
|
||||
case "${SKIP_REASON:-}" in
|
||||
queue_busy_open_proposer_pr)
|
||||
MSG="Ticket queued in proposer queue. An open proposer PR already exists: ${OPEN_PR_URL:-"(URL unavailable)"}. The workflow will resume after merge on main."
|
||||
;;
|
||||
issue_already_has_open_pr)
|
||||
MSG="This batch already has an open proposer PR: ${OPEN_PR_URL:-"(URL unavailable)"}"
|
||||
;;
|
||||
batch_branch_exists_without_pr)
|
||||
MSG="This batch already has a remote batch branch (${OPEN_PR_BRANCH:-"(unknown branch)"}). Manual inspection is required before any new proposer PR is created."
|
||||
;;
|
||||
batch_branch_already_materialized)
|
||||
MSG="This batch was already materialized by another run on branch ${OPEN_PR_BRANCH:-"(unknown branch)"}. No duplicate PR was created."
|
||||
;;
|
||||
explicit_issue_missing_chemin)
|
||||
MSG="Proposer Apply: cannot process this ticket automatically because field Chemin is missing or unreadable."
|
||||
;;
|
||||
explicit_issue_missing_type)
|
||||
MSG="Proposer Apply: cannot process this ticket automatically because field Type is missing or unreadable."
|
||||
;;
|
||||
explicit_issue_not_approved)
|
||||
MSG="Proposer Apply: this ticket is not currently labeled state/approved."
|
||||
;;
|
||||
explicit_issue_rejected)
|
||||
MSG="Proposer Apply: this ticket has state/rejected and is not eligible for the proposer queue."
|
||||
;;
|
||||
no_open_approved_proposer_issue)
|
||||
MSG="No approved proposer ticket is currently waiting."
|
||||
;;
|
||||
*)
|
||||
MSG="Proposer Apply: skip - ${SKIP_REASON:-unspecified reason}."
|
||||
;;
|
||||
esac
|
||||
|
||||
export MSG
|
||||
node --input-type=module - <<'NODE' > /tmp/proposer.skip.comment.json
|
||||
const msg = process.env.MSG || "";
|
||||
process.stdout.write(JSON.stringify({ body: msg }));
|
||||
NODE
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \
|
||||
--data-binary "$PAYLOAD" || true
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_TO_COMMENT/comments" \
|
||||
--data-binary @/tmp/proposer.skip.comment.json || true
|
||||
|
||||
- name: Checkout default branch
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
|
||||
rm -rf .git
|
||||
git init -q
|
||||
git remote add origin "$CLONE_URL"
|
||||
git fetch --depth 1 origin "$DEFAULT_BRANCH"
|
||||
git -c advice.detachedHead=false checkout -q FETCH_HEAD
|
||||
git log -1 --oneline
|
||||
echo "✅ workspace:"
|
||||
ls -la | sed -n '1,120p'
|
||||
|
||||
- name: Detect app dir (repo-root vs ./site)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
|
||||
APP_DIR="."
|
||||
if [[ -d "site" && -f "site/package.json" ]]; then
|
||||
APP_DIR="site"
|
||||
fi
|
||||
|
||||
echo "APP_DIR=$APP_DIR" >> /tmp/proposer.env
|
||||
echo "✅ APP_DIR=$APP_DIR"
|
||||
ls -la "$APP_DIR" | sed -n '1,120p'
|
||||
test -f "$APP_DIR/package.json" || { echo "❌ package.json missing in APP_DIR=$APP_DIR"; exit 1; }
|
||||
test -d "$APP_DIR/scripts" || { echo "❌ scripts/ missing in APP_DIR=$APP_DIR"; exit 1; }
|
||||
|
||||
- name: NPM harden (reduce flakiness)
|
||||
- name: NPM harden
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
cd "$APP_DIR"
|
||||
npm config set fetch-retries 5
|
||||
npm config set fetch-retry-mintimeout 20000
|
||||
npm config set fetch-retry-maxtimeout 120000
|
||||
npm config set registry https://registry.npmjs.org
|
||||
|
||||
- name: Install deps (APP_DIR)
|
||||
- name: Install deps
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
cd "$APP_DIR"
|
||||
npm ci --no-audit --no-fund
|
||||
|
||||
- name: Build dist baseline (APP_DIR)
|
||||
- name: Build dist baseline
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
cd "$APP_DIR"
|
||||
npm run build
|
||||
|
||||
- name: Apply ticket (alias + commit) on bot branch
|
||||
- name: Apply proposer batch on bot branch
|
||||
continue-on-error: true
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
BOT_GIT_NAME: ${{ secrets.BOT_GIT_NAME }}
|
||||
BOT_GIT_EMAIL: ${{ secrets.BOT_GIT_EMAIL }}
|
||||
FORGE_API: ${{ vars.FORGE_API || vars.FORGE_BASE }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "ℹ️ skipped"; exit 0; }
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
git config user.name "${BOT_GIT_NAME:-archicratie-bot}"
|
||||
git config user.name "${BOT_GIT_NAME:-archicratie-bot}"
|
||||
git config user.email "${BOT_GIT_EMAIL:-bot@archicratie.local}"
|
||||
|
||||
START_SHA="$(git rev-parse HEAD)"
|
||||
TS="$(date -u +%Y%m%d-%H%M%S)"
|
||||
BR="bot/proposer-${ISSUE_NUMBER}-${TS}"
|
||||
BR="$BATCH_BRANCH"
|
||||
echo "BRANCH=$BR" >> /tmp/proposer.env
|
||||
git checkout -b "$BR"
|
||||
|
||||
export GITEA_OWNER="$OWNER"
|
||||
export GITEA_REPO="$REPO"
|
||||
export FORGE_BASE="$API_BASE"
|
||||
export FORGE_API="$API_BASE"
|
||||
|
||||
LOG="/tmp/proposer-apply.log"
|
||||
set +e
|
||||
(cd "$APP_DIR" && node scripts/apply-ticket.mjs "$ISSUE_NUMBER" --alias --commit) >"$LOG" 2>&1
|
||||
RC=$?
|
||||
set -e
|
||||
: > "$LOG"
|
||||
|
||||
RC=0
|
||||
FAILED_ISSUE=""
|
||||
|
||||
for ISSUE in $TARGET_ISSUES; do
|
||||
echo "" >> "$LOG"
|
||||
echo "== ticket #$ISSUE ==" >> "$LOG"
|
||||
|
||||
set +e
|
||||
(cd "$APP_DIR" && node scripts/apply-ticket.mjs "$ISSUE" --alias --commit) >> "$LOG" 2>&1
|
||||
STEP_RC=$?
|
||||
set -e
|
||||
|
||||
if [[ "$STEP_RC" -ne 0 ]]; then
|
||||
RC="$STEP_RC"
|
||||
FAILED_ISSUE="$ISSUE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
echo "APPLY_RC=$RC" >> /tmp/proposer.env
|
||||
echo "FAILED_ISSUE=${FAILED_ISSUE}" >> /tmp/proposer.env
|
||||
|
||||
echo "== apply log (tail) =="
|
||||
tail -n 200 "$LOG" || true
|
||||
echo "Apply log (tail):"
|
||||
tail -n 220 "$LOG" || true
|
||||
|
||||
END_SHA="$(git rev-parse HEAD)"
|
||||
|
||||
if [[ "$RC" -ne 0 ]]; then
|
||||
echo "NOOP=0" >> /tmp/proposer.env
|
||||
exit 0
|
||||
@@ -313,7 +501,34 @@ jobs:
|
||||
echo "END_SHA=$END_SHA" >> /tmp/proposer.env
|
||||
fi
|
||||
|
||||
- name: Push bot branch
|
||||
- name: Rebase bot branch on latest main
|
||||
continue-on-error: true
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || exit 0
|
||||
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
||||
|
||||
LOG="/tmp/proposer-apply.log"
|
||||
|
||||
git fetch origin "$DEFAULT_BRANCH"
|
||||
|
||||
set +e
|
||||
git rebase "origin/$DEFAULT_BRANCH" >> "$LOG" 2>&1
|
||||
RC=$?
|
||||
set -e
|
||||
|
||||
if [[ "$RC" -ne 0 ]]; then
|
||||
git rebase --abort || true
|
||||
fi
|
||||
|
||||
echo "REBASE_RC=$RC" >> /tmp/proposer.env
|
||||
|
||||
echo "Rebase log (tail):"
|
||||
tail -n 220 "$LOG" || true
|
||||
|
||||
- name: Comment issues on failure
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
@@ -322,9 +537,86 @@ jobs:
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "ℹ️ apply failed -> skip push"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "ℹ️ no-op -> skip push"; exit 0; }
|
||||
[[ -n "${BRANCH:-}" ]] || { echo "ℹ️ BRANCH unset -> skip push"; exit 0; }
|
||||
APPLY_RC="${APPLY_RC:-0}"
|
||||
REBASE_RC="${REBASE_RC:-0}"
|
||||
|
||||
if [[ "$APPLY_RC" == "0" && "$REBASE_RC" == "0" ]]; then
|
||||
echo "No failure detected"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
test -n "${FORGE_TOKEN:-}" || exit 0
|
||||
|
||||
if [[ -f /tmp/proposer-apply.log ]]; then
|
||||
BODY="$(tail -n 160 /tmp/proposer-apply.log | sed 's/\r$//')"
|
||||
else
|
||||
BODY="(no proposer log found)"
|
||||
fi
|
||||
|
||||
export BODY APPLY_RC REBASE_RC FAILED_ISSUE
|
||||
|
||||
if [[ "$APPLY_RC" != "0" ]]; then
|
||||
export FAILURE_KIND="apply"
|
||||
else
|
||||
export FAILURE_KIND="rebase"
|
||||
fi
|
||||
|
||||
node --input-type=module - <<'NODE' > /tmp/proposer.failure.comment.json
|
||||
const body = process.env.BODY || "";
|
||||
const applyRc = process.env.APPLY_RC || "0";
|
||||
const rebaseRc = process.env.REBASE_RC || "0";
|
||||
const failedIssue = process.env.FAILED_ISSUE || "unknown";
|
||||
const kind = process.env.FAILURE_KIND || "apply";
|
||||
|
||||
const msg =
|
||||
kind === "apply"
|
||||
? `Batch proposer failed on ticket #${failedIssue} (rc=${applyRc}).\n\n\`\`\`\n${body}\n\`\`\`\n`
|
||||
: `Rebase proposer failed on main (rc=${rebaseRc}).\n\n\`\`\`\n${body}\n\`\`\`\n`;
|
||||
|
||||
process.stdout.write(JSON.stringify({ body: msg }));
|
||||
NODE
|
||||
|
||||
for ISSUE in ${TARGET_ISSUES:-}; do
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE/comments" \
|
||||
--data-binary @/tmp/proposer.failure.comment.json || true
|
||||
done
|
||||
|
||||
- name: Late guard against duplicate batch materialization
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || exit 0
|
||||
[[ "${REBASE_RC:-0}" == "0" ]] || exit 0
|
||||
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
||||
|
||||
REMOTE_SHA="$(git ls-remote --heads origin "$BATCH_BRANCH" | awk 'NR==1 {print $1}')"
|
||||
|
||||
if [[ -n "${REMOTE_SHA:-}" && "${REMOTE_SHA}" != "${END_SHA:-}" ]]; then
|
||||
echo 'SKIP=1' >> /tmp/proposer.env
|
||||
echo 'SKIP_REASON="batch_branch_already_materialized"' >> /tmp/proposer.env
|
||||
echo "OPEN_PR_BRANCH=${BATCH_BRANCH}" >> /tmp/proposer.env
|
||||
echo "Remote batch branch already exists at $REMOTE_SHA -> skip duplicate push/PR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Late guard OK"
|
||||
|
||||
- name: Push bot branch
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || { echo "Apply failed -> skip push"; exit 0; }
|
||||
[[ "${REBASE_RC:-0}" == "0" ]] || { echo "Rebase failed -> skip push"; exit 0; }
|
||||
[[ "${NOOP:-0}" == "0" ]] || { echo "No-op -> skip push"; exit 0; }
|
||||
[[ -n "${BRANCH:-}" ]] || { echo "BRANCH unset -> skip push"; exit 0; }
|
||||
|
||||
AUTH_URL="$(node --input-type=module -e '
|
||||
const [clone, tok] = process.argv.slice(1);
|
||||
@@ -337,7 +629,7 @@ jobs:
|
||||
git remote set-url origin "$AUTH_URL"
|
||||
git push -u origin "$BRANCH"
|
||||
|
||||
- name: Create PR + comment issue
|
||||
- name: Create PR + comment issues + close issues
|
||||
if: ${{ always() }}
|
||||
env:
|
||||
FORGE_TOKEN: ${{ secrets.FORGE_TOKEN }}
|
||||
@@ -345,51 +637,152 @@ jobs:
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
[[ "${APPLY_RC:-0}" == "0" ]] || exit 0
|
||||
[[ "${REBASE_RC:-0}" == "0" ]] || exit 0
|
||||
[[ "${NOOP:-0}" == "0" ]] || exit 0
|
||||
[[ -n "${BRANCH:-}" ]] || { echo "ℹ️ BRANCH unset -> skip PR"; exit 0; }
|
||||
[[ -n "${BRANCH:-}" ]] || { echo "BRANCH unset -> skip PR"; exit 0; }
|
||||
|
||||
PR_TITLE="proposer: apply ticket #${ISSUE_NUMBER}"
|
||||
PR_BODY="PR auto depuis ticket #${ISSUE_NUMBER} (state/approved).\n\n- Branche: ${BRANCH}\n- Commit: ${END_SHA:-unknown}\n\nMerge si CI OK."
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "Missing FORGE_TOKEN"; exit 1; }
|
||||
|
||||
PR_PAYLOAD="$(node --input-type=module -e '
|
||||
const [title, body, base, head] = process.argv.slice(1);
|
||||
console.log(JSON.stringify({ title, body, base, head, allow_maintainer_edit: true }));
|
||||
' "$PR_TITLE" "$PR_BODY" "$DEFAULT_BRANCH" "${OWNER}:${BRANCH}")"
|
||||
OPEN_PRS_JSON="$(curl -fsS \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Accept: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls?state=open&limit=100")"
|
||||
|
||||
export OPEN_PRS_JSON BATCH_BRANCH BATCH_KEY
|
||||
|
||||
EXISTING_PR_URL="$(node --input-type=module -e '
|
||||
const pulls = JSON.parse(process.env.OPEN_PRS_JSON || "[]");
|
||||
const branch = String(process.env.BATCH_BRANCH || "");
|
||||
const key = String(process.env.BATCH_KEY || "");
|
||||
const current = Array.isArray(pulls)
|
||||
? pulls.find((pr) => {
|
||||
const ref = String(pr?.head?.ref || "");
|
||||
const body = String(pr?.body || "");
|
||||
return (branch && ref === branch) || (key && body.includes(`Batch-Key: ${key}`));
|
||||
})
|
||||
: null;
|
||||
process.stdout.write(current ? String(current.html_url || current.url || "") : "");
|
||||
')"
|
||||
|
||||
if [[ -n "${EXISTING_PR_URL:-}" ]]; then
|
||||
echo "PR already exists for this batch: $EXISTING_PR_URL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${TARGET_COUNT:-0}" == "1" ]]; then
|
||||
PR_TITLE="proposer: apply ticket #${TARGET_PRIMARY_ISSUE}"
|
||||
else
|
||||
PR_TITLE="proposer: apply ${TARGET_COUNT} tickets on ${TARGET_CHEMIN}"
|
||||
fi
|
||||
|
||||
export PR_TITLE TARGET_CHEMIN TARGET_ISSUES BRANCH END_SHA DEFAULT_BRANCH OWNER BATCH_KEY
|
||||
|
||||
node --input-type=module -e '
|
||||
import fs from "node:fs";
|
||||
|
||||
const issues = String(process.env.TARGET_ISSUES || "")
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean);
|
||||
|
||||
const body = [
|
||||
`PR auto depuis ticket${issues.length > 1 ? "s" : ""} ${issues.map((n) => `#${n}`).join(", ")} (state/approved).`,
|
||||
"",
|
||||
`- Chemin: ${process.env.TARGET_CHEMIN || "(inconnu)"}`,
|
||||
"- Tickets:",
|
||||
...issues.map((n) => ` - #${n}`),
|
||||
`- Branche: ${process.env.BRANCH || ""}`,
|
||||
`- Commit: ${process.env.END_SHA || "unknown"}`,
|
||||
`- Batch-Key: ${process.env.BATCH_KEY || ""}`,
|
||||
"",
|
||||
"Merge si CI OK."
|
||||
].join("\n");
|
||||
|
||||
fs.writeFileSync(
|
||||
"/tmp/proposer.pr.json",
|
||||
JSON.stringify({
|
||||
title: process.env.PR_TITLE || "proposer: apply tickets",
|
||||
body,
|
||||
base: process.env.DEFAULT_BRANCH || "main",
|
||||
head: `${process.env.OWNER}:${process.env.BRANCH}`,
|
||||
allow_maintainer_edit: true
|
||||
})
|
||||
);
|
||||
'
|
||||
|
||||
PR_JSON="$(curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/pulls" \
|
||||
--data-binary "$PR_PAYLOAD")"
|
||||
--data-binary @/tmp/proposer.pr.json)"
|
||||
|
||||
PR_URL="$(node --input-type=module -e '
|
||||
const pr = JSON.parse(process.argv[1] || "{}");
|
||||
console.log(pr.html_url || pr.url || "");
|
||||
' "$PR_JSON")"
|
||||
PR_URL="$(node --input-type=module -e 'const pr = JSON.parse(process.argv[1] || "{}"); console.log(pr.html_url || pr.url || "");' "$PR_JSON")"
|
||||
|
||||
test -n "$PR_URL" || { echo "❌ PR URL missing. Raw: $PR_JSON"; exit 1; }
|
||||
test -n "$PR_URL" || {
|
||||
echo "PR URL missing. Raw: $PR_JSON"
|
||||
exit 1
|
||||
}
|
||||
|
||||
MSG="✅ PR Proposer créée pour ticket #${ISSUE_NUMBER} : ${PR_URL}"
|
||||
C_PAYLOAD="$(node --input-type=module -e 'console.log(JSON.stringify({body: process.argv[1]||""}))' "$MSG")"
|
||||
for ISSUE in $TARGET_ISSUES; do
|
||||
export ISSUE PR_URL
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE_NUMBER/comments" \
|
||||
--data-binary "$C_PAYLOAD"
|
||||
node --input-type=module -e '
|
||||
import fs from "node:fs";
|
||||
|
||||
- name: Finalize (fail job if apply failed)
|
||||
const issue = process.env.ISSUE || "";
|
||||
const url = process.env.PR_URL || "";
|
||||
const msg =
|
||||
`PR proposer creee pour le ticket #${issue} : ${url}\n\n` +
|
||||
`Le ticket est cloture automatiquement ; la discussion peut se poursuivre dans la PR.`;
|
||||
|
||||
fs.writeFileSync(
|
||||
"/tmp/proposer.issue.close.comment.json",
|
||||
JSON.stringify({ body: msg })
|
||||
);
|
||||
'
|
||||
|
||||
curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE/comments" \
|
||||
--data-binary @/tmp/proposer.issue.close.comment.json
|
||||
|
||||
curl -fsS -X PATCH \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" \
|
||||
--data-binary '{"state":"closed"}'
|
||||
|
||||
ISSUE_STATE="$(curl -fsS \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Accept: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" | \
|
||||
node --input-type=module -e 'let s=""; process.stdin.on("data", d => s += d); process.stdin.on("end", () => { const j = JSON.parse(s || "{}"); process.stdout.write(String(j.state || "")); });')"
|
||||
|
||||
[[ "$ISSUE_STATE" == "closed" ]] || {
|
||||
echo "Issue #$ISSUE is still not closed after PATCH"
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
echo "PR: $PR_URL"
|
||||
|
||||
- name: Finalize
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env || true
|
||||
[[ "${SKIP:-0}" != "1" ]] || exit 0
|
||||
|
||||
RC="${APPLY_RC:-0}"
|
||||
if [[ "$RC" != "0" ]]; then
|
||||
echo "❌ apply failed (rc=$RC)"
|
||||
exit "$RC"
|
||||
if [[ "${APPLY_RC:-0}" != "0" ]]; then
|
||||
echo "Apply failed (rc=${APPLY_RC})"
|
||||
exit "${APPLY_RC}"
|
||||
fi
|
||||
echo "✅ apply ok"
|
||||
|
||||
if [[ "${REBASE_RC:-0}" != "0" ]]; then
|
||||
echo "Rebase failed (rc=${REBASE_RC})"
|
||||
exit "${REBASE_RC}"
|
||||
fi
|
||||
|
||||
echo "Proposer queue OK"
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -28,3 +28,7 @@ public/favicon_io.zip
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# local temp workspace
|
||||
.tmp/
|
||||
public/__ops/health.json
|
||||
|
||||
@@ -86,6 +86,10 @@ function rehypeDedupeIds() {
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
legacy: {
|
||||
collectionsBackwardsCompat: true,
|
||||
},
|
||||
|
||||
output: "static",
|
||||
trailingSlash: "always",
|
||||
site: process.env.PUBLIC_SITE ?? "http://localhost:4321",
|
||||
|
||||
15
config/anchor-churn-allowlist.json
Normal file
15
config/anchor-churn-allowlist.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"accepted_resets": {
|
||||
"archicrat-ia/prologue/index.html": "Reset intentionnel des ancres après réimport DOCX et révision substantielle du prologue depuis la source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/chapitre-1/index.html": "Reset intentionnel des ancres après révision doctrinale substantielle du chapitre 1. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/chapitre-2/index.html": "Reset intentionnel des ancres après restauration doctrinale substantielle du chapitre 2 depuis la bonne source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/chapitre-3/index.html": "Reset intentionnel des ancres après réimport DOCX et perfectionnement doctrinal substantiel du chapitre 3 depuis la source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/chapitre-4/index.html": "Reset intentionnel des ancres après réimport DOCX et stabilisation doctrinale substantielle du chapitre 4 depuis la source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/chapitre-5/index.html": "Reset intentionnel des ancres après réimport DOCX et stabilisation doctrinale substantielle du chapitre 5 depuis la source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"archicrat-ia/conclusion/index.html": "Reset intentionnel des ancres après réimport DOCX et révision substantielle de la conclusion depuis la source officielle. Site neuf, sans annotations ni compatibilité descendante à préserver.",
|
||||
"commencer/index.html": "Reset intentionnel des ancres après transformation de la page commencer en page d’entrée éditoriale sous SiteLayout, sans instrumentation de lecture longue à préserver."
|
||||
},
|
||||
"accepted_prefixes": {
|
||||
"glossaire/": "Reset intentionnel des ancres après révision substantielle des fiches paradigmes et doctrines du glossaire. Site neuf, sans annotations ni compatibilité descendante à préserver."
|
||||
}
|
||||
}
|
||||
152
docs/GLOSSARY-GRAPH-GOVERNANCE.md
Normal file
152
docs/GLOSSARY-GRAPH-GOVERNANCE.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Gouvernance du graphe du glossaire
|
||||
|
||||
## Lois actuelles vérifiées automatiquement
|
||||
|
||||
- Aucune fiche sans navigation.
|
||||
- Aucun `primaryNext` mort.
|
||||
- Aucun cycle direct.
|
||||
- Aucun `primaryNext` vers soi-même.
|
||||
- Aucune famille sans defaults.
|
||||
- Aucun hub `primaryNext` au-dessus du seuil 5.
|
||||
- Les paths effectifs tiennent compte des defaults famille.
|
||||
|
||||
## Liens `primaryNext` à surveiller qualitativement
|
||||
|
||||
- `contractualisme-hobbesien → droit-naturel-et-propriete`
|
||||
- `exception-souveraine → droit-naturel-et-propriete`
|
||||
- `regime-de-co-viabilite → gouvernance-des-communs`
|
||||
- `memoire-symbolique-et-instantaneite-computationnelle → meta-regime`
|
||||
- `scene-darchicration → co-viabilite`
|
||||
|
||||
## Critère de décision
|
||||
|
||||
Un `primaryNext` est bon s’il produit au moins l’un des effets suivants :
|
||||
|
||||
- déplier la notion ;
|
||||
- changer de niveau ;
|
||||
- rendre opératoire ;
|
||||
- mettre en tension ;
|
||||
- concrétiser.
|
||||
|
||||
Un `primaryNext` est faible s’il est seulement voisin, décoratif ou encyclopédique.
|
||||
|
||||
---
|
||||
|
||||
## Dynamique réelle du graphe (niveau avancé)
|
||||
|
||||
### 1. Attracteurs
|
||||
|
||||
L’audit de convergence effective met en évidence des nœuds fortement attractifs :
|
||||
|
||||
* dispositifs méthodologiques (audit, cartographie)
|
||||
* concepts fondamentaux (co-viabilité, tension, archicration)
|
||||
|
||||
Ces nœuds structurent la circulation globale du glossaire.
|
||||
|
||||
---
|
||||
|
||||
### 2. Bassins de convergence
|
||||
|
||||
Le graphe ne se distribue pas uniformément.
|
||||
|
||||
Il tend à s’organiser en bassins :
|
||||
|
||||
* bassin conceptuel central
|
||||
* bassin méthodologique
|
||||
* bassins secondaires (paradigmes, pathologies)
|
||||
|
||||
Un bassin est défini comme un ensemble de parcours convergeant vers un même noyau.
|
||||
|
||||
---
|
||||
|
||||
### 3. Risque de surconvergence
|
||||
|
||||
Lorsque trop de parcours convergent vers les mêmes nœuds :
|
||||
|
||||
* les parcours deviennent redondants
|
||||
* la navigation perd en différenciation
|
||||
* l’expérience de lecture devient monotone
|
||||
|
||||
Ce phénomène ne constitue pas une erreur technique, mais un déséquilibre structurel.
|
||||
|
||||
---
|
||||
|
||||
### 4. Bifurcation effective
|
||||
|
||||
Les nœuds à forte bifurcation jouent un rôle clé :
|
||||
|
||||
* ils ouvrent des trajectoires multiples
|
||||
* ils structurent la diversité des parcours
|
||||
|
||||
Ils doivent être préservés comme points d’expansion.
|
||||
|
||||
---
|
||||
|
||||
### 5. Principe de régulation avancée
|
||||
|
||||
L’objectif n’est pas de supprimer les attracteurs, mais de :
|
||||
|
||||
* limiter leur domination excessive
|
||||
* maintenir plusieurs bassins actifs
|
||||
* garantir des trajectoires réellement distinctes
|
||||
|
||||
---
|
||||
|
||||
### 6. Règle pratique
|
||||
|
||||
Avant toute modification :
|
||||
|
||||
* vérifier l’impact sur les convergences effectives
|
||||
* éviter d’ajouter un lien vers un nœud déjà dominant
|
||||
* privilégier l’ouverture de nouvelles zones de circulation
|
||||
|
||||
---
|
||||
|
||||
### 7. Nature du système
|
||||
|
||||
Le glossaire ne doit pas être compris comme :
|
||||
|
||||
* un index
|
||||
* ni un arbre
|
||||
|
||||
mais comme :
|
||||
|
||||
→ un graphe dynamique de circulation conceptuelle
|
||||
|
||||
dont la structure influence directement la pensée du lecteur.
|
||||
|
||||
|
||||
|
||||
# la première désaturation a déplacé les attracteurs vers scene-depreuve, journal-de-justification, regime-de-co-viabilite
|
||||
|
||||
## Attracteurs structurels du graphe
|
||||
|
||||
Certains nœuds présentent une forte convergence effective. Cela ne constitue pas un défaut, mais une propriété structurelle du modèle.
|
||||
|
||||
### Attracteurs assumés
|
||||
|
||||
- scene-depreuve (scène)
|
||||
→ cœur opératoire de mise à l’épreuve des régulations
|
||||
→ point de passage légitime pour une grande partie des parcours
|
||||
|
||||
- audit-archicratique (dispositif méthodologique)
|
||||
→ point d’entrée opératoire vers l’analyse des régulations
|
||||
|
||||
- cartographie-des-scenes-manquantes (dispositif méthodologique)
|
||||
→ prolongement naturel de l’audit vers l’identification des lacunes
|
||||
|
||||
- co-viabilite (concept fondamental)
|
||||
→ horizon de stabilisation des tensions
|
||||
|
||||
### Principe
|
||||
|
||||
Un attracteur est acceptable si :
|
||||
|
||||
- il correspond à un changement de niveau (concept → scène → dispositif)
|
||||
- il rend opératoire une notion
|
||||
- il constitue un passage obligé théoriquement justifié
|
||||
|
||||
Un attracteur devient problématique s’il :
|
||||
|
||||
- absorbe sans transformation
|
||||
- remplace une articulation par une répétition
|
||||
1393
docs/OPS-LOCALHOST-AUTO-SYNC.md
Normal file
1393
docs/OPS-LOCALHOST-AUTO-SYNC.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,147 @@
|
||||
# START-HERE — Archicratie / Édition Web (v2)
|
||||
> Onboarding + exploitation “nickel chrome” (DEV → Gitea → CI → Release → Blue/Green → Edge/SSO)
|
||||
# START-HERE — Archicratie / Édition Web (v3)
|
||||
> Onboarding + exploitation “nickel chrome” (DEV → Gitea → CI → Release → Blue/Green → Edge/SSO → localhost auto-sync)
|
||||
|
||||
## 0) TL;DR (la règle d’or)
|
||||
- **Gitea = source canonique**.
|
||||
- **main est protégé** : toute modification passe par **branche → PR → CI → merge**.
|
||||
- **Le NAS n’est pas la source** : si un hotfix est fait sur NAS, on **backporte** via PR immédiatement.
|
||||
- **Le site est statique Astro** : la prod sert du HTML (nginx), l’accès est contrôlé au niveau reverse-proxy (Traefik + Authelia).
|
||||
|
||||
- **Gitea = source canonique**.
|
||||
- **`main` est protégée** : toute modification passe par **branche → PR → CI → merge**.
|
||||
- **Le NAS n’est pas la source** : si un hotfix est fait sur NAS, il doit être **backporté immédiatement** via PR.
|
||||
- **Le site est statique Astro** : la prod sert du HTML via nginx ; l’accès est contrôlé au niveau reverse-proxy (Traefik + Authelia).
|
||||
- **Le localhost automatique n’est pas le repo de dev** : il tourne depuis un **worktree dédié**, synchronisé sur `origin/main`.
|
||||
|
||||
---
|
||||
|
||||
## 1) Architecture mentale (ultra simple)
|
||||
- **DEV (Mac Studio)** : édition + tests + commit + push
|
||||
- **Gitea** : dépôt canon + PR + CI (CI.yaml)
|
||||
- **NAS (DS220+)** : déploiement “blue/green”
|
||||
- `web_blue` (staging upstream) → `127.0.0.1:8081`
|
||||
- `web_green` (live upstream) → `127.0.0.1:8082`
|
||||
- **Edge (Traefik)** : route les hosts
|
||||
|
||||
- **DEV canonique (Mac Studio)** : édition, dev, tests, commits, pushes
|
||||
- **Gitea** : dépôt canonique, PR, CI, workflows éditoriaux
|
||||
- **NAS (DS220+)** : déploiement blue/green
|
||||
- `web_blue` → staging upstream → `127.0.0.1:8081`
|
||||
- `web_green` → live upstream → `127.0.0.1:8082`
|
||||
- **Edge (Traefik)** : routage des hosts
|
||||
- `staging.archicratie...` → 8081
|
||||
- `archicratie...` → 8082
|
||||
- **Authelia** devant, via middleware `chain-auth@file`
|
||||
- **Localhost auto-sync**
|
||||
- un **repo canonique de développement**
|
||||
- un **worktree localhost miroir de `origin/main`**
|
||||
- un **agent de sync**
|
||||
- un **agent Astro**
|
||||
|
||||
---
|
||||
|
||||
## 2) Répertoires & conventions (repo)
|
||||
|
||||
### 2.1 Contenu canon (édition)
|
||||
- `src/content/**` : contenu MD / MDX canon (Astro content collections)
|
||||
- `src/pages/**` : routes Astro (index, [...slug], etc.)
|
||||
- `src/components/**` : composants UI (SiteNav, TOC, SidePanel, etc.)
|
||||
- `src/layouts/**` : layouts (EditionLayout, SiteLayout)
|
||||
|
||||
- `src/content/**` : contenu MD / MDX canon
|
||||
- `src/pages/**` : routes Astro
|
||||
- `src/components/**` : composants UI
|
||||
- `src/layouts/**` : layouts
|
||||
- `src/styles/**` : CSS global
|
||||
|
||||
### 2.2 Annotations (pré-Édition “tickets”)
|
||||
|
||||
- `src/annotations/<workKey>/<slug>.yml`
|
||||
- Exemple : `src/annotations/archicrat-ia/prologue.yml`
|
||||
- Objectif : stocker “Références / Médias / Commentaires” par page et par paragraphe (`p-...`).
|
||||
- Exemple :
|
||||
`src/annotations/archicrat-ia/prologue.yml`
|
||||
|
||||
Objectif :
|
||||
stocker “Références / Médias / Commentaires” par page et par paragraphe (`p-...`).
|
||||
|
||||
### 2.3 Scripts (tooling / build)
|
||||
- `scripts/inject-anchor-aliases.mjs` : injection aliases dans dist
|
||||
- `scripts/dedupe-ids-dist.mjs` : retire IDs dupliqués dans dist
|
||||
- `scripts/build-para-index.mjs` : index paragraphes (postbuild / predev)
|
||||
- `scripts/build-annotations-index.mjs` : index annotations (postbuild / predev)
|
||||
- `scripts/check-anchors.mjs` : contrat stabilité d’ancres (CI)
|
||||
|
||||
- `scripts/inject-anchor-aliases.mjs` : injection aliases dans `dist`
|
||||
- `scripts/dedupe-ids-dist.mjs` : retrait IDs dupliqués
|
||||
- `scripts/build-para-index.mjs` : index paragraphes
|
||||
- `scripts/build-annotations-index.mjs` : index annotations
|
||||
- `scripts/check-anchors.mjs` : contrat stabilité d’ancres
|
||||
- `scripts/check-annotations*.mjs` : sanity YAML + médias
|
||||
|
||||
> Important : les scripts sont **partie intégrante** de la stabilité (IDs/ancres/indexation).
|
||||
> On évite “la magie” : tout est scripté + vérifié.
|
||||
> Important : ces scripts ne sont pas accessoires.
|
||||
> Ils font partie du contrat de stabilité éditoriale.
|
||||
|
||||
## 3) Workflow Git “pro” (main protégé)
|
||||
### 3.1 Cycle standard (toute modif)
|
||||
en bash :
|
||||
---
|
||||
|
||||
## 3) Les trois espaces à ne jamais confondre
|
||||
|
||||
### 3.1 Repo canonique de développement
|
||||
|
||||
```text
|
||||
/Volumes/FunIA/dev/archicratie-edition/site
|
||||
```
|
||||
|
||||
Usage :
|
||||
|
||||
- développement normal
|
||||
- branches de travail
|
||||
- nouvelles fonctionnalités
|
||||
- corrections manuelles
|
||||
- commits
|
||||
- pushes
|
||||
- PR
|
||||
|
||||
### 3.2 Worktree localhost miroir de `main`
|
||||
|
||||
```text
|
||||
/Users/s-funia/ops-local/archicratie/localhost-worktree
|
||||
```
|
||||
|
||||
Branche attendue :
|
||||
|
||||
```text
|
||||
localhost-sync
|
||||
```
|
||||
|
||||
Usage :
|
||||
|
||||
- exécuter le localhost automatique
|
||||
- refléter `origin/main`
|
||||
- ne jamais servir d’espace de développement
|
||||
|
||||
### 3.3 Ops local hors repo
|
||||
|
||||
```text
|
||||
/Users/s-funia/ops-local/archicratie
|
||||
```
|
||||
|
||||
Usage :
|
||||
|
||||
- scripts d’exploitation
|
||||
- état
|
||||
- logs
|
||||
- automatisation `launchd`
|
||||
|
||||
---
|
||||
|
||||
## 4) Pourquoi cette séparation existe
|
||||
|
||||
Il ne faut pas utiliser le repo canonique de développement comme serveur localhost permanent.
|
||||
|
||||
Sinon on mélange :
|
||||
|
||||
- travail en cours
|
||||
- commits non poussés
|
||||
- essais temporaires
|
||||
- état réellement publié sur `main`
|
||||
|
||||
Le résultat devient ambigu.
|
||||
|
||||
La séparation retenue est donc :
|
||||
|
||||
- **repo canonique** = espace de développement
|
||||
- **worktree localhost** = miroir exécutable de `origin/main`
|
||||
- **ops local** = scripts et automatisation
|
||||
|
||||
C’est cette séparation qui rend le système lisible, robuste et opérable.
|
||||
|
||||
---
|
||||
|
||||
## 5) Workflow Git “pro” (main protégée)
|
||||
|
||||
### 5.1 Cycle standard (toute modif)
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull --ff-only
|
||||
|
||||
@@ -60,37 +156,48 @@ npm run test:anchors
|
||||
git add -A
|
||||
git commit -m "xxx: description claire"
|
||||
git push -u origin "$BR"
|
||||
```
|
||||
|
||||
### 3.2 PR vers main
|
||||
### 5.2 PR vers `main`
|
||||
|
||||
Ouvrir PR dans Gitea
|
||||
- ouvrir une PR dans Gitea
|
||||
- attendre une CI verte
|
||||
- merger
|
||||
- laisser les workflows faire le reste
|
||||
|
||||
CI doit être verte
|
||||
### 5.3 Cas spécial : hotfix prod (NAS)
|
||||
|
||||
Merge PR → main
|
||||
On peut faire un hotfix d’urgence côté NAS si nécessaire.
|
||||
|
||||
### 3.3 Cas spécial : hotfix prod (NAS)
|
||||
Mais l’état final doit toujours revenir dans Gitea :
|
||||
|
||||
On peut faire un hotfix “urgence” en prod/staging si nécessaire…
|
||||
- branche
|
||||
- PR
|
||||
- CI
|
||||
- merge
|
||||
|
||||
MAIS : l’état final doit revenir dans Gitea : branche → PR → CI → merge.
|
||||
---
|
||||
|
||||
## 4) Déploiement (NAS) — principe
|
||||
### 4.1 Release pack
|
||||
## 6) Déploiement (NAS) — principe
|
||||
|
||||
On génère un pack “reproductible” (source + config + scripts) puis on déploie.
|
||||
### 6.1 Release pack
|
||||
|
||||
### 4.2 Blue/Green
|
||||
On génère un pack reproductible, puis on déploie.
|
||||
|
||||
web_blue = staging upstream (8081)
|
||||
### 6.2 Blue/Green
|
||||
|
||||
web_green = live upstream (8082)
|
||||
- `web_blue` = staging (`8081`)
|
||||
- `web_green` = live (`8082`)
|
||||
|
||||
Edge Traefik sélectionne quel host pointe vers quel upstream.
|
||||
Le reverse-proxy choisit l’upstream selon le host demandé.
|
||||
|
||||
## 5) Check-list “≤ 10 commandes” (happy path complet)
|
||||
### 5.1 DEV (Mac)
|
||||
---
|
||||
|
||||
## 7) Happy path complet
|
||||
|
||||
### 7.1 DEV (Mac)
|
||||
|
||||
```bash
|
||||
git checkout main && git pull --ff-only
|
||||
git checkout -b chore/my-change-$(date +%Y%m%d)
|
||||
|
||||
@@ -99,55 +206,258 @@ rm -rf .astro node_modules/.vite dist
|
||||
npm run build
|
||||
npm run test:anchors
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### 5.2 Push + PR
|
||||
### 7.2 Push + PR
|
||||
|
||||
```bash
|
||||
git add -A
|
||||
git commit -m "chore: my change"
|
||||
git push -u origin chore/my-change-YYYYMMDD
|
||||
# ouvrir PR dans Gitea
|
||||
```
|
||||
|
||||
### 5.3 Déploiement NAS (résumé)
|
||||
Puis ouvrir la PR dans Gitea.
|
||||
|
||||
Voir docs/runbooks/DEPLOY-BLUE-GREEN.md.
|
||||
### 7.3 Déploiement NAS
|
||||
|
||||
## 6) Problèmes “classiques” + diagnostic rapide
|
||||
### 6.1 “Le staging ne ressemble pas au local”
|
||||
Voir :
|
||||
|
||||
# Comparer upstream direct 8081 vs 8082 :
|
||||
```text
|
||||
docs/runbooks/DEPLOY-BLUE-GREEN.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8) Localhost auto-sync — ce qu’il faut retenir
|
||||
|
||||
Le localhost automatique sert à voir **la vérité de `main`**, pas à développer du neuf.
|
||||
|
||||
### 8.1 Scripts principaux
|
||||
|
||||
#### Script de sync
|
||||
|
||||
```text
|
||||
~/ops-local/archicratie/auto-sync-localhost.sh
|
||||
```
|
||||
|
||||
Rôle :
|
||||
|
||||
- fetch `origin/main`
|
||||
- réaligner le worktree localhost
|
||||
- lancer `npm ci` si besoin
|
||||
- redéclencher l’agent Astro si nécessaire
|
||||
|
||||
#### Script Astro
|
||||
|
||||
```text
|
||||
~/ops-local/archicratie/run-astro-localhost.sh
|
||||
```
|
||||
|
||||
Rôle :
|
||||
|
||||
- lancer `astro dev`
|
||||
- depuis le bon worktree
|
||||
- avec le bon runtime Node
|
||||
- sur `127.0.0.1:4321`
|
||||
|
||||
> Oui : ce script est nécessaire.
|
||||
> Il isole proprement le lancement du serveur Astro dans un contexte `launchd` stable.
|
||||
|
||||
### 8.2 LaunchAgents
|
||||
|
||||
#### Agent sync
|
||||
|
||||
```text
|
||||
~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
|
||||
```
|
||||
|
||||
#### Agent Astro
|
||||
|
||||
```text
|
||||
~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
|
||||
```
|
||||
|
||||
### 8.3 Document de référence
|
||||
|
||||
Pour tout le détail d’exploitation du localhost automatique, lire :
|
||||
|
||||
```text
|
||||
docs/OPS-LOCALHOST-AUTO-SYNC.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9) Règle d’or : il y a deux usages locaux distincts
|
||||
|
||||
### 9.1 Voir ce qui est réellement sur `main`
|
||||
|
||||
Utiliser :
|
||||
|
||||
```text
|
||||
http://127.0.0.1:4321
|
||||
```
|
||||
|
||||
Ce localhost doit être considéré comme :
|
||||
|
||||
**un miroir local exécutable de `origin/main`**
|
||||
|
||||
### 9.2 Développer / tester une nouvelle fonctionnalité
|
||||
|
||||
Utiliser le repo canonique :
|
||||
|
||||
```bash
|
||||
cd /Volumes/FunIA/dev/archicratie-edition/site
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Donc :
|
||||
|
||||
- **localhost auto-sync** = vérité de `main`
|
||||
- **localhost de dev manuel** = expérimentation en cours
|
||||
|
||||
Il ne faut pas les confondre.
|
||||
|
||||
---
|
||||
|
||||
## 10) Ce qu’il ne faut pas faire
|
||||
|
||||
### 10.1 Ne pas développer dans le worktree localhost
|
||||
|
||||
Le worktree localhost est piloté automatiquement.
|
||||
|
||||
Il peut être :
|
||||
|
||||
- réaligné
|
||||
- nettoyé
|
||||
- redémarré
|
||||
|
||||
Donc :
|
||||
|
||||
- pas de commits dedans
|
||||
- pas de dev feature dedans
|
||||
- pas d’expérimentation de fond dedans
|
||||
|
||||
### 10.2 Ne pas utiliser le repo canonique comme miroir auto-sync
|
||||
|
||||
Sinon on mélange :
|
||||
|
||||
- espace de dev
|
||||
- état publié
|
||||
- serveur local permanent
|
||||
|
||||
### 10.3 Ne pas remettre les scripts ops sur un volume externe
|
||||
|
||||
Les scripts d’ops doivent rester sous `HOME`.
|
||||
|
||||
Le fait de les mettre sous `/Volumes/...` a déjà provoqué des erreurs du type :
|
||||
|
||||
```text
|
||||
Operation not permitted
|
||||
```
|
||||
|
||||
### 10.4 Ne pas supprimer `run-astro-localhost.sh`
|
||||
|
||||
Ce script fait partie de l’architecture actuelle.
|
||||
Le supprimer reviendrait à réintroduire le flou entre sync Git et exécution d’Astro.
|
||||
|
||||
---
|
||||
|
||||
## 11) Commandes de contrôle essentielles
|
||||
|
||||
### 11.1 État global
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
```
|
||||
|
||||
### 11.2 État Git
|
||||
|
||||
```bash
|
||||
git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
|
||||
git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
|
||||
git -C ~/ops-local/archicratie/localhost-worktree branch --show-current
|
||||
```
|
||||
|
||||
### 11.3 État LaunchAgents
|
||||
|
||||
```bash
|
||||
launchctl print "gui/$(id -u)/me.archicratie.localhost-sync" | sed -n '1,160p'
|
||||
launchctl print "gui/$(id -u)/me.archicratie.localhost-astro" | sed -n '1,160p'
|
||||
```
|
||||
|
||||
### 11.4 État logs
|
||||
|
||||
```bash
|
||||
tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
|
||||
tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
|
||||
tail -n 80 ~/Library/Logs/archicratie-localhost-sync.err.log
|
||||
tail -n 80 ~/Library/Logs/archicratie-localhost-astro.err.log
|
||||
```
|
||||
|
||||
### 11.5 État serveur
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:4321 -sTCP:LISTEN
|
||||
PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
|
||||
ps -p "$PID" -o pid=,command=
|
||||
lsof -a -p "$PID" -d cwd
|
||||
```
|
||||
|
||||
### 11.6 Vérification contenu
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:4321/archicrat-ia/prologue/ | grep -n "taxe Zucman"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12) Problèmes classiques + diagnostic
|
||||
|
||||
### 12.1 “Le staging ne ressemble pas au local”
|
||||
|
||||
Comparer les upstream directs :
|
||||
|
||||
```bash
|
||||
curl -sS http://127.0.0.1:8081/ | head -n 2
|
||||
curl -sS http://127.0.0.1:8082/ | head -n 2
|
||||
```
|
||||
|
||||
# Vérifier quel routeur edge répond (header diag) :
|
||||
Vérifier le routeur edge :
|
||||
|
||||
```bash
|
||||
curl -sSI -H 'Host: staging.archicratie.trans-hands.synology.me' http://127.0.0.1:18080/ \
|
||||
| grep -iE 'HTTP/|location:|x-archi-router'
|
||||
```
|
||||
|
||||
# Lire docs/runbooks/EDGE-TRAEFIK.md.
|
||||
Voir :
|
||||
|
||||
### 6.2 Canonical incorrect (localhost en prod)
|
||||
```text
|
||||
docs/runbooks/EDGE-TRAEFIK.md
|
||||
```
|
||||
|
||||
Cause racine : site dans Astro = PUBLIC_SITE non injecté au build.
|
||||
### 12.2 Canonical incorrect
|
||||
|
||||
Fix canonique : voir docs/runbooks/ENV-PUBLIC_SITE.md.
|
||||
Cause probable : `PUBLIC_SITE` mal injecté au build.
|
||||
|
||||
Test :
|
||||
|
||||
```bash
|
||||
curl -sS http://127.0.0.1:8082/ | grep -oE 'rel="canonical" href="[^"]+"' | head -1
|
||||
```
|
||||
|
||||
### 6.3 Contrat “anchors” en échec après migration d’URL
|
||||
Voir :
|
||||
|
||||
Quand on déplace des routes (ex: /archicratie/archicrat-ia/* → /archicrat-ia/*), le test d’ancres peut échouer même si les IDs n’ont pas changé, car les pages ont changé de chemin.
|
||||
```text
|
||||
docs/runbooks/ENV-PUBLIC_SITE.md
|
||||
```
|
||||
|
||||
# Procédure safe :
|
||||
### 12.3 Contrat anchors en échec après migration d’URL
|
||||
|
||||
Backup baseline :
|
||||
Procédure safe :
|
||||
|
||||
```bash
|
||||
cp -a tests/anchors-baseline.json /tmp/anchors-baseline.json.bak.$(date +%F-%H%M%S)
|
||||
|
||||
Mettre à jour les clés (chemins) sans toucher aux IDs :
|
||||
|
||||
node - <<'NODE'
|
||||
import fs from 'fs';
|
||||
const p='tests/anchors-baseline.json';
|
||||
@@ -161,16 +471,213 @@ fs.writeFileSync(p, JSON.stringify(out,null,2)+'\n');
|
||||
console.log('updated keys:', Object.keys(j).length, '->', Object.keys(out).length);
|
||||
NODE
|
||||
|
||||
Re-run :
|
||||
|
||||
npm run test:anchors
|
||||
```
|
||||
|
||||
## 7) Ce que l’étape 9 doit faire (orientation)
|
||||
### 12.4 “Le localhost auto-sync ne montre pas les dernières modifs”
|
||||
|
||||
Stabiliser le pipeline “tickets → YAML annotations”
|
||||
Commande réflexe :
|
||||
|
||||
Formaliser la spec YAML + merge + anti-doublon (voir docs/EDITORIAL-ANNOTATIONS-SPEC.md)
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
```
|
||||
|
||||
Durcir l’onboarding (ce START-HERE + runbooks)
|
||||
Puis :
|
||||
|
||||
Éviter les régressions par tests (anchors / annotations / smoke)
|
||||
```bash
|
||||
git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
|
||||
git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
|
||||
```
|
||||
|
||||
Si les SHA diffèrent :
|
||||
- le sync n’a pas tourné
|
||||
- ou l’agent sync a un problème
|
||||
|
||||
### 12.5 “Le SHA est bon mais le contenu web est faux”
|
||||
|
||||
Vérifier quel Astro écoute réellement :
|
||||
|
||||
```bash
|
||||
lsof -nP -iTCP:4321 -sTCP:LISTEN
|
||||
PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
|
||||
ps -p "$PID" -o pid=,command=
|
||||
lsof -a -p "$PID" -d cwd
|
||||
```
|
||||
|
||||
Attendu :
|
||||
- commande contenant `astro dev`
|
||||
- cwd = `~/ops-local/archicratie/localhost-worktree`
|
||||
|
||||
### 12.6 Erreur `EBADENGINE`
|
||||
|
||||
Cause probable :
|
||||
- Node 23 utilisé au lieu de Node 22
|
||||
|
||||
Résolution :
|
||||
- forcer `node@22` dans les scripts et les LaunchAgents
|
||||
|
||||
### 12.7 Erreur `Operation not permitted`
|
||||
|
||||
Cause probable :
|
||||
- scripts d’ops placés sous `/Volumes/...`
|
||||
|
||||
Résolution :
|
||||
- garder les scripts sous :
|
||||
|
||||
```text
|
||||
~/ops-local/archicratie
|
||||
```
|
||||
|
||||
### 12.8 Erreur `EPERM` sur `astro.mjs`
|
||||
|
||||
Cause probable :
|
||||
- ancien worktree sur volume externe
|
||||
- ancien chemin résiduel
|
||||
- Astro lancé depuis un mauvais emplacement
|
||||
|
||||
Résolution :
|
||||
- worktree localhost sous :
|
||||
|
||||
```text
|
||||
~/ops-local/archicratie/localhost-worktree
|
||||
```
|
||||
|
||||
- scripts cohérents avec ce chemin
|
||||
- réinstallation propre via :
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/install-localhost-sync.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13) Redémarrage machine
|
||||
|
||||
Après reboot, le comportement attendu est :
|
||||
|
||||
1. le LaunchAgent sync se recharge
|
||||
2. le LaunchAgent Astro se recharge
|
||||
3. le worktree localhost est réaligné
|
||||
4. Astro redémarre sur `127.0.0.1:4321`
|
||||
|
||||
### Vérification rapide après reboot
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
```
|
||||
|
||||
Si nécessaire :
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/install-localhost-sync.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14) Procédure de secours manuelle
|
||||
|
||||
### Forcer un sync
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/auto-sync-localhost.sh
|
||||
```
|
||||
|
||||
### Réinstaller proprement le dispositif local
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/install-localhost-sync.sh
|
||||
```
|
||||
|
||||
### Diagnostic complet
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15) Décision d’exploitation finale
|
||||
|
||||
La politique retenue est la suivante :
|
||||
|
||||
- **repo canonique** = espace de développement
|
||||
- **worktree localhost** = miroir automatique de `main`
|
||||
- **ops sous HOME** = scripts, logs, automation
|
||||
- **LaunchAgent sync** = réalignement Git
|
||||
- **LaunchAgent Astro** = exécution stable du serveur local
|
||||
- **Astro local** = lancé uniquement depuis le worktree localhost
|
||||
|
||||
Cette séparation rend le dispositif plus :
|
||||
|
||||
- lisible
|
||||
- robuste
|
||||
- opérable
|
||||
- antifragile
|
||||
|
||||
---
|
||||
|
||||
## 16) Résumé opératoire
|
||||
|
||||
### Pour voir la vérité de `main`
|
||||
|
||||
Ouvrir :
|
||||
|
||||
```text
|
||||
http://127.0.0.1:4321
|
||||
```
|
||||
|
||||
Le serveur doit provenir de :
|
||||
|
||||
```text
|
||||
/Users/s-funia/ops-local/archicratie/localhost-worktree
|
||||
```
|
||||
|
||||
### Pour développer
|
||||
|
||||
Travailler dans :
|
||||
|
||||
```text
|
||||
/Volumes/FunIA/dev/archicratie-edition/site
|
||||
```
|
||||
|
||||
avec les commandes habituelles.
|
||||
|
||||
### Pour réparer vite
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
~/ops-local/archicratie/auto-sync-localhost.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 17) Mémoire courte
|
||||
|
||||
Si un jour plus rien n’est clair, repartir de ces commandes :
|
||||
|
||||
```bash
|
||||
~/ops-local/archicratie/doctor-localhost.sh
|
||||
git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
|
||||
git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
|
||||
lsof -nP -iTCP:4321 -sTCP:LISTEN
|
||||
```
|
||||
|
||||
Puis lire :
|
||||
|
||||
```bash
|
||||
tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
|
||||
tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18) Statut actuel visé
|
||||
|
||||
Quand tout fonctionne correctement :
|
||||
|
||||
- le worktree localhost pointe sur le même SHA que `origin/main`
|
||||
- `astro dev` écoute sur `127.0.0.1:4321`
|
||||
- son cwd est `~/ops-local/archicratie/localhost-worktree`
|
||||
- le contenu servi correspond au contenu mergé sur `main`
|
||||
|
||||
C’est l’état de référence à préserver.
|
||||
100
docs/graph.dot
Normal file
100
docs/graph.dot
Normal file
@@ -0,0 +1,100 @@
|
||||
digraph G {
|
||||
rankdir="LR";
|
||||
node [shape=box, style="rounded"];
|
||||
"agencement-machinique" -> "cratialite";
|
||||
"arcalite" -> "cratialite";
|
||||
"archeogenese" -> "meta-regime-archicratique";
|
||||
"archicratie" -> "arcalite";
|
||||
"archicration-obliteree" -> "hypercratialite";
|
||||
"archicration" -> "scene-depreuve";
|
||||
"archicrations-differentielles-et-formes-hybrides" -> "co-viabilisation";
|
||||
"archicrations-epistemiques" -> "archicrations-differentielles-et-formes-hybrides";
|
||||
"archicrations-esthetico-symboliques" -> "archicrations-historiographiques";
|
||||
"archicrations-guerrieres" -> "archicrations-normativo-politiques";
|
||||
"archicrations-historiographiques" -> "archicrations-epistemiques";
|
||||
"archicrations-marchandes" -> "archicrations-techno-logistiques";
|
||||
"archicrations-normativo-politiques" -> "archicrations-marchandes";
|
||||
"archicrations-proto-symboliques" -> "archicrations-sacrales-non-etatiques";
|
||||
"archicrations-sacrales-non-etatiques" -> "archicrations-scripturo-cosmologiques";
|
||||
"archicrations-scripturo-cosmologiques" -> "archicrations-scripturo-normatives";
|
||||
"archicrations-scripturo-normatives" -> "archicrations-normativo-politiques";
|
||||
"archicrations-techno-logistiques" -> "archicrations-differentielles-et-formes-hybrides";
|
||||
"archicrations-theologiques" -> "archicrations-normativo-politiques";
|
||||
"archicratisation" -> "co-viabilisation";
|
||||
"archidiagnostic" -> "cartographie-des-scenes-manquantes";
|
||||
"audit-archicratique" -> "cartographie-des-scenes-manquantes";
|
||||
"autarchicratie" -> "autarchicration";
|
||||
"autarchicration" -> "obliteration-archicratique";
|
||||
"biopolitique" -> "gouvernementalite-algorithmique";
|
||||
"budget-scenique" -> "visa-daffectation";
|
||||
"cartographie-des-scenes-manquantes" -> "scene-manquante";
|
||||
"co-viabilisation" -> "regime-de-co-viabilite";
|
||||
"co-viabilite" -> "tension";
|
||||
"coexistence-ontologique-et-necessite-regulatrice" -> "formes-de-vie-et-cadres-dhabitabilite";
|
||||
"conatus-et-multitude" -> "configuration-et-interdependance";
|
||||
"configuration-et-interdependance" -> "transduction-et-individuation";
|
||||
"contractualisme-hobbesien" -> "droit-naturel-et-propriete";
|
||||
"cosmopolitique" -> "technodiversite-et-cosmotechnie";
|
||||
"coupe-circuit-citoyen" -> "droit-au-differe-contradictoire";
|
||||
"cratialite" -> "archicration";
|
||||
"cybernetique" -> "gouvernementalite-algorithmique";
|
||||
"decisionnisme-souverain" -> "exception-souveraine";
|
||||
"democratie-deliberative" -> "dissensus-politique";
|
||||
"desarchicration" -> "desarchicratisation";
|
||||
"desarchicratisation" -> "autarchicratie";
|
||||
"dissensus-politique" -> "lieu-vide-du-pouvoir";
|
||||
"domination-legale-rationnelle" -> "democratie-deliberative";
|
||||
"droit-au-differe-contradictoire" -> "tribunal-de-lalgorithme";
|
||||
"droit-naturel-et-propriete" -> "volonte-generale";
|
||||
"egalisation-normative-et-differenciation-singuliere" -> "dissensus-politique";
|
||||
"exception-souveraine" -> "droit-naturel-et-propriete";
|
||||
"fait-social-total" -> "configuration-et-interdependance";
|
||||
"formes-de-vie-et-cadres-dhabitabilite" -> "subsistance-vivante-et-captation-capitalistique";
|
||||
"gouvernance-des-communs" -> "co-viabilite";
|
||||
"gouvernementalite-algorithmique" -> "preemption-algorithmique";
|
||||
"gouvernementalite" -> "biopolitique";
|
||||
"grammatisation-et-proletarisation-cognitive" -> "pharmacologie-technique";
|
||||
"habitus-et-violence-symbolique" -> "obliteration-archicratique";
|
||||
"hyperarcalite" -> "desarchicration";
|
||||
"hypercratialite" -> "hyperarcalite";
|
||||
"inertie-sociale-symbolique" -> "habitus-et-violence-symbolique";
|
||||
"institution-invisible" -> "scene-depreuve";
|
||||
"journal-de-justification" -> "droit-au-differe-contradictoire";
|
||||
"liberte-daction-et-regimes-de-securite-algorithmique" -> "preemption-algorithmique";
|
||||
"lieu-vide-du-pouvoir" -> "visibilite-mediatique-et-reconnaissance-symbolique";
|
||||
"memoire-symbolique-et-instantaneite-computationnelle" -> "meta-regime";
|
||||
"meta-regime-archicratique" -> "archicrations-differentielles-et-formes-hybrides";
|
||||
"meta-regime" -> "meta-regime-archicratique";
|
||||
"monde-instituable" -> "scene-manquante";
|
||||
"obliteration-archicratique" -> "archicration-obliteree";
|
||||
"pensee-complexe" -> "configuration-et-interdependance";
|
||||
"pharmacologie-technique" -> "technodiversite-et-cosmotechnie";
|
||||
"pluralite-natalite-action" -> "dissensus-politique";
|
||||
"preemption-algorithmique" -> "droit-au-differe-contradictoire";
|
||||
"regime-de-co-viabilite" -> "gouvernance-des-communs";
|
||||
"regulation-morphogenetique-des-interdependances" -> "transduction-et-individuation";
|
||||
"regulation-technique-et-legitimation-democratique" -> "tribunal-de-lalgorithme";
|
||||
"regulations-fondatrices" -> "regulations-incorporees";
|
||||
"regulations-incorporees" -> "regulations-procedurales";
|
||||
"regulations-procedurales" -> "regulations-techniques";
|
||||
"regulations-relationnelles" -> "regime-de-co-viabilite";
|
||||
"regulations-techniques" -> "regulations-relationnelles";
|
||||
"resonance-sociale" -> "visibilite-mediatique-et-reconnaissance-symbolique";
|
||||
"scene-darchicration" -> "co-viabilite";
|
||||
"scene-depreuve" -> "scene-darchicration";
|
||||
"scene-empechee" -> "institution-invisible";
|
||||
"scene-manquante" -> "scene-empechee";
|
||||
"souverainetes-territoriales-et-interdependances-globales" -> "gouvernance-des-communs";
|
||||
"subsistance-vivante-et-captation-capitalistique" -> "travail-vivant-et-abstraction-de-la-valeur";
|
||||
"technodiversite-et-cosmotechnie" -> "regime-de-co-viabilite";
|
||||
"tension" -> "regime-de-co-viabilite";
|
||||
"theorie-de-la-justification" -> "journal-de-justification";
|
||||
"theorie-de-la-resonance" -> "resonance-sociale";
|
||||
"theorie-de-lacteur-reseau" -> "agencement-machinique";
|
||||
"transduction-et-individuation" -> "archeogenese";
|
||||
"travail-vivant-et-abstraction-de-la-valeur" -> "grammatisation-et-proletarisation-cognitive";
|
||||
"tribunal-de-lalgorithme" -> "budget-scenique";
|
||||
"visa-daffectation" -> "scene-depreuve";
|
||||
"visibilite-mediatique-et-reconnaissance-symbolique" -> "cartographie-des-scenes-manquantes";
|
||||
"volonte-generale" -> "democratie-deliberative";
|
||||
}
|
||||
BIN
docs/graph.png
Normal file
BIN
docs/graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 359 KiB |
1565
package-lock.json
generated
1565
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@@ -10,10 +10,12 @@
|
||||
"clean": "rm -rf dist",
|
||||
"build": "astro build",
|
||||
"build:clean": "npm run clean && npm run build",
|
||||
"postbuild": "node scripts/inject-anchor-aliases.mjs && node scripts/dedupe-ids-dist.mjs && node scripts/build-para-index.mjs && node scripts/build-annotations-index.mjs && node scripts/purge-dist-dev-whoami.mjs && npx pagefind --site dist",
|
||||
"build:search": "pagefind --site dist",
|
||||
"postbuild": "node scripts/inject-anchor-aliases.mjs && node scripts/dedupe-ids-dist.mjs && node scripts/build-para-index.mjs && node scripts/build-annotations-index.mjs && node scripts/purge-dist-dev-whoami.mjs && npm run build:search",
|
||||
"import": "node scripts/import-docx.mjs",
|
||||
"apply:ticket": "node scripts/apply-ticket.mjs",
|
||||
"audit:dist": "node scripts/audit-dist.mjs",
|
||||
"audit:glossary": "node scripts/audit-glossary-navigation.mjs",
|
||||
"build:para-index": "node scripts/build-para-index.mjs",
|
||||
"build:annotations-index": "node scripts/build-annotations-index.mjs",
|
||||
"test:aliases": "node scripts/check-anchor-aliases.mjs",
|
||||
@@ -25,11 +27,11 @@
|
||||
"ci": "CI=1 npm test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^4.3.13",
|
||||
"astro": "^5.18.0"
|
||||
"@astrojs/mdx": "^5.0.0",
|
||||
"astro": "^6.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/sitemap": "^3.7.0",
|
||||
"@astrojs/sitemap": "^3.7.1",
|
||||
"mammoth": "^1.11.0",
|
||||
"pagefind": "^1.4.0",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
|
||||
@@ -9,8 +9,9 @@ import { spawnSync } from "node:child_process";
|
||||
*
|
||||
* Conçu pour:
|
||||
* - prendre un ticket [Correction]/[Fact-check] (issue) avec Chemin + Ancre + Proposition
|
||||
* - retrouver le bon paragraphe dans le .mdx
|
||||
* - retrouver le bon paragraphe dans le .mdx/.md
|
||||
* - remplacer proprement
|
||||
* - ne JAMAIS toucher au frontmatter
|
||||
* - optionnel: écrire un alias d’ancre old->new (build-time) dans src/anchors/anchor-aliases.json
|
||||
* - optionnel: committer automatiquement
|
||||
* - optionnel: fermer le ticket (après commit)
|
||||
@@ -39,7 +40,7 @@ Env (recommandé):
|
||||
|
||||
Notes:
|
||||
- Si dist/<chemin>/index.html est absent, le script lance "npm run build" sauf si --no-build.
|
||||
- Sauvegarde automatique: <fichier>.bak.issue-<N> (uniquement si on écrit)
|
||||
- Sauvegarde automatique: .tmp/apply-ticket/<fichier>.bak.issue-<N> (uniquement si on écrit)
|
||||
- Avec --alias : le script rebuild pour identifier le NOUVEL id, puis écrit l'alias old->new.
|
||||
- Refuse automatiquement les Pull Requests (PR) : ce ne sont pas des tickets éditoriaux.
|
||||
`);
|
||||
@@ -89,6 +90,7 @@ const CWD = process.cwd();
|
||||
const CONTENT_ROOT = path.join(CWD, "src", "content");
|
||||
const DIST_ROOT = path.join(CWD, "dist");
|
||||
const ALIASES_FILE = path.join(CWD, "src", "anchors", "anchor-aliases.json");
|
||||
const BACKUP_ROOT = path.join(CWD, ".tmp", "apply-ticket");
|
||||
|
||||
/* -------------------------- utils texte / matching -------------------------- */
|
||||
|
||||
@@ -136,31 +138,26 @@ function scoreText(candidate, targetText) {
|
||||
let hit = 0;
|
||||
for (const w of tgtSet) if (blkSet.has(w)) hit++;
|
||||
|
||||
// Bonus si un long préfixe ressemble
|
||||
const tgtNorm = normalizeText(stripMd(targetText));
|
||||
const blkNorm = normalizeText(stripMd(candidate));
|
||||
const prefix = tgtNorm.slice(0, Math.min(180, tgtNorm.length));
|
||||
const prefixBonus = prefix && blkNorm.includes(prefix) ? 1000 : 0;
|
||||
|
||||
// Ratio bonus (0..100)
|
||||
const ratio = hit / Math.max(1, tgtSet.size);
|
||||
const ratioBonus = Math.round(ratio * 100);
|
||||
|
||||
return prefixBonus + hit + ratioBonus;
|
||||
}
|
||||
|
||||
function bestBlockMatchIndex(blocks, targetText) {
|
||||
let best = { i: -1, score: -1 };
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
const sc = scoreText(blocks[i], targetText);
|
||||
if (sc > best.score) best = { i, score: sc };
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
function splitParagraphBlocks(mdxText) {
|
||||
const raw = String(mdxText ?? "").replace(/\r\n/g, "\n");
|
||||
return raw.split(/\n{2,}/);
|
||||
function rankedBlockMatches(blocks, targetText, limit = 5) {
|
||||
return blocks
|
||||
.map((b, i) => ({
|
||||
i,
|
||||
score: scoreText(b, targetText),
|
||||
excerpt: stripMd(b).slice(0, 140),
|
||||
}))
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
function isLikelyExcerpt(s) {
|
||||
@@ -172,6 +169,89 @@ function isLikelyExcerpt(s) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* --------------------------- frontmatter / structure ------------------------ */
|
||||
|
||||
function normalizeNewlines(s) {
|
||||
return String(s ?? "").replace(/^\uFEFF/, "").replace(/\r\n/g, "\n");
|
||||
}
|
||||
|
||||
function splitMdxFrontmatter(src) {
|
||||
const text = normalizeNewlines(src);
|
||||
const m = text.match(/^---\n[\s\S]*?\n---\n?/);
|
||||
|
||||
if (!m) {
|
||||
return {
|
||||
hasFrontmatter: false,
|
||||
frontmatter: "",
|
||||
body: text,
|
||||
};
|
||||
}
|
||||
|
||||
const frontmatter = m[0];
|
||||
const body = text.slice(frontmatter.length);
|
||||
|
||||
return {
|
||||
hasFrontmatter: true,
|
||||
frontmatter,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
function joinMdxFrontmatter(frontmatter, body) {
|
||||
if (!frontmatter) return String(body ?? "");
|
||||
return String(frontmatter) + String(body ?? "");
|
||||
}
|
||||
|
||||
function assertFrontmatterIntegrity({ hadFrontmatter, originalFrontmatter, finalText, filePath }) {
|
||||
if (!hadFrontmatter) return;
|
||||
|
||||
const text = normalizeNewlines(finalText);
|
||||
|
||||
if (!text.startsWith("---\n")) {
|
||||
throw new Error(`Frontmatter perdu pendant la mise à jour de ${filePath}`);
|
||||
}
|
||||
|
||||
if (!text.startsWith(originalFrontmatter)) {
|
||||
throw new Error(`Frontmatter altéré pendant la mise à jour de ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function splitParagraphBlocksPreserve(bodyText) {
|
||||
const text = normalizeNewlines(bodyText);
|
||||
|
||||
if (!text) {
|
||||
return { blocks: [], separators: [] };
|
||||
}
|
||||
|
||||
const blocks = [];
|
||||
const separators = [];
|
||||
|
||||
const re = /(\n{2,})/g;
|
||||
let last = 0;
|
||||
let m;
|
||||
|
||||
while ((m = re.exec(text))) {
|
||||
blocks.push(text.slice(last, m.index));
|
||||
separators.push(m[1]);
|
||||
last = m.index + m[1].length;
|
||||
}
|
||||
|
||||
blocks.push(text.slice(last));
|
||||
|
||||
return { blocks, separators };
|
||||
}
|
||||
|
||||
function joinParagraphBlocksPreserve(blocks, separators) {
|
||||
if (!Array.isArray(blocks) || blocks.length === 0) return "";
|
||||
|
||||
let out = "";
|
||||
for (let i = 0; i < blocks.length; i++) {
|
||||
out += blocks[i];
|
||||
if (i < separators.length) out += separators[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* ------------------------------ utils système ------------------------------ */
|
||||
|
||||
function run(cmd, args, opts = {}) {
|
||||
@@ -251,7 +331,9 @@ function pickSection(body, markers) {
|
||||
.map((m) => ({ m, i: text.toLowerCase().indexOf(m.toLowerCase()) }))
|
||||
.filter((x) => x.i >= 0)
|
||||
.sort((a, b) => a.i - b.i)[0];
|
||||
|
||||
if (!idx) return "";
|
||||
|
||||
const start = idx.i + idx.m.length;
|
||||
const tail = text.slice(start);
|
||||
|
||||
@@ -266,11 +348,13 @@ function pickSection(body, markers) {
|
||||
"\n## Proposition",
|
||||
"\n## Problème",
|
||||
];
|
||||
|
||||
let end = tail.length;
|
||||
for (const s of stops) {
|
||||
const j = tail.toLowerCase().indexOf(s.toLowerCase());
|
||||
if (j >= 0 && j < end) end = j;
|
||||
}
|
||||
|
||||
return tail.slice(0, end).trim();
|
||||
}
|
||||
|
||||
@@ -298,8 +382,6 @@ function extractAnchorIdAnywhere(text) {
|
||||
|
||||
function extractCheminFromAnyUrl(text) {
|
||||
const s = String(text || "");
|
||||
// Exemple: http://localhost:4321/archicratie/prologue/#p-3-xxxx
|
||||
// ou: /archicratie/prologue/#p-3-xxxx
|
||||
const m = s.match(/(\/[a-z0-9\-]+\/[a-z0-9\-\/]+\/)#p-\d+-[0-9a-f]{8}/i);
|
||||
return m ? m[1] : "";
|
||||
}
|
||||
@@ -400,7 +482,7 @@ async function fetchIssue({ forgeApiBase, owner, repo, token, issueNum }) {
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
Accept: "application/json",
|
||||
"User-Agent": "archicratie-apply-ticket/2.0",
|
||||
"User-Agent": "archicratie-apply-ticket/2.1",
|
||||
},
|
||||
});
|
||||
if (!res.ok) {
|
||||
@@ -416,7 +498,7 @@ async function closeIssue({ forgeApiBase, owner, repo, token, issueNum, comment
|
||||
Authorization: `token ${token}`,
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "archicratie-apply-ticket/2.0",
|
||||
"User-Agent": "archicratie-apply-ticket/2.1",
|
||||
};
|
||||
|
||||
if (comment) {
|
||||
@@ -425,7 +507,11 @@ async function closeIssue({ forgeApiBase, owner, repo, token, issueNum, comment
|
||||
}
|
||||
|
||||
const url = `${base}/api/v1/repos/${owner}/${repo}/issues/${issueNum}`;
|
||||
const res = await fetch(url, { method: "PATCH", headers, body: JSON.stringify({ state: "closed" }) });
|
||||
const res = await fetch(url, {
|
||||
method: "PATCH",
|
||||
headers,
|
||||
body: JSON.stringify({ state: "closed" }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const t = await res.text().catch(() => "");
|
||||
@@ -529,10 +615,9 @@ async function main() {
|
||||
console.log(`🔎 Fetch ticket #${issueNum} from ${owner}/${repo} …`);
|
||||
const issue = await fetchIssue({ forgeApiBase, owner, repo, token, issueNum });
|
||||
|
||||
// Guard PR (Pull Request = "Demande d'ajout" = pas un ticket éditorial)
|
||||
if (issue?.pull_request) {
|
||||
console.error(`❌ #${issueNum} est une Pull Request (demande d’ajout), pas un ticket éditorial.`);
|
||||
console.error(`➡️ Ouvre un ticket [Correction]/[Fact-check] depuis le site (Proposer), puis relance apply-ticket sur ce numéro.`);
|
||||
console.error("➡️ Ouvre un ticket [Correction]/[Fact-check] depuis le site (Proposer), puis relance apply-ticket sur ce numéro.");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@@ -553,7 +638,6 @@ async function main() {
|
||||
ancre = (ancre || "").trim();
|
||||
if (ancre.startsWith("#")) ancre = ancre.slice(1);
|
||||
|
||||
// fallback si ticket mal formé
|
||||
if (!ancre) ancre = extractAnchorIdAnywhere(title) || extractAnchorIdAnywhere(body);
|
||||
|
||||
chemin = normalizeChemin(chemin);
|
||||
@@ -592,7 +676,6 @@ async function main() {
|
||||
const distHtmlPath = path.join(DIST_ROOT, chemin.replace(/^\/+|\/+$/g, ""), "index.html");
|
||||
await ensureBuildIfNeeded(distHtmlPath);
|
||||
|
||||
// Texte cible: préférence au texte complet (ticket), sinon dist si extrait probable
|
||||
let targetText = texteActuel;
|
||||
let distText = "";
|
||||
|
||||
@@ -609,21 +692,24 @@ async function main() {
|
||||
throw new Error("Impossible de reconstruire le texte du paragraphe (ni texte actuel, ni dist html).");
|
||||
}
|
||||
|
||||
const original = await fs.readFile(contentFile, "utf-8");
|
||||
const blocks = splitParagraphBlocks(original);
|
||||
const originalRaw = await fs.readFile(contentFile, "utf-8");
|
||||
const { hasFrontmatter, frontmatter, body: originalBody } = splitMdxFrontmatter(originalRaw);
|
||||
|
||||
const best = bestBlockMatchIndex(blocks, targetText);
|
||||
const split = splitParagraphBlocksPreserve(originalBody);
|
||||
const blocks = split.blocks;
|
||||
const separators = split.separators;
|
||||
|
||||
if (!blocks.length) {
|
||||
throw new Error(`Aucun bloc éditorial exploitable dans ${path.relative(CWD, contentFile)}`);
|
||||
}
|
||||
|
||||
const ranked = rankedBlockMatches(blocks, targetText, 5);
|
||||
const best = ranked[0] || { i: -1, score: -1, excerpt: "" };
|
||||
const runnerUp = ranked[1] || null;
|
||||
|
||||
// seuil de sécurité
|
||||
if (best.i < 0 || best.score < 40) {
|
||||
console.error("❌ Match trop faible: je refuse de remplacer automatiquement.");
|
||||
console.error(`➡️ Score=${best.score}. Recommandation: ticket avec 'Texte actuel (copie exacte du paragraphe)'.`);
|
||||
|
||||
const ranked = blocks
|
||||
.map((b, i) => ({ i, score: scoreText(b, targetText), excerpt: stripMd(b).slice(0, 140) }))
|
||||
.sort((a, b) => b.score - a.score)
|
||||
.slice(0, 5);
|
||||
|
||||
console.error("Top candidates:");
|
||||
for (const r of ranked) {
|
||||
console.error(` #${r.i + 1} score=${r.score} ${r.excerpt}${r.excerpt.length >= 140 ? "…" : ""}`);
|
||||
@@ -631,12 +717,34 @@ async function main() {
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (runnerUp) {
|
||||
const ambiguityGap = best.score - runnerUp.score;
|
||||
if (ambiguityGap < 15) {
|
||||
console.error("❌ Match ambigu: le meilleur candidat est trop proche du second.");
|
||||
console.error(`➡️ best=${best.score} / second=${runnerUp.score} / gap=${ambiguityGap}`);
|
||||
console.error("Top candidates:");
|
||||
for (const r of ranked) {
|
||||
console.error(` #${r.i + 1} score=${r.score} ${r.excerpt}${r.excerpt.length >= 140 ? "…" : ""}`);
|
||||
}
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
const beforeBlock = blocks[best.i];
|
||||
const afterBlock = proposition.trim();
|
||||
|
||||
const nextBlocks = blocks.slice();
|
||||
nextBlocks[best.i] = afterBlock;
|
||||
const updated = nextBlocks.join("\n\n");
|
||||
|
||||
const updatedBody = joinParagraphBlocksPreserve(nextBlocks, separators);
|
||||
const updatedRaw = joinMdxFrontmatter(frontmatter, updatedBody);
|
||||
|
||||
assertFrontmatterIntegrity({
|
||||
hadFrontmatter: hasFrontmatter,
|
||||
originalFrontmatter: frontmatter,
|
||||
finalText: updatedRaw,
|
||||
filePath: path.relative(CWD, contentFile),
|
||||
});
|
||||
|
||||
console.log(`🧩 Matched block #${best.i + 1}/${blocks.length} score=${best.score}`);
|
||||
|
||||
@@ -650,13 +758,15 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
// backup uniquement si on écrit
|
||||
const bakPath = `${contentFile}.bak.issue-${issueNum}`;
|
||||
const relContentFile = path.relative(CWD, contentFile);
|
||||
const bakPath = path.join(BACKUP_ROOT, `${relContentFile}.bak.issue-${issueNum}`);
|
||||
await fs.mkdir(path.dirname(bakPath), { recursive: true });
|
||||
|
||||
if (!(await fileExists(bakPath))) {
|
||||
await fs.writeFile(bakPath, original, "utf-8");
|
||||
await fs.writeFile(bakPath, originalRaw, "utf-8");
|
||||
}
|
||||
|
||||
await fs.writeFile(contentFile, updated, "utf-8");
|
||||
await fs.writeFile(contentFile, updatedRaw, "utf-8");
|
||||
console.log("✅ Applied.");
|
||||
|
||||
let aliasChanged = false;
|
||||
@@ -677,13 +787,13 @@ async function main() {
|
||||
|
||||
if (aliasChanged) {
|
||||
console.log(`✅ Alias ajouté: ${chemin} ${ancre} -> ${newId}`);
|
||||
// MàJ dist sans rebuild complet (inject seulement)
|
||||
run("node", ["scripts/inject-anchor-aliases.mjs"], { cwd: CWD });
|
||||
} else {
|
||||
console.log(`ℹ️ Alias déjà présent ou inutile (${ancre} -> ${newId}).`);
|
||||
}
|
||||
|
||||
// garde-fous rapides
|
||||
run("node", ["scripts/check-anchor-aliases.mjs"], { cwd: CWD });
|
||||
run("node", ["scripts/verify-anchor-aliases-in-dist.mjs"], { cwd: CWD });
|
||||
run("npm", ["run", "test:anchors"], { cwd: CWD });
|
||||
run("node", ["scripts/check-inline-js.mjs"], { cwd: CWD });
|
||||
}
|
||||
@@ -713,7 +823,6 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
// mode manuel
|
||||
console.log("Next (manuel) :");
|
||||
console.log(` git diff -- ${path.relative(CWD, contentFile)}`);
|
||||
console.log(
|
||||
@@ -730,4 +839,4 @@ async function main() {
|
||||
main().catch((e) => {
|
||||
console.error("💥", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
72
scripts/audit-docx-source.py
Executable file
72
scripts/audit-docx-source.py
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
from zipfile import ZipFile
|
||||
|
||||
NS = {"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"}
|
||||
|
||||
FORBIDDEN = [
|
||||
"coviabilité",
|
||||
"sacroinstitutionnelle",
|
||||
"technologistique",
|
||||
"scripturonormative",
|
||||
"textesrepères",
|
||||
"ellemême",
|
||||
"opérateur de d’archicration",
|
||||
"systèmes plusieurs statuts",
|
||||
"celle-ci se donne à voir",
|
||||
"Pour autant il serait",
|
||||
"Telles peuvent être le cas de",
|
||||
"la co-viabilité devient ,",
|
||||
]
|
||||
|
||||
|
||||
def norm(s: str) -> str:
|
||||
return unicodedata.normalize("NFC", s or "")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Audit simple d’un DOCX source officiel.")
|
||||
parser.add_argument("docx", help="Chemin du fichier .docx")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
with ZipFile(args.docx) as zf:
|
||||
data = zf.read("word/document.xml")
|
||||
except FileNotFoundError:
|
||||
print(f"ECHEC: fichier introuvable: {args.docx}", file=sys.stderr)
|
||||
return 2
|
||||
except KeyError:
|
||||
print("ECHEC: word/document.xml introuvable dans le DOCX.", file=sys.stderr)
|
||||
return 2
|
||||
except Exception as e:
|
||||
print(f"ECHEC: impossible d’ouvrir le DOCX: {e}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
root = ET.fromstring(data)
|
||||
found = False
|
||||
|
||||
for i, p in enumerate(root.findall(".//w:p", NS), start=1):
|
||||
txt = "".join(t.text or "" for t in p.findall(".//w:t", NS))
|
||||
txt_n = norm(txt)
|
||||
hits = [needle for needle in FORBIDDEN if needle in txt_n]
|
||||
if hits:
|
||||
found = True
|
||||
print(f"\n[paragraphe {i}]")
|
||||
print("Hits :", ", ".join(hits))
|
||||
print(txt_n)
|
||||
|
||||
if found:
|
||||
print("\nECHEC: formes interdites encore présentes dans le DOCX.")
|
||||
return 1
|
||||
|
||||
print("OK: aucune forme interdite trouvée dans le DOCX.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
232
scripts/audit-glossary-navigation.mjs
Normal file
232
scripts/audit-glossary-navigation.mjs
Normal file
@@ -0,0 +1,232 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
const ROOT = "src/content/glossaire";
|
||||
const DEFAULTS_FILE = "src/lib/glossary-navigation-defaults.ts";
|
||||
const HUB_LIMIT = 5;
|
||||
const EFFECTIVE_TOP_LIMIT = 10;
|
||||
const PATH_KEYS = ["understand", "deepen", "compare", "apply"];
|
||||
|
||||
const defaultsRaw = fs.readFileSync(DEFAULTS_FILE, "utf-8");
|
||||
|
||||
const defaultFamilies = new Set(
|
||||
[...defaultsRaw.matchAll(/^\s{4}"?([a-z0-9-]+)"?\s*:/gm)].map((m) => m[1]),
|
||||
);
|
||||
|
||||
const defaultPathKeysByFamily = new Map();
|
||||
const defaultTargetsByFamily = new Map();
|
||||
|
||||
for (const match of defaultsRaw.matchAll(
|
||||
/^\s{4}"?([a-z0-9-]+)"?\s*:\s*\{([\s\S]*?)^\s{4}\},/gm,
|
||||
)) {
|
||||
const family = match[1];
|
||||
const body = match[2];
|
||||
const keys = new Set();
|
||||
const targetsByKey = new Map();
|
||||
|
||||
for (const key of PATH_KEYS) {
|
||||
const pathMatch = body.match(new RegExp(`\\b${key}\\s*:\\s*\\[([^\\]]*)\\]`));
|
||||
const targets = pathMatch
|
||||
? pathMatch[1]
|
||||
.split(",")
|
||||
.map((x) => x.trim().replace(/^["']|["']$/g, ""))
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
|
||||
if (targets.length > 0) keys.add(key);
|
||||
targetsByKey.set(key, targets);
|
||||
}
|
||||
|
||||
defaultPathKeysByFamily.set(family, keys);
|
||||
defaultTargetsByFamily.set(family, targetsByKey);
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(ROOT).filter((f) => f.endsWith(".md"));
|
||||
const slugs = new Set(files.map((f) => f.replace(".md", "")));
|
||||
|
||||
const entries = [];
|
||||
|
||||
for (const file of files) {
|
||||
const full = path.join(ROOT, file);
|
||||
const raw = fs.readFileSync(full, "utf-8");
|
||||
const slug = file.replace(".md", "");
|
||||
|
||||
if (!raw.startsWith("---")) {
|
||||
entries.push({ slug, data: {}, noFrontmatter: true });
|
||||
continue;
|
||||
}
|
||||
|
||||
const frontmatter = raw.split("---", 3)[1];
|
||||
const data = yaml.load(frontmatter) || {};
|
||||
entries.push({ slug, data, noFrontmatter: false });
|
||||
}
|
||||
|
||||
const missingNavigation = [];
|
||||
const missingReason = [];
|
||||
const weakPaths = [];
|
||||
const selfLoops = [];
|
||||
const deadPrimaryNext = [];
|
||||
const directCycles = [];
|
||||
const edges = {};
|
||||
const incoming = {};
|
||||
const families = new Set();
|
||||
|
||||
const effectiveOutgoing = new Map();
|
||||
const effectiveIncoming = new Map();
|
||||
|
||||
function addEffectiveEdge(from, to) {
|
||||
if (!from || !to || from === to || !slugs.has(to)) return;
|
||||
|
||||
if (!effectiveOutgoing.has(from)) effectiveOutgoing.set(from, new Set());
|
||||
if (!effectiveIncoming.has(to)) effectiveIncoming.set(to, new Set());
|
||||
|
||||
effectiveOutgoing.get(from).add(to);
|
||||
effectiveIncoming.get(to).add(from);
|
||||
}
|
||||
|
||||
for (const { slug, data, noFrontmatter } of entries) {
|
||||
if (noFrontmatter) continue;
|
||||
|
||||
if (data.family) families.add(data.family);
|
||||
|
||||
const nav = data.navigation;
|
||||
|
||||
if (!nav) {
|
||||
missingNavigation.push(slug);
|
||||
continue;
|
||||
}
|
||||
|
||||
const next = nav.primaryNext;
|
||||
|
||||
if (next) {
|
||||
edges[slug] = next;
|
||||
incoming[next] = (incoming[next] || 0) + 1;
|
||||
addEffectiveEdge(slug, next);
|
||||
|
||||
if (next === slug) selfLoops.push(slug);
|
||||
if (!slugs.has(next)) deadPrimaryNext.push(`${slug} → ${next}`);
|
||||
|
||||
if (!nav.primaryReason) missingReason.push(slug);
|
||||
}
|
||||
|
||||
const explicitPaths = nav.paths || {};
|
||||
const familyDefaults = defaultPathKeysByFamily.get(data.family) || new Set();
|
||||
const familyDefaultTargets = defaultTargetsByFamily.get(data.family) || new Map();
|
||||
|
||||
const pathCount = PATH_KEYS.filter((key) => {
|
||||
const explicit = Array.isArray(explicitPaths[key]) && explicitPaths[key].length > 0;
|
||||
const fromDefault = familyDefaults.has(key);
|
||||
return explicit || fromDefault;
|
||||
}).length;
|
||||
|
||||
if (pathCount < 2) weakPaths.push(slug);
|
||||
|
||||
for (const key of PATH_KEYS) {
|
||||
const explicitTargets = Array.isArray(explicitPaths[key]) ? explicitPaths[key] : [];
|
||||
const defaultTargets = familyDefaultTargets.get(key) || [];
|
||||
|
||||
for (const target of [...explicitTargets, ...defaultTargets]) {
|
||||
addEffectiveEdge(slug, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const seenPairs = new Set();
|
||||
|
||||
for (const [a, b] of Object.entries(edges)) {
|
||||
if (edges[b] === a) {
|
||||
const pair = [a, b].sort().join(" <-> ");
|
||||
if (!seenPairs.has(pair)) {
|
||||
seenPairs.add(pair);
|
||||
directCycles.push(`${a} <-> ${b}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const missingDefaults = [...families].filter((f) => !defaultFamilies.has(f));
|
||||
|
||||
const bigHubs = Object.entries(incoming)
|
||||
.filter(([, count]) => count > HUB_LIMIT)
|
||||
.sort((a, b) => b[1] - a[1]);
|
||||
|
||||
console.log("\n🔍 Glossary navigation audit");
|
||||
|
||||
if (missingNavigation.length > 0) {
|
||||
console.log("\n❌ Missing navigation:");
|
||||
missingNavigation.forEach((s) => console.log(" -", s));
|
||||
}
|
||||
|
||||
console.log("\n🔍 Direct cycles:");
|
||||
if (directCycles.length) directCycles.forEach((c) => console.log(" -", c));
|
||||
else console.log(" (none)");
|
||||
|
||||
console.log("\n📊 Top hubs:");
|
||||
Object.entries(incoming)
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, 10)
|
||||
.forEach(([slug, n]) => {
|
||||
if (n > HUB_LIMIT) console.log(`⚠️ ${slug}: ${n}`);
|
||||
else console.log(` ${slug}: ${n}`);
|
||||
});
|
||||
|
||||
console.log("\n🔗 Checking dead primaryNext:");
|
||||
if (deadPrimaryNext.length) deadPrimaryNext.forEach((x) => console.log("❌", x));
|
||||
else console.log(" (none)");
|
||||
|
||||
if (missingDefaults.length) {
|
||||
console.log("\n❌ Families without defaults:");
|
||||
missingDefaults.forEach((f) => console.log(" -", f));
|
||||
}
|
||||
|
||||
if (bigHubs.length) {
|
||||
console.log(`\n⚠️ Hubs above limit (${HUB_LIMIT}):`);
|
||||
bigHubs.forEach(([slug, n]) => console.log(` - ${slug}: ${n}`));
|
||||
}
|
||||
|
||||
if (missingReason.length) {
|
||||
console.log("\n⚠️ Missing primaryReason:");
|
||||
missingReason.forEach((s) => console.log(" -", s));
|
||||
}
|
||||
|
||||
if (weakPaths.length) {
|
||||
console.log("\n⚠️ Weak path coverage (<2):");
|
||||
weakPaths.forEach((s) => console.log(" -", s));
|
||||
}
|
||||
|
||||
if (selfLoops.length) {
|
||||
console.log("\n❌ Self-referencing primaryNext:");
|
||||
selfLoops.forEach((s) => console.log(" -", s));
|
||||
}
|
||||
|
||||
console.log(`\n📊 Effective convergence top ${EFFECTIVE_TOP_LIMIT}:`);
|
||||
[...effectiveIncoming.entries()]
|
||||
.map(([slug, sources]) => [slug, sources.size])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, EFFECTIVE_TOP_LIMIT)
|
||||
.forEach(([slug, n]) => {
|
||||
console.log(` ${n} ${slug}`);
|
||||
});
|
||||
|
||||
console.log(`\n📊 Effective branching top ${EFFECTIVE_TOP_LIMIT}:`);
|
||||
[...effectiveOutgoing.entries()]
|
||||
.map(([slug, targets]) => [slug, targets.size])
|
||||
.sort((a, b) => b[1] - a[1])
|
||||
.slice(0, EFFECTIVE_TOP_LIMIT)
|
||||
.forEach(([slug, n]) => {
|
||||
console.log(` ${n} ${slug}`);
|
||||
});
|
||||
|
||||
const hardFailures =
|
||||
missingNavigation.length +
|
||||
directCycles.length +
|
||||
deadPrimaryNext.length +
|
||||
missingDefaults.length +
|
||||
selfLoops.length;
|
||||
|
||||
if (hardFailures > 0) {
|
||||
console.log(`\n❌ Audit failed: ${hardFailures} hard issue(s)`);
|
||||
process.exitCode = 1;
|
||||
} else {
|
||||
console.log("\n✅ Audit done");
|
||||
}
|
||||
@@ -14,6 +14,9 @@ const DIST_DIR = getArg("--dist", "dist");
|
||||
const BASELINE = getArg("--baseline", path.join("tests", "anchors-baseline.json"));
|
||||
const UPDATE = args.has("--update");
|
||||
|
||||
const ACCEPT_GLOSSARY_RESETS =
|
||||
process.env.ACCEPT_GLOSSARY_ANCHOR_RESETS === "1";
|
||||
|
||||
// Ex: 0.2 => 20%
|
||||
const THRESHOLD = Number(getArg("--threshold", process.env.ANCHORS_THRESHOLD ?? "0.2"));
|
||||
const MIN_PREV = Number(getArg("--min-prev", process.env.ANCHORS_MIN_PREV ?? "10"));
|
||||
@@ -74,7 +77,42 @@ function loadAllowMissing() {
|
||||
return new Set(arr.map(String));
|
||||
}
|
||||
|
||||
function loadAnchorChurnAllowlist() {
|
||||
const p = path.resolve("config/anchor-churn-allowlist.json");
|
||||
if (!fssync.existsSync(p)) return { acceptedResets: {}, acceptedPrefixes: {} };
|
||||
const raw = fssync.readFileSync(p, "utf8").trim();
|
||||
if (!raw) return { acceptedResets: {}, acceptedPrefixes: {} };
|
||||
const data = JSON.parse(raw);
|
||||
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
||||
throw new Error("anchor-churn-allowlist.json must be an object");
|
||||
}
|
||||
|
||||
const acceptedResets = data.accepted_resets || {};
|
||||
if (!acceptedResets || typeof acceptedResets !== "object" || Array.isArray(acceptedResets)) {
|
||||
throw new Error("anchor-churn-allowlist.json: accepted_resets must be an object");
|
||||
}
|
||||
|
||||
const acceptedPrefixes = data.accepted_prefixes || {};
|
||||
if (!acceptedPrefixes || typeof acceptedPrefixes !== "object" || Array.isArray(acceptedPrefixes)) {
|
||||
throw new Error("anchor-churn-allowlist.json: accepted_prefixes must be an object");
|
||||
}
|
||||
|
||||
return { acceptedResets, acceptedPrefixes };
|
||||
}
|
||||
|
||||
function acceptedResetReasonForPage(page) {
|
||||
if (ACCEPTED_RESETS[page]) return ACCEPTED_RESETS[page];
|
||||
|
||||
for (const [prefix, reason] of Object.entries(ACCEPTED_PREFIXES)) {
|
||||
if (page.startsWith(prefix)) return reason;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const ALLOW_MISSING = loadAllowMissing();
|
||||
const { acceptedResets: ACCEPTED_RESETS, acceptedPrefixes: ACCEPTED_PREFIXES } =
|
||||
loadAnchorChurnAllowlist();
|
||||
|
||||
async function buildSnapshot() {
|
||||
const absDist = path.resolve(DIST_DIR);
|
||||
@@ -139,6 +177,7 @@ function diffPage(prevIds, curIds) {
|
||||
|
||||
let failed = false;
|
||||
let changedPages = 0;
|
||||
let acceptedPages = 0;
|
||||
|
||||
for (const p of pages) {
|
||||
const prevIds = base[p] || null;
|
||||
@@ -150,12 +189,22 @@ function diffPage(prevIds, curIds) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// page supprimée
|
||||
// page supprimée ou sortie volontairement du contrat d’ancres
|
||||
if (prevIds && !curIds) {
|
||||
const acceptedReason = acceptedResetReasonForPage(p);
|
||||
|
||||
if (ALLOW_MISSING.has(p)) {
|
||||
console.log(`~ PAGE ${p} (missing now) ✅ allowed prevIds=${prevIds.length}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (acceptedReason) {
|
||||
acceptedPages += 1;
|
||||
console.log(`- PAGE ${p} (missing now) prevIds=${prevIds.length}`);
|
||||
console.log(` ✅ accepted reset: ${acceptedReason}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`- PAGE ${p} (missing now) prevIds=${prevIds.length}`);
|
||||
failed = true;
|
||||
continue;
|
||||
@@ -172,6 +221,7 @@ function diffPage(prevIds, curIds) {
|
||||
const prevN = prevIds.length || 1;
|
||||
const churn = (added.length + removed.length) / prevN;
|
||||
const removedRatio = removed.length / prevN;
|
||||
const acceptedReason = acceptedResetReasonForPage(p);
|
||||
|
||||
console.log(
|
||||
`~ ${p} prev=${prevIds.length} now=${curIds.length}` +
|
||||
@@ -182,11 +232,23 @@ function diffPage(prevIds, curIds) {
|
||||
console.log(` removed: ${removed.slice(0, 20).join(", ")}${removed.length > 20 ? " …" : ""}`);
|
||||
}
|
||||
|
||||
if (prevIds.length >= MIN_PREV && churn > THRESHOLD) failed = true;
|
||||
if (prevIds.length >= MIN_PREV && removedRatio > THRESHOLD) failed = true;
|
||||
const exceeds =
|
||||
(prevIds.length >= MIN_PREV && churn > THRESHOLD) ||
|
||||
(prevIds.length >= MIN_PREV && removedRatio > THRESHOLD);
|
||||
|
||||
if (exceeds && acceptedReason) {
|
||||
acceptedPages += 1;
|
||||
console.log(` ✅ accepted reset: ${acceptedReason}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exceeds) failed = true;
|
||||
}
|
||||
|
||||
console.log(`\nSummary: pages compared=${pages.length}, pages changed=${changedPages}`);
|
||||
console.log(
|
||||
`\nSummary: pages compared=${pages.length}, pages changed=${changedPages}, accepted resets=${acceptedPages}`
|
||||
);
|
||||
|
||||
if (failed) {
|
||||
console.error(`FAIL: anchor churn above threshold (threshold=${pct(THRESHOLD)} minPrev=${MIN_PREV})`);
|
||||
process.exit(1);
|
||||
|
||||
241
scripts/convert_docx_to_mdx.py
Executable file
241
scripts/convert_docx_to_mdx.py
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
print("Erreur : PyYAML n'est pas installé. Lance : pip3 install pyyaml")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
EDITION = "archicrat-ia"
|
||||
STATUS = "essai_these"
|
||||
VERSION = "0.1.0"
|
||||
|
||||
|
||||
ORDER_MAP = {
|
||||
"prologue": 10,
|
||||
"chapitre-1": 20,
|
||||
"chapitre-2": 30,
|
||||
"chapitre-3": 40,
|
||||
"chapitre-4": 50,
|
||||
"chapitre-5": 60,
|
||||
"conclusion": 70,
|
||||
}
|
||||
|
||||
|
||||
TITLE_MAP = {
|
||||
"prologue": "Prologue — Fondation, finalité sociopolitique et historique",
|
||||
"chapitre-1": "Chapitre 1 — Fondements épistémologiques et modélisation",
|
||||
"chapitre-2": "Chapitre 2 — Archéogenèse des régimes de co-viabilité",
|
||||
"chapitre-3": "Chapitre 3 — Philosophies du pouvoir et archicration",
|
||||
"chapitre-4": "Chapitre 4 — Histoire archicratique des révolutions industrielles",
|
||||
"chapitre-5": "Chapitre 5 — Tensions, co-viabilités et régulations",
|
||||
"conclusion": "Conclusion — ArchiCraT-IA",
|
||||
}
|
||||
|
||||
|
||||
def slugify_name(path: Path) -> str:
|
||||
stem = path.stem.lower().strip()
|
||||
|
||||
replacements = {
|
||||
" ": "-",
|
||||
"_": "-",
|
||||
"—": "-",
|
||||
"–": "-",
|
||||
"é": "e",
|
||||
"è": "e",
|
||||
"ê": "e",
|
||||
"ë": "e",
|
||||
"à": "a",
|
||||
"â": "a",
|
||||
"ä": "a",
|
||||
"î": "i",
|
||||
"ï": "i",
|
||||
"ô": "o",
|
||||
"ö": "o",
|
||||
"ù": "u",
|
||||
"û": "u",
|
||||
"ü": "u",
|
||||
"ç": "c",
|
||||
"'": "",
|
||||
"’": "",
|
||||
}
|
||||
|
||||
for old, new in replacements.items():
|
||||
stem = stem.replace(old, new)
|
||||
|
||||
stem = re.sub(r"-+", "-", stem).strip("-")
|
||||
|
||||
# normalisations spécifiques
|
||||
stem = stem.replace("chapitre-1-fondements-epistemologiques-et-modelisation-archicratie-version-officielle-revise", "chapitre-1")
|
||||
stem = stem.replace("chapitre-2", "chapitre-2")
|
||||
stem = stem.replace("chapitre-3", "chapitre-3")
|
||||
stem = stem.replace("chapitre-4", "chapitre-4")
|
||||
stem = stem.replace("chapitre-5", "chapitre-5")
|
||||
|
||||
if "prologue" in stem:
|
||||
return "prologue"
|
||||
if "chapitre-1" in stem:
|
||||
return "chapitre-1"
|
||||
if "chapitre-2" in stem:
|
||||
return "chapitre-2"
|
||||
if "chapitre-3" in stem:
|
||||
return "chapitre-3"
|
||||
if "chapitre-4" in stem:
|
||||
return "chapitre-4"
|
||||
if "chapitre-5" in stem:
|
||||
return "chapitre-5"
|
||||
if "conclusion" in stem:
|
||||
return "conclusion"
|
||||
|
||||
return stem
|
||||
|
||||
|
||||
def extract_title_from_markdown(md_text: str) -> str | None:
|
||||
for line in md_text.splitlines():
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith("# "):
|
||||
return line[2:].strip()
|
||||
return None
|
||||
|
||||
|
||||
def remove_first_h1(md_text: str) -> str:
|
||||
lines = md_text.splitlines()
|
||||
out = []
|
||||
removed = False
|
||||
|
||||
for line in lines:
|
||||
if not removed and line.strip().startswith("# "):
|
||||
removed = True
|
||||
continue
|
||||
out.append(line)
|
||||
|
||||
text = "\n".join(out).lstrip()
|
||||
return text
|
||||
|
||||
|
||||
def clean_markdown(md_text: str) -> str:
|
||||
text = md_text.replace("\r\n", "\n").replace("\r", "\n")
|
||||
|
||||
# nettoyer espaces multiples
|
||||
text = re.sub(r"\n{3,}", "\n\n", text)
|
||||
|
||||
# supprimer éventuels signets/artefacts de liens internes Pandoc
|
||||
text = re.sub(r"\[\]\(#.*?\)", "", text)
|
||||
|
||||
# convertir astérismes parasites
|
||||
text = re.sub(r"[ \t]+$", "", text, flags=re.MULTILINE)
|
||||
|
||||
return text.strip() + "\n"
|
||||
|
||||
|
||||
def compute_level(slug: str) -> int:
|
||||
if slug == "prologue":
|
||||
return 1
|
||||
if slug.startswith("chapitre-"):
|
||||
return 1
|
||||
if slug == "conclusion":
|
||||
return 1
|
||||
return 1
|
||||
|
||||
|
||||
def convert_one_file(input_docx: Path, output_dir: Path, source_root: Path):
|
||||
slug = slugify_name(input_docx)
|
||||
output_mdx = output_dir / f"{slug}.mdx"
|
||||
|
||||
cmd = [
|
||||
"pandoc",
|
||||
str(input_docx),
|
||||
"-f",
|
||||
"docx",
|
||||
"-t",
|
||||
"gfm+smart",
|
||||
]
|
||||
|
||||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
md_text = result.stdout
|
||||
|
||||
detected_title = extract_title_from_markdown(md_text)
|
||||
md_body = remove_first_h1(md_text)
|
||||
md_body = clean_markdown(md_body)
|
||||
|
||||
title = TITLE_MAP.get(slug) or detected_title or input_docx.stem
|
||||
order = ORDER_MAP.get(slug, 999)
|
||||
level = compute_level(slug)
|
||||
|
||||
relative_source = input_docx
|
||||
try:
|
||||
relative_source = input_docx.relative_to(source_root)
|
||||
except ValueError:
|
||||
relative_source = input_docx.name
|
||||
|
||||
frontmatter = {
|
||||
"title": title,
|
||||
"edition": EDITION,
|
||||
"status": STATUS,
|
||||
"level": level,
|
||||
"version": VERSION,
|
||||
"concepts": [],
|
||||
"links": [],
|
||||
"order": order,
|
||||
"summary": "",
|
||||
"source": {
|
||||
"kind": "docx",
|
||||
"path": str(relative_source),
|
||||
},
|
||||
}
|
||||
|
||||
yaml_block = yaml.safe_dump(
|
||||
frontmatter,
|
||||
allow_unicode=True,
|
||||
sort_keys=False,
|
||||
default_flow_style=False,
|
||||
).strip()
|
||||
|
||||
final_text = f"---\n{yaml_block}\n---\n{md_body if md_body.startswith(chr(10)) else chr(10) + md_body}"
|
||||
output_mdx.write_text(final_text, encoding="utf-8")
|
||||
print(f"✅ {input_docx.name} -> {output_mdx.name}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Convertit un dossier DOCX en MDX avec frontmatter.")
|
||||
parser.add_argument("input_dir", help="Dossier source contenant les DOCX")
|
||||
parser.add_argument("output_dir", help="Dossier de sortie pour les MDX")
|
||||
args = parser.parse_args()
|
||||
|
||||
input_dir = Path(args.input_dir).expanduser().resolve()
|
||||
output_dir = Path(args.output_dir).expanduser().resolve()
|
||||
|
||||
if not shutil.which("pandoc"):
|
||||
print("Erreur : pandoc n'est pas installé. Lance : brew install pandoc")
|
||||
sys.exit(1)
|
||||
|
||||
if not input_dir.exists() or not input_dir.is_dir():
|
||||
print(f"Erreur : dossier source introuvable : {input_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
docx_files = sorted(input_dir.glob("*.docx"))
|
||||
if not docx_files:
|
||||
print(f"Aucun DOCX trouvé dans : {input_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
for docx_file in docx_files:
|
||||
convert_one_file(docx_file, output_dir, input_dir)
|
||||
|
||||
print()
|
||||
print("Conversion DOCX -> MDX terminée.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
304
scripts/convert_mdx_to_docx.py
Normal file
304
scripts/convert_mdx_to_docx.py
Normal file
@@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
import zipfile
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
print("Erreur : PyYAML n'est pas installé. Lance : pip3 install pyyaml")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from docx import Document
|
||||
except ImportError:
|
||||
print("Erreur : python-docx n'est pas installé. Lance : pip3 install python-docx")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def split_frontmatter(text: str):
|
||||
if not text.startswith("---\n"):
|
||||
return {}, text
|
||||
|
||||
match = re.match(r"^---\n(.*?)\n---\n(.*)$", text, flags=re.DOTALL)
|
||||
if not match:
|
||||
return {}, text
|
||||
|
||||
yaml_block = match.group(1)
|
||||
body = match.group(2)
|
||||
|
||||
try:
|
||||
metadata = yaml.safe_load(yaml_block) or {}
|
||||
except Exception as e:
|
||||
print(f"Avertissement : frontmatter YAML illisible : {e}")
|
||||
metadata = {}
|
||||
|
||||
return metadata, body
|
||||
|
||||
|
||||
def strip_mdx_artifacts(text: str):
|
||||
# imports / exports MDX
|
||||
text = re.sub(r"^\s*(import|export)\s+.+?$", "", text, flags=re.MULTILINE)
|
||||
|
||||
# composants autofermants : <Component />
|
||||
text = re.sub(r"<[A-Z][A-Za-z0-9._-]*\b[^>]*\/>", "", text)
|
||||
|
||||
# composants bloc : <Component ...>...</Component>
|
||||
text = re.sub(
|
||||
r"<([A-Z][A-Za-z0-9._-]*)\b[^>]*>.*?</\1>",
|
||||
"",
|
||||
text,
|
||||
flags=re.DOTALL,
|
||||
)
|
||||
|
||||
# accolades seules résiduelles sur ligne
|
||||
text = re.sub(r"^\s*{\s*}\s*$", "", text, flags=re.MULTILINE)
|
||||
|
||||
# lignes vides multiples
|
||||
text = re.sub(r"\n{3,}", "\n\n", text)
|
||||
|
||||
return text.strip() + "\n"
|
||||
|
||||
|
||||
def inject_h1_from_title(metadata: dict, body: str):
|
||||
title = metadata.get("title", "")
|
||||
if not title:
|
||||
return body
|
||||
|
||||
if re.match(r"^\s*#\s+", body):
|
||||
return body
|
||||
|
||||
return f"# {title}\n\n{body.lstrip()}"
|
||||
|
||||
|
||||
def find_style_by_candidates(doc, candidates):
|
||||
# Cherche d'abord par nom visible
|
||||
for style in doc.styles:
|
||||
for candidate in candidates:
|
||||
if style.name == candidate:
|
||||
return style
|
||||
|
||||
# Puis par style_id Word interne
|
||||
for style in doc.styles:
|
||||
style_id = getattr(style, "style_id", "")
|
||||
if style_id in {"BodyText", "Heading1", "Heading2", "Heading3", "Heading4"}:
|
||||
for candidate in candidates:
|
||||
if candidate in {"Body Text", "Corps de texte"} and style_id == "BodyText":
|
||||
return style
|
||||
if candidate in {"Heading 1", "Titre 1"} and style_id == "Heading1":
|
||||
return style
|
||||
if candidate in {"Heading 2", "Titre 2"} and style_id == "Heading2":
|
||||
return style
|
||||
if candidate in {"Heading 3", "Titre 3"} and style_id == "Heading3":
|
||||
return style
|
||||
if candidate in {"Heading 4", "Titre 4"} and style_id == "Heading4":
|
||||
return style
|
||||
return None
|
||||
|
||||
def strip_leading_paragraph_numbers(text: str):
|
||||
"""
|
||||
Supprime les numéros de paragraphe du type :
|
||||
2. Texte...
|
||||
11. Texte...
|
||||
101. Texte...
|
||||
sans toucher aux titres Markdown (#, ##, ###).
|
||||
"""
|
||||
fixed_lines = []
|
||||
|
||||
for line in text.splitlines():
|
||||
stripped = line.lstrip()
|
||||
|
||||
# Ne jamais toucher aux titres Markdown
|
||||
if stripped.startswith("#"):
|
||||
fixed_lines.append(line)
|
||||
continue
|
||||
|
||||
# Supprime un numéro de paragraphe en début de ligne
|
||||
line = re.sub(r"^\s*\d+\.\s+", "", line)
|
||||
fixed_lines.append(line)
|
||||
|
||||
return "\n".join(fixed_lines) + "\n"
|
||||
|
||||
def normalize_non_heading_paragraphs(docx_path: Path):
|
||||
"""
|
||||
Force tous les paragraphes non-titres en Body Text / Corps de texte.
|
||||
On laisse intacts les Heading 1-4.
|
||||
"""
|
||||
doc = Document(str(docx_path))
|
||||
|
||||
body_style = find_style_by_candidates(doc, ["Body Text", "Corps de texte"])
|
||||
if body_style is None:
|
||||
print(f"Avertissement : style 'Body Text / Corps de texte' introuvable dans {docx_path.name}")
|
||||
return
|
||||
|
||||
heading_names = {
|
||||
"Heading 1", "Heading 2", "Heading 3", "Heading 4",
|
||||
"Titre 1", "Titre 2", "Titre 3", "Titre 4",
|
||||
}
|
||||
heading_ids = {"Heading1", "Heading2", "Heading3", "Heading4"}
|
||||
|
||||
changed = 0
|
||||
|
||||
for para in doc.paragraphs:
|
||||
text = para.text.strip()
|
||||
if not text:
|
||||
continue
|
||||
|
||||
current_style = para.style
|
||||
current_name = current_style.name if current_style else ""
|
||||
current_id = getattr(current_style, "style_id", "") if current_style else ""
|
||||
|
||||
if current_name in heading_names or current_id in heading_ids:
|
||||
continue
|
||||
|
||||
# Tout le reste passe en Body Text
|
||||
para.style = body_style
|
||||
changed += 1
|
||||
|
||||
doc.save(str(docx_path))
|
||||
print(f" ↳ normalisation styles : {changed} paragraphe(s) mis en 'Body Text / Corps de texte'")
|
||||
|
||||
def remove_word_bookmarks(docx_path: Path):
|
||||
"""
|
||||
Supprime les bookmarks Word (signets) du DOCX.
|
||||
Ce sont eux qui apparaissent comme crochets gris dans LibreOffice/Word
|
||||
quand l'affichage des signets est activé.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
tmpdir = Path(tmpdir)
|
||||
|
||||
# Dézipper le docx
|
||||
with zipfile.ZipFile(docx_path, "r") as zin:
|
||||
zin.extractall(tmpdir)
|
||||
|
||||
xml_targets = [
|
||||
tmpdir / "word" / "document.xml",
|
||||
tmpdir / "word" / "footnotes.xml",
|
||||
tmpdir / "word" / "endnotes.xml",
|
||||
tmpdir / "word" / "comments.xml",
|
||||
]
|
||||
|
||||
removed = 0
|
||||
|
||||
for xml_file in xml_targets:
|
||||
if not xml_file.exists():
|
||||
continue
|
||||
|
||||
text = xml_file.read_text(encoding="utf-8")
|
||||
|
||||
# enlever <w:bookmarkStart .../> et <w:bookmarkEnd .../>
|
||||
text, c1 = re.subn(r"<w:bookmarkStart\b[^>]*/>", "", text)
|
||||
text, c2 = re.subn(r"<w:bookmarkEnd\b[^>]*/>", "", text)
|
||||
|
||||
removed += c1 + c2
|
||||
xml_file.write_text(text, encoding="utf-8")
|
||||
|
||||
# Rezipper
|
||||
tmp_output = docx_path.with_suffix(".cleaned.docx")
|
||||
with zipfile.ZipFile(tmp_output, "w", zipfile.ZIP_DEFLATED) as zout:
|
||||
for file in tmpdir.rglob("*"):
|
||||
if file.is_file():
|
||||
zout.write(file, file.relative_to(tmpdir))
|
||||
|
||||
tmp_output.replace(docx_path)
|
||||
print(f" ↳ suppression signets : {removed} balise(s) supprimée(s)")
|
||||
|
||||
def convert_one_file(input_path: Path, output_path: Path, reference_doc: Path | None):
|
||||
raw = input_path.read_text(encoding="utf-8")
|
||||
metadata, body = split_frontmatter(raw)
|
||||
body = strip_mdx_artifacts(body)
|
||||
body = strip_leading_paragraph_numbers(body)
|
||||
body = inject_h1_from_title(metadata, body)
|
||||
|
||||
with tempfile.NamedTemporaryFile("w", suffix=".md", delete=False, encoding="utf-8") as tmp:
|
||||
tmp.write(body)
|
||||
tmp_md = Path(tmp.name)
|
||||
|
||||
cmd = [
|
||||
"pandoc",
|
||||
str(tmp_md),
|
||||
"-f",
|
||||
"markdown",
|
||||
"-o",
|
||||
str(output_path),
|
||||
]
|
||||
|
||||
if reference_doc:
|
||||
cmd.extend(["--reference-doc", str(reference_doc)])
|
||||
|
||||
try:
|
||||
subprocess.run(cmd, check=True)
|
||||
finally:
|
||||
try:
|
||||
tmp_md.unlink()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
normalize_non_heading_paragraphs(output_path)
|
||||
remove_word_bookmarks(output_path)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convertit des fichiers MDX en DOCX en conservant H1/H2/H3/H4 et en forçant le corps en Body Text."
|
||||
)
|
||||
parser.add_argument("input_dir", help="Dossier contenant les .mdx")
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
default=str(Path.home() / "Desktop" / "archicrat-ia-docx"),
|
||||
help="Dossier de sortie DOCX"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--reference-doc",
|
||||
default=None,
|
||||
help="DOCX modèle Word à utiliser comme reference-doc"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
input_dir = Path(args.input_dir)
|
||||
output_dir = Path(args.output_dir)
|
||||
reference_doc = Path(args.reference_doc) if args.reference_doc else None
|
||||
|
||||
if not shutil.which("pandoc"):
|
||||
print("Erreur : pandoc n'est pas installé. Installe-le avec : brew install pandoc")
|
||||
sys.exit(1)
|
||||
|
||||
if not input_dir.exists() or not input_dir.is_dir():
|
||||
print(f"Erreur : dossier introuvable : {input_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
if reference_doc and not reference_doc.exists():
|
||||
print(f"Erreur : reference-doc introuvable : {reference_doc}")
|
||||
sys.exit(1)
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
mdx_files = sorted(input_dir.glob("*.mdx"))
|
||||
if not mdx_files:
|
||||
print(f"Aucun fichier .mdx trouvé dans : {input_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Conversion de {len(mdx_files)} fichier(s)...")
|
||||
print(f"Entrée : {input_dir}")
|
||||
print(f"Sortie : {output_dir}")
|
||||
if reference_doc:
|
||||
print(f"Modèle : {reference_doc}")
|
||||
print()
|
||||
|
||||
for mdx_file in mdx_files:
|
||||
docx_name = mdx_file.with_suffix(".docx").name
|
||||
out_file = output_dir / docx_name
|
||||
print(f"→ {mdx_file.name} -> {docx_name}")
|
||||
convert_one_file(mdx_file, out_file, reference_doc)
|
||||
|
||||
print()
|
||||
print("✅ Conversion terminée.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
132
scripts/fix-docx-source.py
Executable file
132
scripts/fix-docx-source.py
Executable file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import tempfile
|
||||
import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from zipfile import ZIP_DEFLATED, ZipFile
|
||||
|
||||
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||
XML_NS = "http://www.w3.org/XML/1998/namespace"
|
||||
NS = {"w": W_NS}
|
||||
|
||||
ET.register_namespace("w", W_NS)
|
||||
|
||||
|
||||
REPLACEMENTS = {
|
||||
"coviabilité": "co-viabilité",
|
||||
"sacroinstitutionnelle": "sacro-institutionnelle",
|
||||
"technologistique": "techno-logistique",
|
||||
"scripturonormative": "scripturo-normative",
|
||||
"textesrepères": "textes-repères",
|
||||
"ellemême": "elle-même",
|
||||
"opérateur de d’archicration": "opérateur d’archicration",
|
||||
"systèmes plusieurs statuts": "systèmes à plusieurs statuts",
|
||||
"celle-ci se donne à voir": "Celle-ci se donne à voir",
|
||||
"Pour autant il serait": "Pour autant, il serait",
|
||||
"Telles peuvent être le cas de": "Tels peuvent être les cas de",
|
||||
}
|
||||
|
||||
# volontairement NON auto-corrigé : "la co-viabilité devient ,"
|
||||
# ce cas demande une décision éditoriale humaine.
|
||||
|
||||
|
||||
def qn(tag: str) -> str:
|
||||
prefix, local = tag.split(":")
|
||||
if prefix != "w":
|
||||
raise ValueError(tag)
|
||||
return f"{{{W_NS}}}{local}"
|
||||
|
||||
|
||||
def norm(s: str) -> str:
|
||||
return unicodedata.normalize("NFC", s or "")
|
||||
|
||||
|
||||
def paragraph_text(p: ET.Element) -> str:
|
||||
return "".join(t.text or "" for t in p.findall(".//w:t", NS))
|
||||
|
||||
|
||||
def replaced_text(s: str) -> str:
|
||||
out = norm(s)
|
||||
for bad, good in REPLACEMENTS.items():
|
||||
out = out.replace(bad, good)
|
||||
return out
|
||||
|
||||
|
||||
def rewrite_paragraph_text(p: ET.Element, new_text: str) -> None:
|
||||
ppr = p.find("w:pPr", NS)
|
||||
|
||||
for child in list(p):
|
||||
if ppr is not None and child is ppr:
|
||||
continue
|
||||
p.remove(child)
|
||||
|
||||
r = ET.Element(qn("w:r"))
|
||||
t = ET.SubElement(r, qn("w:t"))
|
||||
t.set(f"{{{XML_NS}}}space", "preserve")
|
||||
t.text = new_text
|
||||
p.append(r)
|
||||
|
||||
|
||||
def process_document_xml(xml_path: Path) -> int:
|
||||
tree = ET.parse(xml_path)
|
||||
root = tree.getroot()
|
||||
|
||||
changed = 0
|
||||
|
||||
for p in root.findall(".//w:p", NS):
|
||||
old = paragraph_text(p)
|
||||
new = replaced_text(old)
|
||||
if new != old:
|
||||
rewrite_paragraph_text(p, new)
|
||||
changed += 1
|
||||
|
||||
tree.write(xml_path, encoding="utf-8", xml_declaration=True)
|
||||
return changed
|
||||
|
||||
|
||||
def repack_docx(tmpdir: Path, out_docx: Path) -> None:
|
||||
tmp_out = out_docx.with_suffix(out_docx.suffix + ".tmp")
|
||||
with ZipFile(tmp_out, "w", ZIP_DEFLATED) as zf:
|
||||
for p in sorted(tmpdir.rglob("*")):
|
||||
if p.is_file():
|
||||
zf.write(p, p.relative_to(tmpdir))
|
||||
shutil.move(tmp_out, out_docx)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Répare mécaniquement certaines scories DOCX.")
|
||||
parser.add_argument("docx", help="Chemin du DOCX")
|
||||
parser.add_argument("--in-place", action="store_true", help="Réécrit le DOCX en place")
|
||||
args = parser.parse_args()
|
||||
|
||||
src = Path(args.docx)
|
||||
if not src.exists():
|
||||
print(f"ECHEC: fichier introuvable: {src}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
out = src if args.in_place else src.with_name(src.stem + ".fixed.docx")
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="docx-fix-") as td:
|
||||
td_path = Path(td)
|
||||
with ZipFile(src) as zf:
|
||||
zf.extractall(td_path)
|
||||
|
||||
document_xml = td_path / "word" / "document.xml"
|
||||
if not document_xml.exists():
|
||||
print("ECHEC: word/document.xml absent.", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
changed = process_document_xml(document_xml)
|
||||
repack_docx(td_path, out)
|
||||
|
||||
print(f"OK: DOCX réparé par réécriture paragraphe/XML. Paragraphes modifiés: {changed}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
raise SystemExit(main())
|
||||
@@ -205,12 +205,28 @@ async function main() {
|
||||
const manifestPath = path.resolve(args.manifest);
|
||||
|
||||
const items = await readManifest(manifestPath);
|
||||
const selected = args.all ? items : items.filter(it => args.only.includes(it.slug));
|
||||
const selected = args.all
|
||||
? items
|
||||
: items.filter((it) => {
|
||||
const rawSlug = String(it.slug || "").trim();
|
||||
const rawCollection = String(it.collection || "").trim();
|
||||
const qualified = `${rawCollection}/${rawSlug}`;
|
||||
return args.only.includes(rawSlug) || args.only.includes(qualified);
|
||||
});
|
||||
|
||||
if (!args.all && selected.length !== args.only.length) {
|
||||
const found = new Set(selected.map(s => s.slug));
|
||||
const missing = args.only.filter(s => !found.has(s));
|
||||
throw new Error(`Some --only slugs not found in manifest: ${missing.join(", ")}`);
|
||||
if (!args.all) {
|
||||
const found = new Set(
|
||||
selected.flatMap((s) => {
|
||||
const rawSlug = String(s.slug || "").trim();
|
||||
const rawCollection = String(s.collection || "").trim();
|
||||
return [rawSlug, `${rawCollection}/${rawSlug}`];
|
||||
})
|
||||
);
|
||||
|
||||
const missing = args.only.filter((s) => !found.has(s));
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`Some --only slugs not found in manifest: ${missing.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
const pandocOk = havePandoc();
|
||||
@@ -267,15 +283,30 @@ async function main() {
|
||||
|
||||
// ✅ IMPORTANT: archicrat-ia partage edition/status avec archicratie (pas de migration frontmatter)
|
||||
const schemaDefaultsByCollection = {
|
||||
archicratie: { edition: "archicratie", status: "modele_sociopolitique", level: 1 },
|
||||
"archicrat-ia": { edition: "archicrat-ia", status: "essai_these", level: 1 },
|
||||
ia: { edition: "ia", status: "cas_pratique", level: 1 },
|
||||
traite: { edition: "traite", status: "ontodynamique", level: 1 },
|
||||
glossaire: { edition: "glossaire", status: "lexique", level: 1 },
|
||||
atlas: { edition: "atlas", status: "atlas", level: 1 },
|
||||
archicratie: { edition: "archicratie", status: "modele_sociopolitique", level: 1 },
|
||||
"archicrat-ia": { edition: "archicrat-ia", status: "essai_these", level: 1 },
|
||||
"cas-ia": { edition: "cas-ia", status: "application", level: 1 },
|
||||
traite: { edition: "traite", status: "ontodynamique", level: 1 },
|
||||
glossaire: { edition: "glossaire", status: "lexique", level: 1 },
|
||||
atlas: { edition: "atlas", status: "atlas", level: 1 },
|
||||
};
|
||||
|
||||
const defaults = schemaDefaultsByCollection[outCollection] || { edition: outCollection, status: "draft", level: 1 };
|
||||
// Compat legacy :
|
||||
// manifest collection="archicratie" + slug="archicrat-ia/..."
|
||||
// => on écrit bien dans src/content/archicrat-ia/...
|
||||
// => mais on conserve edition/status historiques de type archicratie/modele_sociopolitique
|
||||
const defaultsKey =
|
||||
String(it.collection || "").trim() === "archicratie" &&
|
||||
String(it.slug || "").trim().startsWith("archicrat-ia/")
|
||||
? "archicratie"
|
||||
: outCollection;
|
||||
|
||||
const defaults =
|
||||
schemaDefaultsByCollection[defaultsKey] || {
|
||||
edition: defaultsKey,
|
||||
status: "draft",
|
||||
level: 1,
|
||||
};
|
||||
|
||||
const fm = [
|
||||
"---",
|
||||
|
||||
241
scripts/pick-proposer-issue.mjs
Normal file
241
scripts/pick-proposer-issue.mjs
Normal file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env node
|
||||
import process from "node:process";
|
||||
|
||||
function getEnv(name, fallback = "") {
|
||||
return String(process.env[name] ?? fallback).trim();
|
||||
}
|
||||
|
||||
function sh(value) {
|
||||
return JSON.stringify(String(value ?? ""));
|
||||
}
|
||||
|
||||
function escapeRegExp(s) {
|
||||
return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function pickLine(body, key) {
|
||||
const re = new RegExp(`^\\s*${escapeRegExp(key)}\\s*:\\s*([^\\n\\r]+)`, "mi");
|
||||
const m = String(body || "").match(re);
|
||||
return m ? m[1].trim() : "";
|
||||
}
|
||||
|
||||
function pickHeadingValue(body, headingKey) {
|
||||
const re = new RegExp(
|
||||
`^##\\s*${escapeRegExp(headingKey)}[^\\n]*\\n([\\s\\S]*?)(?=\\n##\\s|\\n\\s*$)`,
|
||||
"mi"
|
||||
);
|
||||
const m = String(body || "").match(re);
|
||||
if (!m) return "";
|
||||
const lines = m[1].split(/\r?\n/).map((l) => l.trim());
|
||||
for (const l of lines) {
|
||||
if (!l) continue;
|
||||
if (l.startsWith("<!--")) continue;
|
||||
return l.replace(/^\/?/, "/").trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function normalizeChemin(chemin) {
|
||||
let c = String(chemin || "").trim();
|
||||
if (!c) return "";
|
||||
if (!c.startsWith("/")) c = "/" + c;
|
||||
if (!c.endsWith("/")) c += "/";
|
||||
return c;
|
||||
}
|
||||
|
||||
function extractCheminFromAnyUrl(text) {
|
||||
const s = String(text || "");
|
||||
const m = s.match(/(\/[a-z0-9\-]+\/[a-z0-9\-\/]+\/)#p-\d+-[0-9a-f]{8}/i);
|
||||
return m ? m[1] : "";
|
||||
}
|
||||
|
||||
function inferType(issue) {
|
||||
const title = String(issue?.title || "");
|
||||
const body = String(issue?.body || "").replace(/\r\n/g, "\n");
|
||||
const fromBody = String(pickLine(body, "Type") || "").trim().toLowerCase();
|
||||
if (fromBody) return fromBody;
|
||||
|
||||
if (title.startsWith("[Correction]")) return "type/correction";
|
||||
if (title.startsWith("[Fact-check]") || title.startsWith("[Vérification]")) return "type/fact-check";
|
||||
return "";
|
||||
}
|
||||
|
||||
function inferChemin(issue) {
|
||||
const title = String(issue?.title || "");
|
||||
const body = String(issue?.body || "").replace(/\r\n/g, "\n");
|
||||
|
||||
return normalizeChemin(
|
||||
pickLine(body, "Chemin") ||
|
||||
pickHeadingValue(body, "Chemin") ||
|
||||
extractCheminFromAnyUrl(body) ||
|
||||
extractCheminFromAnyUrl(title)
|
||||
);
|
||||
}
|
||||
|
||||
function labelsOf(issue) {
|
||||
return Array.isArray(issue?.labels)
|
||||
? issue.labels.map((l) => String(l?.name || "")).filter(Boolean)
|
||||
: [];
|
||||
}
|
||||
|
||||
function issueNumber(issue) {
|
||||
return Number(issue?.number || issue?.index || 0);
|
||||
}
|
||||
|
||||
function parseMeta(issue) {
|
||||
const labels = labelsOf(issue);
|
||||
const type = inferType(issue);
|
||||
const chemin = inferChemin(issue);
|
||||
const number = issueNumber(issue);
|
||||
|
||||
const hasApproved = labels.includes("state/approved");
|
||||
const hasRejected = labels.includes("state/rejected");
|
||||
const isProposer = type === "type/correction" || type === "type/fact-check";
|
||||
const isOpen = String(issue?.state || "open") === "open";
|
||||
const isPR = Boolean(issue?.pull_request);
|
||||
|
||||
const eligible =
|
||||
number > 0 &&
|
||||
isOpen &&
|
||||
!isPR &&
|
||||
hasApproved &&
|
||||
!hasRejected &&
|
||||
isProposer &&
|
||||
Boolean(chemin);
|
||||
|
||||
return {
|
||||
issue,
|
||||
number,
|
||||
type,
|
||||
chemin,
|
||||
labels,
|
||||
hasApproved,
|
||||
hasRejected,
|
||||
eligible,
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchJson(url, token) {
|
||||
const res = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
Accept: "application/json",
|
||||
"User-Agent": "archicratie-pick-proposer-issue/1.0",
|
||||
},
|
||||
});
|
||||
if (!res.ok) {
|
||||
const t = await res.text().catch(() => "");
|
||||
throw new Error(`HTTP ${res.status} ${url}\n${t}`);
|
||||
}
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async function fetchIssue(apiBase, owner, repo, token, n) {
|
||||
const url = `${apiBase}/api/v1/repos/${owner}/${repo}/issues/${n}`;
|
||||
return await fetchJson(url, token);
|
||||
}
|
||||
|
||||
async function listOpenIssues(apiBase, owner, repo, token) {
|
||||
const out = [];
|
||||
let page = 1;
|
||||
const limit = 100;
|
||||
|
||||
while (true) {
|
||||
const url = `${apiBase}/api/v1/repos/${owner}/${repo}/issues?state=open&page=${page}&limit=${limit}`;
|
||||
const batch = await fetchJson(url, token);
|
||||
if (!Array.isArray(batch) || batch.length === 0) break;
|
||||
out.push(...batch);
|
||||
if (batch.length < limit) break;
|
||||
page += 1;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function emitNone(reason) {
|
||||
process.stdout.write(
|
||||
[
|
||||
`TARGET_FOUND="0"`,
|
||||
`TARGET_REASON=${sh(reason)}`,
|
||||
`TARGET_PRIMARY_ISSUE=""`,
|
||||
`TARGET_ISSUES=""`,
|
||||
`TARGET_COUNT="0"`,
|
||||
`TARGET_CHEMIN=""`,
|
||||
].join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const token = getEnv("FORGE_TOKEN");
|
||||
const owner = getEnv("GITEA_OWNER");
|
||||
const repo = getEnv("GITEA_REPO");
|
||||
const apiBase = (getEnv("FORGE_API") || getEnv("FORGE_BASE")).replace(/\/+$/, "");
|
||||
const explicit = Number(process.argv[2] || 0);
|
||||
|
||||
if (!token) throw new Error("Missing FORGE_TOKEN");
|
||||
if (!owner || !repo) throw new Error("Missing GITEA_OWNER / GITEA_REPO");
|
||||
if (!apiBase) throw new Error("Missing FORGE_API / FORGE_BASE");
|
||||
|
||||
let metas = [];
|
||||
|
||||
if (explicit > 0) {
|
||||
const issue = await fetchIssue(apiBase, owner, repo, token, explicit);
|
||||
const meta = parseMeta(issue);
|
||||
|
||||
if (!meta.eligible) {
|
||||
emitNone(
|
||||
!meta.hasApproved
|
||||
? "explicit_issue_not_approved"
|
||||
: meta.hasRejected
|
||||
? "explicit_issue_rejected"
|
||||
: !meta.type
|
||||
? "explicit_issue_missing_type"
|
||||
: !meta.chemin
|
||||
? "explicit_issue_missing_chemin"
|
||||
: "explicit_issue_not_eligible"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const openIssues = await listOpenIssues(apiBase, owner, repo, token);
|
||||
metas = openIssues.map(parseMeta).filter((m) => m.eligible && m.chemin === meta.chemin);
|
||||
} else {
|
||||
const openIssues = await listOpenIssues(apiBase, owner, repo, token);
|
||||
metas = openIssues.map(parseMeta).filter((m) => m.eligible);
|
||||
|
||||
if (metas.length === 0) {
|
||||
emitNone("no_open_approved_proposer_issue");
|
||||
return;
|
||||
}
|
||||
|
||||
metas.sort((a, b) => a.number - b.number);
|
||||
const first = metas[0];
|
||||
metas = metas.filter((m) => m.chemin === first.chemin);
|
||||
}
|
||||
|
||||
metas.sort((a, b) => a.number - b.number);
|
||||
|
||||
if (metas.length === 0) {
|
||||
emitNone("no_batch_for_path");
|
||||
return;
|
||||
}
|
||||
|
||||
const primary = metas[0];
|
||||
const issues = metas.map((m) => String(m.number));
|
||||
|
||||
process.stdout.write(
|
||||
[
|
||||
`TARGET_FOUND="1"`,
|
||||
`TARGET_REASON="ok"`,
|
||||
`TARGET_PRIMARY_ISSUE=${sh(primary.number)}`,
|
||||
`TARGET_ISSUES=${sh(issues.join(" "))}`,
|
||||
`TARGET_COUNT=${sh(issues.length)}`,
|
||||
`TARGET_CHEMIN=${sh(primary.chemin)}`,
|
||||
].join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error("💥 pick-proposer-issue:", e?.message || e);
|
||||
process.exit(1);
|
||||
});
|
||||
29
scripts/refresh-chapter2.sh
Executable file
29
scripts/refresh-chapter2.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
DOCX="sources/docx/archicrat-ia/Chapitre_2–Archeogenese_des_regimes_de_co-viabilite-version_officielle.docx"
|
||||
MANIFEST="sources/manifest.yml"
|
||||
ONLY="archicrat-ia/chapitre-2"
|
||||
|
||||
echo "== Audit source avant fix =="
|
||||
if ! python3 scripts/audit-docx-source.py "$DOCX"; then
|
||||
echo
|
||||
echo "== Fix source =="
|
||||
python3 scripts/fix-docx-source.py --in-place "$DOCX"
|
||||
|
||||
echo
|
||||
echo "== Audit source après fix =="
|
||||
python3 scripts/audit-docx-source.py "$DOCX"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "== Réimport =="
|
||||
node scripts/import-docx.mjs --manifest "$MANIFEST" --only "$ONLY" --force
|
||||
|
||||
echo
|
||||
echo "== Build =="
|
||||
npm run build
|
||||
|
||||
echo
|
||||
echo "== Tests =="
|
||||
npm test
|
||||
20
scripts/write-ops-health.mjs
Normal file
20
scripts/write-ops-health.mjs
Normal file
@@ -0,0 +1,20 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const root = process.cwd();
|
||||
const outDir = path.join(root, "public", "__ops");
|
||||
const outFile = path.join(outDir, "health.json");
|
||||
|
||||
const payload = {
|
||||
service: "archicratie-site",
|
||||
env: process.env.PUBLIC_OPS_ENV || "unknown",
|
||||
upstream: process.env.PUBLIC_OPS_UPSTREAM || "unknown",
|
||||
buildSha: process.env.PUBLIC_BUILD_SHA || "unknown",
|
||||
builtAt: process.env.PUBLIC_BUILD_TIME || new Date().toISOString(),
|
||||
};
|
||||
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
fs.writeFileSync(outFile, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
||||
|
||||
console.log(`✅ ops health written: ${outFile}`);
|
||||
console.log(payload);
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
sources/docx/commencer/document-de-presentation.docx
Normal file
BIN
sources/docx/commencer/document-de-presentation.docx
Normal file
Binary file not shown.
BIN
sources/docx/manifeste/Manifeste_Archicratique.docx
Normal file
BIN
sources/docx/manifeste/Manifeste_Archicratique.docx
Normal file
Binary file not shown.
@@ -1,107 +0,0 @@
|
||||
---
|
||||
import SiteNav from "../components/SiteNav.astro";
|
||||
import LevelToggle from "../components/LevelToggle.astro";
|
||||
import BuildStamp from "../components/BuildStamp.astro";
|
||||
import "../styles/global.css";
|
||||
|
||||
const {
|
||||
title,
|
||||
editionLabel,
|
||||
editionKey,
|
||||
statusLabel,
|
||||
statusKey,
|
||||
level,
|
||||
version
|
||||
} = Astro.props;
|
||||
|
||||
const lvl = level ?? 1;
|
||||
|
||||
const canonical = Astro.site
|
||||
? new URL(Astro.url.pathname, Astro.site).href
|
||||
: Astro.url.href;
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title ? `${title} — Archicratie` : "Archicratie"}</title>
|
||||
|
||||
<link rel="canonical" href={canonical} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<meta data-pagefind-filter="edition[content]" content={String(editionKey ?? editionLabel)} />
|
||||
<meta data-pagefind-filter="level[content]" content={String(lvl)} />
|
||||
<meta data-pagefind-filter="status[content]" content={String(statusKey ?? statusLabel)} />
|
||||
|
||||
<meta data-pagefind-meta={`edition:${String(editionKey ?? editionLabel)}`} />
|
||||
<meta data-pagefind-meta={`level:${String(lvl)}`} />
|
||||
<meta data-pagefind-meta={`status:${String(statusKey ?? statusLabel)}`} />
|
||||
<meta data-pagefind-meta={`version:${String(version ?? "")}`} />
|
||||
</head>
|
||||
|
||||
<body data-doc-title={title} data-doc-version={version}>
|
||||
<header>
|
||||
<SiteNav />
|
||||
<div class="edition-bar">
|
||||
<span class="badge"><strong>Édition</strong> : {editionLabel}</span>
|
||||
<span class="badge"><strong>Statut</strong> : {statusLabel}</span>
|
||||
<span class="badge"><strong>Niveau</strong> : {lvl}</span>
|
||||
<span class="badge"><strong>Version</strong> : {version}</span>
|
||||
<LevelToggle initialLevel={lvl} />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<article class="reading" data-pagefind-body>
|
||||
<slot />
|
||||
<BuildStamp />
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const title = document.body.dataset.docTitle || document.title;
|
||||
const version = document.body.dataset.docVersion || "";
|
||||
|
||||
const paras = Array.from(document.querySelectorAll(".reading p[id]"));
|
||||
for (const p of paras) {
|
||||
if (p.querySelector(".para-tools")) continue;
|
||||
|
||||
const tools = document.createElement("span");
|
||||
tools.className = "para-tools";
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.className = "para-anchor";
|
||||
a.href = `#${p.id}`;
|
||||
a.setAttribute("aria-label", "Lien vers ce paragraphe");
|
||||
a.textContent = "¶";
|
||||
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "para-cite";
|
||||
btn.textContent = "Citer";
|
||||
|
||||
btn.addEventListener("click", async () => {
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = p.id;
|
||||
const cite = `${title}${version ? ` (v${version})` : ""} — ${url.toString()}`;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(cite);
|
||||
const prev = btn.textContent;
|
||||
btn.textContent = "Copié";
|
||||
setTimeout(() => (btn.textContent = prev), 900);
|
||||
} catch {
|
||||
window.prompt("Copiez la citation :", cite);
|
||||
}
|
||||
});
|
||||
|
||||
tools.appendChild(a);
|
||||
tools.appendChild(btn);
|
||||
p.appendChild(tools);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,165 +0,0 @@
|
||||
---
|
||||
import SiteNav from "../components/SiteNav.astro";
|
||||
import LevelToggle from "../components/LevelToggle.astro";
|
||||
import BuildStamp from "../components/BuildStamp.astro";
|
||||
import "../styles/global.css";
|
||||
|
||||
const {
|
||||
title,
|
||||
editionLabel,
|
||||
editionKey,
|
||||
statusLabel,
|
||||
statusKey,
|
||||
level,
|
||||
version
|
||||
} = Astro.props;
|
||||
|
||||
const lvl = level ?? 1;
|
||||
|
||||
const canonical = Astro.site
|
||||
? new URL(Astro.url.pathname, Astro.site).href
|
||||
: Astro.url.href;
|
||||
|
||||
// Cible Gitea (injectée au build)
|
||||
const GITEA_BASE = import.meta.env.PUBLIC_GITEA_BASE ?? "";
|
||||
const GITEA_OWNER = import.meta.env.PUBLIC_GITEA_OWNER ?? "";
|
||||
const GITEA_REPO = import.meta.env.PUBLIC_GITEA_REPO ?? "";
|
||||
---
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{title ? `${title} — Archicratie` : "Archicratie"}</title>
|
||||
|
||||
<link rel="canonical" href={canonical} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<meta data-pagefind-filter="edition[content]" content={String(editionKey ?? editionLabel)} />
|
||||
<meta data-pagefind-filter="level[content]" content={String(lvl)} />
|
||||
<meta data-pagefind-filter="status[content]" content={String(statusKey ?? statusLabel)} />
|
||||
|
||||
<meta data-pagefind-meta={`edition:${String(editionKey ?? editionLabel)}`} />
|
||||
<meta data-pagefind-meta={`level:${String(lvl)}`} />
|
||||
<meta data-pagefind-meta={`status:${String(statusKey ?? statusLabel)}`} />
|
||||
<meta data-pagefind-meta={`version:${String(version ?? "")}`} />
|
||||
</head>
|
||||
|
||||
<body data-doc-title={title} data-doc-version={version}>
|
||||
<header>
|
||||
<SiteNav />
|
||||
<div class="edition-bar">
|
||||
<span class="badge"><strong>Édition</strong> : {editionLabel}</span>
|
||||
<span class="badge"><strong>Statut</strong> : {statusLabel}</span>
|
||||
<span class="badge"><strong>Niveau</strong> : {lvl}</span>
|
||||
<span class="badge"><strong>Version</strong> : {version}</span>
|
||||
<LevelToggle initialLevel={lvl} />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<article class="reading" data-pagefind-body>
|
||||
<slot />
|
||||
<BuildStamp />
|
||||
</article>
|
||||
</main>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const title = document.body.dataset.docTitle || document.title;
|
||||
const version = document.body.dataset.docVersion || "";
|
||||
|
||||
const GITEA_BASE = {JSON.stringify(GITEA_BASE)};
|
||||
const GITEA_OWNER = {JSON.stringify(GITEA_OWNER)};
|
||||
const GITEA_REPO = {JSON.stringify(GITEA_REPO)};
|
||||
const giteaReady = Boolean(GITEA_BASE && GITEA_OWNER && GITEA_REPO);
|
||||
|
||||
function buildIssueURL(anchorId, excerpt) {
|
||||
const base = String(GITEA_BASE).replace(/\/+$/, "");
|
||||
const issue = new URL(`${base}/${GITEA_OWNER}/${GITEA_REPO}/issues/new`);
|
||||
|
||||
const page = new URL(window.location.href);
|
||||
page.hash = anchorId;
|
||||
|
||||
const issueTitle = `Correction: ${anchorId} — ${title}`;
|
||||
const body = [
|
||||
`Page: ${page.toString()}`,
|
||||
`Ancre: #${anchorId}`,
|
||||
`Version: ${version || "(non renseignée)"}`,
|
||||
``,
|
||||
`Texte actuel (extrait):`,
|
||||
`> ${excerpt}`,
|
||||
``,
|
||||
`Proposition (remplacer par):`,
|
||||
``,
|
||||
`Justification:`,
|
||||
``,
|
||||
`---`,
|
||||
`Note: issue générée depuis le site (pré-remplissage).`
|
||||
].join("\n");
|
||||
|
||||
issue.searchParams.set("title", issueTitle);
|
||||
issue.searchParams.set("body", body);
|
||||
return issue.toString();
|
||||
}
|
||||
|
||||
const paras = Array.from(document.querySelectorAll(".reading p[id]"));
|
||||
for (const p of paras) {
|
||||
if (p.querySelector(".para-tools")) continue;
|
||||
|
||||
const tools = document.createElement("span");
|
||||
tools.className = "para-tools";
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.className = "para-anchor";
|
||||
a.href = `#${p.id}`;
|
||||
a.setAttribute("aria-label", "Lien vers ce paragraphe");
|
||||
a.textContent = "¶";
|
||||
|
||||
const citeBtn = document.createElement("button");
|
||||
citeBtn.type = "button";
|
||||
citeBtn.className = "para-cite";
|
||||
citeBtn.textContent = "Citer";
|
||||
|
||||
citeBtn.addEventListener("click", async () => {
|
||||
const url = new URL(window.location.href);
|
||||
url.hash = p.id;
|
||||
const cite = `${title}${version ? ` (v${version})` : ""} — ${url.toString()}`;
|
||||
|
||||
try {
|
||||
await navigator.clipboard.writeText(cite);
|
||||
const prev = citeBtn.textContent;
|
||||
citeBtn.textContent = "Copié";
|
||||
setTimeout(() => (citeBtn.textContent = prev), 900);
|
||||
} catch {
|
||||
window.prompt("Copiez la citation :", cite);
|
||||
}
|
||||
});
|
||||
|
||||
tools.appendChild(a);
|
||||
tools.appendChild(citeBtn);
|
||||
|
||||
if (giteaReady) {
|
||||
const propose = document.createElement("a");
|
||||
propose.className = "para-propose";
|
||||
propose.href = "#";
|
||||
propose.textContent = "Proposer";
|
||||
propose.setAttribute("aria-label", "Proposer une correction sur Gitea");
|
||||
|
||||
propose.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
const raw = (p.textContent || "").trim().replace(/\s+/g, " ");
|
||||
const excerpt = raw.length > 420 ? (raw.slice(0, 420) + "…") : raw;
|
||||
const url = buildIssueURL(p.id, excerpt);
|
||||
window.open(url, "_blank", "noopener,noreferrer");
|
||||
});
|
||||
|
||||
tools.appendChild(propose);
|
||||
}
|
||||
|
||||
p.appendChild(tools);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,161 +1,123 @@
|
||||
version: 1
|
||||
|
||||
docs:
|
||||
# =========================
|
||||
# Document d’entrée
|
||||
# =========================
|
||||
- source: sources/docx/commencer/document-de-presentation.docx
|
||||
collection: commencer
|
||||
slug: document-de-presentation
|
||||
title: "Document de présentation"
|
||||
order: 0
|
||||
|
||||
# =========================
|
||||
# Archicratie — Essai-thèse "ArchiCraT-IA"
|
||||
# =========================
|
||||
- source: sources/docx/archicrat-ia/Prologue—Archicratie-fondation_et_finalite_sociopolitique_et_historique-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/prologue
|
||||
title: "Prologue — Fondation et finalité sociopolitique et historique"
|
||||
collection: archicrat-ia
|
||||
slug: prologue
|
||||
title: "Prologue — Fondation, finalité sociopolitique et historique"
|
||||
order: 10
|
||||
|
||||
- source: sources/docx/archicrat-ia/Chapitre_1—Fondements_epistemologiques_et_modelisation_Archicratie-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/chapitre-1
|
||||
collection: archicrat-ia
|
||||
slug: chapitre-1
|
||||
title: "Chapitre 1 — Fondements épistémologiques et modélisation"
|
||||
order: 20
|
||||
|
||||
- source: sources/docx/archicrat-ia/Chapitre_2–Archeogenese_des_regimes_de_co-viabilite-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/chapitre-2
|
||||
collection: archicrat-ia
|
||||
slug: chapitre-2
|
||||
title: "Chapitre 2 — Archéogenèse des régimes de co-viabilité"
|
||||
order: 30
|
||||
|
||||
- source: sources/docx/archicrat-ia/Chapitre_3—Philosophies_du_pouvoir_et_Archicration-pour_une_topologie_differenciee_des_regimes_regulateurs-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/chapitre-3
|
||||
collection: archicrat-ia
|
||||
slug: chapitre-3
|
||||
title: "Chapitre 3 — Philosophies du pouvoir et archicration"
|
||||
order: 40
|
||||
|
||||
- source: sources/docx/archicrat-ia/Chapitre_4—Vers_une_histoire_archicratique_des_revolutions_industrielles-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/chapitre-4
|
||||
collection: archicrat-ia
|
||||
slug: chapitre-4
|
||||
title: "Chapitre 4 — Histoire archicratique des révolutions industrielles"
|
||||
order: 50
|
||||
|
||||
- source: sources/docx/archicrat-ia/Chapitre_5—Problematiques_des_tensions_des_co-viabilites_et_des_regulations_archicratiques-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/chapitre-5
|
||||
collection: archicrat-ia
|
||||
slug: chapitre-5
|
||||
title: "Chapitre 5 — Tensions, co-viabilités et régulations"
|
||||
order: 60
|
||||
|
||||
- source: sources/docx/archicrat-ia/Conclusion-Archicrat-IA-version_officielle.docx
|
||||
collection: archicratie
|
||||
slug: archicrat-ia/conclusion
|
||||
collection: archicrat-ia
|
||||
slug: conclusion
|
||||
title: "Conclusion — ArchiCraT-IA"
|
||||
order: 70
|
||||
|
||||
# =========================
|
||||
# IA — Cas pratique (1 page = 1 chapitre)
|
||||
# NOTE: on n'inclut PAS le monolithe "Cas_IA-... .docx" dans le manifeste.
|
||||
# Cas pratique — Gouvernance des systèmes IA
|
||||
# =========================
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Introduction_generale—Mettre_en_scene_un_systeme_IA.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/introduction
|
||||
title: "Cas pratique — Introduction générale : Mettre en scène un système IA"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Introduction.docx
|
||||
collection: cas-ia
|
||||
slug: introduction
|
||||
title: "Introduction générale — Mettre un système d’IA en scène"
|
||||
order: 110
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_I—Epreuve_de_detectabilite.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-1
|
||||
title: "Cas pratique — Chapitre I : Épreuve de détectabilité"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_1_Epreuve_de_detectabilite.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-1
|
||||
title: "Chapitre I — Épreuve de détectabilité"
|
||||
order: 120
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_II—Epreuve_topologique.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-2
|
||||
title: "Cas pratique — Chapitre II : Épreuve topologique"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_2_Epreuve_Topologique.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-2
|
||||
title: "Chapitre II — Épreuve topologique"
|
||||
order: 130
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_III—Epreuve_archeogenetique.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-3
|
||||
title: "Cas pratique — Chapitre III : Épreuve archéogénétique"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_3_Epreuve_archeogenetique.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-3
|
||||
title: "Chapitre III — Épreuve archéogénétique"
|
||||
order: 140
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_IV—Epreuve_morphologique.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-4
|
||||
title: "Cas pratique — Chapitre IV : Épreuve morphologique"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_4_Epreuve_Morphologique.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-4
|
||||
title: "Chapitre IV — Épreuve morphologique"
|
||||
order: 150
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_V—Epreuve_historique.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-5
|
||||
title: "Cas pratique — Chapitre V : Épreuve historique"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_5_Epreuve_Historique.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-5
|
||||
title: "Chapitre V — Épreuve historique"
|
||||
order: 160
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_VI—Epreuve_de_co-viabilite.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-6
|
||||
title: "Cas pratique — Chapitre VI : Épreuve de co-viabilité"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_6_Epreuve_de_Co-viabilite.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-6
|
||||
title: "Chapitre VI — Épreuve de co-viabilité"
|
||||
order: 170
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_VII—Gestes_archicratiques_concrets_pour_un_systeme_IA.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/chapitre-7
|
||||
title: "Cas pratique — Chapitre VII : Gestes archicratiques concrets"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Chapitre_7_Gestes_archicratiques_concrets_pour_un_systeme_IA.docx
|
||||
collection: cas-ia
|
||||
slug: chapitre-7
|
||||
title: "Chapitre VII — Gestes archicratiques concrets pour un système d’IA"
|
||||
order: 180
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Conclusion.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/conclusion
|
||||
title: "Cas pratique — Conclusion"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Conclusion.docx
|
||||
collection: cas-ia
|
||||
slug: conclusion
|
||||
title: "Conclusion"
|
||||
order: 190
|
||||
|
||||
- source: sources/docx/cas-ia/Cas_IA-Archicratie_et_gouvernance_des_systemes_IA-Annexe—Glossaire_archicratique_pour_audit_des_systemes_IA.docx
|
||||
collection: ia
|
||||
slug: cas-pratique/annexe-glossaire-audit
|
||||
title: "Cas pratique — Annexe : Glossaire archicratique pour audit des systèmes IA"
|
||||
- source: sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Annexe_Glossaire_Archicratique_Cas_IA.docx
|
||||
collection: cas-ia
|
||||
slug: annexe-glossaire-audit
|
||||
title: "Annexe — Glossaire archicratique pour l’audit des systèmes d’IA"
|
||||
order: 195
|
||||
|
||||
# =========================
|
||||
# Traité — Ontodynamique générative (1 page = 1 chapitre)
|
||||
# NOTE: on n'inclut PAS le monolithe "Traite-...-version_officielle.docx" dans le manifeste.
|
||||
# =========================
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Introduction-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/introduction
|
||||
title: "Traité — Introduction"
|
||||
order: 210
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_1—Le_flux_ontogenetique-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-1
|
||||
title: "Traité — Chapitre 1 : Le flux ontogénétique"
|
||||
order: 220
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_2—economie_du_reel-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-2
|
||||
title: "Traité — Chapitre 2 : Économie du réel"
|
||||
order: 230
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_3—Le_reel_comme_systeme_regulateur-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-3
|
||||
title: "Traité — Chapitre 3 : Le réel comme système régulateur"
|
||||
order: 240
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_4—Arcalite-structures_formes_invariants-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-4
|
||||
title: "Traité — Chapitre 4 : Arcalité — structures, formes, invariants"
|
||||
order: 250
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_5-Cratialite-forces_flux_gradients-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-5
|
||||
title: "Traité — Chapitre 5 : Cratialité — forces, flux, gradients"
|
||||
order: 260
|
||||
|
||||
- source: sources/docx/traite/Traite-Ontodynamique_Generative-Fondements_Archicratie-Chapitre_6—Archicration-version_officielle.docx
|
||||
collection: traite
|
||||
slug: ontodynamique/chapitre-6
|
||||
title: "Traité — Chapitre 6 : Archicration"
|
||||
order: 270
|
||||
|
||||
# =========================
|
||||
# Glossaire / Lexique
|
||||
# =========================
|
||||
@@ -169,4 +131,4 @@ docs:
|
||||
collection: glossaire
|
||||
slug: mini-glossaire-verbes
|
||||
title: "Mini-glossaire des verbes de la scène archicratique"
|
||||
order: 910
|
||||
order: 910
|
||||
@@ -1,5 +1 @@
|
||||
{
|
||||
"/archicrat-ia/chapitre-3/": {
|
||||
"p-1-60c7ea48": "p-1-a21087b0"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
---
|
||||
import { SITE_RELEASE, BUILD_TIME_ISO } from "../lib/buildInfo";
|
||||
---
|
||||
<footer class="build-stamp" aria-label="Informations d’édition">
|
||||
<small>
|
||||
Édition web — release <strong>{SITE_RELEASE}</strong> · build <time datetime={BUILD_TIME_ISO}>{BUILD_TIME_ISO}</time>
|
||||
Archicratie — Web Edition · Version de lecture critique · Conception, rédaction et édition : Sylvain Noyon
|
||||
</small>
|
||||
</footer>
|
||||
</footer>
|
||||
@@ -1,48 +1,80 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
|
||||
const { currentSlug } = Astro.props;
|
||||
const {
|
||||
currentSlug,
|
||||
collection = "archicrat-ia",
|
||||
basePath = "/archicrat-ia",
|
||||
label = "Table des matières"
|
||||
} = Astro.props;
|
||||
|
||||
// ✅ Après migration : TOC = collection "archicrat-ia"
|
||||
const entries = (await getCollection("archicrat-ia"))
|
||||
.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
|
||||
const slugOf = (entry) => String(entry.id).replace(/\.(md|mdx)$/i, "");
|
||||
const hrefOf = (entry) => `${basePath}/${slugOf(entry)}/`;
|
||||
|
||||
const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
const collator = new Intl.Collator("fr", { sensitivity: "base", numeric: true });
|
||||
|
||||
const entries = [...await getCollection(collection)].sort((a, b) => {
|
||||
const ao = Number(a.data.order ?? 9999);
|
||||
const bo = Number(b.data.order ?? 9999);
|
||||
if (ao !== bo) return ao - bo;
|
||||
|
||||
const at = String(a.data.title ?? a.data.term ?? slugOf(a));
|
||||
const bt = String(b.data.title ?? b.data.term ?? slugOf(b));
|
||||
return collator.compare(at, bt);
|
||||
});
|
||||
|
||||
const tocId = `toc-global-${collection}-${String(basePath).replace(/[^\w-]+/g, "-")}`;
|
||||
---
|
||||
|
||||
<nav class="toc-global" aria-label="Table des matières — ArchiCraT-IA">
|
||||
<div class="toc-global__head">
|
||||
<div class="toc-global__title">Table des matières</div>
|
||||
</div>
|
||||
<nav
|
||||
class="toc-global"
|
||||
data-mobile-default="closed"
|
||||
aria-label={label}
|
||||
data-toc-global
|
||||
data-toc-key={`global:${collection}:${basePath}`}
|
||||
>
|
||||
<button
|
||||
class="toc-global__head toc-global__toggle"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls={tocId}
|
||||
>
|
||||
<span class="toc-global__title">{label}</span>
|
||||
<span class="toc-global__chevron" aria-hidden="true">▾</span>
|
||||
</button>
|
||||
|
||||
<ol class="toc-global__list">
|
||||
{entries.map((e) => {
|
||||
const active = e.slug === currentSlug;
|
||||
return (
|
||||
<li class={`toc-item ${active ? "is-active" : ""}`}>
|
||||
<a class="toc-link" href={href(e.slug)} aria-current={active ? "page" : undefined}>
|
||||
<span class="toc-link__row">
|
||||
{active ? (
|
||||
<span class="toc-active-indicator" aria-hidden="true">👉</span>
|
||||
) : (
|
||||
<span class="toc-active-spacer" aria-hidden="true"></span>
|
||||
)}
|
||||
<div class="toc-global__body-clip" id={tocId} hidden>
|
||||
<div class="toc-global__body">
|
||||
<ol class="toc-global__list">
|
||||
{entries.map((e) => {
|
||||
const slug = slugOf(e);
|
||||
const active = slug === currentSlug;
|
||||
|
||||
<span class="toc-link__title">{e.data.title}</span>
|
||||
return (
|
||||
<li class={`toc-item ${active ? "is-active" : ""}`}>
|
||||
<a class="toc-link" href={hrefOf(e)} aria-current={active ? "page" : undefined}>
|
||||
<span class="toc-link__row">
|
||||
<span class={`toc-active-mark ${active ? "is-on" : ""}`} aria-hidden="true">
|
||||
<span class="toc-active-mark__dot"></span>
|
||||
</span>
|
||||
|
||||
{active && (
|
||||
<span class="toc-badge" aria-label="Chapitre en cours">
|
||||
En cours
|
||||
<span class="toc-link__title">{e.data.title}</span>
|
||||
|
||||
{active && (
|
||||
<span class="toc-badge" aria-label="Chapitre en cours">
|
||||
En cours
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
||||
{active && <span class="toc-underline" aria-hidden="true"></span>}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
{active && <span class="toc-underline" aria-hidden="true"></span>}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
@@ -53,7 +85,22 @@ const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
background: rgba(127,127,127,0.06);
|
||||
}
|
||||
|
||||
.toc-global__toggle{
|
||||
width: 100%;
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toc-global__head{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px dashed rgba(127,127,127,0.25);
|
||||
@@ -66,11 +113,36 @@ const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
opacity: .88;
|
||||
}
|
||||
|
||||
.toc-global__chevron{
|
||||
font-size: 12px;
|
||||
opacity: .7;
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
|
||||
.toc-global__body-clip{
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
transition:
|
||||
grid-template-rows 220ms ease,
|
||||
opacity 160ms ease,
|
||||
margin-top 220ms ease;
|
||||
}
|
||||
|
||||
.toc-global__body{
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toc-global__list{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 44vh;
|
||||
overflow: auto;
|
||||
padding-right: 8px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
.toc-global__list li::marker{ content: ""; }
|
||||
|
||||
.toc-item{ margin: 6px 0; }
|
||||
@@ -96,13 +168,33 @@ const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toc-active-indicator{
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
.toc-active-mark{
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
border-radius: 999px;
|
||||
border: 1px solid transparent;
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
.toc-active-spacer{
|
||||
width: 14px;
|
||||
.toc-active-mark__dot{
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 999px;
|
||||
background: currentColor;
|
||||
opacity: .65;
|
||||
}
|
||||
|
||||
.toc-active-mark.is-on{
|
||||
border-color: rgba(127,127,127,0.34);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toc-active-mark.is-on .toc-active-mark__dot{
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.toc-link__title{
|
||||
@@ -140,11 +232,70 @@ const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.toc-global__list{
|
||||
max-height: 44vh;
|
||||
overflow: auto;
|
||||
padding-right: 8px;
|
||||
scrollbar-gutter: stable;
|
||||
@media (max-width: 980px){
|
||||
.toc-global{
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.toc-global__head{
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.toc-global__title{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.toc-global__body-clip{
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.toc-global.is-collapsed .toc-global__body-clip{
|
||||
grid-template-rows: 0fr;
|
||||
opacity: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.toc-global__body{
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 180ms ease;
|
||||
}
|
||||
|
||||
.toc-global.is-collapsed .toc-global__body{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toc-global.is-collapsed .toc-global__chevron{
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.toc-link{
|
||||
padding: 7px 9px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.toc-link__title{
|
||||
font-size: 12.5px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
|
||||
.toc-badge{
|
||||
font-size: 10px;
|
||||
padding: 2px 7px;
|
||||
}
|
||||
|
||||
.toc-global__list{
|
||||
max-height: min(42vh, 360px);
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.toc-global__body-clip[hidden]{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
@@ -152,12 +303,95 @@ const href = (slug) => `/archicrat-ia/${slug}/`;
|
||||
.toc-link:hover{ background: rgba(255,255,255,0.06); }
|
||||
.toc-item.is-active .toc-link{ background: rgba(255,255,255,0.06); }
|
||||
.toc-badge{ background: rgba(255,255,255,0.06); }
|
||||
.toc-active-mark.is-on{ border-color: rgba(255,255,255,0.22); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const active = document.querySelector(".toc-global .toc-item.is-active");
|
||||
if (active) active.scrollIntoView({ block: "nearest" });
|
||||
function init() {
|
||||
document.querySelectorAll("[data-toc-global]").forEach((nav) => {
|
||||
if (nav.dataset.tocReady === "1") return;
|
||||
nav.dataset.tocReady = "1";
|
||||
|
||||
const toggle = nav.querySelector(".toc-global__toggle");
|
||||
const bodyClip = nav.querySelector(".toc-global__body-clip");
|
||||
const active = nav.querySelector(".toc-item.is-active");
|
||||
const mq = window.matchMedia("(max-width: 980px)");
|
||||
const key = `archicratie:${nav.dataset.tocKey || "toc-global"}`;
|
||||
|
||||
if (!toggle || !bodyClip) return;
|
||||
|
||||
const read = () => {
|
||||
try {
|
||||
const v = localStorage.getItem(key);
|
||||
if (v === "open") return true;
|
||||
if (v === "closed") return false;
|
||||
} catch {}
|
||||
return null;
|
||||
};
|
||||
|
||||
const write = (open) => {
|
||||
try { localStorage.setItem(key, open ? "open" : "closed"); } catch {}
|
||||
};
|
||||
|
||||
const setOpen = (open, { persist = true } = {}) => {
|
||||
const isMobile = mq.matches;
|
||||
const effectiveOpen = isMobile ? open : true;
|
||||
|
||||
nav.classList.toggle("is-collapsed", isMobile && !effectiveOpen);
|
||||
toggle.setAttribute("aria-expanded", effectiveOpen ? "true" : "false");
|
||||
|
||||
if (bodyClip) {
|
||||
bodyClip.hidden = isMobile && !effectiveOpen;
|
||||
}
|
||||
|
||||
if (persist && isMobile) write(effectiveOpen);
|
||||
};
|
||||
|
||||
const initState = () => {
|
||||
if (!mq.matches) {
|
||||
setOpen(true, { persist: false });
|
||||
if (active) active.scrollIntoView({ block: "nearest" });
|
||||
return;
|
||||
}
|
||||
|
||||
const stored = read();
|
||||
const open = stored == null ? false : stored;
|
||||
setOpen(open, { persist: false });
|
||||
|
||||
if (open && active) active.scrollIntoView({ block: "nearest" });
|
||||
};
|
||||
|
||||
toggle.addEventListener("click", () => {
|
||||
const open = toggle.getAttribute("aria-expanded") !== "true";
|
||||
setOpen(open);
|
||||
if (open && active) active.scrollIntoView({ block: "nearest" });
|
||||
|
||||
if (open) {
|
||||
window.dispatchEvent(new CustomEvent("archicratie:tocGlobalOpen"));
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("archicratie:tocLocalOpen", () => {
|
||||
if (!mq.matches) return;
|
||||
setOpen(false);
|
||||
});
|
||||
|
||||
if (mq.addEventListener) {
|
||||
mq.addEventListener("change", initState);
|
||||
} else if (mq.addListener) {
|
||||
mq.addListener(initState);
|
||||
}
|
||||
|
||||
initState();
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
window.addEventListener("DOMContentLoaded", init, { once: true });
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
529
src/components/GlossaryAside.astro
Normal file
529
src/components/GlossaryAside.astro
Normal file
@@ -0,0 +1,529 @@
|
||||
---
|
||||
import {
|
||||
getGlossaryEntryAsideData,
|
||||
getGlossaryPortalLinks,
|
||||
hrefOfGlossaryEntry,
|
||||
slugOfGlossaryEntry,
|
||||
} from "../lib/glossary";
|
||||
|
||||
const {
|
||||
currentEntry,
|
||||
allEntries = [],
|
||||
} = Astro.props;
|
||||
|
||||
const currentSlug = slugOfGlossaryEntry(currentEntry);
|
||||
|
||||
const {
|
||||
displayFamily,
|
||||
displayDomain,
|
||||
displayLevel,
|
||||
showNoyau,
|
||||
showSameFamily,
|
||||
fondamentaux,
|
||||
sameFamilyTitle,
|
||||
sameFamilyEntries,
|
||||
relationSections,
|
||||
contextualTheory,
|
||||
} = getGlossaryEntryAsideData(currentEntry, allEntries);
|
||||
|
||||
const portalLinks = getGlossaryPortalLinks();
|
||||
---
|
||||
|
||||
<nav class="glossary-aside" aria-label="Navigation du glossaire">
|
||||
<div class="glossary-aside__block glossary-aside__block--intro">
|
||||
<a class="glossary-aside__back" href="/glossaire/">← Retour au glossaire</a>
|
||||
<div class="glossary-aside__title">Glossaire archicratique</div>
|
||||
|
||||
<div class="glossary-aside__pills" aria-label="Repères de lecture">
|
||||
<span class="glossary-aside__pill glossary-aside__pill--family">
|
||||
{displayFamily}
|
||||
</span>
|
||||
|
||||
{displayDomain && (
|
||||
<span class="glossary-aside__pill">{displayDomain}</span>
|
||||
)}
|
||||
|
||||
{displayLevel && (
|
||||
<span class="glossary-aside__pill">{displayLevel}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details class="glossary-aside__block glossary-aside__disclosure">
|
||||
<summary class="glossary-aside__summary">
|
||||
<span class="glossary-aside__heading">Portails</span>
|
||||
<span class="glossary-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-aside__panel">
|
||||
<ul class="glossary-aside__list">
|
||||
{portalLinks.map((item) => (
|
||||
<li><a href={item.href}>{item.label}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{showNoyau && (
|
||||
<details class="glossary-aside__block glossary-aside__disclosure">
|
||||
<summary class="glossary-aside__summary">
|
||||
<span class="glossary-aside__heading">Noyau archicratique</span>
|
||||
<span class="glossary-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-aside__panel">
|
||||
<ul class="glossary-aside__list">
|
||||
{fondamentaux.map((entry) => {
|
||||
const active = slugOfGlossaryEntry(entry) === currentSlug;
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={hrefOfGlossaryEntry(entry)}
|
||||
aria-current={active ? "page" : undefined}
|
||||
class={active ? "is-active" : undefined}
|
||||
>
|
||||
{entry.data.term}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{showSameFamily && (
|
||||
<details class="glossary-aside__block glossary-aside__disclosure">
|
||||
<summary class="glossary-aside__summary">
|
||||
<span class="glossary-aside__heading">{sameFamilyTitle}</span>
|
||||
<span class="glossary-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-aside__panel">
|
||||
<ul class="glossary-aside__list">
|
||||
{sameFamilyEntries.map((entry) => {
|
||||
const active = slugOfGlossaryEntry(entry) === currentSlug;
|
||||
return (
|
||||
<li>
|
||||
<a
|
||||
href={hrefOfGlossaryEntry(entry)}
|
||||
aria-current={active ? "page" : undefined}
|
||||
class={active ? "is-active" : undefined}
|
||||
>
|
||||
{entry.data.term}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{relationSections.length > 0 && (
|
||||
<details class="glossary-aside__block glossary-aside__disclosure">
|
||||
<summary class="glossary-aside__summary">
|
||||
<span class="glossary-aside__heading">Autour de cette fiche</span>
|
||||
<span class="glossary-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-aside__panel">
|
||||
{relationSections.map((section) => (
|
||||
<>
|
||||
<h3 class="glossary-aside__subheading">{section.title}</h3>
|
||||
<ul class="glossary-aside__list">
|
||||
{section.items.map((entry) => (
|
||||
<li><a href={hrefOfGlossaryEntry(entry)}>{entry.data.term}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{contextualTheory.length > 0 && (
|
||||
<details class="glossary-aside__block glossary-aside__disclosure">
|
||||
<summary class="glossary-aside__summary">
|
||||
<span class="glossary-aside__heading">Paysage théorique</span>
|
||||
<span class="glossary-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-aside__panel">
|
||||
<ul class="glossary-aside__list">
|
||||
{contextualTheory.map((entry) => (
|
||||
<li><a href={hrefOfGlossaryEntry(entry)}>{entry.data.term}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.glossary-aside{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-aside__block{
|
||||
border: 1px solid rgba(127,127,127,0.22);
|
||||
border-radius: 16px;
|
||||
padding: 14px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-aside__block--intro{
|
||||
padding-top: 13px;
|
||||
padding-bottom: 13px;
|
||||
}
|
||||
|
||||
.glossary-aside__back{
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-aside__title{
|
||||
font-size: 18px;
|
||||
font-weight: 850;
|
||||
letter-spacing: .1px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
|
||||
.glossary-aside__pills{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.glossary-aside__pill{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid rgba(127,127,127,0.24);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
opacity: .92;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-aside__pill--family{
|
||||
border-color: rgba(127,127,127,0.38);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure{
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glossary-aside__summary{
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.glossary-aside__summary::-webkit-details-marker{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-aside__summary:hover{
|
||||
background: rgba(127,127,127,0.035);
|
||||
}
|
||||
|
||||
.glossary-aside__heading{
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 850;
|
||||
line-height: 1.28;
|
||||
opacity: .97;
|
||||
}
|
||||
|
||||
.glossary-aside__chevron{
|
||||
flex: 0 0 auto;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
opacity: .72;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 160ms ease, opacity 160ms ease;
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure[open] .glossary-aside__chevron{
|
||||
transform: rotate(180deg);
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
.glossary-aside__panel{
|
||||
padding: 0 14px 14px;
|
||||
}
|
||||
|
||||
.glossary-aside__subheading{
|
||||
margin: 13px 0 8px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
opacity: .82;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .04em;
|
||||
}
|
||||
|
||||
.glossary-aside__list{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.glossary-aside__list li{
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.glossary-aside__list a{
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.glossary-aside__list a.is-active{
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-aside{
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-aside__block{
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.glossary-aside__block--intro{
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.glossary-aside__back{
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
.glossary-aside__title{
|
||||
font-size: 19px;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.glossary-aside__pills{
|
||||
gap: 6px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.glossary-aside__pill{
|
||||
padding: 4px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.26;
|
||||
}
|
||||
|
||||
.glossary-aside__summary{
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.glossary-aside__heading{
|
||||
font-size: 17px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.glossary-aside__panel{
|
||||
padding: 0 12px 12px;
|
||||
}
|
||||
|
||||
.glossary-aside__subheading{
|
||||
margin: 10px 0 6px;
|
||||
font-size: 11.5px;
|
||||
line-height: 1.26;
|
||||
}
|
||||
|
||||
.glossary-aside__list li{
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.glossary-aside__list a{
|
||||
font-size: 14px;
|
||||
line-height: 1.34;
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure:not([open]) .glossary-aside__panel{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-aside__disclosure{
|
||||
background: rgba(127,127,127,0.045);
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure[open] .glossary-aside__summary{
|
||||
border-bottom: 1px solid rgba(127,127,127,0.12);
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-aside{
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-aside__block{
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.glossary-aside__block--intro{
|
||||
padding: 10px 11px;
|
||||
}
|
||||
|
||||
.glossary-aside__back{
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.glossary-aside__title{
|
||||
font-size: 16px;
|
||||
line-height: 1.14;
|
||||
}
|
||||
|
||||
.glossary-aside__pills{
|
||||
gap: 5px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.glossary-aside__pill{
|
||||
padding: 3px 8px;
|
||||
font-size: 11px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.glossary-aside__summary{
|
||||
padding: 10px 11px;
|
||||
}
|
||||
|
||||
.glossary-aside__heading{
|
||||
font-size: 15px;
|
||||
line-height: 1.16;
|
||||
}
|
||||
|
||||
.glossary-aside__panel{
|
||||
padding: 0 11px 10px;
|
||||
}
|
||||
|
||||
.glossary-aside__subheading{
|
||||
margin: 8px 0 5px;
|
||||
font-size: 11px;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.glossary-aside__list li{
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.glossary-aside__list a{
|
||||
font-size: 13px;
|
||||
line-height: 1.28;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: portrait) and (max-width: 1024px) and (pointer: coarse){
|
||||
.glossary-aside{
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure{
|
||||
background: rgba(127,127,127,0.045);
|
||||
}
|
||||
|
||||
.glossary-aside__disclosure:not([open]) .glossary-aside__panel{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-aside__summary{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.glossary-aside__chevron{
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 861px) and (hover: hover) and (pointer: fine){
|
||||
.glossary-aside__summary{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.glossary-aside__chevron{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-aside__block,
|
||||
.glossary-aside__pill{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.glossary-aside__summary:hover{
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const syncMobileDisclosure = () => {
|
||||
const mobile = window.matchMedia(
|
||||
"(max-width: 860px), ((orientation: portrait) and (max-width: 1024px) and (pointer: coarse))"
|
||||
).matches;
|
||||
const smallLandscape = window.matchMedia(
|
||||
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
|
||||
).matches;
|
||||
|
||||
const compact = mobile || smallLandscape;
|
||||
|
||||
document
|
||||
.querySelectorAll(".glossary-aside__disclosure")
|
||||
.forEach((el, index) => {
|
||||
if (!(el instanceof HTMLDetailsElement)) return;
|
||||
|
||||
if (compact) {
|
||||
if (!el.dataset.mobileInit) {
|
||||
el.open = false;
|
||||
el.dataset.mobileInit = "true";
|
||||
}
|
||||
} else {
|
||||
el.open = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", syncMobileDisclosure, { once: true });
|
||||
} else {
|
||||
syncMobileDisclosure();
|
||||
}
|
||||
|
||||
window.addEventListener("resize", syncMobileDisclosure);
|
||||
window.addEventListener("pageshow", syncMobileDisclosure);
|
||||
})();
|
||||
</script>
|
||||
110
src/components/GlossaryCardGrid.astro
Normal file
110
src/components/GlossaryCardGrid.astro
Normal file
@@ -0,0 +1,110 @@
|
||||
---
|
||||
import { hrefOfGlossaryEntry, type GlossaryEntry } from "../lib/glossary";
|
||||
|
||||
export interface Props {
|
||||
entries?: GlossaryEntry[];
|
||||
wide?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
entries = [],
|
||||
wide = false,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div class="glossary-cards">
|
||||
{entries.map((entry) => (
|
||||
<a
|
||||
class:list={[
|
||||
"glossary-card",
|
||||
wide && "glossary-card--wide",
|
||||
]}
|
||||
href={hrefOfGlossaryEntry(entry)}
|
||||
>
|
||||
<strong>{entry.data.term}</strong>
|
||||
<span>{entry.data.definitionShort}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glossary-cards{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.glossary-card{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
padding: 13px 14px;
|
||||
border: 1px solid var(--glossary-border);
|
||||
border-radius: 16px;
|
||||
background: var(--glossary-bg-soft);
|
||||
text-decoration: none;
|
||||
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-card:hover{
|
||||
transform: translateY(-1px);
|
||||
background: var(--glossary-bg-soft-strong);
|
||||
border-color: rgba(0,217,255,0.16);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-card--wide{
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.glossary-card strong{
|
||||
color: var(--glossary-accent);
|
||||
font-size: 1.02rem;
|
||||
line-height: 1.24;
|
||||
}
|
||||
|
||||
.glossary-card span{
|
||||
color: inherit;
|
||||
font-size: .98rem;
|
||||
line-height: 1.46;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-cards{
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.glossary-card{
|
||||
gap: 6px;
|
||||
padding: 12px 12px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.glossary-card strong{
|
||||
font-size: .98rem;
|
||||
}
|
||||
|
||||
.glossary-card span{
|
||||
font-size: .94rem;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.glossary-card--wide{
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-card{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.glossary-card:hover{
|
||||
background: rgba(255,255,255,0.07);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
26
src/components/GlossaryEntryBody.astro
Normal file
26
src/components/GlossaryEntryBody.astro
Normal file
@@ -0,0 +1,26 @@
|
||||
<div class="glossary-entry-body">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glossary-entry-body{
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.glossary-entry-body > :last-child{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-entry-body{
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.glossary-entry-body h2),
|
||||
:global(.glossary-entry-body h3),
|
||||
:global(.glossary-relations h2),
|
||||
:global(.glossary-relations h3){
|
||||
scroll-margin-top: calc(var(--sticky-offset-px, 96px) + 18px);
|
||||
}
|
||||
</style>
|
||||
264
src/components/GlossaryEntryHero.astro
Normal file
264
src/components/GlossaryEntryHero.astro
Normal file
@@ -0,0 +1,264 @@
|
||||
---
|
||||
interface Props {
|
||||
term: string;
|
||||
definitionShort: string;
|
||||
displayFamily: string;
|
||||
displayDomain?: string;
|
||||
displayLevel?: string;
|
||||
mobilizedAuthors?: string[];
|
||||
comparisonTraditions?: string[];
|
||||
}
|
||||
|
||||
const {
|
||||
term,
|
||||
definitionShort,
|
||||
displayFamily,
|
||||
displayDomain = "",
|
||||
displayLevel = "",
|
||||
mobilizedAuthors = [],
|
||||
comparisonTraditions = [],
|
||||
} = Astro.props;
|
||||
|
||||
const hasScholarlyMeta =
|
||||
mobilizedAuthors.length > 0 ||
|
||||
comparisonTraditions.length > 0;
|
||||
---
|
||||
|
||||
<header class="glossary-entry-head" data-ge-hero>
|
||||
<div class="glossary-entry-head__title">
|
||||
<h1>{term}</h1>
|
||||
</div>
|
||||
|
||||
<div class="glossary-entry-summary">
|
||||
<p class="glossary-entry-dek">
|
||||
<em>{definitionShort}</em>
|
||||
</p>
|
||||
|
||||
<div class="glossary-entry-signals" aria-label="Repères de lecture">
|
||||
<span class="glossary-pill glossary-pill--family">
|
||||
<strong>Famille :</strong> {displayFamily}
|
||||
</span>
|
||||
|
||||
{displayDomain && (
|
||||
<span class="glossary-pill">
|
||||
<strong>Domaine :</strong> {displayDomain}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{displayLevel && (
|
||||
<span class="glossary-pill">
|
||||
<strong>Niveau :</strong> {displayLevel}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasScholarlyMeta && (
|
||||
<div class="glossary-entry-meta">
|
||||
{mobilizedAuthors.length > 0 && (
|
||||
<p>
|
||||
<strong>Auteurs mobilisés :</strong> {mobilizedAuthors.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{comparisonTraditions.length > 0 && (
|
||||
<p>
|
||||
<strong>Traditions de comparaison :</strong> {comparisonTraditions.join(" / ")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.glossary-entry-head{
|
||||
position: sticky;
|
||||
top: var(--sticky-header-h, 0px);
|
||||
z-index: 11;
|
||||
margin: 0 0 22px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 24px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
|
||||
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
overflow: hidden;
|
||||
transition:
|
||||
border-radius 180ms ease,
|
||||
box-shadow 180ms ease,
|
||||
border-color 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-head__title{
|
||||
padding:
|
||||
var(--entry-hero-pad-top, 18px)
|
||||
var(--entry-hero-pad-x, 18px)
|
||||
calc(var(--entry-hero-pad-top, 18px) - 2px);
|
||||
transition: padding 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-head h1{
|
||||
margin: 0;
|
||||
font-size: var(--entry-hero-h1-size, clamp(2.2rem, 4vw, 3.15rem));
|
||||
line-height: 1.02;
|
||||
letter-spacing: -.04em;
|
||||
font-weight: 850;
|
||||
transition: font-size 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-summary{
|
||||
display: grid;
|
||||
gap: var(--entry-hero-gap, 14px);
|
||||
padding:
|
||||
calc(var(--entry-hero-pad-bottom, 18px) - 2px)
|
||||
var(--entry-hero-pad-x, 18px)
|
||||
var(--entry-hero-pad-bottom, 18px);
|
||||
border-top: 1px solid rgba(127,127,127,0.14);
|
||||
background: rgba(255,255,255,0.02);
|
||||
transition: gap 180ms ease, padding 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-dek{
|
||||
margin: 0;
|
||||
max-width: var(--entry-hero-dek-maxw, 76ch);
|
||||
font-size: var(--entry-hero-dek-size, 1.04rem);
|
||||
line-height: var(--entry-hero-dek-lh, 1.55);
|
||||
opacity: .94;
|
||||
transition:
|
||||
max-width 180ms ease,
|
||||
font-size 180ms ease,
|
||||
line-height 180ms ease;
|
||||
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 4;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glossary-entry-signals{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
margin: 0;
|
||||
transition: gap 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-pill{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 9px;
|
||||
border: 1px solid rgba(127,127,127,0.24);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
font-size: 12.5px;
|
||||
line-height: 1.28;
|
||||
transition:
|
||||
padding 180ms ease,
|
||||
font-size 180ms ease,
|
||||
background 120ms ease,
|
||||
border-color 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-pill--family{
|
||||
border-color: rgba(127,127,127,0.36);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.glossary-entry-meta{
|
||||
margin: 0;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 12px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
max-height: var(--entry-hero-meta-max-h, 12rem);
|
||||
opacity: var(--entry-hero-meta-opacity, 1);
|
||||
overflow: hidden;
|
||||
transition:
|
||||
max-height 180ms ease,
|
||||
opacity 140ms ease,
|
||||
padding 180ms ease,
|
||||
border-color 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-entry-meta p{
|
||||
margin: 0;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.glossary-entry-meta p + p{
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-entry-head{
|
||||
position: static;
|
||||
border-radius: 18px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.glossary-entry-head__title{
|
||||
padding: 12px 12px 10px;
|
||||
}
|
||||
|
||||
.glossary-entry-summary{
|
||||
gap: 9px;
|
||||
padding: 10px 12px 12px;
|
||||
}
|
||||
|
||||
.glossary-entry-dek{
|
||||
max-width: none;
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
|
||||
.glossary-entry-signals{
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.glossary-pill{
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px){
|
||||
.glossary-entry-head{
|
||||
border-radius: 16px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.glossary-entry-head__title{
|
||||
padding: 10px 10px 9px;
|
||||
}
|
||||
|
||||
.glossary-entry-summary{
|
||||
gap: 9px;
|
||||
padding: 9px 10px 11px;
|
||||
}
|
||||
|
||||
.glossary-entry-dek{
|
||||
display: block;
|
||||
max-width: none;
|
||||
overflow: visible;
|
||||
-webkit-line-clamp: unset;
|
||||
-webkit-box-orient: unset;
|
||||
}
|
||||
|
||||
.glossary-pill{
|
||||
font-size: 11.5px;
|
||||
padding: 3px 7px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-entry-meta{
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
|
||||
.glossary-pill{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
31
src/components/GlossaryEntryLegacyNote.astro
Normal file
31
src/components/GlossaryEntryLegacyNote.astro
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
interface Props {
|
||||
canonicalHref: string;
|
||||
term: string;
|
||||
}
|
||||
|
||||
const { canonicalHref, term } = Astro.props;
|
||||
---
|
||||
|
||||
<p class="glossary-legacy-note">
|
||||
Cette entrée a été renommée. L’intitulé canonique est :
|
||||
<a href={canonicalHref}>{term}</a>.
|
||||
</p>
|
||||
|
||||
<style>
|
||||
.glossary-legacy-note{
|
||||
padding: 10px 12px;
|
||||
border: 1px solid rgba(127,127,127,0.22);
|
||||
border-radius: 12px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-legacy-note{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
291
src/components/GlossaryEntryStickySync.astro
Normal file
291
src/components/GlossaryEntryStickySync.astro
Normal file
@@ -0,0 +1,291 @@
|
||||
<script is:inline>
|
||||
(() => {
|
||||
const boot = () => {
|
||||
const body = document.body;
|
||||
const root = document.documentElement;
|
||||
const hero = document.querySelector("[data-ge-hero]");
|
||||
const follow = document.getElementById("reading-follow");
|
||||
const mqMobile = window.matchMedia("(max-width: 860px)");
|
||||
const mqSmallLandscape = window.matchMedia(
|
||||
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
|
||||
);
|
||||
|
||||
if (!body || !root || !hero || !follow) return;
|
||||
|
||||
const BODY_CLASS = "is-glossary-entry-page";
|
||||
const FOLLOW_ON_CLASS = "glossary-entry-follow-on";
|
||||
|
||||
let lastHeight = -1;
|
||||
let lastFollowOn = null;
|
||||
let raf = 0;
|
||||
|
||||
body.classList.add(BODY_CLASS);
|
||||
|
||||
const isCompactViewport = () =>
|
||||
mqMobile.matches || mqSmallLandscape.matches;
|
||||
|
||||
const heroHeight = () => {
|
||||
const rect = hero.getBoundingClientRect();
|
||||
return Math.max(0, Math.round(rect.height || 0));
|
||||
};
|
||||
|
||||
const neutralizeGlobalFollowIfCompact = () => {
|
||||
if (!isCompactViewport()) {
|
||||
follow.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
follow.classList.remove("is-on");
|
||||
follow.setAttribute("aria-hidden", "true");
|
||||
follow.style.display = "none";
|
||||
root.style.setProperty("--followbar-h", "0px");
|
||||
};
|
||||
|
||||
const computeFollowOn = () =>
|
||||
!isCompactViewport() &&
|
||||
follow.classList.contains("is-on") &&
|
||||
follow.style.display !== "none" &&
|
||||
follow.getAttribute("aria-hidden") !== "true";
|
||||
|
||||
const syncFollowState = () => {
|
||||
const on = computeFollowOn();
|
||||
|
||||
if (on) {
|
||||
if (lastFollowOn === true) return;
|
||||
lastFollowOn = true;
|
||||
body.classList.add(FOLLOW_ON_CLASS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastFollowOn === false) return;
|
||||
lastFollowOn = false;
|
||||
body.classList.remove(FOLLOW_ON_CLASS);
|
||||
};
|
||||
|
||||
const stripLocalSticky = () => {
|
||||
document
|
||||
.querySelectorAll(
|
||||
".glossary-entry-body h2, .glossary-entry-body h3, .glossary-relations h2, .glossary-relations h3"
|
||||
)
|
||||
.forEach((el) => {
|
||||
el.classList.remove("is-sticky");
|
||||
el.removeAttribute("data-sticky-active");
|
||||
});
|
||||
};
|
||||
|
||||
const applyLocalStickyHeight = () => {
|
||||
const h = isCompactViewport() ? 0 : heroHeight();
|
||||
if (h === lastHeight) return;
|
||||
lastHeight = h;
|
||||
|
||||
if (typeof window.__archiSetLocalStickyHeight === "function") {
|
||||
window.__archiSetLocalStickyHeight(h);
|
||||
} else {
|
||||
root.style.setProperty("--glossary-local-sticky-h", `${h}px`);
|
||||
}
|
||||
};
|
||||
|
||||
const syncAll = () => {
|
||||
neutralizeGlobalFollowIfCompact();
|
||||
stripLocalSticky();
|
||||
syncFollowState();
|
||||
applyLocalStickyHeight();
|
||||
};
|
||||
|
||||
const schedule = () => {
|
||||
if (raf) return;
|
||||
raf = requestAnimationFrame(() => {
|
||||
raf = 0;
|
||||
syncAll();
|
||||
});
|
||||
};
|
||||
|
||||
const followObserver = new MutationObserver(schedule);
|
||||
followObserver.observe(follow, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class", "style", "aria-hidden"],
|
||||
subtree: false,
|
||||
});
|
||||
|
||||
const heroResizeObserver =
|
||||
typeof ResizeObserver !== "undefined"
|
||||
? new ResizeObserver(schedule)
|
||||
: null;
|
||||
|
||||
heroResizeObserver?.observe(hero);
|
||||
|
||||
window.addEventListener("resize", schedule);
|
||||
window.addEventListener("pageshow", schedule);
|
||||
|
||||
if (document.fonts?.ready) {
|
||||
document.fonts.ready.then(schedule).catch(() => {});
|
||||
}
|
||||
|
||||
if (mqMobile.addEventListener) {
|
||||
mqMobile.addEventListener("change", schedule);
|
||||
} else if (mqMobile.addListener) {
|
||||
mqMobile.addListener(schedule);
|
||||
}
|
||||
|
||||
if (mqSmallLandscape.addEventListener) {
|
||||
mqSmallLandscape.addEventListener("change", schedule);
|
||||
} else if (mqSmallLandscape.addListener) {
|
||||
mqSmallLandscape.addListener(schedule);
|
||||
}
|
||||
|
||||
schedule();
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot, { once: true });
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global(body.is-glossary-entry-page #reading-follow){
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head h1){
|
||||
letter-spacing: -.03em;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
|
||||
gap: 8px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-top-color: rgba(127,127,127,0.10);
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-dek){
|
||||
display: block;
|
||||
-webkit-line-clamp: unset;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-signals){
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-pill){
|
||||
gap: 4px;
|
||||
padding: 3px 7px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-meta){
|
||||
padding: 0;
|
||||
border-color: transparent;
|
||||
max-height: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow){
|
||||
transform: none;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on #reading-follow .reading-follow__inner){
|
||||
margin-top: 0;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h2.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h2[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h3.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-entry-body h3[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h2.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h2[data-sticky-active="true"]),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h3.is-sticky),
|
||||
:global(body.is-glossary-entry-page .glossary-relations h3[data-sticky-active="true"]){
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
backdrop-filter: none !important;
|
||||
-webkit-backdrop-filter: none !important;
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
:global(body.is-glossary-entry-page #reading-follow),
|
||||
:global(body.is-glossary-entry-page #reading-follow .reading-follow__inner){
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page){
|
||||
--followbar-h: 0px !important;
|
||||
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
|
||||
margin-bottom: 18px;
|
||||
border-radius: 20px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
|
||||
gap: 6px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-dek){
|
||||
display: block;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-signals){
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-pill){
|
||||
padding: 3px 6px;
|
||||
font-size: 10.5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
:global(body.is-glossary-entry-page #reading-follow),
|
||||
:global(body.is-glossary-entry-page #reading-follow .reading-follow__inner){
|
||||
display: none !important;
|
||||
opacity: 0 !important;
|
||||
pointer-events: none !important;
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page){
|
||||
--followbar-h: 0px !important;
|
||||
--sticky-offset-px: calc(var(--sticky-header-h, 0px) + var(--page-gap, 12px)) !important;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-head){
|
||||
margin-bottom: 14px;
|
||||
border-radius: 16px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-summary){
|
||||
gap: 5px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-entry-dek){
|
||||
display: none;
|
||||
}
|
||||
|
||||
:global(body.is-glossary-entry-page.glossary-entry-follow-on .glossary-pill){
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
384
src/components/GlossaryHomeAside.astro
Normal file
384
src/components/GlossaryHomeAside.astro
Normal file
@@ -0,0 +1,384 @@
|
||||
---
|
||||
import {
|
||||
getFondamentaux,
|
||||
getGlossaryHomeStats,
|
||||
getGlossaryPortalLinks,
|
||||
hrefOfGlossaryEntry,
|
||||
} from "../lib/glossary";
|
||||
|
||||
const {
|
||||
allEntries = [],
|
||||
} = Astro.props;
|
||||
|
||||
const fondamentaux = getFondamentaux(allEntries);
|
||||
const portalLinks = getGlossaryPortalLinks();
|
||||
|
||||
const {
|
||||
totalEntries,
|
||||
paradigmesCount,
|
||||
doctrinesCount,
|
||||
metaRegimesCount,
|
||||
} = getGlossaryHomeStats(allEntries);
|
||||
---
|
||||
|
||||
<nav class="glossary-home-aside" aria-label="Navigation du portail du glossaire">
|
||||
<div class="glossary-home-aside__block glossary-home-aside__block--intro">
|
||||
<div class="glossary-home-aside__title">Glossaire archicratique</div>
|
||||
<div class="glossary-home-aside__meta">
|
||||
portail de lecture · cartographie conceptuelle
|
||||
</div>
|
||||
|
||||
<div class="glossary-home-aside__pills" aria-label="Repères de navigation">
|
||||
<span class="glossary-home-aside__pill">{totalEntries} entrées</span>
|
||||
<span class="glossary-home-aside__pill">{metaRegimesCount} méta-régimes</span>
|
||||
<span class="glossary-home-aside__pill">
|
||||
{doctrinesCount} doctrine{doctrinesCount > 1 ? "s" : ""} · {paradigmesCount} paradigme{paradigmesCount > 1 ? "s" : ""}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<details class="glossary-home-aside__block glossary-home-aside__disclosure" open>
|
||||
<summary class="glossary-home-aside__summary">
|
||||
<span class="glossary-home-aside__heading">Parcours du glossaire</span>
|
||||
<span class="glossary-home-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-home-aside__panel">
|
||||
<ul class="glossary-home-aside__list">
|
||||
{portalLinks.map((item) => (
|
||||
<li><a href={item.href}>{item.label}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{fondamentaux.length > 0 && (
|
||||
<details class="glossary-home-aside__block glossary-home-aside__disclosure" open>
|
||||
<summary class="glossary-home-aside__summary">
|
||||
<span class="glossary-home-aside__heading">Noyau archicratique</span>
|
||||
<span class="glossary-home-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-home-aside__panel">
|
||||
<ul class="glossary-home-aside__list">
|
||||
{fondamentaux.map((entry) => (
|
||||
<li><a href={hrefOfGlossaryEntry(entry)}>{entry.data.term}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.glossary-home-aside{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block{
|
||||
border: 1px solid rgba(127,127,127,0.22);
|
||||
border-radius: 16px;
|
||||
padding: 14px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block--intro{
|
||||
padding-top: 13px;
|
||||
padding-bottom: 13px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__title{
|
||||
font-size: 18px;
|
||||
font-weight: 850;
|
||||
letter-spacing: .1px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
|
||||
.glossary-home-aside__meta{
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pills{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 7px;
|
||||
margin-top: 11px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pill{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid rgba(127,127,127,0.24);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
opacity: .92;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__disclosure{
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary{
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary::-webkit-details-marker{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary:hover{
|
||||
background: rgba(127,127,127,0.035);
|
||||
}
|
||||
|
||||
.glossary-home-aside__heading{
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 850;
|
||||
line-height: 1.28;
|
||||
opacity: .97;
|
||||
}
|
||||
|
||||
.glossary-home-aside__chevron{
|
||||
flex: 0 0 auto;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
opacity: .72;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 160ms ease, opacity 160ms ease;
|
||||
}
|
||||
|
||||
.glossary-home-aside__disclosure[open] .glossary-home-aside__chevron{
|
||||
transform: rotate(180deg);
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
.glossary-home-aside__panel{
|
||||
padding: 0 14px 14px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list li{
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list a{
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
line-height: 1.42;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-home-aside{
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block{
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block--intro{
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__title{
|
||||
font-size: 15px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.glossary-home-aside__meta{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pills{
|
||||
gap: 5px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pill{
|
||||
padding: 3px 8px;
|
||||
font-size: 11px;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary{
|
||||
padding: 9px 11px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__heading{
|
||||
font-size: 14px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.glossary-home-aside__panel{
|
||||
padding: 0 11px 10px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list li{
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list a{
|
||||
font-size: 13px;
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
.glossary-home-aside__disclosure:not([open]) .glossary-home-aside__panel{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-home-aside__disclosure{
|
||||
background: rgba(127,127,127,0.045);
|
||||
}
|
||||
|
||||
.glossary-home-aside__disclosure[open] .glossary-home-aside__summary{
|
||||
border-bottom: 1px solid rgba(127,127,127,0.12);
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-home-aside{
|
||||
gap: 7px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block{
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__block--intro{
|
||||
padding: 9px 10px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__title{
|
||||
font-size: 14px;
|
||||
line-height: 1.12;
|
||||
}
|
||||
|
||||
.glossary-home-aside__meta{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pills{
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__pill{
|
||||
padding: 2px 7px;
|
||||
font-size: 10.5px;
|
||||
line-height: 1.16;
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary{
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__heading{
|
||||
font-size: 13px;
|
||||
line-height: 1.12;
|
||||
}
|
||||
|
||||
.glossary-home-aside__panel{
|
||||
padding: 0 10px 9px;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list li{
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.glossary-home-aside__list a{
|
||||
font-size: 12px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 981px){
|
||||
.glossary-home-aside__summary{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.glossary-home-aside__chevron{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-home-aside__block,
|
||||
.glossary-home-aside__pill{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.glossary-home-aside__summary:hover{
|
||||
background: rgba(255,255,255,0.03);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
let wasCompact = null;
|
||||
|
||||
const syncMobileDisclosure = () => {
|
||||
const stackedLayout = window.matchMedia("(max-width: 980px)").matches;
|
||||
const smallLandscape = window.matchMedia(
|
||||
"(orientation: landscape) and (max-width: 920px) and (max-height: 520px)"
|
||||
).matches;
|
||||
|
||||
const compact = stackedLayout || smallLandscape;
|
||||
const enteringCompact = compact && wasCompact !== true;
|
||||
|
||||
document
|
||||
.querySelectorAll(".glossary-home-aside__disclosure")
|
||||
.forEach((el) => {
|
||||
if (!(el instanceof HTMLDetailsElement)) return;
|
||||
|
||||
if (compact) {
|
||||
if (enteringCompact) {
|
||||
el.open = false;
|
||||
}
|
||||
} else {
|
||||
el.open = true;
|
||||
}
|
||||
});
|
||||
|
||||
wasCompact = compact;
|
||||
};
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", syncMobileDisclosure, { once: true });
|
||||
} else {
|
||||
syncMobileDisclosure();
|
||||
}
|
||||
|
||||
window.addEventListener("resize", syncMobileDisclosure);
|
||||
window.addEventListener("pageshow", syncMobileDisclosure);
|
||||
})();
|
||||
</script>
|
||||
748
src/components/GlossaryHomeHero.astro
Normal file
748
src/components/GlossaryHomeHero.astro
Normal file
@@ -0,0 +1,748 @@
|
||||
---
|
||||
export interface Props {
|
||||
kicker?: string;
|
||||
title?: string;
|
||||
intro?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
kicker = "Référentiel terminologique",
|
||||
title = "Glossaire archicratique",
|
||||
intro = "Ce glossaire n’est pas seulement un index de définitions. Il constitue une porte d’entrée dans la pensée archicratique : une cartographie raisonnée des concepts fondamentaux, des scènes, des dynamiques et des méta-régimes à partir desquels une société peut être décrite comme organisation de tensions et recherche de co-viabilité.",
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<header class="glossary-hero" id="glossary-hero">
|
||||
<p class="glossary-kicker">{kicker}</p>
|
||||
<h1>{title}</h1>
|
||||
|
||||
<div class="glossary-hero__collapsible">
|
||||
<p
|
||||
class="glossary-intro"
|
||||
id="glossary-hero-intro"
|
||||
aria-hidden="false"
|
||||
>
|
||||
{intro}
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="glossary-hero__toggle"
|
||||
id="glossary-hero-toggle"
|
||||
type="button"
|
||||
aria-controls="glossary-hero-intro"
|
||||
aria-expanded="false"
|
||||
hidden
|
||||
>
|
||||
lire la suite
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
class="glossary-hero-follow"
|
||||
id="glossary-hero-follow"
|
||||
aria-hidden="true"
|
||||
></h2>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
.glossary-hero{
|
||||
position: sticky;
|
||||
top: var(--glossary-sticky-top);
|
||||
z-index: 12;
|
||||
margin-bottom: 28px;
|
||||
padding: 14px 16px 18px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 28px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.90)),
|
||||
radial-gradient(900px 240px at 20% 0%, rgba(0,217,255,0.08), transparent 60%);
|
||||
transition:
|
||||
padding 220ms cubic-bezier(.22,.8,.22,1),
|
||||
border-radius 220ms cubic-bezier(.22,.8,.22,1),
|
||||
background 300ms cubic-bezier(.22,.8,.22,1),
|
||||
border-color 300ms cubic-bezier(.22,.8,.22,1),
|
||||
box-shadow 300ms cubic-bezier(.22,.8,.22,1);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
display: grid;
|
||||
row-gap: 12px;
|
||||
min-width: 0;
|
||||
overflow: clip;
|
||||
}
|
||||
|
||||
.glossary-kicker{
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
opacity: .72;
|
||||
}
|
||||
|
||||
.glossary-hero h1{
|
||||
margin: 0;
|
||||
font-size: clamp(2.2rem, 4vw, 3.15rem);
|
||||
line-height: 1.02;
|
||||
letter-spacing: -.04em;
|
||||
font-weight: 850;
|
||||
transition:
|
||||
font-size 220ms cubic-bezier(.22,.8,.22,1),
|
||||
line-height 220ms cubic-bezier(.22,.8,.22,1);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-hero__collapsible{
|
||||
display: grid;
|
||||
row-gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
margin: 0;
|
||||
max-width: 72ch;
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.55;
|
||||
opacity: .94;
|
||||
min-width: 0;
|
||||
transition:
|
||||
font-size 220ms cubic-bezier(.22,.8,.22,1),
|
||||
line-height 220ms cubic-bezier(.22,.8,.22,1),
|
||||
max-height 220ms cubic-bezier(.22,.8,.22,1),
|
||||
opacity 180ms ease;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"] .glossary-hero p#glossary-hero-intro){
|
||||
padding-right: 0;
|
||||
scroll-margin-top: 0;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
min-height: 30px;
|
||||
padding: 3px 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
letter-spacing: .01em;
|
||||
opacity: .72;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 2px;
|
||||
transition:
|
||||
opacity 120ms ease,
|
||||
transform 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle:hover{
|
||||
opacity: .92;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.glossary-hero__toggle:focus-visible{
|
||||
outline: 2px solid rgba(0,217,255,0.24);
|
||||
outline-offset: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle[hidden]{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
margin: 2px 0 0;
|
||||
min-height: var(--glossary-follow-height);
|
||||
display: block;
|
||||
max-width: min(100%, 22ch);
|
||||
opacity: 0;
|
||||
transform: translateY(10px) scale(.985);
|
||||
filter: blur(6px);
|
||||
transition:
|
||||
opacity 220ms cubic-bezier(.22,1,.36,1),
|
||||
transform 320ms cubic-bezier(.22,1,.36,1),
|
||||
filter 320ms cubic-bezier(.22,1,.36,1);
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
will-change: opacity, transform, filter;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-hero-follow.is-visible{
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero{
|
||||
padding: 12px 14px 14px;
|
||||
border-bottom-left-radius: 18px;
|
||||
border-bottom-right-radius: 18px;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.7rem, 3.2vw, 2.2rem);
|
||||
line-height: 1.02;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
font-size: .94rem;
|
||||
line-height: 1.34;
|
||||
max-height: 2.7em;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero__toggle{
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-hero{
|
||||
top: calc(var(--glossary-sticky-top) - 2px);
|
||||
padding: 12px 14px 16px;
|
||||
border-radius: 22px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-hero h1{
|
||||
font-size: clamp(1.9rem, 8vw, 2.45rem);
|
||||
line-height: 1.02;
|
||||
letter-spacing: -.03em;
|
||||
}
|
||||
|
||||
.glossary-hero__collapsible{
|
||||
row-gap: 7px;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: .98rem;
|
||||
line-height: 1.44;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero{
|
||||
padding: 10px 13px 12px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.45rem, 6vw, 1.8rem);
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: .86rem;
|
||||
line-height: 1.24;
|
||||
max-height: 2.48em;
|
||||
-webkit-line-clamp: 2;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle{
|
||||
min-height: 28px;
|
||||
font-size: 11.5px;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
max-width: min(100%, 24ch);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px){
|
||||
.glossary-hero{
|
||||
padding: 11px 12px 14px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: .94rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero{
|
||||
padding: 9px 11px 11px;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
font-size: .84rem;
|
||||
line-height: 1.22;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-hero{
|
||||
padding: 10px 12px 12px;
|
||||
border-radius: 16px;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-kicker{
|
||||
font-size: 10px;
|
||||
letter-spacing: .1em;
|
||||
}
|
||||
|
||||
.glossary-hero h1{
|
||||
font-size: clamp(1.35rem, 4vw, 1.8rem);
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
font-size: .84rem;
|
||||
line-height: 1.24;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero{
|
||||
padding: 9px 11px 10px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.1rem, 3vw, 1.35rem);
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
font-size: .8rem;
|
||||
line-height: 1.18;
|
||||
max-height: 2.36em;
|
||||
-webkit-line-clamp: 2;
|
||||
opacity: .88;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle{
|
||||
min-height: 24px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
max-width: min(100%, 26ch);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
margin-bottom: 18px !important;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
display: none !important;
|
||||
min-height: 0 !important;
|
||||
opacity: 0 !important;
|
||||
transform: none !important;
|
||||
filter: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
margin-bottom: 14px !important;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
display: none !important;
|
||||
min-height: 0 !important;
|
||||
opacity: 0 !important;
|
||||
transform: none !important;
|
||||
filter: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Neutralisation mobile/tablette : le hero n'est plus sticky, donc aucun état condensé. */
|
||||
@media (max-width: 860px){
|
||||
.glossary-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
margin-bottom: 18px !important;
|
||||
padding: 12px 14px 16px !important;
|
||||
border-radius: 22px !important;
|
||||
row-gap: 8px !important;
|
||||
}
|
||||
|
||||
.glossary-hero h1,
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(2rem, 6.2vw, 2.75rem) !important;
|
||||
line-height: 1.04 !important;
|
||||
letter-spacing: -.035em !important;
|
||||
max-width: 100%;
|
||||
overflow-wrap: normal;
|
||||
word-break: normal;
|
||||
hyphens: none;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
.glossary-intro,
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
max-height: none !important;
|
||||
overflow: visible !important;
|
||||
display: block !important;
|
||||
-webkit-line-clamp: unset !important;
|
||||
-webkit-box-orient: unset !important;
|
||||
font-size: .94rem !important;
|
||||
line-height: 1.4 !important;
|
||||
opacity: .94 !important;
|
||||
padding-right: 0 !important;
|
||||
scroll-margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.glossary-hero__toggle,
|
||||
.glossary-hero-follow{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile paysage compact : même logique, mais plus dense. */
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-hero{
|
||||
padding: 8px 10px 9px !important;
|
||||
border-radius: 14px !important;
|
||||
row-gap: 5px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
.glossary-kicker{
|
||||
font-size: 9px !important;
|
||||
letter-spacing: .11em !important;
|
||||
}
|
||||
|
||||
.glossary-hero h1,
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.55rem, 4.2vw, 1.9rem) !important;
|
||||
line-height: 1.03 !important;
|
||||
letter-spacing: -.025em !important;
|
||||
}
|
||||
|
||||
.glossary-intro,
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-intro{
|
||||
font-size: .72rem !important;
|
||||
line-height: 1.18 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablette large / iPad landscape : le follow reste lisible, jamais tronqué brutalement. */
|
||||
@media (min-width: 861px) and (max-width: 1240px){
|
||||
.glossary-hero h1{
|
||||
font-size: clamp(2.35rem, 4.2vw, 3.05rem) !important;
|
||||
line-height: 1.03 !important;
|
||||
}
|
||||
|
||||
.glossary-hero-follow{
|
||||
max-width: 100% !important;
|
||||
white-space: normal !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
font-size: clamp(1.55rem, 3.1vw, 2.05rem) !important;
|
||||
line-height: 1.08 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
Glossaire home — états du hero sticky
|
||||
========================================================= */
|
||||
|
||||
/*
|
||||
Principe :
|
||||
- le follow peut respirer sans ellipsis brutal ;
|
||||
- l’intro reste strictement clampée en mode collapsed ;
|
||||
- l’intro ne redevient complète qu’en mode expanded ;
|
||||
- mobile/tablette <= 860px reste neutralisé plus haut.
|
||||
*/
|
||||
|
||||
.glossary-hero-follow{
|
||||
height: auto;
|
||||
max-height: none;
|
||||
max-width: min(100%, 34ch);
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
text-overflow: clip;
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero{
|
||||
min-height: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on) .glossary-hero h1{
|
||||
white-space: normal;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
/*
|
||||
État collapsed :
|
||||
l’intro DOIT rester compactée. Cette règle doit gagner contre
|
||||
les anciennes règles anti-troncature du follow.
|
||||
*/
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
max-height: calc(2 * 1.34em);
|
||||
overflow: hidden;
|
||||
white-space: normal;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero__toggle{
|
||||
display: inline-flex;
|
||||
margin-top: 2px;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
/*
|
||||
État expanded :
|
||||
l’utilisateur a explicitement demandé à lire la suite,
|
||||
donc l’intro redevient complète.
|
||||
*/
|
||||
:global(body.glossary-home-hero-expanded) .glossary-hero p#glossary-hero-intro{
|
||||
display: block;
|
||||
-webkit-line-clamp: unset;
|
||||
line-clamp: unset;
|
||||
-webkit-box-orient: unset;
|
||||
max-height: none;
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
:global(body.glossary-home-hero-expanded) .glossary-hero__toggle{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 861px) and (max-width: 1240px){
|
||||
.glossary-hero-follow{
|
||||
max-width: min(100%, 36ch);
|
||||
font-size: clamp(1.55rem, 3.1vw, 2.05rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
}
|
||||
/* =========================================================
|
||||
Glossaire home — sticky compact mobile/tablette avec H2 local
|
||||
========================================================= */
|
||||
|
||||
@media (max-width: 980px){
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero{
|
||||
position: sticky !important;
|
||||
top: calc(var(--sticky-header-h, 0px) + 8px) !important;
|
||||
z-index: 8 !important;
|
||||
transform: none !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
|
||||
padding: 8px 10px 9px !important;
|
||||
row-gap: 4px !important;
|
||||
border-radius: 16px !important;
|
||||
margin-bottom: 10px !important;
|
||||
box-shadow: 0 12px 30px rgba(0,0,0,.22) !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-kicker{
|
||||
font-size: 9px !important;
|
||||
line-height: 1.05 !important;
|
||||
letter-spacing: .11em !important;
|
||||
opacity: .72 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.35rem, 5.1vw, 1.72rem) !important;
|
||||
line-height: 1.02 !important;
|
||||
letter-spacing: -.03em !important;
|
||||
white-space: normal !important;
|
||||
overflow: visible !important;
|
||||
text-overflow: clip !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
|
||||
display: -webkit-box !important;
|
||||
-webkit-box-orient: vertical !important;
|
||||
-webkit-line-clamp: 2 !important;
|
||||
line-clamp: 2 !important;
|
||||
max-height: calc(2 * 1.12em) !important;
|
||||
overflow: hidden !important;
|
||||
white-space: normal !important;
|
||||
text-overflow: clip !important;
|
||||
font-size: .72rem !important;
|
||||
line-height: 1.12 !important;
|
||||
opacity: .78 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero__toggle{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero-follow{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
min-height: 0 !important;
|
||||
margin-top: 4px !important;
|
||||
padding-top: 6px !important;
|
||||
border-top: 1px solid rgba(127,127,127,.18) !important;
|
||||
opacity: .98 !important;
|
||||
transform: none !important;
|
||||
filter: none !important;
|
||||
white-space: normal !important;
|
||||
overflow: hidden !important;
|
||||
text-overflow: clip !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 761px) and (max-width: 980px){
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
|
||||
padding: 10px 14px 11px !important;
|
||||
row-gap: 5px !important;
|
||||
border-radius: 18px !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.65rem, 3.4vw, 2.1rem) !important;
|
||||
line-height: 1.02 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
|
||||
font-size: .82rem !important;
|
||||
line-height: 1.18 !important;
|
||||
max-height: calc(2 * 1.18em) !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
|
||||
margin-top: 5px !important;
|
||||
padding-top: 7px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
|
||||
top: calc(var(--sticky-header-h, 0px) + 6px) !important;
|
||||
padding: 6px 9px 7px !important;
|
||||
row-gap: 3px !important;
|
||||
border-radius: 13px !important;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-kicker{
|
||||
font-size: 8px !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero h1{
|
||||
font-size: clamp(1.08rem, 3.2vw, 1.34rem) !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on:not(.glossary-home-hero-expanded)) .glossary-hero p#glossary-hero-intro{
|
||||
-webkit-line-clamp: 1 !important;
|
||||
line-clamp: 1 !important;
|
||||
max-height: 1.08em !important;
|
||||
font-size: .64rem !important;
|
||||
line-height: 1.08 !important;
|
||||
opacity: .72 !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
|
||||
margin-top: 3px !important;
|
||||
padding-top: 4px !important;
|
||||
font-size: .86rem !important;
|
||||
line-height: 1.04 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
Glossaire home — polish premium fluidité sticky
|
||||
========================================================= */
|
||||
|
||||
@media (max-width: 980px){
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero{
|
||||
transition:
|
||||
padding 180ms ease,
|
||||
border-radius 180ms ease,
|
||||
box-shadow 180ms ease,
|
||||
background 180ms ease,
|
||||
margin-bottom 180ms ease;
|
||||
will-change: padding, border-radius, box-shadow;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0) !important;
|
||||
background: rgba(0,0,0,.92) !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero h1,
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-kicker,
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero p#glossary-hero-intro{
|
||||
transition:
|
||||
font-size 180ms ease,
|
||||
line-height 180ms ease,
|
||||
opacity 180ms ease,
|
||||
max-height 180ms ease;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"]) .glossary-hero-follow{
|
||||
display: block !important;
|
||||
max-height: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
padding-top: 0 !important;
|
||||
border-top: 0 !important;
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
overflow: hidden !important;
|
||||
transform: translateY(-4px) !important;
|
||||
transition:
|
||||
max-height 180ms ease,
|
||||
opacity 180ms ease,
|
||||
transform 180ms ease,
|
||||
padding-top 180ms ease,
|
||||
margin-top 180ms ease;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
|
||||
max-height: 3.2em !important;
|
||||
opacity: .98 !important;
|
||||
visibility: visible !important;
|
||||
transform: translateY(0) !important;
|
||||
border-top: 1px solid rgba(127,127,127,.18) !important;
|
||||
}
|
||||
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero{
|
||||
background: rgba(0,0,0,.96) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
:global(body[data-edition-key="glossaire"][data-sticky-mode="glossary-home"].glossary-home-follow-on) .glossary-hero-follow.is-visible{
|
||||
max-height: 2.4em !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
133
src/components/GlossaryHomeSection.astro
Normal file
133
src/components/GlossaryHomeSection.astro
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
export interface Props {
|
||||
id?: string;
|
||||
title: string;
|
||||
intro?: string;
|
||||
followSection?: string;
|
||||
ctaHref?: string;
|
||||
ctaLabel?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
intro,
|
||||
followSection,
|
||||
ctaHref,
|
||||
ctaLabel,
|
||||
} = Astro.props;
|
||||
|
||||
const resolvedFollowSection = (followSection || title || "").trim();
|
||||
const showCta = Boolean(ctaHref && ctaLabel);
|
||||
---
|
||||
|
||||
<section id={id} class="glossary-section">
|
||||
<div class="glossary-section__head">
|
||||
<div>
|
||||
<h2 data-follow-section={resolvedFollowSection}>{title}</h2>
|
||||
|
||||
{intro && (
|
||||
<p class="glossary-intro">{intro}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showCta && (
|
||||
<a class="glossary-cta" href={ctaHref}>
|
||||
{ctaLabel}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.glossary-section{
|
||||
margin-top: 34px;
|
||||
scroll-margin-top: calc(var(--glossary-sticky-top) + 150px);
|
||||
}
|
||||
|
||||
.glossary-section__head{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.glossary-section h2{
|
||||
margin: 0;
|
||||
font-size: clamp(1.8rem, 3vw, 2.55rem);
|
||||
line-height: 1.06;
|
||||
letter-spacing: -.03em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
margin: 0;
|
||||
max-width: 72ch;
|
||||
font-size: 1rem;
|
||||
line-height: 1.52;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.glossary-section__head .glossary-intro{
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.glossary-cta{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 38px;
|
||||
border: 1px solid var(--glossary-border-strong);
|
||||
border-radius: 999px;
|
||||
padding: 6px 13px;
|
||||
color: var(--glossary-accent);
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
transition: transform 120ms ease, background 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-cta:hover{
|
||||
background: var(--glossary-bg-soft-strong);
|
||||
text-decoration: none;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-section{
|
||||
margin-top: 24px;
|
||||
scroll-margin-top: calc(var(--glossary-sticky-top) + 110px);
|
||||
}
|
||||
|
||||
.glossary-section__head{
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.glossary-section h2{
|
||||
font-size: clamp(1.45rem, 6vw, 1.95rem);
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.glossary-intro{
|
||||
font-size: .95rem;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.glossary-section__head .glossary-intro{
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.glossary-cta{
|
||||
width: fit-content;
|
||||
min-height: 35px;
|
||||
padding: 5px 12px;
|
||||
font-size: .95rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
286
src/components/GlossaryPortalAside.astro
Normal file
286
src/components/GlossaryPortalAside.astro
Normal file
@@ -0,0 +1,286 @@
|
||||
---
|
||||
interface LinkItem {
|
||||
href: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
ariaLabel: string;
|
||||
title: string;
|
||||
meta?: string;
|
||||
backHref?: string;
|
||||
backLabel?: string;
|
||||
pageItems?: LinkItem[];
|
||||
usefulLinks?: LinkItem[];
|
||||
}
|
||||
|
||||
const {
|
||||
ariaLabel,
|
||||
title,
|
||||
meta,
|
||||
backHref = "/glossaire/",
|
||||
backLabel = "← Retour au glossaire",
|
||||
pageItems = [],
|
||||
usefulLinks = [],
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<nav class="glossary-portal-aside" aria-label={ariaLabel}>
|
||||
<div class="glossary-portal-aside__block">
|
||||
<a class="glossary-portal-aside__back" href={backHref}>{backLabel}</a>
|
||||
<div class="glossary-portal-aside__title">{title}</div>
|
||||
{meta && <div class="glossary-portal-aside__meta">{meta}</div>}
|
||||
</div>
|
||||
|
||||
{pageItems.length > 0 && (
|
||||
<details class="glossary-portal-aside__block glossary-portal-aside__disclosure">
|
||||
<summary class="glossary-portal-aside__summary">
|
||||
<span class="glossary-portal-aside__heading">Dans cette page</span>
|
||||
<span class="glossary-portal-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-portal-aside__panel">
|
||||
<ul class="glossary-portal-aside__list">
|
||||
{pageItems.map((item) => (
|
||||
<li><a href={item.href}>{item.label}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
|
||||
{usefulLinks.length > 0 && (
|
||||
<details class="glossary-portal-aside__block glossary-portal-aside__disclosure">
|
||||
<summary class="glossary-portal-aside__summary">
|
||||
<span class="glossary-portal-aside__heading">Renvois utiles</span>
|
||||
<span class="glossary-portal-aside__chevron" aria-hidden="true">▾</span>
|
||||
</summary>
|
||||
|
||||
<div class="glossary-portal-aside__panel">
|
||||
<ul class="glossary-portal-aside__list">
|
||||
{usefulLinks.map((item) => (
|
||||
<li><a href={item.href}>{item.label}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.glossary-portal-aside{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__block{
|
||||
border: 1px solid rgba(127,127,127,0.22);
|
||||
border-radius: 16px;
|
||||
padding: 14px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
}
|
||||
|
||||
.glossary-portal-aside__back{
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__title{
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
letter-spacing: .2px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__meta{
|
||||
margin-top: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__heading{
|
||||
margin: 0 0 11px;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list li{
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list a{
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-portal-aside{
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__block{
|
||||
padding: 12px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-portal-aside{
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__block{
|
||||
padding: 11px 12px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__back{
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__title{
|
||||
font-size: 15px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__meta{
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.32;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__heading{
|
||||
margin-bottom: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list li{
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list a{
|
||||
font-size: 12.5px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-portal-aside{
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__block{
|
||||
padding: 9px 10px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__back{
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__title{
|
||||
font-size: 14px;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__meta{
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
line-height: 1.24;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__heading{
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.18;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list li{
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__list a{
|
||||
font-size: 11.5px;
|
||||
line-height: 1.22;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-portal-aside__block{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.glossary-portal-aside__disclosure{
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__summary{
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__summary::-webkit-details-marker{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__summary .glossary-portal-aside__heading{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__disclosure:not([open]) .glossary-portal-aside__panel{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__chevron{
|
||||
flex: 0 0 auto;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
opacity: .72;
|
||||
transition: transform 160ms ease, opacity 160ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__disclosure[open] .glossary-portal-aside__chevron{
|
||||
transform: rotate(180deg);
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__panel{
|
||||
padding: 0 14px 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-portal-aside__summary{
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.glossary-portal-aside__panel{
|
||||
padding: 0 12px 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
67
src/components/GlossaryPortalCta.astro
Normal file
67
src/components/GlossaryPortalCta.astro
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
export interface Props {
|
||||
href: string;
|
||||
label: string;
|
||||
icon?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
href,
|
||||
label,
|
||||
icon = "↗",
|
||||
className,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<a class:list={["glossary-portal-cta", className]} href={href}>
|
||||
<span>{label}</span>
|
||||
<span aria-hidden="true">{icon}</span>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
.glossary-portal-cta{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 40px;
|
||||
padding: 7px 14px;
|
||||
border: 1px solid rgba(127,127,127,0.24);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
text-decoration: none;
|
||||
line-height: 1.2;
|
||||
transition:
|
||||
transform 120ms ease,
|
||||
background 120ms ease,
|
||||
border-color 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-cta:hover{
|
||||
transform: translateY(-1px);
|
||||
background: rgba(127,127,127,0.08);
|
||||
border-color: rgba(0,217,255,0.18);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-portal-cta:focus-visible{
|
||||
outline: 2px solid rgba(0,217,255,0.28);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-portal-cta{
|
||||
min-height: 36px;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-portal-cta{
|
||||
min-height: 32px;
|
||||
padding: 5px 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
118
src/components/GlossaryPortalGrid.astro
Normal file
118
src/components/GlossaryPortalGrid.astro
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
export type GlossaryPortalGridItem = {
|
||||
href: string;
|
||||
title: string;
|
||||
description: string;
|
||||
meta: string;
|
||||
};
|
||||
|
||||
export interface Props {
|
||||
items?: GlossaryPortalGridItem[];
|
||||
secondary?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
items = [],
|
||||
secondary = false,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
"glossary-portals",
|
||||
secondary && "glossary-portals--secondary",
|
||||
]}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<a class="glossary-portal-card" href={item.href}>
|
||||
<strong>{item.title}</strong>
|
||||
<span>{item.description}</span>
|
||||
<small>{item.meta}</small>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glossary-portals{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.glossary-portal-card{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
padding: 14px 15px;
|
||||
border: 1px solid var(--glossary-border);
|
||||
border-radius: 16px;
|
||||
background: var(--glossary-bg-soft);
|
||||
text-decoration: none;
|
||||
transition: transform 120ms ease, background 120ms ease, border-color 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-card:hover{
|
||||
transform: translateY(-1px);
|
||||
background: var(--glossary-bg-soft-strong);
|
||||
border-color: rgba(0,217,255,0.16);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-portal-card strong{
|
||||
color: var(--glossary-accent);
|
||||
font-size: 1.04rem;
|
||||
line-height: 1.24;
|
||||
}
|
||||
|
||||
.glossary-portal-card span{
|
||||
color: inherit;
|
||||
font-size: .98rem;
|
||||
line-height: 1.46;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.glossary-portal-card small{
|
||||
color: var(--glossary-accent);
|
||||
font-size: .9rem;
|
||||
line-height: 1.28;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-portals{
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.glossary-portal-card{
|
||||
padding: 12px 12px;
|
||||
border-radius: 14px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.glossary-portal-card strong{
|
||||
font-size: .98rem;
|
||||
}
|
||||
|
||||
.glossary-portal-card span{
|
||||
font-size: .94rem;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.glossary-portal-card small{
|
||||
font-size: .85rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-portal-card{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.glossary-portal-card:hover{
|
||||
background: rgba(255,255,255,0.07);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
260
src/components/GlossaryPortalHero.astro
Normal file
260
src/components/GlossaryPortalHero.astro
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
interface Props {
|
||||
prefix: string;
|
||||
kicker: string;
|
||||
title: string;
|
||||
intro: string;
|
||||
moreParagraphs?: string[];
|
||||
introMaxWidth?: string;
|
||||
followIntroMaxWidth?: string;
|
||||
moreMaxHeight?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
prefix,
|
||||
kicker,
|
||||
title,
|
||||
intro,
|
||||
moreParagraphs = [],
|
||||
introMaxWidth = "70ch",
|
||||
followIntroMaxWidth = "62ch",
|
||||
moreMaxHeight = "18rem",
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class="glossary-portal-hero glossary-page-hero"
|
||||
data-glossary-portal-hero
|
||||
style={`--portal-hero-intro-max-w:${introMaxWidth}; --portal-hero-follow-intro-max-w:${followIntroMaxWidth}; --portal-hero-secondary-max-h:${moreMaxHeight};`}
|
||||
>
|
||||
<p class="glossary-portal-hero__kicker">{kicker}</p>
|
||||
|
||||
<h1>{title}</h1>
|
||||
|
||||
<p class="glossary-portal-hero__intro glossary-portal-hero__intro--lead">
|
||||
{intro}
|
||||
</p>
|
||||
|
||||
{moreParagraphs.length > 0 && (
|
||||
<div class="glossary-portal-hero__collapsible">
|
||||
<div
|
||||
class="glossary-portal-hero__more"
|
||||
id={`${prefix}-hero-more`}
|
||||
data-glossary-portal-more
|
||||
aria-hidden="false"
|
||||
>
|
||||
{moreParagraphs.map((paragraph) => (
|
||||
<p class="glossary-portal-hero__intro glossary-portal-hero__intro--more">
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="glossary-portal-hero__toggle"
|
||||
id={`${prefix}-hero-toggle`}
|
||||
data-glossary-portal-toggle
|
||||
type="button"
|
||||
aria-controls={`${prefix}-hero-more`}
|
||||
aria-expanded="false"
|
||||
hidden
|
||||
>
|
||||
lire la suite
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glossary-portal-hero{
|
||||
position: sticky;
|
||||
top: var(--glossary-sticky-top);
|
||||
z-index: 12;
|
||||
margin-bottom: var(--portal-hero-margin-bottom, 28px);
|
||||
padding:
|
||||
var(--portal-hero-pad-top, 20px)
|
||||
var(--portal-hero-pad-x, 18px)
|
||||
var(--portal-hero-pad-bottom, 22px);
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 28px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(0,0,0,0.60), rgba(0,0,0,0.92)),
|
||||
radial-gradient(980px 260px at 18% 0%, rgba(0,217,255,0.08), transparent 60%);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
display: grid;
|
||||
row-gap: var(--portal-hero-gap, 16px);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
background 280ms cubic-bezier(.22,.8,.22,1),
|
||||
border-color 220ms cubic-bezier(.22,.8,.22,1),
|
||||
box-shadow 220ms cubic-bezier(.22,.8,.22,1),
|
||||
border-radius 220ms ease,
|
||||
padding 220ms ease,
|
||||
row-gap 220ms ease,
|
||||
margin-bottom 220ms ease;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255,255,255,0.02),
|
||||
0 10px 26px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.glossary-portal-hero__kicker{
|
||||
margin: 0;
|
||||
font-size: var(--portal-kicker-size, 12px);
|
||||
line-height: var(--portal-kicker-lh, 1.2);
|
||||
letter-spacing: var(--portal-kicker-spacing, .14em);
|
||||
text-transform: uppercase;
|
||||
font-weight: 650;
|
||||
opacity: .74;
|
||||
}
|
||||
|
||||
.glossary-portal-hero h1{
|
||||
margin: 0;
|
||||
font-size: var(--portal-hero-h1-size, clamp(3rem, 4.8vw, 4.15rem));
|
||||
line-height: var(--portal-hero-h1-lh, .98);
|
||||
letter-spacing: var(--portal-hero-h1-spacing, -.045em);
|
||||
font-weight: 850;
|
||||
text-wrap: balance;
|
||||
transition:
|
||||
font-size 180ms ease,
|
||||
line-height 180ms ease,
|
||||
letter-spacing 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__intro{
|
||||
margin: 0;
|
||||
max-width: var(--portal-hero-intro-max-w, 70ch);
|
||||
font-size: var(--portal-hero-intro-size, 1.06rem);
|
||||
line-height: var(--portal-hero-intro-lh, 1.6);
|
||||
text-wrap: pretty;
|
||||
transition:
|
||||
font-size 180ms ease,
|
||||
line-height 180ms ease,
|
||||
max-width 180ms ease,
|
||||
opacity 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__intro--lead{ opacity: .95; }
|
||||
.glossary-portal-hero__intro--more{ opacity: .89; }
|
||||
|
||||
.glossary-portal-hero__collapsible{
|
||||
display: grid;
|
||||
row-gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__more{
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
max-height: var(--portal-hero-secondary-max-h, 20em);
|
||||
overflow: hidden;
|
||||
opacity: var(--portal-hero-secondary-opacity, .92);
|
||||
min-width: 0;
|
||||
transition:
|
||||
max-height 220ms ease,
|
||||
opacity 180ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__toggle{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: fit-content;
|
||||
min-height: 34px;
|
||||
padding: 5px 0;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.2;
|
||||
letter-spacing: .01em;
|
||||
opacity: .72;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 2px;
|
||||
transition:
|
||||
opacity 120ms ease,
|
||||
transform 120ms ease;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__toggle:hover{
|
||||
opacity: .92;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.glossary-portal-hero__toggle:focus-visible{
|
||||
outline: 2px solid rgba(0,217,255,0.24);
|
||||
outline-offset: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__toggle[hidden]{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-portal-hero{
|
||||
border-radius: 24px;
|
||||
}
|
||||
|
||||
.glossary-portal-hero h1{
|
||||
text-wrap: pretty;
|
||||
}
|
||||
|
||||
.glossary-portal-hero__more{
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 860px){
|
||||
.glossary-portal-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
margin-bottom: 18px !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
.glossary-portal-hero h1,
|
||||
.glossary-portal-hero__intro,
|
||||
.glossary-portal-hero__more,
|
||||
.glossary-portal-hero__collapsible{
|
||||
min-width: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-portal-hero{
|
||||
position: static !important;
|
||||
top: auto !important;
|
||||
z-index: auto !important;
|
||||
margin-bottom: 14px !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
min-width: 0 !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
|
||||
.glossary-portal-hero h1,
|
||||
.glossary-portal-hero__intro,
|
||||
.glossary-portal-hero__more,
|
||||
.glossary-portal-hero__collapsible{
|
||||
min-width: 0 !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-portal-hero{
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255,255,255,0.02),
|
||||
0 14px 34px rgba(0,0,0,0.16);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
127
src/components/GlossaryPortalPanel.astro
Normal file
127
src/components/GlossaryPortalPanel.astro
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
export interface Props {
|
||||
id?: string;
|
||||
title: string;
|
||||
count?: string;
|
||||
intro?: string;
|
||||
surface?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
count,
|
||||
intro,
|
||||
surface = false,
|
||||
className,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div
|
||||
class:list={[
|
||||
"glossary-portal-panel",
|
||||
surface && "glossary-portal-panel--surface",
|
||||
className,
|
||||
]}
|
||||
>
|
||||
<div class="glossary-portal-panel__head">
|
||||
<h3 id={id}>{title}</h3>
|
||||
{count && <span class="glossary-portal-panel__count">{count}</span>}
|
||||
</div>
|
||||
|
||||
{intro && <p class="glossary-portal-panel__intro">{intro}</p>}
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.glossary-portal-panel{
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-portal-panel--surface{
|
||||
padding:
|
||||
var(--portal-panel-pad-y, 16px)
|
||||
var(--portal-panel-pad-x, 16px);
|
||||
border: 1px solid var(--glossary-border, rgba(127,127,127,0.18));
|
||||
border-radius: var(--portal-panel-radius, 18px);
|
||||
background:
|
||||
var(--glossary-bg-soft, rgba(127,127,127,0.035));
|
||||
}
|
||||
|
||||
.glossary-portal-panel__head{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__head h3{
|
||||
margin: 0;
|
||||
font-size: var(--portal-local-h3-size, clamp(1.35rem, 2vw, 1.7rem));
|
||||
line-height: var(--portal-local-h3-lh, 1.15);
|
||||
letter-spacing: -.02em;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__count{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 26px;
|
||||
padding: 0 9px;
|
||||
border: 1px solid rgba(127,127,127,0.20);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
font-size: 11.5px;
|
||||
line-height: 1.2;
|
||||
opacity: .8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__intro{
|
||||
margin: 0;
|
||||
font-size: var(--portal-card-text-size, 14px);
|
||||
line-height: var(--portal-card-text-lh, 1.45);
|
||||
opacity: .92;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-portal-panel{
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__head{
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__count{
|
||||
min-height: 23px;
|
||||
padding: 0 8px;
|
||||
font-size: 10.5px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-portal-panel{
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__head{
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.glossary-portal-panel__count{
|
||||
min-height: 21px;
|
||||
padding: 0 7px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-portal-panel--surface{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
143
src/components/GlossaryPortalSection.astro
Normal file
143
src/components/GlossaryPortalSection.astro
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
interface Props {
|
||||
id: string;
|
||||
title: string;
|
||||
count?: string;
|
||||
intro?: string;
|
||||
final?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
id,
|
||||
title,
|
||||
count,
|
||||
intro,
|
||||
final = false,
|
||||
className,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class:list={["glossary-portal-section", final && "glossary-portal-section--final", className]}>
|
||||
<div class="glossary-portal-section__head">
|
||||
<h2 id={id}>{title}</h2>
|
||||
{count && <span class="glossary-portal-section__count">{count}</span>}
|
||||
</div>
|
||||
|
||||
{intro && <p class="glossary-portal-section__intro">{intro}</p>}
|
||||
|
||||
<slot />
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.glossary-portal-section{
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.glossary-portal-section h2{
|
||||
margin: 0;
|
||||
font-size: clamp(1.8rem, 3vw, 2.35rem);
|
||||
line-height: 1.05;
|
||||
letter-spacing: -.03em;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.glossary-portal-section__head{
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.glossary-portal-section__count{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid rgba(127,127,127,0.20);
|
||||
border-radius: 999px;
|
||||
background: rgba(127,127,127,0.04);
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
opacity: .8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.glossary-portal-section__intro{
|
||||
margin: 0;
|
||||
max-width: 76ch;
|
||||
font-size: var(--portal-body-size, 1rem);
|
||||
line-height: var(--portal-body-lh, 1.55);
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.glossary-portal-section--final{
|
||||
margin-top: 34px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.glossary-portal-section{
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.glossary-portal-section h2{
|
||||
font-size: clamp(1.6rem, 4.4vw, 2rem);
|
||||
line-height: 1.04;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-portal-section{
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.glossary-portal-section__head{
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.glossary-portal-section h2{
|
||||
font-size: clamp(1.34rem, 6.5vw, 1.72rem);
|
||||
line-height: 1.04;
|
||||
letter-spacing: -.022em;
|
||||
}
|
||||
|
||||
.glossary-portal-section__count{
|
||||
min-height: 24px;
|
||||
padding: 0 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.glossary-portal-section--final{
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (orientation: landscape) and (max-width: 920px) and (max-height: 520px){
|
||||
.glossary-portal-section{
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.glossary-portal-section__head{
|
||||
gap: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.glossary-portal-section h2{
|
||||
font-size: clamp(1.12rem, 4.2vw, 1.34rem);
|
||||
line-height: 1.02;
|
||||
}
|
||||
|
||||
.glossary-portal-section__count{
|
||||
min-height: 22px;
|
||||
padding: 0 7px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.glossary-portal-section--final{
|
||||
margin-top: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1182
src/components/GlossaryPortalStickySync.astro
Normal file
1182
src/components/GlossaryPortalStickySync.astro
Normal file
File diff suppressed because it is too large
Load Diff
132
src/components/GlossaryRelationCards.astro
Normal file
132
src/components/GlossaryRelationCards.astro
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
import type { GlossaryRelationBlock } from "../lib/glossary";
|
||||
import { hrefOfGlossaryEntry } from "../lib/glossary";
|
||||
|
||||
interface Props {
|
||||
relationBlocks: GlossaryRelationBlock[];
|
||||
}
|
||||
|
||||
const { relationBlocks = [] } = Astro.props;
|
||||
const relationsHeadingId = "relations-conceptuelles";
|
||||
---
|
||||
|
||||
{relationBlocks.length > 0 && (
|
||||
<section
|
||||
class="glossary-relations"
|
||||
aria-labelledby={relationsHeadingId}
|
||||
>
|
||||
<h2 id={relationsHeadingId}>Relations conceptuelles</h2>
|
||||
<div class="glossary-relations-grid">
|
||||
{relationBlocks.map((block) => (
|
||||
<section class={`glossary-relations-card ${block.className}`}>
|
||||
<h3>{block.title}</h3>
|
||||
<ul>
|
||||
{block.items.map((item) => (
|
||||
<li>
|
||||
<a href={hrefOfGlossaryEntry(item)}>{item.data.term}</a>
|
||||
<span> — {item.data.definitionShort}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.glossary-relations{
|
||||
margin-top: 14px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid rgba(127,127,127,0.18);
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-relations{
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.glossary-relations h2{
|
||||
margin: 0 0 12px;
|
||||
font-size: clamp(1.35rem, 3vw, 1.8rem);
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.glossary-relations-grid{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.glossary-relations-card{
|
||||
border: 1px solid rgba(127,127,127,0.20);
|
||||
border-radius: 14px;
|
||||
padding: 12px 13px;
|
||||
background: rgba(127,127,127,0.05);
|
||||
}
|
||||
|
||||
.glossary-relations-card h3{
|
||||
margin: 0 0 8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.glossary-relations-card ul{
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.glossary-relations-card li{
|
||||
margin-bottom: 7px;
|
||||
font-size: 13.5px;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.glossary-relations-card li:last-child{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.glossary-relations-card span{
|
||||
opacity: .88;
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-relations{
|
||||
margin-top: 18px;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.glossary-relations h2{
|
||||
margin-bottom: 10px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.glossary-relations-grid{
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-relations-card{
|
||||
padding: 11px 11px;
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.glossary-relations-card h3{
|
||||
font-size: 13px;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.glossary-relations-card li{
|
||||
font-size: 13px;
|
||||
line-height: 1.38;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-relations-card{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
307
src/components/GlossarySmartNav.astro
Normal file
307
src/components/GlossarySmartNav.astro
Normal file
@@ -0,0 +1,307 @@
|
||||
---
|
||||
import type { GlossarySmartNavigation } from "../lib/glossary";
|
||||
import { hrefOfGlossaryEntry } from "../lib/glossary";
|
||||
|
||||
interface Props {
|
||||
smartNavigation?: GlossarySmartNavigation;
|
||||
}
|
||||
|
||||
const { smartNavigation } = Astro.props;
|
||||
|
||||
const hasPrimary = Boolean(smartNavigation?.primaryNext);
|
||||
const paths = smartNavigation?.paths ?? [];
|
||||
const flows = smartNavigation?.flows ?? [];
|
||||
const hasPaths = paths.length > 0;
|
||||
const hasFlows = flows.length > 0;
|
||||
---
|
||||
|
||||
{(hasPrimary || hasPaths || hasFlows) && (
|
||||
<section class="glossary-smart-nav" aria-label="Navigation guidée du glossaire">
|
||||
<div class="glossary-smart-nav__eyebrow">Explorer les prolongements</div>
|
||||
|
||||
{smartNavigation?.primaryNext && (
|
||||
<div class="glossary-smart-nav__primary">
|
||||
<span class="glossary-smart-nav__label">Étape suivante</span>
|
||||
<a href={hrefOfGlossaryEntry(smartNavigation.primaryNext)}>
|
||||
{smartNavigation.primaryNext.data.term}
|
||||
</a>
|
||||
|
||||
{smartNavigation.primaryReason && (
|
||||
<p>{smartNavigation.primaryReason}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasFlows && (
|
||||
<div class="glossary-smart-nav__flows" aria-label="Parcours contextuels">
|
||||
<span class="glossary-smart-nav__label">Parcours contextuels</span>
|
||||
|
||||
<div class="glossary-smart-nav__flow-list">
|
||||
{flows.map((flow) => (
|
||||
flow.primaryNext && (
|
||||
<a class="glossary-smart-nav__flow" href={hrefOfGlossaryEntry(flow.primaryNext)}>
|
||||
<span class="glossary-smart-nav__flow-label">{flow.label}</span>
|
||||
<strong>{flow.primaryNext.data.term}</strong>
|
||||
{flow.primaryReason && <span>{flow.primaryReason}</span>}
|
||||
</a>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasPaths && (
|
||||
<div class="glossary-smart-nav__paths" aria-label="Parcours de lecture">
|
||||
{paths.map((path) => {
|
||||
const panelId = `smart-nav-${path.key}`;
|
||||
|
||||
return (
|
||||
<div class="glossary-smart-nav__path">
|
||||
<button
|
||||
class="glossary-smart-nav__path-button"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls={panelId}
|
||||
>
|
||||
<span>{path.label}</span>
|
||||
<span class="glossary-smart-nav__chevron" aria-hidden="true">▾</span>
|
||||
</button>
|
||||
|
||||
<ul id={panelId} class="glossary-smart-nav__path-panel" hidden>
|
||||
{path.entries.map((entry) => (
|
||||
<li>
|
||||
<a href={hrefOfGlossaryEntry(entry)}>{entry.data.term}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
<style>
|
||||
.glossary-smart-nav{
|
||||
margin: 14px 0 18px;
|
||||
padding: 14px;
|
||||
border: 1px solid rgba(127,127,127,0.20);
|
||||
border-radius: 18px;
|
||||
background: rgba(127,127,127,0.045);
|
||||
}
|
||||
|
||||
.glossary-smart-nav__primary{
|
||||
display: grid;
|
||||
gap: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__label{
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
opacity: .76;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__primary a{
|
||||
width: fit-content;
|
||||
font-size: clamp(1.05rem, 2vw, 1.22rem);
|
||||
font-weight: 900;
|
||||
line-height: 1.18;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__primary p{
|
||||
max-width: 72ch;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.45;
|
||||
opacity: .88;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__paths{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path{
|
||||
align-self: flex-start;
|
||||
min-width: min(180px, 100%);
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 14px;
|
||||
background: rgba(127,127,127,0.035);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-button{
|
||||
width: 100%;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
background: rgba(127,127,127,0.06);
|
||||
color: inherit;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 850;
|
||||
line-height: 1.25;
|
||||
text-align: left;
|
||||
user-select: none;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-button:hover{
|
||||
background: rgba(127,127,127,0.10);
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-button:active{
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.glossary-smart-nav__chevron{
|
||||
flex: 0 0 auto;
|
||||
font-size: 13px;
|
||||
line-height: 1;
|
||||
opacity: .72;
|
||||
transform: rotate(0deg);
|
||||
transition: transform 160ms ease, opacity 160ms ease;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-button[aria-expanded="true"] .glossary-smart-nav__chevron{
|
||||
transform: rotate(180deg);
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-panel{
|
||||
margin: 0;
|
||||
padding: 0 11px 10px 24px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-panel[hidden]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-panel li{
|
||||
margin: 5px 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.32;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path-panel a{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flows{
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow-list{
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow{
|
||||
display: grid;
|
||||
gap: 3px;
|
||||
padding: 10px 11px;
|
||||
border: 1px solid rgba(127,127,127,0.18);
|
||||
border-radius: 14px;
|
||||
background: rgba(127,127,127,0.035);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow-label{
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
opacity: .72;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow strong{
|
||||
font-size: 14px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow span:last-child{
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
opacity: .84;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__eyebrow{
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 850;
|
||||
letter-spacing: .08em; /* légèrement augmenté */
|
||||
text-transform: uppercase;
|
||||
opacity: .78; /* un poil plus visible */
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.glossary-smart-nav{
|
||||
margin: 12px 0 16px;
|
||||
padding: 12px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__paths{
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
.glossary-smart-nav{
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.glossary-smart-nav__path{
|
||||
background: rgba(255,255,255,0.035);
|
||||
}
|
||||
|
||||
.glossary-smart-nav__flow{
|
||||
background: rgba(255,255,255,0.035);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
document.querySelectorAll(".glossary-smart-nav").forEach((nav) => {
|
||||
nav
|
||||
.querySelectorAll(".glossary-smart-nav__path-button")
|
||||
.forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
const panelId = button.getAttribute("aria-controls");
|
||||
const panel = panelId ? document.getElementById(panelId) : null;
|
||||
if (!panel || !nav.contains(panel)) return;
|
||||
|
||||
const expanded = button.getAttribute("aria-expanded") === "true";
|
||||
const nextExpanded = !expanded;
|
||||
|
||||
button.setAttribute("aria-expanded", nextExpanded ? "true" : "false");
|
||||
panel.hidden = !nextExpanded;
|
||||
|
||||
button
|
||||
.closest(".glossary-smart-nav__path")
|
||||
?.classList.toggle("is-open", nextExpanded);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@@ -68,7 +68,6 @@ const { initialLevel = 1 } = Astro.props;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// init : storage > initialLevel
|
||||
let start = clampLevel(initialLevel);
|
||||
try {
|
||||
const stored = localStorage.getItem(KEY);
|
||||
@@ -77,13 +76,11 @@ const { initialLevel = 1 } = Astro.props;
|
||||
|
||||
applyLevel(start, { persist: false });
|
||||
|
||||
// clicks
|
||||
wrap.addEventListener("click", (ev) => {
|
||||
const btn = ev.target?.closest?.("button[data-level]");
|
||||
if (!btn) return;
|
||||
ev.preventDefault();
|
||||
|
||||
// ✅ crucial : on capture la position AVANT le reflow lié au changement de niveau
|
||||
captureBeforeLevelSwitch();
|
||||
applyLevel(btn.dataset.level);
|
||||
});
|
||||
@@ -95,6 +92,8 @@ const { initialLevel = 1 } = Astro.props;
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.level-btn{
|
||||
@@ -106,6 +105,7 @@ const { initialLevel = 1 } = Astro.props;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: filter .12s ease, transform .12s ease, background .12s ease, border-color .12s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.level-btn:hover{
|
||||
@@ -125,4 +125,21 @@ const { initialLevel = 1 } = Astro.props;
|
||||
.level-btn:active{
|
||||
transform: translateY(1px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@media (max-width: 980px){
|
||||
.level-toggle{
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.level-btn{
|
||||
padding: 5px 9px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px){
|
||||
.level-toggle{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,49 +3,128 @@ const { headings } = Astro.props;
|
||||
|
||||
// H2/H3 seulement
|
||||
const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
const tocId = `toc-local-${Math.random().toString(36).slice(2, 9)}`;
|
||||
---
|
||||
|
||||
{items.length > 0 && (
|
||||
<nav class="toc-local" aria-label="Dans ce chapitre">
|
||||
<div class="toc-local__title">Dans ce chapitre</div>
|
||||
<nav class="toc-local" aria-label="Dans ce chapitre" data-toc-local data-mobile-default="closed">
|
||||
<button
|
||||
class="toc-local__head toc-local__toggle"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-controls={tocId}
|
||||
>
|
||||
<span class="toc-local__title">Dans ce chapitre</span>
|
||||
<span class="toc-local__chevron" aria-hidden="true">▾</span>
|
||||
</button>
|
||||
|
||||
<ol class="toc-local__list">
|
||||
{items.map((h) => (
|
||||
<li
|
||||
class={`toc-local__item d${h.depth}`}
|
||||
data-toc-item
|
||||
data-depth={h.depth}
|
||||
data-id={h.slug}
|
||||
>
|
||||
<a href={`#${h.slug}`} data-toc-link data-slug={h.slug}>
|
||||
{h.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<div class="toc-local__body-clip" id={tocId} hidden>
|
||||
<div class="toc-local__body">
|
||||
<ol class="toc-local__list">
|
||||
{items.map((h) => (
|
||||
<li
|
||||
class={`toc-local__item d${h.depth}`}
|
||||
data-toc-item
|
||||
data-depth={h.depth}
|
||||
data-id={h.slug}
|
||||
>
|
||||
<a href={`#${h.slug}`} data-toc-link data-slug={h.slug}>
|
||||
<span class="toc-local__mark" aria-hidden="true"></span>
|
||||
<span class="toc-local__text">{h.text}</span>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
)}
|
||||
|
||||
<script is:inline>
|
||||
(() => {
|
||||
function init() {
|
||||
const toc = document.querySelector(".toc-local");
|
||||
if (!toc) return;
|
||||
const toc = document.querySelector(".toc-local[data-toc-local]");
|
||||
if (!toc || toc.dataset.tocReady === "1") return;
|
||||
toc.dataset.tocReady = "1";
|
||||
|
||||
const toggle = toc.querySelector(".toc-local__toggle");
|
||||
const bodyClip = toc.querySelector(".toc-local__body-clip");
|
||||
const mq = window.matchMedia("(max-width: 980px)");
|
||||
const KEY = `archicratie:toc-local:${window.location.pathname}`;
|
||||
|
||||
if (!toggle || !bodyClip) return;
|
||||
|
||||
const readState = () => {
|
||||
try {
|
||||
const v = localStorage.getItem(KEY);
|
||||
if (v === "open") return true;
|
||||
if (v === "closed") return false;
|
||||
} catch {}
|
||||
return null;
|
||||
};
|
||||
|
||||
const writeState = (open) => {
|
||||
try { localStorage.setItem(KEY, open ? "open" : "closed"); } catch {}
|
||||
};
|
||||
|
||||
const setOpen = (open, { persist = true, emit = true } = {}) => {
|
||||
const isMobile = mq.matches;
|
||||
const effectiveOpen = isMobile ? open : true;
|
||||
|
||||
toc.classList.toggle("is-collapsed", isMobile && !effectiveOpen);
|
||||
toggle.setAttribute("aria-expanded", effectiveOpen ? "true" : "false");
|
||||
|
||||
if (bodyClip) {
|
||||
bodyClip.hidden = isMobile && !effectiveOpen;
|
||||
}
|
||||
|
||||
if (persist && isMobile) writeState(effectiveOpen);
|
||||
|
||||
if (emit && effectiveOpen && isMobile) {
|
||||
window.dispatchEvent(new CustomEvent("archicratie:tocLocalOpen"));
|
||||
}
|
||||
};
|
||||
|
||||
const initAccordion = () => {
|
||||
if (!mq.matches) {
|
||||
setOpen(true, { persist: false, emit: false });
|
||||
return;
|
||||
}
|
||||
const stored = readState();
|
||||
setOpen(stored == null ? false : stored, { persist: false, emit: false });
|
||||
};
|
||||
|
||||
toggle.addEventListener("click", () => {
|
||||
const next = toggle.getAttribute("aria-expanded") !== "true";
|
||||
setOpen(next);
|
||||
});
|
||||
|
||||
if (mq.addEventListener) {
|
||||
mq.addEventListener("change", initAccordion);
|
||||
} else if (mq.addListener) {
|
||||
mq.addListener(initAccordion);
|
||||
}
|
||||
|
||||
const itemEls = Array.from(toc.querySelectorAll("[data-toc-item]"));
|
||||
if (!itemEls.length) return;
|
||||
if (!itemEls.length) {
|
||||
initAccordion();
|
||||
return;
|
||||
}
|
||||
|
||||
const ordered = itemEls
|
||||
.map((li) => {
|
||||
const a = li.querySelector("a[data-toc-link]");
|
||||
const id = li.getAttribute("data-id") || a?.dataset.slug || "";
|
||||
const depth = Number(li.getAttribute("data-depth") || "0");
|
||||
const el = id ? document.getElementById(id) : null; // span.details-anchor OU h3[id]
|
||||
const el = id ? document.getElementById(id) : null;
|
||||
return (a && id && el) ? { id, depth, li, a, el } : null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
if (!ordered.length) return;
|
||||
if (!ordered.length) {
|
||||
initAccordion();
|
||||
return;
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
for (const t of ordered) {
|
||||
@@ -55,14 +134,29 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
};
|
||||
|
||||
const openDetailsIfNeeded = (el) => {
|
||||
const d = el?.closest?.("details");
|
||||
if (d && !d.open) d.open = true;
|
||||
try {
|
||||
if (!el) return;
|
||||
|
||||
let d = el.closest?.("details") || null;
|
||||
|
||||
if (!d && el.classList?.contains("details-anchor")) {
|
||||
const n = el.nextElementSibling;
|
||||
if (n && n.tagName === "DETAILS") d = n;
|
||||
}
|
||||
|
||||
if (!d) {
|
||||
const s = el.closest?.("summary");
|
||||
if (s && s.parentElement && s.parentElement.tagName === "DETAILS") d = s.parentElement;
|
||||
}
|
||||
|
||||
if (d && d.tagName === "DETAILS" && !d.open) d.open = true;
|
||||
} catch {}
|
||||
};
|
||||
|
||||
let current = "";
|
||||
|
||||
const setCurrent = (id) => {
|
||||
if (!id || id === current) return;
|
||||
const setCurrent = (id, { autoOpen = true } = {}) => {
|
||||
if (!id) return;
|
||||
const t = ordered.find((x) => x.id === id);
|
||||
if (!t) return;
|
||||
|
||||
@@ -74,17 +168,22 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
t.a.setAttribute("aria-current", "true");
|
||||
t.li.classList.add("is-current");
|
||||
|
||||
// ✅ IMPORTANT: plus de scrollIntoView ici
|
||||
// sinon ça scroll l'aside pendant le scroll du reading => TOC global “disparaît”.
|
||||
// Sur mobile/tablette, le suivi actif ne doit pas rouvrir automatiquement la TOC.
|
||||
if (!mq.matches && autoOpen && toc.classList.contains("is-collapsed")) {
|
||||
setOpen(true);
|
||||
}
|
||||
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("archicratie:tocLocalActive", { detail: { id } })
|
||||
);
|
||||
};
|
||||
|
||||
const computeActive = () => {
|
||||
const visible = ordered.filter((t) => {
|
||||
const d = t.el.closest?.("details");
|
||||
if (d && !d.open) {
|
||||
// Si l'élément est dans <summary>, il reste visible même details fermé
|
||||
const inSummary = !!t.el.closest?.("summary");
|
||||
if (!inSummary) return false;
|
||||
if (!inSummary && !t.el.classList?.contains("details-anchor")) return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
@@ -102,7 +201,7 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
}
|
||||
|
||||
if (!best) best = visible[0];
|
||||
setCurrent(best.id);
|
||||
if (best && best.id !== current) setCurrent(best.id, { autoOpen: true });
|
||||
};
|
||||
|
||||
let ticking = false;
|
||||
@@ -117,11 +216,14 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
|
||||
const syncFromHash = () => {
|
||||
const id = (location.hash || "").slice(1);
|
||||
if (!id) { computeActive(); return; }
|
||||
if (!id) {
|
||||
computeActive();
|
||||
return;
|
||||
}
|
||||
|
||||
const el = document.getElementById(id);
|
||||
if (el) openDetailsIfNeeded(el);
|
||||
setCurrent(id);
|
||||
setCurrent(id, { autoOpen: false });
|
||||
};
|
||||
|
||||
toc.addEventListener("click", (ev) => {
|
||||
@@ -133,13 +235,14 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
const el = document.getElementById(id);
|
||||
if (el) openDetailsIfNeeded(el);
|
||||
|
||||
setCurrent(id);
|
||||
setCurrent(id, { autoOpen: true });
|
||||
});
|
||||
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
window.addEventListener("resize", onScroll);
|
||||
window.addEventListener("hashchange", syncFromHash);
|
||||
|
||||
initAccordion();
|
||||
syncFromHash();
|
||||
onScroll();
|
||||
}
|
||||
@@ -153,30 +256,187 @@ const items = (headings || []).filter((h) => h.depth >= 2 && h.depth <= 3);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.toc-local{margin-top:12px;border:1px solid rgba(127,127,127,.25);border-radius:16px;padding:12px}
|
||||
.toc-local__title{font-size:13px;opacity:.85;margin-bottom:8px}
|
||||
|
||||
.toc-local__list{list-style:none;margin:0;padding:0}
|
||||
.toc-local__item::marker{content:""}
|
||||
.toc-local__item{margin:6px 0}
|
||||
.toc-local__item.d3{margin-left:12px;opacity:.9}
|
||||
|
||||
.toc-local__item.is-current > a{
|
||||
font-weight: 750;
|
||||
text-decoration: underline;
|
||||
.toc-local{
|
||||
margin-top: 12px;
|
||||
border: 1px solid rgba(127,127,127,.25);
|
||||
border-radius: 16px;
|
||||
padding: 12px;
|
||||
background: rgba(127,127,127,0.03);
|
||||
}
|
||||
|
||||
.toc-local a{
|
||||
display:inline-block;
|
||||
max-width:100%;
|
||||
text-decoration:none;
|
||||
.toc-local__toggle{
|
||||
width: 100%;
|
||||
appearance: none;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.toc-local a:hover{ text-decoration: underline; }
|
||||
|
||||
.toc-local__head{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.toc-local__title{
|
||||
font-size: 13px;
|
||||
opacity: .85;
|
||||
}
|
||||
|
||||
.toc-local__chevron{
|
||||
font-size: 12px;
|
||||
opacity: .72;
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
|
||||
.toc-local__body-clip{
|
||||
display: grid;
|
||||
grid-template-rows: 1fr;
|
||||
transition:
|
||||
grid-template-rows 220ms ease,
|
||||
opacity 160ms ease,
|
||||
margin-top 220ms ease;
|
||||
}
|
||||
|
||||
.toc-local__body{
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toc-local__list{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
max-height: 44vh;
|
||||
overflow: auto;
|
||||
padding-right: 8px;
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
|
||||
</style>
|
||||
.toc-local__item::marker{ content:""; }
|
||||
.toc-local__item{ margin: 6px 0; }
|
||||
|
||||
.toc-local__item.d3{
|
||||
margin-left: 14px;
|
||||
opacity: .94;
|
||||
}
|
||||
|
||||
.toc-local a{
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 8px;
|
||||
align-items: start;
|
||||
max-width: 100%;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.toc-local a:hover{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.toc-local__mark{
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: .36em;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(127,127,127,.34);
|
||||
background: transparent;
|
||||
opacity: .68;
|
||||
}
|
||||
|
||||
.toc-local__text{
|
||||
line-height: 1.28;
|
||||
}
|
||||
|
||||
.toc-local__item.is-current > a{
|
||||
font-weight: 760;
|
||||
}
|
||||
|
||||
.toc-local__item.is-current > a .toc-local__mark{
|
||||
background: currentColor;
|
||||
border-color: currentColor;
|
||||
box-shadow: 0 0 0 3px rgba(127,127,127,.10);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 980px){
|
||||
.toc-local{
|
||||
padding: 10px 12px;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.toc-local__head{
|
||||
margin-bottom: 0;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.toc-local__body-clip{
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.toc-local.is-collapsed .toc-local__body-clip{
|
||||
grid-template-rows: 0fr;
|
||||
opacity: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.toc-local__body{
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
transition: opacity 180ms ease;
|
||||
}
|
||||
|
||||
.toc-local.is-collapsed .toc-local__body{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toc-local.is-collapsed .toc-local__chevron{
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.toc-local__title{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.toc-local__list{
|
||||
max-height: min(42vh, 360px);
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.toc-local__item{
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.toc-local__item.d2 > a .toc-local__text{
|
||||
font-size: 12.9px;
|
||||
line-height: 1.24;
|
||||
font-weight: 680;
|
||||
}
|
||||
|
||||
.toc-local__item.d3{
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.toc-local__item.d3 > a .toc-local__text{
|
||||
font-size: 12.1px;
|
||||
line-height: 1.22;
|
||||
opacity: .95;
|
||||
}
|
||||
|
||||
.toc-local__item.d3 > a .toc-local__mark{
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: .42em;
|
||||
opacity: .55;
|
||||
}
|
||||
|
||||
.toc-local__body-clip[hidden]{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -382,7 +382,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (Array.isArray(data.authors) && data.authors.length) {
|
||||
if (Array.isArray(data.mobilizedAuthors) && data.mobilizedAuthors.length) {
|
||||
const h = document.createElement("h3");
|
||||
h.className = "panel-subtitle";
|
||||
h.textContent = "Auteurs";
|
||||
@@ -390,7 +390,7 @@
|
||||
|
||||
const ul = document.createElement("ul");
|
||||
ul.className = "panel-list";
|
||||
for (const a of data.authors) {
|
||||
for (const a of data.mobilizedAuthors) {
|
||||
const li = document.createElement("li");
|
||||
li.textContent = esc(a);
|
||||
ul.appendChild(li);
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
---
|
||||
const path = Astro.url.pathname;
|
||||
|
||||
const isActive = (href: string) => {
|
||||
if (href === "/") return path === "/";
|
||||
return path === href || path.startsWith(href);
|
||||
};
|
||||
---
|
||||
|
||||
<nav class="site-nav" aria-label="Navigation principale">
|
||||
<a href="/">Accueil</a><span aria-hidden="true"> · </span>
|
||||
<a href="/editions/">Carte des œuvres</a><span aria-hidden="true"> · </span>
|
||||
<a href="/methode/">Méthode</a><span aria-hidden="true"> · </span>
|
||||
<a href="/recherche/">Recherche</a><span aria-hidden="true"> · </span>
|
||||
<a href="/archicrat-ia/">Essai-thèse</a><span aria-hidden="true"> · </span>
|
||||
<a href="/traite/">Traité</a><span aria-hidden="true"> · </span>
|
||||
<a href="/ia/">Cas IA</a><span aria-hidden="true"> · </span>
|
||||
<a href="/glossaire/">Glossaire</a><span aria-hidden="true"> · </span>
|
||||
<a href="/atlas/">Atlas</a>
|
||||
</nav>
|
||||
<a href="/" aria-current={isActive("/") ? "page" : undefined}>Accueil</a>
|
||||
<span aria-hidden="true"> · </span>
|
||||
|
||||
<a href="/manifeste/" aria-current={isActive("/manifeste/") ? "page" : undefined}>Manifeste</a>
|
||||
<span aria-hidden="true"> · </span>
|
||||
|
||||
<a href="/archicrat-ia/" aria-current={isActive("/archicrat-ia/") ? "page" : undefined}>Essai-thèse</a>
|
||||
<span aria-hidden="true"> · </span>
|
||||
|
||||
<a href="/cas-ia/" aria-current={isActive("/cas-ia/") ? "page" : undefined}>Gouvernance IA</a>
|
||||
<span aria-hidden="true"> · </span>
|
||||
|
||||
<a href="/glossaire/" aria-current={isActive("/glossaire/") ? "page" : undefined}>Glossaire</a>
|
||||
<span aria-hidden="true"> · </span>
|
||||
|
||||
<a href="/recherche/" aria-current={isActive("/recherche/") ? "page" : undefined}>Recherche</a>
|
||||
</nav>
|
||||
145
src/content.config.ts
Normal file
145
src/content.config.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { defineCollection, z } from "astro:content";
|
||||
|
||||
const linkSchema = z.object({
|
||||
type: z.enum(["definition", "appui", "transposition"]),
|
||||
target: z.string().min(1),
|
||||
note: z.string().optional()
|
||||
});
|
||||
|
||||
const baseTextSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
level: z.union([z.literal(1), z.literal(2), z.literal(3)]).default(1),
|
||||
version: z.string().min(1),
|
||||
concepts: z.array(z.string().min(1)).default([]),
|
||||
links: z.array(linkSchema).default([]),
|
||||
order: z.number().int().nonnegative().optional(),
|
||||
summary: z.string().optional()
|
||||
});
|
||||
|
||||
// Éditions (séparation stricte : edition + status verrouillés par collection)
|
||||
|
||||
const casIa = defineCollection({
|
||||
type: "content",
|
||||
schema: baseTextSchema.extend({
|
||||
edition: z.literal("cas-ia"),
|
||||
status: z.literal("application")
|
||||
})
|
||||
});
|
||||
|
||||
const commencer = defineCollection({
|
||||
type: "content",
|
||||
schema: baseTextSchema.extend({
|
||||
edition: z.literal("commencer"),
|
||||
status: z.union([z.literal("presentation"), z.literal("draft")])
|
||||
})
|
||||
});
|
||||
|
||||
// ✅ NOUVELLE collection : archicrat-ia (Essai-thèse)
|
||||
// NOTE : on accepte temporairement edition/status "archicratie/modele_sociopolitique"
|
||||
// si tes MDX n’ont pas encore été normalisés.
|
||||
// Quand tu voudras "strict", on passera à edition="archicrat-ia" status="essai_these"
|
||||
// + update frontmatter des 7 fichiers.
|
||||
const archicratIa = defineCollection({
|
||||
type: "content",
|
||||
schema: baseTextSchema.extend({
|
||||
edition: z.union([z.literal("archicrat-ia"), z.literal("archicratie")]),
|
||||
status: z.union([z.literal("essai_these"), z.literal("modele_sociopolitique")])
|
||||
})
|
||||
});
|
||||
|
||||
const glossaryNavigationFlowSchema = z.object({
|
||||
label: z.string().min(1),
|
||||
primaryNext: z.string().min(1).optional(),
|
||||
primaryReason: z.string().min(1).optional(),
|
||||
});
|
||||
|
||||
const glossaryNavigationSchema = z.object({
|
||||
primaryNext: z.string().min(1).optional(),
|
||||
primaryReason: z.string().min(1).optional(),
|
||||
paths: z
|
||||
.object({
|
||||
understand: z.array(z.string().min(1)).default([]),
|
||||
deepen: z.array(z.string().min(1)).default([]),
|
||||
compare: z.array(z.string().min(1)).default([]),
|
||||
apply: z.array(z.string().min(1)).default([]),
|
||||
})
|
||||
.default({
|
||||
understand: [],
|
||||
deepen: [],
|
||||
compare: [],
|
||||
apply: [],
|
||||
}),
|
||||
flows: z
|
||||
.record(z.string(), glossaryNavigationFlowSchema)
|
||||
.default({}),
|
||||
relationWeights: z
|
||||
.record(z.string(), z.number().int().nonnegative())
|
||||
.default({}),
|
||||
});
|
||||
|
||||
// Glossaire (référentiel terminologique)
|
||||
const glossaire = defineCollection({
|
||||
type: "content",
|
||||
schema: z.object({
|
||||
title: z.string().min(1),
|
||||
term: z.string().min(1),
|
||||
aliases: z.array(z.string().min(1)).default([]),
|
||||
urlAliases: z
|
||||
.array(z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/))
|
||||
.default([]),
|
||||
mobilizedAuthors: z.array(z.string().min(1)).default([]),
|
||||
comparisonTraditions: z.array(z.string().min(1)).default([]),
|
||||
edition: z.literal("glossaire"),
|
||||
status: z.literal("referentiel"),
|
||||
version: z.string().min(1),
|
||||
definitionShort: z.string().min(1),
|
||||
concepts: z.array(z.string().min(1)).default([]),
|
||||
links: z.array(linkSchema).default([]),
|
||||
|
||||
kind: z.enum([
|
||||
"concept",
|
||||
"topologie",
|
||||
"diagnostic",
|
||||
"verbe",
|
||||
"paradigme",
|
||||
"doctrine",
|
||||
"dispositif",
|
||||
"figure",
|
||||
"qualification",
|
||||
"epistemologie",
|
||||
]),
|
||||
family: z.enum([
|
||||
"concept-fondamental",
|
||||
"scene",
|
||||
"dynamique",
|
||||
"pathologie",
|
||||
"topologie",
|
||||
"meta-regime",
|
||||
"paradigme",
|
||||
"doctrine",
|
||||
"verbe",
|
||||
"dispositif-ia",
|
||||
"dispositif-methodologique",
|
||||
"dispositif-documentaire",
|
||||
"tension-irreductible",
|
||||
"figure",
|
||||
"qualification",
|
||||
"epistemologie"
|
||||
]
|
||||
)
|
||||
.optional(),
|
||||
domain: z.enum(["transversal", "theorie", "cas-ia"]),
|
||||
level: z.enum(["fondamental", "intermediaire", "avance"]),
|
||||
related: z.array(z.string().min(1)).default([]),
|
||||
opposedTo: z.array(z.string().min(1)).default([]),
|
||||
seeAlso: z.array(z.string().min(1)).default([]),
|
||||
navigation: glossaryNavigationSchema.optional()
|
||||
})
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
commencer,
|
||||
"archicrat-ia": archicratIa,
|
||||
"cas-ia": casIa,
|
||||
glossaire,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,251 +1,471 @@
|
||||
---
|
||||
title: "Conclusion — ArchiCraT-IA"
|
||||
edition: "archicratie"
|
||||
status: "modele_sociopolitique"
|
||||
title: Conclusion — L’exigence archicratique
|
||||
edition: archicrat-ia
|
||||
status: essai_these
|
||||
level: 1
|
||||
version: "0.1.0"
|
||||
version: 0.1.0
|
||||
concepts: []
|
||||
links: []
|
||||
order: 70
|
||||
summary: ""
|
||||
summary: ''
|
||||
source:
|
||||
kind: docx
|
||||
path: "sources/docx/archicrat-ia/Conclusion-Archicrat-IA-version_officielle.docx"
|
||||
path: sources/docx/archicrat-ia/Conclusion—Archicratie-La_tenue_des_mondes-version_resserree.docx
|
||||
---
|
||||
Nous n’assistons ni à un retour primordial du désordre ni à une raréfaction du pouvoir. Nous assistons à autre chose, plus discret et plus décisif : la montée en régime d’une régulation sans scène, où la plupart des décisions qui engagent nos vies sont prises et appliquées sans lieu institué d’énonciation, sans temps de différé, sans exposition reconnaissable de leurs auteurs. La conflictualité ne disparaît pas, elle est déportée : reconfigurée en flux, en scores, en interfaces, en procédures qui se présentent comme de simples enchaînements techniques, alors même qu’elles opèrent des choix normatifs massifs.
|
||||
|
||||
Ce manque n’est pas un vide. Il ne signifie ni l’absence de normes, ni l’effondrement du pouvoir, mais ce que nous avons appelé, dans cet essai-thèse, une *oblitération archicratique* : le recouvrement progressif de la scène par la seule *cratialité*, c’est-à-dire par des dispositifs d’exécution qui se substituent à la mise en débat des fondements et des effets. Ce n’est pas le pouvoir qui manque, c’est la possibilité de le voir, de le nommer, de l’adresser. La régulation se donne alors comme évidence fonctionnelle, là où elle devrait apparaître comme ce qu’elle est toujours : un ordre situé, contestable et révisable en respect des promesses démocratiques.
|
||||
|
||||
La thèse défendue ici n’est pas que toute scène aurait disparu, ni que l’histoire se réduirait à un glissement univoque vers l’automatisation intégrale. Elle est plus précise et plus falsifiable : dans les principaux dispositifs contemporains – sociaux, écologiques, numériques –, les conditions d’existence d’une scène archicrative sont systématiquement affaiblies, fragmentées, reléguées à la marge. Là où l’on pouvait autrefois identifier un lieu, un temps, des personnes et des procédures pour organiser et mettre à l’épreuve les décisions, nous rencontrons désormais, de manière récurrente, des agencements où l’énonciation s’efface derrière la mesure, la simulation, la plateformisation et l’automatisation.
|
||||
|
||||
C’est pourquoi nous avons forgé la notion d’*oblitération archicratique* : non pas l’absence de tout recours, mais la tendance structurelle à produire de la norme hors scène, à démultiplier les dispositifs auto-référés – architectures, algorithmes, barèmes, chaînes logistiques – les supports d’une régulation qui se déclare purement opératoire, *autarchicratique*. Cette hypothèse est réfutable : elle serait démentie si l’on observait, de façon durable, que les grandes décisions collectives demeurent adossées à des scènes d’épreuve robustes, effectives, où les fondements et les effets peuvent être mis en discussion. Elle gagnerait, inversement, en pertinence si des enquêtes empiriques mettent en évidence l’inaccessibilité des recours, l’ineffectivité des délibérations politiques, la surdétermination des décisions par des scripts non publics protégés par le droit des affaires.
|
||||
|
||||
Dans cette perspective, l’*archicratie* n’est ni un idéal abstrait, ni un type de régime supplémentaire qu’il s’agirait d’ajouter à la liste déjà longue des formes politiques. Elle désigne la *condition minimale pour que la régulation soit habitable* : la possibilité, pour une société, d’instituer et de maintenir un seuil où l’*arcalité* (ce qui fonde et justifie), la *cratialité* (ce qui opère et exécute) et l’*archicration* (ce qui met en épreuve, suspend, requalifie) demeurent distinctes, articulées et exposées. Un ordre est archicratique en ce sens précis : non parce qu’il serait juste ou égalitaire par essence, mais parce qu’il se laisse amener en scène, devant des instances où ses prétentions peuvent être critiquées, ses instruments retravaillés, ses effets révisés.
|
||||
|
||||
L’enjeu de cette conclusion est de formuler une hypothèse régulatrice : la *co-viabilité* des collectifs humains et non-humains dépend de la capacité à maintenir, à rouvrir, à inventer des scènes d’épreuve où l’on puisse adresser le pouvoir qui nous affecte. En nommant l’*archicratie*, nous ne sacralisons pas un modèle ; nous désignons un seuil au-dessous duquel la régulation se dégrade en pure opération, et au-dessus duquel elle devient au moins discutable, opposable, révisable, modifiable, différant.
|
||||
|
||||
C’est là seulement que prend sens le projet d’une “cinquième révolution régulatrice”. Non pas un nouvel âge d’or, ni une résolution miraculeuse des contradictions, mais un changement de régime dans la manière même de fabriquer la régulation : un tournant où la scène – longtemps tenue pour « simple décor du pouvoir », ou pour « luxe démocratique » – redeviendrait son opérateur central. L’ambition de ce texte n’est pas de prédire cette révolution, et moins encore de la prescrire. Il s’agit de montrer en quel sens elle est pensable, souhaitable, et sous quelles conditions elle pourrait être mise à l’épreuve et mise en pratique.
|
||||
|
||||
Ce que nous observons n’est ni une défaillance accidentelle du cadre démocratique, ni une déviation autoritaire des gouvernances contemporaines, mais une mutation profonde des formes de régulation effective qui tendent à se configurer hors-scène démocratique instituée. Dans ce modèle, le pouvoir tend à se déporter hors scène instituée : privatisé, déterritorialisé, encapsulé, automatisé, rendu algorithme, crypté, incompréhensible et incritiquable du fait de sa propre efficacité. Il ne s’explique plus, il fonctionne. Il ne s’adresse plus, il déclenche. Il ne fonde plus, il applique. Et la régulation tend à se déporter hors scène instituée, tandis que persistent des scènes partielles, alternatives ou multilatérales qu’il faut reconnaître et saluer. Que ce soit la méta-fédération des soulèvements de la Terre, ou l’initiative internationale PauseIA, pour ne nommer que ces deux, leurs discours et leurs expertises apportent informations et discernements sur les enjeux écologiques et les développements d’intelligences artificielles.
|
||||
|
||||
Le processus est d’autant plus difficile à saisir qu’il est continu, fonctionnel, présenté comme neutre. Les interfaces remplacent les guichets, les tableaux de bord supplantent les délibérations, les retours algorithmiques prennent la place de la justification publique. Le conflit est désamorcé au nom de la fluidité. Le délai est perçu comme dysfonction. L’exposition est dégradée en transparence visuelle sans énoncé.
|
||||
|
||||
Le terme d’oblitération archicratique, proposé et conceptualisé dans les chapitres précédents, désigne exactement cette dynamique : une substitution douce, mais décisive, de la scène par l’exécution, du différé par l’automaticité, de l’énonciation par la trace, de l’épreuve par la donnée. Cette oblitération ne se donne pas comme rupture, mais comme évidence : elle agit dans la logique même des dispositifs, dans leur conception, leur design, leur temporalité.
|
||||
|
||||
Il faut ici refuser toute simplification. Si la scène instituée se raréfie, des scènes latérales, résiduelles ou émergentes persistent (communautés locales, contre-institutions, pratiques rituelles), il est grand temps de les reconnaître et de les légitimer au lieu de les folkloriser. Ce monde sans scène n’est ni totalement anarchique, ni totalement chaotique, ni même nécessairement injuste dans ses effets immédiats. Il est, en revanche, injustifiable. Il est insoutenable dans le temps, car il est aveugle à sa propre normativité. Il ne dispose d’aucun espace dans lequel ses propres décisions peuvent être rejouées, exposées, interrogées, rediscutées ou reformulées.
|
||||
|
||||
Cette régulation sans scène est une régulation sans mémoire, sans récit, sans réversibilité. Elle fonctionne, et souvent très efficacement. Mais elle ne critique rien, ne se confronte à rien, ne se relance jamais à partir de ses propres limites. Elle produit de la norme sans fondement énoncé, du pilotage sans institution, de la gouvernance sans fondation, un pouvoir simulé à tout moment proche du simulacre.
|
||||
|
||||
Les conséquences sont majeures. On croit pouvoir protester, mais l’interlocuteur est absent. On tente un recours, mais le délai est dépassé avant même d’avoir commencé. On cherche le fondement d’une décision, mais on ne trouve qu’un critère, un score, une règle d’application sans justification. Derrière la façade d’efficacité, ce qui s’installe, c’est un monde post-scénique, dans lequel les gouvernés n’ont plus d’espace institué pour adresser la régulation qui les constitue.
|
||||
|
||||
Ce diagnostic n’est pas une vérité révélée, mais une hypothèse régulatrice : il postule que, dans les principaux dispositifs contemporains (sociaux, écologiques, numériques), l’instance scénique a été reléguée au profit d’une *cratialité autarcique*. Il pourrait être réfuté par des recherches qui montreraient l’inverse, par exemple des domaines où des scènes archicratives fortes subsistent et structurent réellement les décisions. Il serait confirmé, au contraire, par des enquêtes qui objectiveraient l’inaccessibilité des scènes, l’ineffectivité des recours, la pure performativité des algorithmes.
|
||||
|
||||
Et c’est précisément là que s’ouvre une nécessité, celle de refonder la scène comme condition première de toute co-viabilité. Une scène véritable : espace différé, public, opposable, fondé — nullement décorative ni consultative. Une scène où la décision peut être relancée. Où le fondement peut être demandé. Où le délai est un droit. Où l’épreuve est reconnue comme condition de la légitimité.
|
||||
|
||||
Cette scène, c’est ce que nous avons nommé, construit, modélisé et mis à l’épreuve : l’*archicration*. Et c’est depuis son absence — criante et tout à la fois quasi imperceptible — que la cinquième révolution régulatrice devient pensable et indispensable.
|
||||
|
||||
Au seuil d’une ère où la capacité à ordonner, à segmenter, à attribuer ou à refuser se maintient sans que jamais ces gestes soient exposés à une scène d’épreuve instituée, la nécessité d’un concept opératoire, capable de désigner la forme régulatrice minimale d’un monde habitable, s’impose. Ce concept, l’*archicratie*, nous l’avons forgé, au-delà de la nécessité d’invention lexicale, comme condensation critique d’un constat systémique.
|
||||
|
||||
L’*archicratie* ne renvoie ni à un type de régime spécifique, ni à une forme de gouvernement, ni même à une institution particulière. Elle ne se laisse classer dans aucune catégorie traditionnelle de la philosophie politique, car elle opère en amont des structures représentatives, et en-deça et au-delà des appareils juridiques. Elle est le mode de consistance scénique d’une régulation soutenable, c’est-à-dire d’un ordre qui accepte d’être mis en épreuve, d’être différé dans son exécution, d’être reconfiguré à partir de sa propre exposition sous critères de viabilité. Il s’agit donc du régime d’apparition du pouvoir fondé, là où une décision peut être tenue, dans le temps et dans l’espace, devant ceux qu’elle affecte, au sein d’une scène constituée pour qu’elle puisse être questionnée, retardée, amendée, voire annulée.
|
||||
|
||||
Dans cette perspective, l’*archicratie* n’est ni utopie ni abstraction : elle fixe la condition de viabilité de tout dispositif qui prétend réguler — affecter des conduites, orienter des possibles, ouvrir/fermer des accès — sans se refermer sur une logique exclusivement opératoire. Il ne s’agit pas ici de défendre une figure morale du pouvoir, mais de poser une exigence structurelle : aucune régulation n’est légitime si elle ne peut être exposée à son propre fondement. Et aucune scène d’exposition n’est opérante si elle ne permet pas la convocation des énonciateurs, la traçabilité des instruments, la suspension des effets, la confrontation des justifications et la reconfiguration des décisions.
|
||||
|
||||
L’*archicratie* devient ainsi le seuil entre une régulation purement fonctionnelle, capable d’exécuter des procédures, de reproduire des normes, de gérer des flux automatisés et une régulation fondée, visible, opposable, justifiable, révisable — c’est-à-dire co-viable décidée par des humains.
|
||||
|
||||
Comme annoncé dans le Prologue, cette conclusion opère la remontée critique à partir des cinq épreuves de notre essai-thèse : (1) l’épreuve de détectabilité des régimes (chapitre 1), (2) l’archéogenèse des formes scéniques de régulation (chapitre 2), (3) la morphologie des régimes de pouvoir (chapitre 3), (4) la généalogie des révolutions régulatrices (chapitre 4), et (5) les tensions de co-viabilité au sein des dispositifs contemporains (chapitre 5).
|
||||
|
||||
Pour penser ce seuil, notre essai-thèse a posé les conditions de détection d’une régulation minimale, structurée autour de trois prises fondamentales, dont la coprésence différenciée et articulée constitue la grammaire de toute régulation habitée. Ces trois prises — *arcalité, cratialité, archicration* — ne sont pas des dimensions logiques. Elles sont des formes d’existence différenciées de l’ordre régulateur.
|
||||
|
||||
Comme nous l’avons vu, l’*arcalité* désigne la condition d’énonciation du pouvoir. Elle ne se réduit pas à une source légale ou à une tradition instituée. Elle renvoie à la capacité d’un dispositif à exposer son propre fondement comme fondement, à dire : "voici sur quoi nous agissons, pourquoi, selon quelle vision du monde, selon quelle fiction ou axiome opératoire". Dans une régulation archicratique, l’*arcalité* est convoquée, nommée, rendue visible, tenue.
|
||||
|
||||
La *cratialité*, quant à elle, n’est pas simplement l’opérativité technique. Elle est l’ensemble des vecteurs concrets d’application du pouvoir, qu’il s’agisse de normes, d’algorithmes, de barèmes, de codes juridiques, de métriques ou d’interfaces. C’est le pouvoir dans sa matérialité d’exécution. Loin de n’être qu’un détail de mise en œuvre, la *cratialité* constitue une scène en elle-même : celle où les affects se traduisent en effets, celle où le monde se divise, s’organise, se hiérarchise. Toute *cratialité* rendue opaque tend à devenir *hypertopique*.
|
||||
|
||||
Enfin, l’*archicration* désigne la scène elle-même : l’espace-temps de l’épreuve régulatrice. Il ne se réduit pas à un dispositif de concertation ou à un droit de recours formel, mais bien plus à une scène effective, où le délai est institué, où les énoncés sont exposés, où les décisions sont suspendues, et où les parties affectées peuvent agir sur les termes mêmes de la régulation. Une *archicration* est une scène fondée, différée, ouverte, documentée, capable de transformer ce qu’elle expose. Elle se doit donc d’être publique et aucunement privée.
|
||||
|
||||
Ces trois prises ne sont jamais données d’emblée. Elles sont inégalement distribuées, plus ou moins visibles, plus ou moins consolidées. Ce que permet l’*archicratie*, c’est d’en faire un modèle d’analyse différentielle, capable d’évaluer non ce que les institutions disent d’elles-mêmes, mais ce qu’elles rendent effectivement détectable, contestable, modifiable.
|
||||
|
||||
C’est à ce titre que nous avons élaboré une axiomatique de la détectabilité régulatrice, qui ne repose pas sur la conformité juridique, mais sur des critères structurels : la coprésence des prises ; leur différenciation fonctionnelle ; la publicité du fondement ; l’opposabilité des normes ; la révision périodique ; le droit au différé contradictoire.
|
||||
|
||||
Ce modèle ne s’oppose pas aux régimes existants. Il les requalifie selon une grammaire de viabilité. Il ne dit pas ce qui est souhaitable. Il indique ce qui est soutenable.
|
||||
|
||||
Au terme de ce parcours, notre essai-thèse propose d’abord une *grammaire minimale de la régulation habitable*, en distinguant rigoureusement *arcalité, cratialité* et *archicration* comme trois prises irréductibles mais articulables du pouvoir. Il offre ensuite une archéogenèse comparée des méta-régimes régulateurs, qui permet de lire des configurations très hétérogènes – proto-symboliques, scripturaires, marchandes, guerrières, techno-logistiques – sans les rabattre sur le seul État, le seul marché ou la seule souveraineté. Il recompose en outre les “révolutions industrielles” comme des *révolutions régulatrices*, c’est-à-dire comme des histoires différentielles de la scène et de son oblitération, jusqu’au diagnostic contemporain d’*autarchicratie* et de *régulation hors scène*. Enfin, il élabore une politique des épreuves viables, en substituant à la fiction de la durabilité le paradigme de la *co-viabilité archicratique*, et en proposant quelques gestes concrets par lesquels cette exigence pourrait être mise à l’épreuve dans les institutions, les territoires et les dispositifs numériques.
|
||||
|
||||
Là réside le geste décisif : faire de l’*archicratie* une condition de lisibilité du pouvoir, dans un monde où les décisions sont de plus en plus produites sans fondement explicité, en accès et décision privés, appliquées sans seuil d’entrée et exécutées sans scène d’exposition explicite (algorithmes de recommandations, IA générative, etc.).
|
||||
|
||||
En nommant l’*archicratie*, ce que nous faisons, ce n’est pas inventer une utopie : c’est dégager, à partir des marges critiques de la modernité régulatrice, un principe de consistance qui permet de discerner ce qui peut encore être appelé régulation — et ce qui ne relève plus que de l’automatisation du monde, son *autarchicratie*.
|
||||
|
||||
Comme l’écrivait Claude Lefort, “le lieu du pouvoir est vide, et le pouvoir est un lieu duquel personne ne peut s’arroger la propriété” : c’est ce vide fondateur qui, sans scène, devient mutisme algorithmique. L’archicration ne cherche pas à combler ce vide, mais à en réinstituer l’adresse. Car l’*archicratie* n’est pas l’alternative à un système. Elle est ce qui rend possible le fait même qu’un système puisse tenir, mais aussi être interrompu, contesté et refondé. Elle est la condition minimale de tout ordre qui ne veut pas être sa propre clôture et sa pure et propre reproduction sociale.
|
||||
|
||||
Ce que nous avons appelé scène n’est ni une métaphore théâtrale, ni un artifice rhétorique, ni un dispositif de communication. Elle n’est pas le lieu décoratif du pouvoir. Elle n’est pas ce que l’on ajoute, une fois la régulation pensée, pour en valider symboliquement l’acceptabilité. Elle est, à l’inverse, la matrice originaire d’où émerge toute forme de régulation vivable, bien avant l’État, bien avant le droit, et en dehors des grammaires modernes du gouvernement.
|
||||
|
||||
L’histoire politique des sociétés humaines ne commence pas avec l’institution étatique. Elle commence par l’apparition de lieux différés dans lesquels le pouvoir se donne à voir, se laisse convoquer, se met en épreuve. Partout où l’on observe la mise en forme de relations collectives durables, on trouve, en amont de la codification, des scènes d’ajournement et de dispute — des espaces de rituel, de conseil, de circulation de paroles, de suspension du geste immédiat.
|
||||
|
||||
Les premières figures de la régulation ne sont pas des lois, mais des cercles. Ce sont des feux autour desquels on raconte ce qui a été fait, ce qui pourrait être fait autrement, ce qui doit être décidé ensemble. Ce sont des seuils marqués, des temps de deuil ou de conflit ajourné, des prises de parole dans l’épure, où l’événement est reconvoqué dans un espace plus grand que lui. C’est ici que s’institue, sans formalisation juridique, la première *archicration* : une scène anthropologique de différé, de dispute et de co-présence du conflit.
|
||||
|
||||
L’archéogenèse de la scène, telle que nous l’avons retracé, ne vise pas à plaquer une origine sur le concept d’*archicratie*. Elle permet de montrer que l’exposition du pouvoir au regard des autres n’est pas un luxe moderne, mais une fonction vitale de toute organisation collective. On ne fonde pas une cité parce que l’on a établi une loi ; on fonde une cité parce que l’on a produit un lieu dans lequel des lois pouvaient être explicitées, différées, adressées et même révisées.
|
||||
|
||||
Les régimes que nous considérons comme proto-politiques ne reposent pas sur des appareils coercitifs permanents, mais sur des dispositifs de régulation publique des différends. Ce que l’on appelait jadis “conseil des anciens”, “assemblée des vivants”, “place de palabres”, “moment du jugement”, sont autant de scènes dans lesquelles une communauté suspend l’exécution brute des normes pour produire, en commun, un réexamen des conditions du vivre-ensemble.
|
||||
|
||||
Il y a ici un renversement majeur : ce n’est pas la violence qui précède l’ordre, mais la scène qui ajourne la violence par son existence et son efficience. Loin d’être un ornement cérémoniel, la scène permet de transformer une décision en acte politique, en la soumettant à un espace d’apparition partagé. On ne gouverne pas parce qu’on détient un pouvoir, on gouverne parce que l’on accepte d’exposer le pouvoir dans un lieu où il peut être contesté.
|
||||
|
||||
Et ce geste ne disparaît pas avec la modernité : il se déplace, se reconfigure, parfois se cache. Les formes scéniques de l’époque moderne — parlements, tribunaux, conseils, consultations — ne sont pas des innovations *ex nihilo*, mais des transpositions institutionnelles d’une exigence anthropologique fondamentale : rendre le pouvoir visible dans un espace différé, public, contradictoire pour le légitimer dans son autorité.
|
||||
|
||||
Ce que nous appelons aujourd’hui *archicratie*, à travers le triptyque *arcalité-cratialité-archicration*, ne naît donc pas avec la gouvernance algorithmique, ni avec la crise contemporaine des institutions : c’est le nom que nous donnons à une exigence anthropologique longue — rendre le pouvoir visible dans un espace différé, public, contradictoire — que les mutations modernes ont partiellement codifiée, mais jamais totalement effacée. Même les régimes autoritaires, même les appareils technocratiques les plus opaques, tentent de simuler la scène, d’en mimer les apparences, comme si toute régulation devait, d’une manière ou d’une autre, s’appuyer sur une forme scénique minimale, fût-elle falsifiée, spectaculaire, dé-saisissante ou accablante.
|
||||
|
||||
L’effondrement actuel, tel que nous le décrivons, ne réside pas dans une rupture historique absolue, mais dans une disjonction cumulative, où la régulation devient performative, prédictive, auto-exécutive, tout en prétendant conserver l’apparence de la scène. Ce que les plateformes administratives, les interfaces de recours, les simulateurs d’opinion, les visualisations de données simulent, ce n’est pas la norme — c’est le différé. Ce n’est pas la décision — c’est la contestation.
|
||||
|
||||
En ce sens, réinstituer la scène aujourd’hui revient à réactiver une fonction anthropologique enfouie, plutôt qu’à restaurer une forme passée enfouie sous les couches technocratiques, une fonction sans laquelle aucun ordre collectif ne peut être su, ajusté, ni corrigé, ni habité. Nous ne parlons pas ici de restauration, plutôt d’un geste de fondation renouvelé : retrouver la capacité de construire des espaces différés d’apparition du pouvoir, où la norme peut être visible, où la décision peut être suspendue, où l’effet peut être discuté, et où le fondement peut être relancé.
|
||||
|
||||
L’histoire de la scène est celle de l’ajournement du pouvoir brut au nom d’une *co-présence* instituée du conflit. Et c’est à cette hauteur anthropologique que l’*archicratie* trouve son origine : non dans une forme constitutionnelle, mais dans la structure même du fait politique, dès lors qu’il accepte de s’exposer à autre chose que sa propre exécution.
|
||||
|
||||
Ce que nous appelons *co-viabilité* n’est pensable que dans cette tension : entre un pouvoir qui affecte, et un espace où cette affectation peut être exposée. La scène est le seul lieu où cette tension devient habitable, c’est-à-dire vivable, réformable, traversable. Sans scène, ce sont alors des processus, des opérations, des machines qui dominent.
|
||||
|
||||
C’est pourquoi, dans notre essai-thèse, l’*archicration* n’est jamais une solution institutionnelle, ni une forme de gouvernement. Elle est la condition minimale d’un monde capable de s’interrompre pour se refonder. Elle est ce qui permet, encore et toujours, de revenir sur ce qui est en train de s’imposer. Elle est le lieu de surgissement de la viabilité.
|
||||
|
||||
Et c’est à partir de cette scène — non comme décor, mais comme forme originaire de la dispute différée — que peut se penser la cinquième révolution régulatrice, non comme innovation technologique, mais comme retour conscient à une exigence anthropologique que l’histoire n’a jamais pu dissoudre.
|
||||
|
||||
Il n’est désormais plus possible d’analyser la question de la régulation sans la confronter à ses deux périls contemporains les plus massifs, les plus urgents, les plus irréversibles : la désintégration sociale et l’inhabitation écologique. Ces deux lignes de fracture ne constituent pas des petits “sujets” parmi d’autres, ni même des crises externes au champ de la régulation. Elles sont les lieux mêmes où se joue la soutenabilité du monde commun, et à partir desquels se manifeste avec la plus grande intensité la nécessité d’une scène archicratique instituée.
|
||||
|
||||
D’un côté, les sociétés contemporaines sont travaillées par une fragmentation socio-économique accélérée, où les droits, les protections, les accès aux ressources, à la santé, à l’habitat, au travail, à la dignité même, sont de plus en plus conditionnés par des régimes de critères invisibles, des barèmes automatisés, des normes silencieuses dont la contestation n’a ni lieu ni délai. Ce que produit cette fragmentation, ce n’est pas simplement de l’injustice – c’est une dés-institution du social, un abandon de la possibilité de fonder l’ordre sur une scène où les règles peuvent être exposées, comprises, opposées, amendées.
|
||||
|
||||
De l’autre côté, l’environnement planétaire est soumis à un processus de désintégration physique, chimique, biologique, systémique, dont les causes sont connues, mesurées, modélisées — mais dont la régulation demeure hors scène. Les grands instruments du pilotage écologique (quotas carbone, marché de droits d’émission, régulations incitatives, taxonomies vertes, solutions technologiques à haute intensité énergétique) ne répondent pas à une scène fondée de délibération environnementale. Ils opèrent à partir d’indicateurs pré-déterminés, de modèles économétriques rendus irréfutables, d’institutions fermées dont les critères ne sont ni exposés, ni opposables.
|
||||
|
||||
Dans les deux cas, ce que l’on appelle régulation n’est souvent rien d’autre qu’un régime d’administration automatique de la catastrophe, une façon de moduler les seuils d’acceptabilité de l’inacceptable, de stabiliser temporairement des déséquilibres massifs sans jamais exposer les choix, les arbitrages, les priorités, à une scène d’épreuve collective.
|
||||
|
||||
Le mot “durabilité”, en ce sens, est devenu l’un des opérateurs les plus puissants de cette fiction régulatrice. Il donne à croire qu’un monde peut être maintenu dans son état actuel par l’ajustement de ses paramètres – sans refondation, sans débat, sans conflit institué. On parle de transition, mais sans définir qui en décide, selon quels délais, avec quelles conséquences, sur quels critères effectifs et crédibles. On évoque l’empreinte carbone, sans jamais dire ce que cela signifie politiquement, historiquement, anthropologiquement. Le terme de “durabilité” produit ainsi un effet de clôture : il neutralise la conflictualité fondatrice de toute scène régulatrice, en la remplaçant par un récit d’optimisation continue, une fable pseudo-scientifique.
|
||||
|
||||
Ce que notre essai-thèse oppose à cette fiction, ce n’est pas une autre gestion du risque, ni une autre version du développement : c’est une refondation de la question régulatrice à partir du paradigme de la co-viabilité. Et cette refondation ne peut s’opérer que dans une configuration archicratique, c’est-à-dire dans une scène fondée où la viabilité ne se décrète pas, mais s’éprouve, se dispute, se rejoue, se transforme.
|
||||
|
||||
Celle-ci ne peut être réduite à la compatibilité des intérêts humains et non-humains, ni à une coexistence pacifiée entre agents différenciés. Elle n’est pas un horizon harmonique. Elle est un régime d’épreuves instituées, dans lequel les formes du vivant, des milieux, des infrastructures, des symboles, peuvent être convoquées dans des scènes de régulation explicite, différée, révisable.
|
||||
|
||||
Ce que cela signifie, très concrètement, c’est que les arbitrages sur l’eau, l’énergie, la mobilité, le territoire, la réparation, le soin, ne peuvent plus être externalisés dans des modèles d’impact ou des courbes d’efficacité. Ils doivent être rapportés à une scène, où l’on peut interroger non seulement les effets des décisions, mais leurs fondements, leurs seuils, leurs instruments, leurs alternatives.
|
||||
|
||||
L’*archicration* écologique prend corps dans des scènes territoriales où les habitants font apparaître les conditions d’habitabilité, contestent les infrastructures et proposent des configurations de subsistance — bien au-delà des conférences quinquennales et des tableaux de bord numériques d’objectifs carbone ; elle suppose une arcalisation du vivant (reconnaissance des entités existantes), une cratialisation des milieux (identification des champs de forces et d’agissements), une archicration du conflit, autrement dit une politisation complète de ce qui, trop longtemps, a été traité comme données environnementales inertes.
|
||||
|
||||
Sur le plan social, la logique est la même. Là où les critères d’attribution des droits deviennent des scripts inaccessibles, là où les plateformes d’accès remplacent les guichets, là où les aides sociales sont suspendues sans énoncé, là où les publics fragiles sont évalués par des algorithmes de “comportement à risque”, le droit s’automatise, la scène disparaît, la dignité s’efface.
|
||||
|
||||
Ce n’est pas une question d’inefficacité, ni même d’injustice procédurale : c’est une désactivation du régime de reconnaissance. Car un droit qui ne peut plus être justifié publiquement, réclamé dans une scène, opposé dans un délai, contesté par une présence, n’est plus un droit. C’est une variable. Une allocation. Un flux à tout moment suspensif.
|
||||
|
||||
Réinstituer l’*archicration* sociale, c’est donc reconstruire la scène de l’accès au droit — non pas à travers des démarches participatives formelles, mais à travers des dispositifs d’exposition des critères, des formes de visibilité des seuils, des délais contradictoires obligatoires, des lieux de confrontation réels, soutenus, équipés, où l’on peut dire : “Ce n’est pas acceptable. Voici pourquoi. Voici ce que je propose.” C’est là, dans ces scènes de l’interpellation, que la co-viabilité sociale devient pensable.
|
||||
|
||||
Il ne s’agit donc pas de “réconcilier” social et écologie, mais de comprendre qu’ils sont deux effets d’une même oblitération scénique, et que leur relance ne peut passer que par la reconstruction des conditions archicratiques du désaccord institué.
|
||||
|
||||
C’est ici que l’hypothèse archicratique devient non plus un outil critique, mais une proposition civilisationnelle. Car un monde durablement privé de scènes archicratives tend à devenir aveugle à sa propre destinée. Un monde incapable de suspendre ce qu’il fait. Incapable d’en répondre. Incapable de s’ajuster depuis elles et ceux qu’il affecte.
|
||||
|
||||
À cette cécité organisée, la *co-viabilité* oppose la lumière rugueuse de l’exposition et du différé, la présence irréductible des vivants, la scène ouverte des conflits fondateurs canalisés par les récits de vie. Et c’est à partir de cette tension — vitale et irréductible — que peut encore s’inventer un devenir commun concerté fondé sur nos vulnérabilités, nos résiliences et nos robustesses conscientisées et politisées.
|
||||
|
||||
La régulation contemporaine, dans sa formulation dominante, se présente sous les atours rassurants d’un horizon consensuel : celui de la durabilité. Ce terme, devenu mantra technico-politique, irrigue désormais l’intégralité des champs du discours institutionnel — des traités internationaux aux chartes locales, des stratégies d’entreprise aux programmes éducatifs, des plans d’action gouvernementaux aux critères d’investissement privé. Il semble à la fois évident et indiscutable, fédérateur et apaisant. À son contact, les conflits s’effacent, les alternatives se suspendent, la temporalité se normalise, et la gouvernance acquiert une forme de légitimité indolore, anesthésiante, presque tautologique. Au point que ce qui est durable est ce qui mérite de durer.
|
||||
|
||||
Sous sa surface consensuelle, la “durabilité” se ferme à l’épreuve : elle transforme le dissensus en anomalie et recode la régulation en pilotage continu d’indicateurs non révisables. Le problème n’est pas l’objectif écologique en lui-même, mais la structure régulatrice qui l’impose hors scène, et surtout, dans bien des cas, hors-sol, et bien plus, par cœur ou par calcul. Contre cette clôture, la politique des épreuves viables et soutenables substitue à l’optimisation des variables la publicité des critères recevables, au pilotage continu automatisé le jeu et le différé contradictoire, à la gestion d’impacts et de risques l’énonciation de leurs fondements pour éveiller les raisons — conditions pour que les décisions redeviennent adressables et opposables sous critères de discernement.
|
||||
|
||||
Car jusqu’à présent, les stratégies dites “durables” fonctionnent selon un régime algorithmique d’autorité, où les seuils d’acceptabilité sont déterminés en amont, les priorités dictées par les logiques d’efficience, les variables manipulées sans scène d’exposition. On y parle de neutralité carbone, de trajectoires optimales, de plans de transition, mais jamais de fondement public de ces trajectoires, ni de scène dans laquelle elles pourraient être tenues devant ceux qu’elles affectent. C’est dans cette évacuation de la scène — plus encore que dans le contenu des politiques — que réside le cœur du problème. Ce n’est pas l’objectif de durabilité qui est en cause, c’est la structure régulatrice silencieuse qui le porte, le légitime et l’impose.
|
||||
|
||||
Pour rendre ce mécanisme visible, rappelons-nous que toute régulation suppose deux opérations irréductibles : d’une part, une *normativité explicite* (ce qui doit être régulé, pourquoi, selon quels principes) ; d’autre part, une *opérativité outillée* (comment cette norme s’applique, à travers quels instruments, sur quels objets). Mais entre les deux, il faut une troisième instance, que la durabilité contemporaine tend précisément à effacer : la *scène d’épreuve*. C’est cette scène — différée, fondée, contradictoire — qui permet aux décisions de ne pas s’exécuter à l’abri du regard, aux critères d’être rendus publics, aux seuils d’être contestés, aux affects d’être entendus, aux représentations de monde d’être opposables contre tout arbitraire.
|
||||
|
||||
En ce sens, la durabilité n’est pas qu’une fiction apolitique : elle est le récit qui permet l’exécution sans scène, la régulation sans fondement, la gouvernance sans convocation. Elle naturalise les instruments, en les présentant comme neutres. Elle réduit les choix à des données. Elle transforme la dispute en friction technique. Et ce faisant, elle désarme les communautés, les privant du droit d’ajourner, de relancer, de reformuler ce qui les affecte.
|
||||
|
||||
La politique des épreuves viables, telle que nous la proposons ici, ne consiste pas à rejeter les impératifs écologiques et sociaux. Elle ne nie pas la nécessité de limites, ni l’urgence de transformations structurelles. Elle refuse simplement que ces transformations soient imposées hors scène, dans le silence d’une régulation désaffectée. Elle postule que toute décision ayant un impact majeur sur les conditions de vie, de subsistance, d’habitation, doit passer par une épreuve fondée, différée, partagée. Elle postule que ce qui n’est pas disputé ne peut être dit durable.
|
||||
|
||||
Une politique des épreuves viables suppose donc une ré-institution intégrale des conditions archicratiques de la décision. Plutôt qu’une réforme des indicateurs, une amélioration des consultations ou une simple transparence des données, il faut une scénarisation complète de la régulation selon une grammaire fondée sur quatre principes irréductibles, que nous avons articulés dans le cadre de notre modèle archicratique.
|
||||
|
||||
Quatre exigences, déjà inscrites dans notre axiomatique, organisent la politique des épreuves viables : le *différé contradictoire*, qui rend l’exécution ajournable ; la *publicité des critères*, qui reconduit tout seuil à son fondement ; l’*opposabilité réelle*, qui ouvre la décision à l’intervention des affectés avant, pendant et après son application ; la *révision périodique*, qui interdit la clôture. Ces exigences ne sont pas des ajouts déontologiques : elles matérialisent l’axiome de détectabilité, confirment la disjonction fonctionnelle des prises arcales, cratiales et archicratiques et prolongent l’épreuve critique comme norme de vitalité du régime.
|
||||
|
||||
Mais ces principes ne peuvent exister qu’à une condition : qu’il y ait une scène. Une scène qui ne se contente ni de spectacle, ni de consultation décorative, ni de simple mise en visibilité : mais bien une scène où le pouvoir se fonde en apparaissant, où l’épreuve est instituée, où la transformation s’engage réellement depuis ceux qu’elle affecte.
|
||||
|
||||
L’exemple des politiques climatiques l’illustre avec une clarté glaçante. Les objectifs de réduction d’émissions sont fixés par des trajectoires macroéconomiques, sans fondement ontologique débattu. Les instruments de marché (droits d’émission, taxes, incitations) sont conçus dans des enceintes d’expertise, sans contre-scène démocratique. Les seuils sont négociés entre États et industries, sans présence des vivants affectés. Résultat : une régulation performante sur le papier, mais fondamentalement désarchicratique. Elle s’exécute sans différé, sans dispute, sans publicité explicite des choix de monde. Là encore, la matrice d’audit du chapitre 1 s’applique : hypotopies (prises faibles sous-déterminantes), hypertopies (prises surdéterminantes), atopies (pseudo-scènes indéterminées), permettant de situer les déficits scéniques des dispositifs climatiques.
|
||||
|
||||
Ce que nous appelons épreuve viable, c’est ce qui manque ici : la possibilité d’exposer les décisions à une scène où le fondement devient enjeu, où les milieux parlent, où les vivants contestent, où les représentations s’affrontent, dans un cadre institué, soutenu, non clôturé.
|
||||
|
||||
Dans les politiques sociales, le mécanisme est identique. Les aides sont attribuées selon des barèmes automatisés, les exclusions sont déclenchées sans justification explicite, les critères de mérite, de comportement, de mobilité sont intégrés dans des scripts sans adresse. On parle d’insertion, d’autonomie, de parcours. *Mais où est la scène ?* Où est l’espace où une personne peut dire : « ce critère est inacceptable » ; « cette décision ne me reconnaît pas » ; « je demande une autre forme de régulation » ?
|
||||
|
||||
Dans bien des dispositifs sociaux, la scène formelle d’accès au droit s’est amincie ou s’est déportée dans des circuits techniques ; elle persiste toutefois sous des formes intermittentes, fragiles ou latérales — commissions locales, permanences associatives, médiations juridictionnelles *ad hoc*. Le problème n’est donc pas l’absence pure et simple, mais la dégradation topologique de la scène : hypotopies (prises faibles ou non reliées), hypertopies (cratialité sur-déterminante) et atopies (simulacres participatifs). Comme nous l’avons établi au chapitre 1, cette typologie ne renvoie pas à un argument d’autorité mais à une matrice d’audit : elle sert à objectiver les prises, à situer les déficits et à rouvrir la possibilité d’une scène tenue, opposable et révisable.
|
||||
|
||||
Face à cela, une politique des épreuves viables implique une redéfinition complète de l’acte régulateur : non plus l’ajustement d’un système fermé, mais l’institution d’une scène ouverte. Elle transforme le pouvoir de gouverner en obligation de se laisser apparaître. Elle transforme le savoir d’expert en condition de fondation publique. Elle transforme la gestion en exposition contradictoire.
|
||||
|
||||
Nous nommons *autarchicratie* la concrétisation achevée de la désarchicration : le méta-régime dans lequel la *cratialité se met en autarcie*. Il ne s’agit pas d’un gouvernement de la société par elle-même, mais d’un *gouvernement de la régulation par elle-même* : indicateurs, modèles de risque, scripts algorithmiques et procédures de contrôle deviennent leurs propres critères de validité, sans plus devoir passer par des scènes d’épreuve archicratives praticables. La *cratialité* s’y auto-référence et s’y auto-certifie, dans des boucles où tout fonctionne — calculs, audits, *reporting* — mais où plus rien ne se laisse vraiment contester ni même énoncer. Cette mise en autarcie de la régulation prolonge, jusqu’à son point de bascule, les tendances déjà repérées dans la gouvernementalité néolibérale, la rationalisation managériale et la numérisation intégrale des prises.
|
||||
|
||||
L’*autarchicratie* n’est donc pas une fiction dystopique : c’est une destination possible, et déjà en partie à l’œuvre, de nos régimes contemporains. Nous en avons suivi les lignes de force dans l’histoire des révolutions industrielles (chapitre 4), lorsque la scène archicrative se trouve progressivement oblitérée au profit de modèles prédictifs et de dispositifs d’automatisation normative. Nous en avons observé les manifestations concrètes dans les politiques climatiques, sociales et numériques (chapitre 5), sous la forme de *pseudo-archicrations fantômes* et de pilotages algorithmiques où les circuits de décision se suffisent à eux-mêmes. Dans tous ces cas, la régulation subsiste juridiquement et techniquement, mais elle se maintient hors seuil d’opposabilité : scènes inaccessibles, délais inopérants, motifs indisponibles, paramètres non auditables. Tout fonctionne ; mais plus rien ne s’expose ni ne s’explique.
|
||||
|
||||
Dès lors, le péril n’est pas l’*archicratie* elle-même — qui institue la scène et rend contestables les effets —, mais sa disparition performative dans l’*autarchicratie*. Ainsi, refaire monter la scène n’est pas un supplément procédural : c’est revenir au seuil où la décision cesse d’être instrument et redevient fondation ; c’est rétablir, contre l’autarcie régulatrice, la possibilité d’un différé, d’une justification opposable, d’une réversibilité des effets — conditions sans lesquelles il n’y a plus de politique, mais seulement à terme des processus automatiques ou des pratiques machinales.
|
||||
|
||||
Or c’est bien cela que permet encore le concept d’*archicratie* — refonder la régulation comme scène, comme institution de l’interruption, de la dispute, du différé, de la relance — puisqu’elle permet de nommer ce qui auparavant était pour bonne part occulté bien que présent. Les épreuves viables posent ainsi la question de ce qui fait tenir un monde — et à quelles conditions il mérite encore de tenir.
|
||||
|
||||
Si la scène constitue, comme nous l’avons montré, la matrice fondamentale de toute régulation archicratique, alors son absence appelle autre chose qu’un tragique constat critique ou une nostalgie institutionnelle. Elle appelle un geste pleinement instituant, une projection agissante, un surgissement conceptuel doublé d’un projet matériel. Il ne suffit pas de rappeler que les normes s’appliquent aujourd’hui sans exposition, que les seuils gouvernent sans adresse, que les décisions s’exécutent sans différé. Il faut désormais concevoir les formes par lesquelles cette dérive peut être arrêtée, inversée, transformée depuis son propre cœur. Cela ne relève ni du réformisme, ni de l’utopie, mais d’un geste proprement archicratique : instituer une scène là où il n’y a plus que des tentations de scripts ou de prompts.
|
||||
|
||||
Car l’absence de scène ne signifie pas une absence de régulation. Celle-ci se manifeste au contraire par un trop-plein de formes technico-administratives, de logiques procédurales, de chaînes opératoires sans seuil de réflexivité. Elle ne manque pas d’arènes, de plateformes, de forums, de simulateurs. Par contre, elle manque de lieux où l’on peut exprimer que ce pouvoir n’est pas encore justifié ; que cette norme ne peut pas encore s’appliquer ; que cette décision doit être suspendue tant qu’elle n’a pas été tenue devant celles et ceux qu’elle transforme. La scène n’est donc pas une instance supplémentaire : elle est ce sans quoi toute instance devient violence déguisée et insidieuse. Elle ne s’ajoute pas au dispositif ; elle en est la condition de légitimité. Et pour cette raison, elle doit être conçue, pensée, projetée et instituée.
|
||||
|
||||
Toutefois, réinstituer les scènes ne consiste pas nécessairement à restaurer les formes d’assemblée du passé. Il ne s’agit pas non plus de réanimer les dispositifs dévitalisés de la représentation, ni de raviver des formes symboliques inertes. Il s’agit de penser des espaces dans lesquels une décision devient apparente avant d’être exécutoire, dans lesquels le temps retrouve sa densité, dans lesquels le pouvoir accepte d’être différé, exposé, débattu, amendé, contredit. Cela implique une refondation des infrastructures de la régulation, dans leur matérialité comme dans leur temporalité.
|
||||
|
||||
Il faudra donc des lieux, des calendriers, des organisations, des compétences, des statuts, des rôles, des budgets, des procédures, mais aussi des rituels, des langues, des gestes, des seuils symboliques. Car la scène est un dispositif complexe : elle n’émerge pas d’elle-même, elle ne se décrète pas, elle s’institue par un ensemble cohérent de formes, d’espaces, d’affects et de récits. Et c’est ce tissu qu’il faut aujourd’hui remailler.
|
||||
|
||||
Là où les politiques publiques s’élaborent aujourd’hui dans les anti-chambres des ministères ou la clôture des cabinets d’audit, il faudra ouvrir des délais formels et irréductibles à la consultation. Là où les plateformes déploient leurs standards d’usage sur des populations entières sans épreuve fondatrice, il faudra bâtir des scènes où le code peut être ajourné, explicité, confronté. Là où les territoires sont remodelés par des logiques d’optimisation, il faudra des lieux où l’habitabilité puisse être requalifiée, où les milieux puissent apparaître, où les formes de vie puissent être représentées dans leur altérité. Là où les seuils d’attribution, de sanction, d’accès ou d’aide s’appliquent par automatisme, il faudra instituer des régimes où la décision elle-même devienne visible, révisable, reconductible à partir d’une épreuve contradictoire menée par des humains. Et cela ne se fera pas par décret. Cela exigera de nouveaux dispositifs, de nouvelles instances, de nouvelles formes, à inventer depuis le cœur même de la crise scénique contemporaine, probablement au plus près des acteurs de l’économie sociale et solidaire et des professions intermédiaires.
|
||||
|
||||
Qu’on admette alors ceci : bien comprendre la scène dans l’ordonnancement archicratique n’est pas une option procédurale. Elle est l’un des opérateurs décisifs par lesquels une décision cesse d’être purement instrumentale pour devenir fondatrice. C’est dans la scène que le pouvoir se redéploie en se laissant affecter. C’est dans la scène que la temporalité s’épaissit, que les conséquences deviennent lisibles, que les fondements peuvent être relancés. C’est là que peut émerger une *co-viabilité régulatrice*. Il ne s’agit donc pas d’ajouter de la transparence, de la participation ou de la communication. Il s’agit de concevoir des scènes dans lesquelles l’ordre peut être temporairement suspendu, afin d’être à nouveau institué depuis ceux qu’il affecte.
|
||||
|
||||
Ce travail d’institution scénique ne peut être séparé de la question de l’échelle. Il devra se jouer à tous les niveaux : au sein des écoles, des collectivités, des juridictions, des plateformes, des infrastructures, des régulations techniques, des politiques sociales, des gouvernances écologiques... Chaque strate et lieu de l’*archicratie* doit trouver sa scène d’apparition, adaptée à sa texture propre, mais reliée par une même exigence : qu’*aucune décision structurante et agissante ne puisse advenir sans être tenue devant celles et ceux qu’elle engage.* Cela implique un effort considérable de conception, de financement, de formation, mais surtout un déplacement ontologique de ce qu’on appelle “gouverner”. Gouverner ne consistera plus à appliquer avec justesse des normes bien conçues, mais à rendre ces normes ajournables, discutables, contestables, et davantage réformables sous conditions de *co-viabilité* sociale et écologique.
|
||||
|
||||
Il faudra, pour cela, concevoir de nouveaux lieux : des espaces où l’on entre sans savoir ce qui va en sortir, mais où chacun peut venir avec ses preuves, ses objections, ses propositions, ses arguments. Des scènes sans clôture prédéterminée, mais avec un cadre fort. Des scènes traversées par les conflits, mais équipées pour les soutenir. Des scènes lentes, où l’on prend le temps de tout reprendre. Des scènes modestes, parfois, mais constantes. Des scènes qui ne soient pas des moments exceptionnels, mais des rythmes d’institution continue. Des scènes enfin que l’on peut convoquer, non pour donner son avis, mais pour rouvrir le monde.
|
||||
|
||||
Et plus encore que des lieux, il faudra forger une culture. Une culture du différé, une culture de la justification, une culture de l’apparition fondée. Il faudra former à la convocation des énonciateurs, à la reconstruction des axiomes, à la mise en tension des critères. Il faudra réapprendre à interrompre sans détruire, à disputer sans humilier, à exposer sans désincarner. Il faudra bâtir des formes dans lesquelles le pouvoir n’aura plus peur d’être vu, entendu, relancé. Mais cela, aucun règlement ne pourra le prescrire. Ce sera l’œuvre d’une génération entière, qui ne se contentera pas de revendiquer des droits, mais qui acceptera de se constituer comme puissance politique — non dans l’éclat du spectaculaire, mais dans la densité fondatrice d’un monde à relancer avant effondrement déjà annoncée.
|
||||
|
||||
À ce niveau, réinstituer la scène n’est plus un programme. C’est un appel. Un appel à cesser de gérer les régulations comme si elles pouvaient s’auto-référencer indéfiniment. Un appel à rouvrir l’espace du pouvoir comme espace d’épreuve, non de domination. Un appel à instituer un régime dans lequel les décisions cessent d’être des faits pour redevenir des actes. C’est ainsi que l’*archicratie* prendra sens comme projet. Non comme forme de gouvernement, mais comme respiration de toute société qui veut encore se gouverner elle-même dans son évolution pour assurer son avenir.
|
||||
|
||||
Toute régulation s’inscrit dans une histoire. Non seulement dans l’histoire des régimes politiques ou des institutions juridiques, mais dans une histoire plus profonde encore : celle des formes d’articulation entre pouvoir, savoir, énergie et monde, autrement dit, celle des configurations systémiques au sein desquelles l’humain, le vivant, le milieu, la technique et le symbolique se tiennent dans un certain rapport. Ce que nous appelons ici “révolution régulatrice” ne renvoie donc pas à un événement politique localisé, ni à une invention technologique isolée, mais à une transformation systémique de la manière dont un monde se produit, se reproduit, se règle et se légitime.
|
||||
|
||||
Les quatre grandes révolutions qui scandent la modernité industrielle – mécanisation disciplinaire, organisation taylorienne-fordiste, cybernétisation néolibérale, numérisation généralisée – ne sont pas seulement des séquences techniques. Chacune a reconfiguré, à sa manière, le rapport entre *arcalité*, *cratialité* et *archicration* : nouvelles promesses de légitimation, nouveaux dispositifs d’exécution, nouvelles formes – ou nouvelles défausses – de scène. La quatrième, celle de la numérisation intégrale des systèmes de décision, pousse à l’extrême un mouvement amorcé de longue date : la possibilité de faire fonctionner la régulation sans passer par des lieux reconnaissables d’épreuve.
|
||||
|
||||
C’est précisément contre cette clôture systémique que se dessine ce que nous proposons d’appeler, par convention, une cinquième révolution régulatrice. Elle ne consisterait pas en un supplément d’automatisation ni en une sophistication accrue des instruments. Elle tiendrait dans un déplacement plus radical : le fait de considérer que l’instance décisive n’est plus l’outil, ni le flux, ni la procédure, mais la scène où ces éléments deviennent adressables, critiquables, reconfigurables. Là où les révolutions précédentes ont privilégié l’extension des capacités de calcul, de production ou de circulation, celle-ci privilégierait la capacité à exposer ces capacités, à en faire l’objet d’une épreuve publique.
|
||||
|
||||
Dire qu’il s’agit d’une “cinquième révolution” ne signifie pas que l’histoire suivrait un cours nécessaire et linéaire. Le terme n’a ici qu’une valeur heuristique : il sert à concentrer, sous un même nom, des processus multiples, hétérochrones et dispersés – luttes pour la transparence effective des algorithmes, revendications de recours opposables, demandes de traçabilité des décisions, inventions de nouvelles assemblées scéniques. Il serait erroné de croire que cette révolution viendra s’ajouter, comme une étape finale, à une série déjà écrite. Elle pourrait tout aussi bien rester avortée, partielle, récupérée par les dispositifs qu’elle prétend contraindre.
|
||||
|
||||
C’est pourquoi l’idée de cinquième révolution doit être explicitement soumise à épreuve. Elle serait infirmée si l’on constatait que les scènes existantes continuent de se vider de toute capacité effective, que les espaces de recours se réduisent à des simulacres sans impact sur les décisions, que les tentatives de réinstitutions scéniques se réduisent à des mises en récit sans prise sur les cratialités. Elle gagnerait en consistance, au contraire, si des pratiques, même locales, montraient que la création de nouvelles scènes – dans les hôpitaux, les écoles, les plateformes, les politiques écologiques – modifie réellement la manière dont les décisions sont justifiées, prises et requalifiées pour assurer la co-viabilité.
|
||||
|
||||
Nous parlons donc de “cinquième révolution” moins comme d’un avenir promis que comme d’un horizon de travail : une invitation à relire les conflits présents à partir de la question scénique. Chaque fois qu’un collectif lutte pour un délai, pour un droit au différé, pour la présence d’énonciateurs identifiables, pour un droit de regard sur les paramètres qui le gouvernent, quelque chose de cette révolution est mis en jeu. Chaque fois qu’il s’en remet au contraire à la seule fluidité des ajustements automatiques, il s’en éloigne. Le paradigme archicratique propose de tenir ensemble ces expériences, de les comparer, de les instruire, sans décider à l’avance de leur issue.
|
||||
|
||||
Cette cinquième révolution n’est pas un futur abstrait : elle advient chaque fois qu’un seuil est suspendu, qu’un fondement est reconvoqué, qu’un recours devient opérant, qu’une voix exclue trouve scène, délai et adresse ; chaque fois que la transparence cesse d’être visualisation pour redevenir publicité des critères, que la participation quitte la consultation pour rejoindre l’épreuve, que la gouvernance s’efface devant la fondation.
|
||||
|
||||
Nous parlons de “cinquième révolution” par convention, en pleine conscience que l’histoire effective procède par recompositions régulatrices hétérochrones, stratifiées, feuilletées, sérialisées, désynchronisées selon les régimes symboliques et techniques ; de sorte que l’“événement” est devenu une prise de pouvoir de la scène dans l’architecture des dispositifs industriels.
|
||||
|
||||
Dans un monde où les infrastructures et les superstructures établissent les nouvelles normes, où les plateformes sont devenues de nouveaux territoires de nos êtres, où les systèmes d’intelligence artificielle s’inventent comme nouveaux gouvernants, seule une révolution scénique permettrait au politique de ne pas s’effacer. Mais celle-ci ne se décrète pas : elle se sculpte par des décisions, des dispositifs, des pratiques et des langages ; elle requiert des artisans, des architectes, des dramaturges du politique ; elle accepte de perdre en fluidité ce qu’elle gagne en légitimité. Et c’est cela, précisément, que nomme l’*archicration* : une révolution régulatrice en acte, bien plus que le modèle institutionnel qu’elle instaurerait en conscience — l’*archicratie*.
|
||||
|
||||
Il faut aujourd’hui prononcer un appel ancré dans le vivant, là où les institutions ne font plus apparaître les polémiques, là où les discordes tendent à se faire taire. Un appel qui ne soit ni un cri, ni un mot d’ordre, ni un acte de foi, mais une *archicration vivante* : c’est-à-dire l’apparition d’énoncés fondés, publics, discutables, adressables, révisables — une parole instituante, dans ce sens précis et exigeant que nous redonnons au mot “politique”. Le droit de vivre ! De tous vivre ! De chacun selon ses capacités, et à chacun selon ses besoins…
|
||||
|
||||
Car ce que notre monde ne sait plus faire advenir, ce ne sont pas les règles ou les normes en elles-mêmes, mais la capacité populaire à les exposer, à les critiquer, à les instituer grâce à des formes collectives constituées par celles et ceux qui les subissent en premier lieu. Cette incapacité n’est ni technique, ni conjoncturelle. Elle est le symptôme d’un effondrement structurel qui au-delà de produire de l’injustice, nous conduit à une crise de viabilité civilisationnelle.
|
||||
|
||||
Nous vivons dans un monde où l’humain et le non-humain sont liés par une même absence de considération. Le vivant, dans son immense diversité de formes, d’agencements, de langages, de résistances, est soustrait à la parole, au temps et à la forme. Et ce que notre thèse a permis de penser, c’est que ce processus d’invisibilisation n’est pas un effet secondaire des logiques économiques, mais une conséquence directe de la disparition de la scène de confrontation. Là où il n’y a plus de scène, il ne peut y avoir ni représentant, ni interpellation, ni suspension, ni réparation. Le vivant devient bruit, excès, variable et externalité à occulter.
|
||||
|
||||
Il faut donc le dire avec toute la solennité nécessaire. Un monde sans *archicration* rend le vivant politiquement illisible, donc indéfendable : le conflit perd sa scène et le temps son droit de retour ; or le vivant a besoin d’un lieu d’apparition où les règles qui le concernent sont convoquées, tenues et différées. Parce qu’il a besoin d’une scène comme condition de reconnaissance mutuelle inscrite dans le temps. Parce qu’il ne peut être gouverné sans être d’abord entendu, représenté, institué dans la durée de conflits assumés, dans la densité d’un désaccord soutenu qui permette tout de même la *co-viabilité*.
|
||||
|
||||
Ce n’est pas tant un supplément de conscience écologique que requiert notre temps, qu’une réinscription du vivant dans le cœur même de la procédure régulatrice. Il faut que le vivant retrouve la capacité de faire épreuve comme acteur instituant. Cela implique de convoquer de nouveaux formats d’apparition, de nouvelles figures de représentation, de nouvelles juridictions du milieu, de nouveaux porte-paroles, mais aussi de nouvelles scènes, de nouveaux délais et de nouveaux seuils. Et c’est là que l’*archicration*, en tant que forme d’institution de la régulation, redevient notre outil premier, notre geste inaugural, notre obligation radicale.
|
||||
|
||||
Mais ce ne sont pas uniquement les vivants non-humains qui exigent ce retour à la scène. Ce sont les humains eux-mêmes, dans leur diversité traversée de douleurs, de silences, de disparitions, de récits corrompus ou interrompus. Ce sont les corps précarisés, expulsés, captés, mécanisés, racisés, discriminés. Ce sont les voix sans adresse, les luttes étouffées, les demandes restées lettres mortes, les souffrances converties en statistiques, les communautés disqualifiées ou reléguées. Là encore, ce n’est pas un “plus de participation” qu’il faut : c’est un *acte fondateur de refondation*. Ce n’est pas une réforme du droit d’accès à la parole : c’est l’institution d’un droit d’épreuve et de reconnaissance. Encore une fois d’*archicrations* !
|
||||
|
||||
Et ce droit n’est pas métaphysique. Il est scénique. Il s’inscrit dans des formes précises, différées, structurées. Il suppose des lieux, des interlocuteurs, des procédures, des archives, des délais, des énonciateurs. Il suppose des scènes que l’on peut habiter, relancer, réinterroger. Il suppose que la parole soit tenue, mais aussi reçue, exposée, contredite, reformulée. Il suppose, enfin, que cette parole soit retenue dans l’architecture même de la régulation. Qu’il ne soit pas juste documenté, mais qu’il puisse reconfigurer le pouvoir sous condition de recevabilité.
|
||||
|
||||
Nous appelons donc, à ce stade ultime de notre œuvre, à un réarmement du commun par la scène. Rien d’un théâtre idéologique bien plutôt une architecture du vivre-ensemble, régulatrice, opposable, fondée. Car il ne peut y avoir de monde commun sans scène, pas de scène sans différé, et pas de différé sans pouvoir qui accepte de suspendre son exécution pour se reformuler devant celles et ceux qu’il engage. Ce geste — si simple en apparence, si révolutionnaire en pratique — est le cœur de toute *archicration*. Et c’est à ce titre probablement à tout le moins l’une des rares voies régulatrices encore tenables et viables à nos yeux.
|
||||
|
||||
À ce point de saturation du monde — saturation des flux, des normes, des récits, des simulacres de participation — une respiration possible reste celle du moratoire et du différé. La seule densité politique entendable est celle de l’épreuve et des preuves. La seule fondation encore pensable est celle qui accepte de se laisser instituer à nouveau, à se laisser revisiter, en pleine conscience. C’est cela, notre appel : reconvoquer le vivant dans la scène, refaire de l’apparition, de l’information, de l’opinion un droit, refonder la régulation politique en processus d’*archicrations*. Il ne s’agit pas d’ajouter un modèle de plus, mais de rendre à l’histoire ce dont elle a été amputée, faute d’occultation et faute d’impensé : ses scènes fondatrices, émancipatrices et régulatrices qui font tenir les mondes.
|
||||
|
||||
Ce que notre époque appelle, est bien plus qu’un ajustement marginal des politiques existantes, bien plus qu’une moralisation incrémentale des comportements individuels. C’est une réinvention des conditions mêmes d’apparition et de mobilisation du pouvoir : des lieux, des temps, des procédures, des mémoires où les décisions qui nous engagent puissent être amenées en scène, instruites, contestées, requalifiées. Les dix gestes qui suivent n’ont pas valeur de programme clos ni de plan de réforme. Ils constituent des hypothèses archicratiques : des propositions de formes scéniques minimales, destinées à être discutées, expérimentées, corrigées. Ils n’ont de sens que s’ils deviennent eux-mêmes objets d’épreuve.
|
||||
|
||||
Premier geste : *instaurer un droit universel au différé contradictoire*. Un tel droit ne viserait pas à ralentir indistinctement toute décision, mais à garantir que toute mesure qui affecte substantiellement une existence – allocation, sanction, fermeture, expulsion, tri automatique – puisse être suspendue dans un temps institué, devant une instance capable d’en reconsidérer les motifs et les effets. Ce droit ne se confond pas avec un simple droit au recours individuel : il institue le temps scénique comme composante non négociable de la régulation.
|
||||
|
||||
Deuxième geste : *fonder, pour chaque grand dispositif de régulation, un journal de justification*. Il ne s’agirait pas d’ajouter un rapport de plus aux archives administratives, mais d’exiger que toute décision structurante (création d’un algorithme, d’un barème, d’un standard) soit accompagnée d’un récit argumenté de ses raisons, des alternatives écartées, des effets anticipés. Ce journal ne sanctifie pas la décision ; il la rend adressable. Il crée une mémoire publique du fondement, à partir de laquelle critiques, révisions et contre-propositions peuvent être formulées.
|
||||
|
||||
Troisième geste : *instituer un visa d’affectation pour les principaux instruments de calcul et de qualification*. Chaque indicateur, chaque score, chaque seuil ne serait plus un simple paramètre technique, mais devrait porter trace de ses finalités déclarées et des collectifs qui en ont débattu. Ce visa n’autorise pas seulement l’utilisation d’un outil ; il inscrit l’outil dans une adresse éthique et politique : qui décide que telle métrique est légitime pour mesurer telle réalité, avec quelles garanties, pour combien de temps ?
|
||||
|
||||
Quatrième geste : *instaurer un coupe-circuit citoyen*. Il s’agirait de prévoir, dans les dispositifs institutionnels, des mécanismes par lesquels des collectifs – citoyens, usagers, travailleurs, habitants – puissent suspendre temporairement l’application d’une décision lorsque des contradictions graves, des effets non prévus ou des injustices manifestes apparaissent. Ce geste n’est pas un droit d’obstruction généralisée ; il est la traduction procédurale du principe selon lequel aucune *cratialité* ne doit se déployer sans possibilité d’interruption scénique.
|
||||
|
||||
Cinquième geste : *instituer un tribunal de l’algorithme*. Non pas un organe chargé de valider ou d’interdire abstraitement la technique, mais une scène d’épreuve où les architectures computationnelles qui organisent l’accès aux droits, à l’information, aux ressources, soient rendues comparables, critiquables, reformulables. Un tel tribunal devrait être doté de compétences mixtes – juridiques, techniques, sociales – et travailler à partir de cas concrets. Il ne s’agit pas d’ajouter une couche de contrôle symbolique ; il s’agit de ramener l’algorithme en scène.
|
||||
|
||||
Sixième geste : *rétablir des assemblées d’affectation là où les décisions sont aujourd’hui dispersées dans des chaînes opaques*. Dans les hôpitaux, les écoles, les agences sociales, les plateformes, ces assemblées seraient chargées d’instruire publiquement la manière dont les ressources, les priorités, les charges et les risques sont distribués. Elles ne se substituent pas aux institutions existantes ; elles en constituent le moment scénique, là où les critères implicites peuvent être explicités, contestés, ajustés.
|
||||
|
||||
Septième geste : *rendre révocables les mandats au sein des institutions qui conçoivent, paramètrent et pilotent les dispositifs de régulation*, qu’il s’agisse d’instances publiques, de régulateurs indépendants ou de structures privées investies de missions d’intérêt général. La révocabilité ne signifie pas l’instabilité permanente, mais la possibilité, pour les collectifs concernés, de mettre fin à un mandat lorsqu’il apparaît que l’*archicration* est durablement contournée, que les fondements ne sont plus adressés, que la *cratialité* s’est autonomisée.
|
||||
|
||||
Huitième geste : *instaurer, dans chaque budget institutionnel d’importance, un budget scénique*. Il ne s’agirait pas d’un poste décoratif, mais d’une ligne dédiée au financement des dispositifs d’épreuve : temps de délibération, traduction, médiation, expertise contradictoire, dispositifs de restitution. Un budget sans scène est un budget sans adresse. Reconnaître un budget scénique, c’est faire de la scène non plus un coût superflu, mais une condition de validité de la dépense.
|
||||
|
||||
Neuvième geste : *inscrire dans le droit un principe général de révision archicrative*. Ce principe reconnaîtrait que toute architecture régulatrice – loi, norme, algorithme, accord institutionnel – doit pouvoir être réexaminée à intervalles déterminés, à partir d’instances où les personnes affectées, les savoirs concernés, les effets constatés sont convoqués. Il ne s’agit pas d’instaurer une instabilité chronique, mais de consacrer le fait que la *co-viabilité* ne se maintient qu’au prix d’une disponibilité organisée à la révision.
|
||||
|
||||
Dixième geste : *construire des cartographies des scènes manquantes.* Au lieu de se contenter d’analyses sectorielles, il s’agirait de repérer, sur un territoire ou dans une chaîne de valeur, les lieux où des décisions structurantes sont prises sans scène identifiable, où les personnes affectées n’ont ni adresse, ni délai, ni recours. De telles cartographies ne sont pas un exercice académique de plus : elles constituent un instrument politique central pour orienter l’invention des scènes à venir, pour ne pas laisser invisible ce que la seule *cratialité* préfère absorber.
|
||||
|
||||
Ces dix gestes n’épuisent pas la gamme des formes possibles d’*archicration*. Ils ne valent pas comme une doctrine à appliquer, mais comme une invitation à la mise à l’épreuve : ils devront être discutés, critiqués, complétés, remplacés, parfois abandonnés. Leur seule prétention est de montrer que l’*archicratie*, loin d’être un mot abstrait, peut se traduire en dispositifs concrets, situés, qui redonnent aux collectifs la capacité d’adresser le pouvoir qui les traverse. Et c’est à ce niveau que nous posons notre ouvrage, moins une doctrine qu’une mise à l’épreuve publique, moins un programme qu’une fondation différée : l’invitation à restaurer la capacité institutionnelle de refonder le “pouvoir de” et le “pouvoir sur” nos vies, à hauteur de la *téra-machine* en constitution*.*
|
||||
Nous n'assistons ni à un retour primordial du désordre ni à une
|
||||
raréfaction du pouvoir. Ce qui se transforme, plus discrètement mais de
|
||||
manière décisive, ce sont les conditions mêmes sous lesquelles la
|
||||
régulation peut apparaître. Ce qui se reconfigure sous nos yeux n'est
|
||||
pas d'abord la quantité de pouvoir à l'œuvre dans nos sociétés, mais la
|
||||
manière dont ce pouvoir se rend — ou ne se rend plus — visible,
|
||||
adressable, contestable. La conflictualité ne disparaît pas ; elle
|
||||
change de régime d'existence. Là où elle trouvait autrefois à se
|
||||
formuler dans des lieux identifiables, à se différer dans des
|
||||
temporalités instituées, à se confronter dans des espaces d'énonciation
|
||||
reconnus, elle se trouve de plus en plus distribuée dans des chaînes
|
||||
opératoires qui se présentent comme de simples enchaînements techniques.
|
||||
Flux, scores, interfaces, barèmes, protocoles : autant de formes qui
|
||||
n'abolissent pas les décisions, mais en modifient profondément les
|
||||
conditions d'apparition. Ce qui relevait d'une épreuve devient
|
||||
traitement. Ce qui relevait d'une adresse devient calcul. Ce qui
|
||||
relevait d'une justification devient paramétrage.
|
||||
|
||||
Ce déplacement est d'autant plus difficile à saisir qu'il ne se donne
|
||||
pas comme rupture. Il s'installe dans la continuité apparente des
|
||||
dispositifs, dans l'amélioration de leur efficacité, dans la promesse
|
||||
d'une gestion plus rapide, plus fluide, plus objective des situations.
|
||||
Le conflit y est désamorcé au nom de la performance. Le différé y est
|
||||
perçu comme ralentissement. L'exposition y est remplacée par une
|
||||
visibilité sans interlocution, où tout semble disponible sans que rien
|
||||
ne soit véritablement tenu. Il ne s'agit pas d'un vide. Il ne s'agit pas
|
||||
d'un monde sans normes ni d'un effondrement du pouvoir. Il s'agit d'un
|
||||
recouvrement. Ce que cet essai-thèse a nommé oblitération archicratique
|
||||
désigne cette dynamique précise : la substitution progressive de la
|
||||
scène par l'exécution, du différé par l'automaticité, de l'énonciation
|
||||
par la trace, de l'épreuve par la donnée.
|
||||
|
||||
Le pouvoir ne cesse pas d'opérer ; il cesse de comparaître. Les
|
||||
décisions continuent d'être prises ; elles cessent d'être tenues. Elles
|
||||
ne disparaissent pas ; elles se soustraient aux formes où elles
|
||||
pourraient être rapportées à leurs fondements, confrontées à leurs
|
||||
effets, reprises à partir de ce qu'elles affectent.
|
||||
|
||||
Ce qui caractérise ainsi notre situation n'est pas une crise de la
|
||||
conflictualité, mais une crise de sa tenue. Non l'absence de dissensus,
|
||||
mais l'altération des formes capables de le porter. Non la disparition
|
||||
du politique, mais sa désarticulation progressive hors des scènes où il
|
||||
pouvait encore apparaître comme tel. À ce niveau, ce que nous appelons
|
||||
crise ne relève plus d'un dysfonctionnement partiel, ni d'une dérive
|
||||
simplement sectorielle. Elle engage les conditions mêmes dans lesquelles
|
||||
un monde peut encore faire apparaître, soutenir et transformer ce qui le
|
||||
traverse.
|
||||
|
||||
Ce déplacement du regard a commandé tout le parcours accompli. Il a
|
||||
d'abord fallu apprendre à voir. Distinguer ce qui fonde de ce qui opère,
|
||||
ce qui opère de ce qui met à l'épreuve. Rompre avec les confusions qui
|
||||
font passer l'exécution pour la justification, la visibilité pour
|
||||
l'opposabilité, la procédure pour la scène. Sans cette discipline de
|
||||
détectabilité, le présent demeure illisible et la critique se dissout
|
||||
dans des généralités. L'une des ambitions premières de ce travail aura
|
||||
donc été de donner des prises : non pas inventer un vocabulaire pour le
|
||||
plaisir de l'invention, mais rendre discernable ce que les descriptions
|
||||
ordinaires du pouvoir, de la gouvernance et de l'administration laissent
|
||||
trop souvent se confondre.
|
||||
|
||||
Encore fallait-il que ce vocabulaire n'usurpe pas sa propre nécessité.
|
||||
L'archicratie ne vaut pas parce qu'elle pourrait tout redire dans sa
|
||||
langue ; elle vaut là où elle permet de discerner quelque chose qui,
|
||||
sans elle, resterait confondu, euphémisé ou inaperçu. Elle ne constitue
|
||||
donc ni une théorie totale du politique, ni une clef universelle des
|
||||
mondes historiques, mais un instrument critique situé, tenu à une
|
||||
obligation de retenue : se taire là où il n'apporte aucun gain de
|
||||
lisibilité, et répondre de ses distinctions là où il prétend en
|
||||
produire. C'est à cette condition seulement qu'un paradigme cesse d'être
|
||||
un idiome de surplomb pour devenir une épreuve réelle de connaissance.
|
||||
|
||||
Il a fallu ensuite remonter plus loin. Reconnaître que les formes
|
||||
d'épreuve ne sont ni des raffinements tardifs des modernités
|
||||
représentatives, ni des suppléments ajoutés à des ordres déjà
|
||||
constitués, mais des conditions plus profondes de la tenue des mondes
|
||||
humains. Avant les États, avant les bureaucraties, avant les
|
||||
codifications juridiques stabilisées, des collectifs ont dû inventer des
|
||||
manières de différer, de ritualiser, d'exposer ce qui les traversait.
|
||||
L'histoire du politique ne commence pas avec la souveraineté constituée
|
||||
; elle commence avec la nécessité, pour un monde traversé de forces
|
||||
hétérogènes, de ne pas s'abandonner à leur pure immédiateté. Elle
|
||||
commence là où quelque chose comme une reprise devient possible, là où
|
||||
ce qui affecte peut être reconduit à une forme d'exposition, si
|
||||
rudimentaire, violente ou dissymétrique soit-elle. La scène n'est donc
|
||||
pas un luxe moderne. Elle appartient à la structure même des mondes qui
|
||||
tiennent.
|
||||
|
||||
Il a fallu également traverser les grandes pensées du pouvoir, non pour
|
||||
les annuler, ni pour les annexer de force à un nouveau système, mais
|
||||
pour en mesurer les prises et les limites. Certaines ont privilégié le
|
||||
fondement, d'autres l'opération, d'autres encore la conflictualité, la
|
||||
dispersion des dispositifs, l'individuation, la justification ou le
|
||||
dissensus. Toutes ont saisi quelque chose de réel ; aucune n'a tenu
|
||||
entièrement ensemble les conditions d'une régulation habitable. Ce que
|
||||
cette traversée a rendu possible, ce n'est pas une synthèse des
|
||||
doctrines, mais une méta-grammaire du politique, capable de les relire à
|
||||
partir de ce qu'elles permettent — ou non — de penser : comment un
|
||||
ordre se fonde, comment il opère, comment il accepte d'être mis à
|
||||
l'épreuve.
|
||||
|
||||
Il a fallu enfin éprouver cette grammaire dans l'histoire effective des
|
||||
transformations modernes, là où les capacités de régulation ont atteint
|
||||
une intensité inédite. Ce qui apparaît alors n'est pas seulement une
|
||||
succession d'innovations techniques, mais une série de reconfigurations
|
||||
du rapport entre fondement, opération et épreuve. Chaque révolution
|
||||
industrielle a redessiné ce triangle ; chacune a accru certaines
|
||||
puissances tout en décalant, fragmentant ou fragilisant les formes
|
||||
capables de les soutenir. L'histoire moderne n'apparaît plus comme celle
|
||||
d'un progrès simplement technique ; elle devient lisible comme celle des
|
||||
déplacements successifs du lieu où le pouvoir se rend — ou cesse de se
|
||||
rendre — comparable, contestable, révisable.
|
||||
|
||||
C'est au point le plus brûlant du présent que cette exigence se révèle
|
||||
avec la plus grande netteté. Les tensions contemporaines ne se laissent
|
||||
pas comprendre comme des crises séparées, ni comme des anomalies
|
||||
sectorielles. Elles manifestent, chacune à leur manière, la difficulté
|
||||
croissante à instituer des formes dans lesquelles ce qui est affecté par
|
||||
les décisions peut être reconduit à une épreuve. Ce qui manque n'est pas
|
||||
la capacité à produire des normes, des infrastructures, des critères,
|
||||
des instruments. Ce qui manque, de plus en plus, ce sont les formes
|
||||
capables de les porter. De là la nécessité d'un déplacement conceptuel
|
||||
décisif : substituer au lexique lisse de la durabilité la notion de
|
||||
co-viabilité. Non pas un équilibre supposé entre intérêts déjà
|
||||
constitués, ni la correction technocratique d'externalités, mais
|
||||
l'institution toujours fragile, toujours révisable, toujours
|
||||
conflictuelle, des conditions sous lesquelles des formes de vie
|
||||
hétérogènes peuvent encore tenir ensemble sans destruction irréversible.
|
||||
|
||||
Ce qui se dégage ainsi de l'ensemble n'est pas une doctrine
|
||||
supplémentaire, encore moins un système clos. C'est une condition — qui ne garantit ni harmonie ni salut, mais sans laquelle aucune
|
||||
régulation ne peut être tenue comme monde. Cette condition peut
|
||||
désormais être formulée simplement : une régulation ne devient habitable
|
||||
qu'à la mesure où ce qui la fonde, ce qui l'opère et ce qui la met à
|
||||
l'épreuve demeurent distinguables, articulés et exposables. Toute la
|
||||
difficulté tient alors à ceci : maintenir cette distinction sans les
|
||||
dissocier, et cette articulation sans les confondre. Là où ces
|
||||
dimensions se confondent, se disjoignent ou se dérobent à l'exposition,
|
||||
la régulation peut continuer à fonctionner ; elle cesse de se tenir.
|
||||
|
||||
C'est cette condition minimale que nous avons nommée archicratie. Ni
|
||||
régime parmi d'autres, ni forme institutionnelle déterminée, ni idéal
|
||||
moral à incarner : un seuil. Le seuil au-dessous duquel la régulation se
|
||||
réduit à sa propre opérativité, et au-dessus duquel elle devient, au
|
||||
moins en droit, habitable, parce qu'elle laisse ouverte la possibilité
|
||||
de sa reprise. L'archicratie ne désigne ni la justice, ni la bonté, ni
|
||||
la douceur des décisions ; elle désigne la condition sans laquelle ces
|
||||
questions elles-mêmes cessent de pouvoir être posées politiquement.
|
||||
|
||||
Ce qui fonde une régulation ne se confond ni avec une autorité
|
||||
abstraite, ni avec un texte, ni avec une tradition invoquée une fois
|
||||
pour toutes ; cela renvoie à la capacité d'un ordre à exposer ses
|
||||
raisons comme telles. Ce qui opère désigne les instruments, procédures
|
||||
et dispositifs par lesquels le monde est effectivement découpé,
|
||||
distribué, transformé. Ce qui met à l'épreuve, enfin, ne relève ni d'une
|
||||
consultation formelle ni d'un recours marginal, mais de formes
|
||||
instituées où fondements, opérations et effets peuvent être suspendus,
|
||||
confrontés, repris.
|
||||
|
||||
Ces prises ne sont jamais données à l'état pur. Elles se recouvrent, se
|
||||
déplacent, se distribuent inégalement selon les configurations. Mais
|
||||
leur coprésence différenciée constitue la condition minimale d'une
|
||||
régulation vivable. Là où l'opération se déploie sans être reconduite à
|
||||
ses raisons, là où les décisions s'appliquent sans passer par des
|
||||
épreuves effectives, la régulation bascule vers une forme de fermeture
|
||||
qui ne relève ni du chaos ni du retrait du pouvoir, mais de son
|
||||
auto-suffisance. Dans une telle configuration, le pouvoir ne se retire
|
||||
pas ; il s'accomplit sans comparution. Il produit des effets, parfois
|
||||
avec une grande précision, mais sans se laisser reprendre dans des
|
||||
formes où ces effets pourraient être rapportés à des raisons
|
||||
discutables. Il opère, mais ne s'expose plus. Il décide, mais ne se
|
||||
laisse plus adresser. Tout fonctionne ; mais plus rien ne s'expose ni ne
|
||||
s'explique.
|
||||
|
||||
La différence décisive se situe là. Entre une régulation capable
|
||||
d'exécuter des procédures, de reproduire des normes, de gérer des flux,
|
||||
et une régulation capable de se rapporter à elle-même à partir de ce
|
||||
qu'elle affecte, la différence ne tient pas à l'intensité du pouvoir,
|
||||
mais à la possibilité de sa mise à l'épreuve. Ce qui rend un monde
|
||||
habitable n'est ni l'absence de tensions, ni la stabilité de ses
|
||||
équilibres, ni la pure efficacité de ses dispositifs. C'est la forme
|
||||
dans laquelle ses tensions peuvent être portées sans déni, différées
|
||||
sans dissolution, exposées sans annihilation.
|
||||
|
||||
À partir de là, la question n'est plus d'abord celle d'un bon régime,
|
||||
mais celle d'un monde qui tient. Ce monde n'a pas besoin d'être pacifié,
|
||||
homogène ou réconcilié ; il doit pouvoir porter ce qui le traverse sans
|
||||
s'abolir dans sa propre exécution. Un monde qui ne tient ni par inertie,
|
||||
ni par répétition, ni par l'évidence supposée de ses fondements. Il
|
||||
tient parce qu'il est capable de porter ce qui le traverse sans le nier,
|
||||
de différer ce qui l'affecte sans le dissoudre, d'exposer ce qui le
|
||||
gouverne sans s'effondrer sous sa propre mise en question. Habiter un
|
||||
monde ne signifie pas simplement vivre en son sein. Cela signifie
|
||||
disposer d'une scène où comparaître, demander d'où parle ce qui décide,
|
||||
identifier ce qui opère, rouvrir le temps lorsque l'exécution tend à se
|
||||
refermer sur elle-même, faire apparaître ce qui, sans cette épreuve,
|
||||
demeurerait converti en variable, en score, en flux.
|
||||
|
||||
La scène prend ici son sens le plus fort. Elle n'est ni un supplément
|
||||
institutionnel, ni un décor ajouté au pouvoir pour en améliorer
|
||||
l'acceptabilité, ni une métaphore commode pour désigner des espaces de
|
||||
parole. Elle est l'une des formes à travers lesquelles un ordre cesse
|
||||
d'être purement opératoire pour devenir politiquement tenable. Là où une
|
||||
telle forme existe — espace différé, documenté, institué, capable de
|
||||
suspendre et de requalifier — la régulation ne se contente pas d'agir
|
||||
: elle accepte de comparaître. C'est dans cette comparution que se joue
|
||||
la possibilité, pour un monde, de ne pas se réduire à ce qu'il exécute.
|
||||
|
||||
Il faut ici maintenir une distinction que tout ce travail a jugée
|
||||
décisive. Dire que la scène est condition de viabilité ne signifie
|
||||
nullement que toute scène serait en elle-même juste, démocratique ou
|
||||
émancipatrice. L'histoire des formes politiques, juridiques,
|
||||
religieuses, administratives, guerrières, marchandes ou sacrificielles
|
||||
montre au contraire que des scènes peuvent être violentes,
|
||||
dissymétriques, inquisitoriales, spectaculaires, capturées. La scène
|
||||
n'est pas bonne parce qu'elle apparaît ; elle devient politiquement
|
||||
décisive lorsqu'elle institue réellement l'épreuve de ce qu'elle expose.
|
||||
Ce qui compte n'est pas l'existence abstraite d'un lieu d'apparition,
|
||||
mais la possibilité effective qu'il ouvre : peut-on y demander les
|
||||
fondements ? Les instruments peuvent-ils y être rendus visibles ? Les
|
||||
effets peuvent-ils y être rapportés à ceux qu'ils affectent ? Le différé
|
||||
est-il réel ou purement fictif ? La suspension a-t-elle une force
|
||||
transformatrice ou n'est-elle qu'un rite sans prise ?
|
||||
|
||||
Il n'en demeure pas moins que, sans scène, la régulation se dégrade
|
||||
qualitativement. Elle peut continuer à fonctionner ; elle peut même
|
||||
gagner en efficacité apparente. Mais elle perd sa mémoire, sa
|
||||
réversibilité, sa capacité à se rapporter à elle-même autrement que par
|
||||
recalibrage interne. Elle applique, classe, répartit, déclenche, module
|
||||
; mais elle ne se reprend plus. Elle produit des normes sans en exposer
|
||||
les raisons, des décisions sans en instituer l'épreuve, des effets sans
|
||||
en organiser le retour. Elle tient encore ; mais elle ne sait plus
|
||||
répondre de la manière dont elle tient. C'est en ce point qu'un monde
|
||||
sans scène devient injustifiable. Non pas nécessairement injuste dans
|
||||
chacun de ses effets immédiats ; non pas chaotique ; non pas dépourvu de
|
||||
cohérence locale. Il peut très bien fonctionner, produire des résultats,
|
||||
stabiliser provisoirement des situations, maintenir des chaînes
|
||||
d'obéissance ou d'adaptation. Mais il devient injustifiable parce qu'il
|
||||
ne dispose plus des formes dans lesquelles ses propres décisions peuvent
|
||||
être rejouées, exposées, interrogées, reformulées.
|
||||
|
||||
On comprend alors ce que la co-viabilité signifie exactement. Elle ne
|
||||
désigne ni la simple coexistence de formes de vie différentes, ni leur
|
||||
compatibilité gestionnaire, ni un optimum de répartition des ressources
|
||||
ou des charges. Elle désigne la capacité, toujours fragile, toujours
|
||||
située, toujours révisable, d'un monde à instituer des formes dans
|
||||
lesquelles les hétérogènes qui le traversent peuvent être mis en tension
|
||||
sans être soit mutuellement détruits, soit administrativement
|
||||
neutralisés. Elle est moins un état qu'un régime d'épreuves. Elle ne se
|
||||
mesure pas seulement à l'efficacité des ajustements ; elle se mesure à
|
||||
la possibilité qu'un ordre laisse ouvert sa propre reprise à partir de
|
||||
ce qu'il affecte.
|
||||
|
||||
C'est à ce niveau que le diagnostic du présent trouve sa formulation la
|
||||
plus nette. Non dans l'idée d'un monde privé de régulation, mais dans
|
||||
celle d'un monde où la régulation tend à se déployer hors des formes qui
|
||||
permettaient de la tenir. Qu'il s'agisse des droits sociaux, de
|
||||
l'habitabilité écologique des milieux ou des architectures numériques de
|
||||
décision, la même logique se renforce : les dispositifs deviennent plus
|
||||
puissants au moment même où les formes capables d'en soutenir l'épreuve
|
||||
deviennent plus fragiles, plus tardives, plus périphériques. Ce ne sont
|
||||
pas les décisions qui disparaissent ; ce sont les manières dont elles
|
||||
pourraient être tenues.
|
||||
|
||||
C'est en ce sens que l'autarchicratie peut être nommée comme la
|
||||
contre-figure terminale de l'archicratie. Non un régime au sens
|
||||
classique, ni une idéologie, ni un type d'État, mais une configuration
|
||||
dans laquelle la régulation tend à se refermer sur sa propre
|
||||
opérativité, à produire ses propres critères de validité, à
|
||||
s'auto-justifier sans passer par des épreuves effectives. Dans une telle
|
||||
configuration, les instruments, les modèles, les indicateurs, les
|
||||
procédures deviennent à la fois ce qui opère et ce qui justifie. Les
|
||||
boucles se ferment. Les ajustements se font à partir de leurs propres
|
||||
résultats. Les audits vérifient la conformité à des critères produits
|
||||
par les systèmes eux-mêmes. La régulation devient auto-référentielle.
|
||||
|
||||
Son intensité contemporaine tient toutefois à ce qu'elle ne demeure pas
|
||||
extérieure aux sujets qu'elle régule. Lorsque chacun apprend à se noter,
|
||||
se comparer, s'optimiser, se rendre compatible, anticiper les seuils qui
|
||||
le classent ou prévenir son propre déclassement, la régulation ne
|
||||
s'exerce plus seulement depuis des dispositifs séparés. Elle se prolonge
|
||||
dans des conduites d'ajustement par lesquelles les existences deviennent
|
||||
les opératrices contraintes de leur propre conformité. L'autarchicratie
|
||||
atteint alors son degré le plus intime : non seulement lorsque l'ordre
|
||||
se mesure lui-même, mais lorsque les sujets se gouvernent eux-mêmes
|
||||
selon des critères qu'ils n'ont pas institués.
|
||||
|
||||
Cette intimité nouvelle de la régulation a trouvé dans la rationalité
|
||||
néolibérale l'un de ses vecteurs historiques les plus puissants. Non
|
||||
comme doctrine extérieure aux mutations industrielles récentes, mais
|
||||
comme manière de faire tenir ensemble l'âge informatique et l'âge
|
||||
algorithmique. La troisième révolution industrielle a donné au signal, à
|
||||
l'indicateur, à la concurrence, à l'audit, à la solvabilité et au
|
||||
feedback une autorité pratique inédite : institutions, entreprises,
|
||||
services publics, individus ont été conduits à se rendre lisibles dans
|
||||
des formats de performance. La quatrième fait descendre cette autorité
|
||||
plus avant dans les conduites. Le classement prépare la décision.
|
||||
L'évaluation aménage l'environnement où certains gestes deviennent
|
||||
probables, certains parcours préférables, certains écarts coûteux avant
|
||||
même d'être nommés. Le sujet n'est plus simplement contraint d'obéir ;
|
||||
il apprend à devancer les critères qui l'évaluent, à prévenir son
|
||||
déclassement, à traduire son temps, son travail, sa santé, ses désirs,
|
||||
ses déplacements en signes compatibles avec les dispositifs qui le
|
||||
rendent calculable. L'autarchicratisation contemporaine se noue dans
|
||||
cette intériorisation : la régulation fabrique les formats de réalité
|
||||
auxquels les existences doivent ensuite se rendre conformes.
|
||||
|
||||
Mais cette rationalité ne flotte pas dans un ciel d'indicateurs. Sa
|
||||
vérité matérielle affleure dans les ressources dont dépend l'habitation
|
||||
commune. Depuis les révolutions industrielles, chaque promesse de
|
||||
puissance engage des sous-sols, des fleuves, des nappes, des ports, des
|
||||
mines, des pipelines, des câbles, des centres de données, des corps
|
||||
exposés, des territoires rendus disponibles. Le charbon et le pétrole
|
||||
ont porté la mécanisation, les transports, la guerre industrielle et
|
||||
l'accélération des échanges ; le gaz et l'uranium ont reconfiguré
|
||||
l'énergie comme dépendance stratégique ; le sable, le cuivre, la
|
||||
bauxite, le cobalt, le coltan, les terres rares, le germanium, le
|
||||
tritium ou l'hélium-3 engagent désormais la construction,
|
||||
l'électronique, le calcul, les batteries, les réseaux, les armements et
|
||||
les promesses de transition. Une ressource n'est jamais une matière
|
||||
posée devant un besoin. Elle est un nœud d'autorisation, d'extraction,
|
||||
d'acheminement, de travail, de dette écologique, de violence
|
||||
territoriale et de futur engagé. La co-viabilité atteint ici son sol le
|
||||
plus concret : savoir qui prélève, qui consomme, qui stocke, qui manque,
|
||||
qui respire les poussières, qui traverse les pénuries, qui habite les
|
||||
paysages défaits, qui supporte les déchets, qui paie la puissance des
|
||||
autres par la fragilisation de son propre monde.
|
||||
|
||||
L'exigence archicratique engage ainsi la comparution des chaînes
|
||||
matérielles autant que celle des décisions, des normes et des
|
||||
algorithmes qu'elles soutiennent. Les circuits d'extraction,
|
||||
d'approvisionnement, de combustion, de refroidissement, de transport, de
|
||||
stockage, de calcul, de financement et de sécurisation appartiennent
|
||||
pleinement à la scène. Interroger une régulation revient alors à
|
||||
demander d'où viennent ses ressources, quels milieux elle transforme,
|
||||
quelles dépendances elle installe, quels territoires elle expose, quels
|
||||
corps elle requiert, quelles formes de vie elle rend possibles ou
|
||||
impossibles. Sans cette comparution matérielle, la co-viabilité
|
||||
manquerait son point le plus sensible : la lutte pour les conditions
|
||||
terrestres de l'habitation commune.
|
||||
|
||||
Cette bascule ne doit pas être dramatisée comme si elle était totale,
|
||||
homogène, déjà parfaitement accomplie. Des scènes subsistent, parfois
|
||||
robustes, parfois fragiles. Des espaces de contestation, de délibération
|
||||
et de reprise continuent d'exister. Mais ils apparaissent souvent comme
|
||||
disjoints des lieux où les décisions se prennent effectivement. La
|
||||
tension se joue moins entre présence et absence de scène qu'entre leur
|
||||
centralité et leur marginalisation. Le problème décisif n'est pas de
|
||||
savoir si toute scène a disparu ; il est de comprendre que la dynamique
|
||||
dominante tend à rendre optionnelle l'épreuve dont dépend pourtant la
|
||||
viabilité de la régulation.
|
||||
|
||||
C'est ici que la distinction entre durabilité et co-viabilité prend
|
||||
toute sa force. La durabilité, telle qu'elle s'est imposée dans les
|
||||
discours contemporains, ne doit pas être critiquée d'abord pour ses
|
||||
intentions, mais pour sa forme. Elle tend à fonctionner comme un
|
||||
opérateur de neutralisation de la conflictualité : en posant comme
|
||||
objectif la préservation ou l'ajustement de certains équilibres, elle
|
||||
déplace l'attention vers la gestion des variables, l'optimisation des
|
||||
paramètres, la correction des trajectoires. Ce déplacement n'est pas
|
||||
illégitime en soi ; il le devient lorsqu'il s'accompagne d'une
|
||||
évacuation des formes dans lesquelles les choix qui structurent ces
|
||||
trajectoires pourraient être discutés. La durabilité peut alors
|
||||
s'accommoder d'une régulation sans scène. La co-viabilité, elle, en fait
|
||||
une impossibilité.
|
||||
|
||||
La différence est décisive. La première tend à organiser la continuité
|
||||
des systèmes ; la seconde à instituer les conditions de leur reprise. La
|
||||
première privilégie l'ajustement des variables ; la seconde la mise à
|
||||
l'épreuve des fondements. La première peut se satisfaire d'une
|
||||
gouvernance qui corrige des déséquilibres ; la seconde exige des formes
|
||||
dans lesquelles les conditions mêmes de ces corrections peuvent être
|
||||
adressées, contestées, transformées. Ainsi comprise, la co-viabilité ne
|
||||
constitue pas un idéal abstrait. Elle désigne le régime minimal dans
|
||||
lequel un monde peut continuer à se transformer sans se soustraire à sa
|
||||
propre interrogation. Elle n'abolit pas les tensions ; elle en organise
|
||||
la tenue. Elle n'élimine pas les conflits ; elle en rend l'épreuve
|
||||
possible. Elle ne garantit pas la justice ; elle rend au moins pensable
|
||||
sa recherche.
|
||||
|
||||
Il reste alors à comprendre ce qui, en dernière instance, est affecté
|
||||
par cette transformation. Non pas seulement des institutions, des
|
||||
règles, des procédures, mais des formes d'existence. Des vies. Des
|
||||
milieux. Des devenirs. Si l'archicratie prend finalement une telle
|
||||
importance, ce n'est pas parce qu'elle offrirait une théorie plus
|
||||
satisfaisante du pouvoir ; c'est parce qu'elle reconduit l'analyse à ce
|
||||
qui, sans scène, devient politiquement illisible. Là où la régulation se
|
||||
déploie sous forme de flux, de calculs, de traitements, le vivant tend à
|
||||
être reconduit à des variables. Les milieux deviennent des stocks ou des
|
||||
contraintes. Les corps deviennent des profils, des trajectoires, des
|
||||
cas. Les expériences deviennent des données d'ajustement. Ce processus
|
||||
n'est pas nécessairement intentionnel. Il résulte de la logique même des
|
||||
dispositifs qui, pour fonctionner, doivent simplifier, catégoriser,
|
||||
standardiser. Mais cette simplification a un effet décisif : elle
|
||||
désinscrit le vivant de la scène. Elle le rend opérable sans qu'il ait à
|
||||
apparaître.
|
||||
|
||||
Le vivant ne disparaît pas ; il devient politiquement illisible. Il est
|
||||
là, partout affecté, mobilisé, transformé — mais de moins en moins
|
||||
capable de faire retour sur ce qui l'affecte. Un monde sans archicration
|
||||
est un monde dans lequel le vivant est présent sans être représentable,
|
||||
affecté sans être adressable, engagé sans être entendu. Il est pris dans
|
||||
des opérations, mais il ne peut plus apparaître comme ce à partir de
|
||||
quoi celles-ci devraient être interrogées.
|
||||
|
||||
C'est en ce sens que l'oblitération archicratique produit une crise de
|
||||
reconnaissance. Non pas au sens restreint d'une reconnaissance morale ou
|
||||
symbolique, mais au sens plus fondamental d'une reconnaissance comme
|
||||
condition d'apparition dans un espace où l'on peut être pris en compte.
|
||||
Reconnaître ne signifie pas simplement identifier ou décrire. Cela
|
||||
signifie instituer des formes dans lesquelles ce qui est affecté peut
|
||||
être reconduit à une scène, où il peut être exposé, où il peut entrer en
|
||||
relation avec ce qui décide. Sans cette reconnaissance, le vivant peut
|
||||
être protégé, géré, optimisé ; il ne peut pas être politiquement tenu.
|
||||
|
||||
Il faut alors comprendre que la question de la scène n'est pas
|
||||
extérieure à celle de la liberté. Elle en constitue l'une des conditions
|
||||
minimales. Non la liberté comme autonomie absolue, mais comme
|
||||
possibilité d'intervenir sur les conditions qui nous affectent. Une
|
||||
société qui ne dispose plus de formes dans lesquelles ses propres
|
||||
régulations puissent être interrogées tend à se percevoir comme soumise
|
||||
à des nécessités. Elle perd la capacité de distinguer ce qui relève de
|
||||
contraintes inévitables et ce qui relève de choix. À l'inverse, une
|
||||
société qui institue des épreuves se dote de la possibilité de se
|
||||
rapporter à elle-même comme à un ensemble de décisions révisables. Elle
|
||||
ne supprime pas les contraintes, mais elle les inscrit dans des formes
|
||||
où elles peuvent être discutées.
|
||||
|
||||
À ce point, aucune réponse définitive ne peut être apportée. Aucun
|
||||
modèle achevé ne peut être proposé. Mais une exigence demeure, désormais
|
||||
visible et irréductible. Un monde ne devient inhabitable ni parce qu'il
|
||||
est traversé de tensions, ni parce qu'il doit décider dans
|
||||
l'incertitude, ni parce qu'il affronte des contraintes puissantes. Il le
|
||||
devient lorsqu'il ne dispose plus des formes capables de porter ce qui
|
||||
le traverse autrement que par la pure exécution. Ce qui est en jeu n'est
|
||||
ni la suppression du conflit, ni l'optimisation des dispositifs, ni la
|
||||
stabilisation d'un équilibre. Ce qui est en jeu, c'est la possibilité de
|
||||
maintenir ouvertes les formes dans lesquelles un monde peut se rapporter
|
||||
à lui-même à partir de ce qu'il affecte. La possibilité, toujours
|
||||
fragile, toujours menacée, de ne pas confondre ce qui fonctionne avec ce
|
||||
qui se tient.
|
||||
|
||||
Rendre à la régulation les formes dans lesquelles elle peut encore être
|
||||
tenue comme monde : telle est l'exigence à laquelle reconduit l'ensemble
|
||||
de ce parcours. Non comme un programme, ni comme une promesse, mais
|
||||
comme ce sans quoi aucune transformation ne peut être habitée. Car ce
|
||||
n'est jamais l'ordre seul qui fait tenir un monde. C'est la possibilité,
|
||||
pour cet ordre, d'être interrompu, exposé, repris. Là où cette
|
||||
possibilité se ferme, le monde peut continuer à marcher ; il cesse peu à
|
||||
peu d'être habitable. Là où elle demeure ouverte, fût-ce dans le
|
||||
conflit, sous contrainte, précairement, quelque chose du politique
|
||||
subsiste encore : non la paix, ni l'innocence, ni l'harmonie, mais la
|
||||
capacité d'un monde à ne pas se confondre avec sa propre exécution.
|
||||
|
||||
C'est là, précisément, que commence l'exigence archicratique : pouvoir
|
||||
encore demander pourquoi, suivre comment, et rouvrir la scène où ce qui
|
||||
s'exécute au nom d'un monde doit répondre de ce qu'il fait à ce monde.
|
||||
|
||||
@@ -1,290 +1,578 @@
|
||||
---
|
||||
title: "Prologue — Fondation, finalité sociopolitique et historique"
|
||||
edition: "archicratie"
|
||||
status: "modele_sociopolitique"
|
||||
title: Prologue — Fondation, finalité sociopolitique et historique
|
||||
edition: archicrat-ia
|
||||
status: essai_these
|
||||
level: 1
|
||||
version: "0.1.0"
|
||||
version: 0.1.0
|
||||
concepts: []
|
||||
links: []
|
||||
order: 10
|
||||
summary: ""
|
||||
summary: ''
|
||||
source:
|
||||
kind: docx
|
||||
path: "sources/docx/archicrat-ia/Prologue—Archicratie-fondation_et_finalite_sociopolitique_et_historique-version_officielle.docx"
|
||||
path: sources/docx/archicrat-ia/Prologue—Archicratie-La_tenue_des_mondes-version_resserree.docx
|
||||
---
|
||||
Nous vivons dans une époque saturée de diagnostics sur les formes de domination, les mutations du pouvoir, les détournements de la souveraineté. Depuis une vingtaine d’années, les appellations s’accumulent : *démocratie illibérale*, *ploutocratie*, *happycratie*, *gouvernement algorithmique*, *démocrature*… À travers ces tentatives de nommer le désordre du présent, un fait se répète, de manière sourde : la scène politique semble désorientée. Les catégories héritées — *État*, *pouvoir*, *représentation*, *volonté générale*, *contrat social* — apparaissent de moins en moins capables de décrire ce qui nous gouverne effectivement.
|
||||
|
||||
C’est cette perte de prise sur le réel que ce livre souhaite prendre au sérieux. Non pour lui ajouter un terme de plus au lexique fatigué des contre-pouvoirs ou des impuissances, mais pour repartir d’un point plus fondamental, presque en-deçà de la question politique classique. Ce point, c’est celui de la *tenue d’un monde commun* — c’est-à-dire la possibilité, pour des êtres dissemblables, vulnérables, inégaux, traversés de contradictions et situés dans des temporalités hétérogènes, de coexister sans s’annihiler.
|
||||
|
||||
Cette tenue du monde n’équivaut ni à la paix civile, ni à la stabilité des institutions, ni à l’ordre établi. C’est une difficulté conceptuelle que d’envisager *la possibilité pour un ordre de durer sans s’effondrer*, alors même qu’il est traversé en permanence par des forces et des légitimités qui le travaillent, l’éprouvent, le modifient, l’usent, le contestent, le prolongent ou le sapent. Cette possibilité de tenir le monde commun, nous la nommons *co-viabilité*.
|
||||
|
||||
Le terme n’est pas trivial. Il ne s’agit pas simplement d’une viabilité partagée, ni d’une coexistence pacifique, ni même d’une durabilité écologique élargie. Il s’agit d’un état dynamique, instable, fragile, dans lequel un ensemble — une société, d’un système biologique, d’une formation historique, d’un milieu technique ou d’un monde institué — parvient à maintenir une *existence viable*, *malgré et grâce à ses tensions constitutives*.
|
||||
|
||||
La *co-viabilité* ne désigne ni un état d’équilibre, ni une finalité normative. Elle nomme un état dynamique et instable, dans lequel un monde — société, milieu technique, formation historique — tient non pas par homogénéité ou harmonie, mais parce qu’il parvient à réguler ce qui le menace sans se détruire lui-même. Il compose entre des éléments hétérogènes — forces d’inertie et d’innovation, attachements profonds et ruptures nécessaires — sans chercher à les unifier. C’est cette disposition active, faite de compromis fragiles et d’ajustements toujours révisables, que nous tenons pour première, et non dérivée.
|
||||
|
||||
Ce qui revient à dire que la question politique — au sens fort — n’a peut-être jamais été qui commande ? Mais bien plus : *Comment un ordre tient-il malgré ce qui le défait ?* *Quels sont les dispositifs qui permettent à une société de ne pas se désagréger sous l’effet de ses propres contradictions ?* *Comment sont régulées les tensions qui traversent le tissu du monde commun sans le déchirer ?*
|
||||
Cette bascule de perspective prolonge des intuitions anciennes. Max Weber (*Économie et société*, 1922) rappelait que ce qui fait tenir un ordre, ce n’est pas seulement la force ou la loi, mais les « chances de validité » socialement reconnues. Norbert Elias (*La dynamique de l’Occident*, 1939/1975) montrait, quant à lui, que les sociétés se maintiennent par des équilibres toujours précaires entre interdépendances, rivalités et pacifications. Notre démarche s’inscrit dans ce sillage : travailler cette interrogation sur les *conditions de viabilité d’un monde commun*.
|
||||
|
||||
Ce changement de perspective implique une rupture profonde dans la manière même de poser la question politique. Pendant des siècles, les sociétés ont pensé le politique à partir de principes transcendants — Dieu, Nature, Volonté générale, Pacte social. Ces principes, supposés extérieurs aux conflits du présent, garantissaient l’ordre en surplomb. Comme le rappelle Michel Foucault, il n’y a pas de principe extérieur au jeu des forces : seulement des rapports de pouvoir situés, modulés, réversibles. C’est précisément cette exigence — trouver dans les relations elles-mêmes les ressources nécessaires pour maintenir des mondes vivables — qui définit notre époque.
|
||||
|
||||
Ce qui émerge n’est pas de nouveaux principes, ni une nouvelle idéologie, mais une exigence beaucoup plus modeste, mais aussi beaucoup plus difficile à satisfaire : celle de trouver dans les relations elles-mêmes — entre groupes, entre institutions, entre individus, entre temporalités — les ressources nécessaires pour maintenir leurs mondes viables. Autrement dit : c’est *dans* les tensions, *à même* les conflits, *au sein* des alliances, *au cœur* des désaccords et des polémiques, que semble se construire la régulation. Non plus *au-dessus*, par un décret transcendant, mais *au-dedans*, par un agencement toujours révisable. C’est cela que nous voulons dire — sans technicité inutile — quand nous parlons d’un déplacement vers une *instance de régulation située de co-viabilité* : un espace commun où les forces hétérogènes, souvent antagonistes, peuvent coexister, se contredire, se confronter, s’éprouver, sans se détruire mutuellement.
|
||||
|
||||
Penser le politique depuis cette approche, c’est renoncer à l’idée même qu’un ordre puisse se fonder définitivement, une fois pour toutes. C’est reconnaître que ce qui fait tenir une société n’est jamais un principe unique, un commandement souverain, une légitimité première, mais *un espace d’épreuve toujours rejoué* où se négocient, se recadrent, s’opposent, s’ajustent des forces hétérogènes dont l’accord est constamment partiel, toujours temporaire, perpétuellement instable.
|
||||
|
||||
Par conséquent, un ordre durerait moins par ses fondements proclamés que par ses *capacités régulatrices effectives*. Autant dire que ce sont les dispositifs, les formats, les médiations — parfois massifs, parfois imperceptibles — par lesquels un ordre parvient à faire coexister ce qui, en droit, pourrait s’exclure : des intérêts antagonistes, des affects discordants, des récits historiques incompatibles, des régimes de valeur irréconciliables, des temporalités sociales déphasées, des exigences contradictoires en matière de justice, d'efficacité, de mémoire ou d'avenir.
|
||||
|
||||
Cet ordre ne les efface pas. Il ne les réconcilie pas dans un consensus fictif. Il ne les fusionne pas dans une synthèse idéologique illusoire. Il les tient ensemble sans les résoudre, par des équilibres instables, des arrangements contingents, des formats d’ajustement plus ou moins durables. C’est là que se situe toute la puissance — et la fragilité — de la régulation : tenir sans annuler, moduler sans effacer, organiser sans clore.
|
||||
|
||||
Cette capacité régulatrice, si elle échappe aux regards, n’en est pas moins structurante. Elle repose sur des agencements concrets, souvent silencieux, mais puissamment opératoires : une réforme budgétaire qui stabilise un conflit de génération sans le nommer ; un indicateur économique qui requalifie des arbitrages sociaux sans débat préalable ; un protocole logistique qui reconfigure la hiérarchie entre producteurs et distributeurs sans qu’aucune loi ne l’impose. Chaque fois, il y a régulation sans visibilité, composition sans consensus, opération sans fondement explicite.
|
||||
|
||||
Il ne faut donc pas s’y tromper : ce que nous décrivons ici ne relève ni d’un effondrement soudain, ni d’un basculement spectaculaire, mais d’un déplacement discret — pourtant d’une portée considérable : la désactivation lente et diffuse des opérateurs classiques de lisibilité du politique. Ce qui se défait sous nos yeux est une grammaire d’interprétation, un outillage cognitif collectif, une capacité de mise en récit. Et ce processus est loin d’être anodin.
|
||||
|
||||
Il y eut un temps — disons moderne — où l’on pouvait encore localiser les lieux de pouvoir, identifier les détenteurs de l’autorité, délimiter les instances de délibération, nommer les figures de légitimation. On pouvait encore croire que la loi émanait d’un espace visible, que la souveraineté résidait quelque part, que la représentation engageait effectivement une parole au nom d’un collectif déterminé. Ce temps — sans avoir disparu totalement — a cessé de produire des repères fonctionnels. Car si les formes demeurent — constitutions, institutions, procédures, déclarations —, une part décisive des arbitrages réels s’est déplacée hors de leur emprise. Le centre de gravité régulateur a migré vers des configurations hors des prises démocratiques.
|
||||
|
||||
Désormais, ce sont des métriques qui tranchent à la place des normes, des calculs prédictifs qui se substituent aux débats. Les interfaces filtrent les droits et les voix sans qu’aucune instance délibérative n’ait statué sur leurs paramètres. Les arbitrages majeurs — ceux qui orientent les seuils d’émission de gaz à effet de serre, la sélection scolaire, les répartitions budgétaires, les politiques de logement, les trajectoires de migration, ou l’organisation des chaînes d’approvisionnement mondialisées — ne sont plus discutés collectivement, mais disséminés dans des protocoles techniques, financiers, juridiques, algorithmiques, souvent conçus hors scène, et inaccessibles à toute mise en cause publique.
|
||||
|
||||
Cela ne veut pas dire que le politique ait disparu, mais plutôt qu’il tend peu à peu à se rendre indiscernable. Il ne s’exerce plus à travers des figures lisibles, mais à travers des chaînes d’acteurs, des scripts techniques, des formats d’optimisation, des boucles de retour automatisées. Cette dissémination ne relève pas d’une abstraction théorique ou d’un soupçon idéologique : elle se manifeste chaque jour dans des configurations concrètes, reconnaissables, pourtant rarement nommées pour ce qu’elles sont. Son efficacité tient précisément à sa banalité. Car désormais, ce ne sont plus des figures du commandement qui décident en surplomb, mais des dispositifs encastrés, des logiques de fonctionnement intégrées à des protocoles d’apparence neutre.
|
||||
|
||||
C’est un marché carbone qui, au nom de seuils agrégés à l’échelle continentale, conduit à la fermeture d’un site industriel local, sans qu’aucune figure politique ne puisse rendre visible ni opposable l’arbitrage opéré. C’est un algorithme de régulation hospitalière qui, face à une tension budgétaire ou épidémiologique, déprogramme automatiquement des interventions chirurgicales — sans qu’aucun médecin, aucun patient, aucun responsable politique ne puisse véritablement en discuter les critères. C’est une plateforme numérique de traitement des titres de séjour qui suspend une demande pour “anomalie de saisie”, sans contact humain, sans justification claire, sans voie de recours instituée. C’est un logiciel de pilotage budgétaire, adossé à des indicateurs d’efficience, qui impose la réduction d’une politique sociale sans passage par une arène délibérative. C’est aussi un score algorithmique de risque bancaire qui écarte discrètement une famille d’un prêt, bien avant qu’elle ait pu formuler son projet.
|
||||
|
||||
Contrairement aux apparences, ce qui s’offre au regard n’est plus la figure massive du pouvoir trônant dans la clarté de ses apparats, mais la trame patiente d’une régulation en mouvement. Disparues, les instances fixes ; effacée, la demeure solennelle de l’autorité. Le réel geste de gouvernance s’insinue insidieusement dans des protocoles, se glisse sournoisement dans la routine, s’entrelace irrémédiablement dans les habitudes, se ramifie inextricablement dans d’innombrables appareils sans visage. Nul acte inaugural n’en marque ostensiblement la naissance, nulle proclamation n’en scande les rythmes. On constate seulement que la régulation avance sans fracas, tisse patiemment la toile discrète sur laquelle se déplacent nos vies. Ce n’est plus tant le décret ni la loi qui pèsent, bien plus les enchevêtrements de normes, l’imperceptible maillage de procédures et l’ajustement continu de directives flexibles.
|
||||
|
||||
La contrainte n’accable plus par l’ostentation de l’ordre, mais s’inocule par la subtilité des systèmes. Ainsi, il s’agit désormais de façonner, par l’agencement soigné d’équilibres, de données, de flux, où chacun se trouve relié, indexé, impliqué à même cette dentelle administrative, sans jamais croiser le centre, sans jamais savoir nommer celui ou ce qui agit. La régulation moderne tresse ainsi un univers de seuils mobiles et d’agencements souples, où l’on ne peut jamais tout à fait fixer le moment ni le lieu du pouvoir agissant — mais où, à chaque pli de la vie collective, se lit l’empreinte d’une architecture invisible.
|
||||
|
||||
La difficulté d’y résister tient moins à une violence perceptible qu’à leur ontologie d’évidence. Elles ne s’avancent pas comme autorités, ne se proclament pas comme pouvoir : elles fonctionnent, nous relient et donc elles opèrent. Et cette opération sans légitimation démocratique — pouvoir sans figure, contrainte sans théâtre — rend caduques nos anciennes grilles d’interprétation. Désormais, ce qui nous affecte le plus ne s’énonce plus, il s’impose sans discours jusqu’au plus intime.
|
||||
|
||||
Cela signifie que le politique s’est décousu de ses formes historiques. Il continue d’agir, de décider, d’orienter — mais sous d’autres modalités, dans d’autres lieux, avec d’autres instruments, selon des régimes d’opérativité qu’aucune des catégories anciennes ne parvient plus à saisir, à rendre intelligible et à traduire sans trahir.
|
||||
|
||||
Autrement dit, nous avons changé d’époque sans encore avoir pu changé de lexique. Nous continuons de penser avec des formes obsolètes ce qui s’active sous nos yeux. Nous employons les mots d’hier pour décrire des processus qui les excèdent de toutes parts. Nous parlons de gouvernements, là où il faudrait parler de structures de régulation composite. Nous discutons de lois, là où il faudrait décrire des protocoles, des seuils, des scénographies d’ajustement, des mécanismes de *feedback* algorithmique, des normes sans normalisateurs.
|
||||
|
||||
Cette disjonction entre l’expérience vécue de la contrainte et le vocabulaire disponible pour la dire n’est pas qu’un problème théorique. Elle produit une désorientation profonde. Elle empêche de penser le réel, de localiser les responsabilités et rend inopérantes les critiques. Elle altère la capacité collective à formuler des exigences, jusqu’à dissoudre les repères et les registres d’action.
|
||||
|
||||
Cette impuissance démocratique généralisée à nommer, situer, orienter les formes réelles de la régulation se donne parfois à voir dans des situations d’apparente clarté — et c’est peut-être là le plus troublant. Prenons un exemple rendu brûlant par l’actualité française en 2025 : la proposition de ce que l’on appelle la *taxe Zucman*. Formulée par l’éminent économiste Gabriel Zucman, cette mesure vise à instaurer un impôt minimal annuel sur le patrimoine des ultra-riches — en France et dans le monde — au-delà d’un seuil (autour de 100 millions d’euros). Le taux proposé est d’environ 2 % sur la valeur totale du patrimoine net, qu’il soit liquide ou partiellement non liquide (actions non cotées, participations, biens immobiliers), ce qui pose des défis de paiement et d’évaluation.
|
||||
|
||||
L’idée est de corriger ce que Zucman identifie comme un déséquilibre fiscal majeur : les très grandes fortunes paient aujourd’hui, proportionnellement, beaucoup moins que ce que permettrait une imposition équitable et progressive, notamment en raison de l’évasion fiscale, de la mise sous structures opaques par *holding*, du transfert du patrimoine privée en patrimoine professionnel ou de la dissociation entre richesse effective et revenu imposable.
|
||||
|
||||
Si cette proposition est débattue publiquement — soutenue dans certains milieux politiques, évoquée dans les médias, portée par des organisations internationales — elle ne s’est pourtant pas traduite jusqu’à présent en un espace de régulation pleinement opérationnel : pas de dispositif stable, pas de mécanisme universel, pas d’instance de coordination internationale suffisamment puissante ni opposable pour rendre cette taxe effective à l’échelle voulue.
|
||||
|
||||
Ce cas illustre une modalité très contemporaine de la régulation suspendue : il montre comment une idée peut être reconnue, débattue, même populairement validée, sans jamais franchir le seuil d’une véritable effectuation. On y voit comment une idée peut être claire et manifeste, mais rester orpheline de lieu de confrontation effectif, incapable de franchir les seuils institutionnels, techniques ou politiques qui rendent un projet pleinement opérant. Le principe est clair. Le diagnostic est étayé avec un large appui académique. L’urgence sociale est incontestable. Et pourtant, rien ne se passe. Ou plutôt, rien ne se produit. Il y a blocage. Symbole parfait d’une époque où les régulations se pensent plus vite qu’elles ne s’instituent, où les décisions les plus urgentes s’évaporent faute de structure pour les porter.
|
||||
|
||||
Ce hiatus, cette fracture, ce décrochage entre l’enjeu perceptible dans l’espace public et le blocage des régulations effectives — voilà ce que nous tentons de cerner, pour en faire le lieu même de notre interrogation. Car c’est bien cela qui se joue, dans le trouble du présent : non pas une simple crise des institutions, mais une crise de lisibilité de la régulation elle-même. Nous ne savons plus nommer ce qui nous oblige, ni situer ce qui nous gouverne, ni identifier ce qui structure encore nos appartenances. Et ce, faute d’un cadre d’intelligibilité commun, apte à relier ce qui contraint et ce qui tient, ce qui évolue et ce qui persiste, ce qui menace et ce qui protège.
|
||||
|
||||
À mesure que les instruments du pouvoir deviennent techniques, que les décisions se diluent dans des protocoles, que les normes se déterritorialisent dans des scripts ou des seuils, la question de la régulation glisse en dehors du périmètre politique, comme si elle n’avait plus de lieu propre, plus d’arène reconnaissable, plus de langage pour s’énoncer. Le politique ne disparaît pas — il se désinscrit, il se dissimule dans d’autres formats, il s’internalise dans les infrastructures, il se pulvérise dans des régularités sans délibération démocratique.
|
||||
|
||||
Et face à cette évanescence, deux réflexes s’affrontent. L’un, nostalgique, cherche à réhabiliter les anciennes figures du pouvoir : l’autorité, la loi, la souveraineté, comme si elles pouvaient encore réactiver un ordre en désagrégation. L’autre, sceptique, postule qu’il n’y a plus rien à faire — que nous vivons l’épuisement définitif de l’arène politique, sa disparition dans le flux, le calcul, le désordre entropique des systèmes.
|
||||
|
||||
Pour autant quelque chose continue d’agir, de structurer, de différencier, même en l’absence de pouvoir identifiable. Ce quelque chose, c’est la manière dont une société régule ses tensions internes : non plus en les effaçant, mais en les tenant, en les exposant, en les configurant dans des dispositifs — visibles ou non — capables de contenir sans abolir, de moduler sans figer, de différer sans éluder.
|
||||
|
||||
Nous devons donc reprendre à neuf la question la plus enfouie de la politique : *qu’est-ce qui fait qu’un monde collectif tient ?* Non plus dans l’abstrait, mais dans la matérialité de ses pratiques, la texture de ses conflits, l’architecture de ses médiations. *Par quels agencements tient-il ?* *À travers quelles épreuves ? Selon quelles temporalités ? Et sous quelles conditions de réversibilité ?*
|
||||
|
||||
Car il ne suffit plus de dire que le pouvoir est diffus, que les normes sont flexibles, que les algorithmes décident. Encore faut-il comprendre comment ces formes apparemment dispersées composent — ou échouent à composer — un monde co-viable, c’est-à-dire capable de réguler sans brutaliser, de transformer sans dissoudre, d’être robuste et de résister sans exclure. C’est là, dans cette capacité à organiser la tension sans basculer dans la clôture ni dans le chaos, que se joue le cœur du politique contemporain — non plus comme souveraineté, mais comme scénographie régulatrice.
|
||||
|
||||
Mais cette scénographie, aujourd’hui, est inopérante. Soit elle est fantomatique — laissée à l’abandon, réduite à un formalisme creux. Soit elle est confisquée — captée par des dispositifs opaques, des rationalités techniques, des opérateurs propriétaires fermés. Dans les deux cas, ce qui est mis en péril, ce n’est pas uniquement l’idée de démocratie ou le jeu des institutions, mais la possibilité même d’un monde de confrontation et de controverse, d’un espace commun où la régulation pourrait être rendue visible, négociable, opposable, réversible.
|
||||
|
||||
Or ce qui s’efface désormais, c’est la capacité collective à en formuler les conditions, à en penser les formes, à en situer les nouvelles arènes de pouvoir. Ce qui s’érode, plus encore qu’une architecture institutionnelle, c’est notre aptitude à dire ce qui oblige, à comprendre ce qui ajuste, à situer ce qui contraint. L’étrangeté du présent ne réside pas dans une hypertrophie du pouvoir — comme on le répète trop souvent — mais dans un brouillage profond de ses modes d’existence. Nous ne savons plus vraiment comment le pouvoir s’exerce, où il opère, par quels instruments il module, ni selon quels critères il ajuste. Et pourtant, nous continuons de mobiliser les mêmes mots : *monarchie*, *oligarchie*, *démocratie*, *technocratie*, *bureaucratie*, *ploutocratie*, *méritocratie*… Comme si ces termes suffisaient encore à décrire ce qui nous affecte.
|
||||
|
||||
Ces termes politiques s’organisent autour de deux grands suffixes — *-archie* et *-cratie* — forgés dans les débats de la Grèce antique, et largement sédimentés dans les lexiques modernes. Les suffixes en -*archie* désignent un *pouvoir fondé sur un principe premier* (*arkhè*), une origine ou une légitimité verticale : monarchie, oligarchie, etc. Ceux en -*cratie* désignent plutôt les *modalités pratiques d’exercice du pouvoir* (*kratos*) : démocratie, technocratie, bureaucratie, etc.
|
||||
|
||||
Cette distinction entre fondement et exercice, légitimation et opération, traverse toute la modernité politique. Cependant cette séparation ne permet plus aujourd’hui de saisir la réalité des régulations effectives. Car les scènes d’*arkhè* se sont en grande partie effondrées sans être remplacées, tandis que les *kratos* contemporains tendent à s’exercer sans adresse, sans représentation, sans théâtre. L’on disserte sur les vertus de la démocratie, mais le *dèmos* n’a plus de lieu de confrontation effective : *serait-ce la rue ? Seraient-ce les réseaux sociaux ? Seraient-ce les médias ? Serait-ce le Parlement ?* Force est de constater qu’il n’en est rien : la première est réprimée ; les seconds sont filtrés et compartimentés ; les suivants sont contrôlés par une poignée de milliardaires ; et ce dernier est loin de représenter l’entièreté de la société. L’on invoque la République, mais la *res publica* — la chose publique, appellation la plus vague et la plus creuse que l’on puisse donner de l’espace politique — se dissout dans des logiques qui échappent à toute délibération commune : absence de débat, passage en force, brutalisme institutionnel. Quant au langage politique, il continue d’énoncer des structures et des projets, mais il ne parvient pas à décrire les opérations effectives de régulation tant le système socio-économique, en plus de s’être libéralisé et privatisé, s’est étendu et complexifié tout en confiant les leviers d’action au niveau supranational.
|
||||
|
||||
C’est précisément cette disjonction — entre les principes supposés légitimer le pouvoir, et les dispositifs qui en assurent l’effectuation — qui produit aujourd’hui notre impuissance à penser la régulation. Car aucun de ces termes ne dit où se tiennent les tensions, comment elles sont traitées, par quelles instances elles sont articulées. Aucune *-archie* ne garantit aujourd’hui la dispute de ses fondements. Aucune *-cratie* n’organise les conditions de son opposabilité. Et pendant que nous nous obstinons à nommer des formes de régime, les processus réels de régulation — eux — échappent à tout espace visible d’épreuve. Jusqu’à présent, ils opèrent privés de contradictoire, dépourvus de délai, amputés d’institutions de réversibilité. Autrement dit : nous sommes gouvernés sans être gouvernés, régulés sans régulation légitimée, affectés sans instance délibérative.
|
||||
|
||||
Cette dissociation — entre pouvoir nommé et régulation agissante — peut sembler abstraite. Elle ne l’est pas. Un cas devenu emblématique en offre la preuve saisissante. Entre 2010 et 2011, l’État belge a connu une situation institutionnelle inédite — près de 540 jours sans gouvernement fédéral de plein exercice. Aucun exécutif formel, aucun nouveau mandat, aucune majorité parlementaire opérationnelle. Et pourtant, rien ne s’est effondré. Les institutions ont continué à fonctionner. Les services publics ont été assurés. L’économie n’a pas sombré. La diplomatie s’est poursuivie. Et la société belge a tenu malgré les tensions communautaires.
|
||||
|
||||
Cet épisode, souvent évoqué sur le ton de l’anecdote, mérite d’être considéré ici comme un symptôme politique majeur. Il indique que la régulation ne passe plus nécessairement par la verticalité du pouvoir, mais par des dispositifs latents, des agencements structurels, des inerties normatives et des coordinations transversales ou distribuées. Il montre qu’un ordre peut fonctionner sans fondement renouvelé, tenir sans pilotage, résister sans commande visible. Pierre Rosanvallon le soulignait (*La Légitimité démocratique*, 2008), en affirmant que les sociétés reposent aussi sur des « formes de légitimité latentes », moins spectaculaires que le vote ou la loi, mais non moins décisives. L’expérience belge illustre avec force cette persistance d’une régulation sans gouvernement explicite. Ce phénomène suggère que l’architecture régulatrice n’est plus identifiable aux lieux habituels de la souveraineté.
|
||||
|
||||
Pour autant, le pouvoir n’a pas disparu ; il s’exerce désormais depuis d’autres formes que celles qui le légitimaient. C’est qu’il s’est délocalisé, désinstitutionnalisé, déréférencé— tout en continuant à structurer silencieusement la vie collective. Et c’est dans cet écart grandissant — entre l’absence de gouvernement et la persistance d’une régulation — que se dessine le cœur de la problématique contemporain : pour nombre d’entre nous, nous continuons à chercher le pouvoir là où il n’est plus, et à négliger les régulations implicites là où elles deviennent de plus en plus décisives.
|
||||
|
||||
Pour que ces dispositifs puissent fonctionner ainsi, discrètement et efficacement, sans qu’on puisse les identifier ni les contester, il faut d’abord que les lieux où ils auraient pu être exposés, discutés ou débattus soient neutralisés, effacés, disqualifiés ou rendus inutiles. Les lieux de pouvoir s’évaporent progressivement, les moyens d’expression se désagrègent, et les cadres d’appel à la responsabilité deviennent in-entendables. L’arène du politique ne disparaît pas brutalement, elle se désagrège lentement à mesure que ses conditions d’existence — la mise en scène, la confrontation et la mise à l’épreuve — se retirent.
|
||||
|
||||
Les anciens espaces d’exposition — Parlement, place publique, journal, commission, agora, tribune — ne remplissent plus leur fonction instituante. Non qu’elles soient abolies : elles subsistent, mais tournent à vide par éléments de langage superposés, par logiques oblitératrices, par interruptions des moments d’interpellation et des déploiements de pensées contre-propositionnelles. Elles parlent sans prise. Elles évoquent sans effet. Elles promettent sans adossement réel. En somme, elles hypnotisent et désactivent. Et tandis qu’elles persistent comme formes, les lieux effectifs de la régulation — là où s’arbitrent réellement les seuils, s’ajustent véritablement les normes, se décident les niveaux de tolérance ou d’exclusion — se déplacent hors de la portée de tous.
|
||||
|
||||
Cette désactivation des anciennes scènes de visibilité ne relève pas d’une abstraction — elle a connu, en France, un moment décisif et révélateur : le référendum de 2005 sur le traité constitutionnel européen (TCE). Ce vote ayant pourtant agrégé près de 55 % de refus, n’a pas produit les effets régulateurs que l’on aurait pu attendre. Deux ans plus tard, son contenu central était repris dans le traité de Lisbonne, adopté par voie parlementaire à Versailles, sans jamais redonner la parole aux citoyens.
|
||||
|
||||
Ce court-circuitage du résultat du référendum n’a pas seulement provoqué un malaise démocratique : il a signalé l’obsolescence d’une arène politique qui prétend encore incarner la souveraineté populaire, tout en s’ajustant aux impératifs d’une régulation supranationale désindexée de tout espace de débat. Depuis, les grandes décisions se prennent largement hors scène, dans des configurations qui échappent aux rituels de la légitimation représentative. L’épisode du TCE fut ainsi moins une exception qu’un révélateur : la souveraineté n’a pas disparu, elle s’est déplacée et s’est muée ; et ce sont les lieux traditionnels de confrontation — qui permettaient de la contester — qui perdent peu à peu de leur puissance.
|
||||
|
||||
Il en résulte aujourd’hui ce que l’on pourrait nommer *une vacance des figures politiques*. Non pas un vide institutionnel — les appareils demeurent — ni un abandon total du pouvoir — les décisions continuent de tomber — mais bien un effacement progressif des repères identifiables à travers lesquelles ce pouvoir pouvait encore être *pensé*, *nommé*, *interrogé*, *disputé ou dénoncé*. Ce qui fait défaut, ce ne sont ni les procédures, ni les organigrammes, ni les énoncés de façade ; le pouvoir ne parvient plus à lui donner horizon partagé, épreuve contradictoire et adresse signifiante.
|
||||
|
||||
Les institutions demeurent et fonctionnent. Des lois sont encore votées, des ordonnances promulguées, des discours prononcés dans des formes toujours codifiées. Pourtant, ces énoncés institutionnels, malgré leur constance formelle, peinent à produire de l’attachement, du conflit réglé, du récit commun. Il faut dire que lorsque les grandes directives sont déjà prises ailleurs, et qu’une tutelle s’exerce sur les marges de manœuvre budgétaire et les politiques publiques, tout programme de rupture avec l’existant ne peut advenir sans discrédit. Ceci conduit jusqu’à présent à entériner et à traduire en terme juridique les grands principes issus des institutions européennes. Ainsi nos partis politiques et nos institutions s’amenuisent et perdent de leur influence : elles peinent à percer les seuils et à générer des événements rassembleurs. Ils surviennent, puis s’évanouissent — rarement débattus, rarement disputés. Ils tentent néanmoins de transformer mais restent peu crédibles. La vie démocratique ne parvient plus à infléchir ledit pouvoir dans une visée d’horizon partagé puisque sa souveraineté s’est vue entachée.
|
||||
|
||||
Peut-être faut-il alors suspendre un instant le flux de l’analyse pour ouvrir la perspective : entendre ce que cette disparition fait à nos imaginaires. Car perdre les lieux de confrontation, ce n’est pas perdre uniquement un espace politique — c’est voir s’effacer le langage commun de la mise en tension, de l’épreuve contradictoire, du désaccord rendu partageable. En somme, celui-ci n’est pas qu’un cadre, mais aussi une forme sensible, un rythme, un tempo, un théâtre où pouvaient s’exprimer les dissensus, mais aussi se nouer des alliances, des compromis, des co-habitations et des promesses de coexistence. C’est cette mise en forme qui vacille aujourd’hui — et avec elle, notre capacité à rendre visibles les lignes de fracture, les régimes d’attachement, les besoins vitaux et leurs modalités d’arbitrage.
|
||||
|
||||
Depuis les élections se succèdent, mais l’offre programmatique s’uniformise. En France, lors des campagnes présidentielles de 2022, plusieurs observateurs ont noté une quasi-absence de débats contradictoires sur les infrastructures écologiques, la gestion de l’eau, les algorithmes de tri social ou les seuils budgétaires européens — sujets pourtant structurants et centraux des problématiques actuelles. La parole politique reste intense, mais elle survole en ignorant les points réels d’adhérence et de discordance. Elle n’expose ni la texture du monde vécu ni la réalité du tissu productif. Elle ne donne plus à saisir ni la forme ni la scène où les arbitrages s’opèrent. Elle devient commentaire sans impact, phrase choc ou viralité polémique sans colonne vertébrale permettant de se figurer les problématiques.
|
||||
|
||||
Côté médias, le constat est plus ambivalent, mais tout aussi troublant. D’un côté, l’information est surabondante ; de l’autre, les controverses s’enlisent dans le flux. On discute des intentions, rarement des formats. On spécule sur les effets, sans jamais problématiser les dispositifs. Un exemple emblématique en est l’émission télévisée *“Face à Baba”*, ou le “Grand Débat National” post-Gilets Jaunes qui suscita beaucoup de prises de parole, tout en ayant peu de prise sur le réel. Il y eut certes des paroles fortes, mais sans aucune structure d’intégration. Il y eut des opinions tranchées, mais sans l’énonciation d’une architecture délibérative. De sorte que la parole a circulé, mais elle n’avait pas autorité à s’instituer. Elle n’était pas dans le bon lieu. Le plateau télévisé n’ayant pour autre vocation que l’audience.
|
||||
|
||||
Même les commissions d’enquête, qui historiquement cristallisaient un moment de vérité ou de remaniement, semblent affectées. Le rapport de l’Assemblée nationale sur la gestion de la pandémie de Covid-19, par exemple, a bien été publié en 2022. Il formule des dizaines de propositions. Mais pourtant, peu d’entre elles ont fait l’objet d’une reprise effective, ni dans la sphère politique, ni dans la sphére médiatique, ni dans la transformation des pratiques administratives. Là encore, la procédure opère — mais sans relai, sans engagement, sans espace de transformation. Même s’il faut le reconnaître, certaines analyses ont néanmoins nourri un débat plus large sur l’état de la santé publique, contribuant à renforcer la vigilance citoyenne sur les infrastructures hospitalières.
|
||||
|
||||
Un autre exemple, plus récent encore, illustre avec une intensité toute particulière ce décalage entre mise en scène délibérative et opérativité réelle : celui de la Convention Citoyenne pour le Climat, initiée en France en 2019 à la suite du mouvement des Gilets Jaunes. Ce dispositif inédit proposait à 150 citoyennes et citoyens, tirés au sort, de formuler des propositions concrètes pour réduire les émissions de gaz à effet de serre (GES) dans un esprit de justice sociale. La procédure fut longue, exigeante, documentée. Les membres furent encadrés par des scientifiques, des spécialistes, des juristes, des praticiens. Leurs recommandations — 149 au total — furent saluées, y compris par les experts du climat, comme ambitieuses, sérieuses, largement compatibles avec les engagements climatiques de la France. Le président de la République s’était engagé à les transmettre « sans filtre ».
|
||||
|
||||
Et pourtant. À l’issue de la convention, la grande majorité des propositions furent vidées de leur substance, renvoyées en commissions, ou transformées jusqu’à l’inverse de leur logique initiale. Certaines furent reprises à la marge dans la loi « Climat et Résilience », d’autres enterrées sans débat, d’autres encore tournées en dérision. L’expression « sans filtre » fut rapidement abandonnée, remplacée par des formules dilatoires. L’instance réflexive a existé, mais elle n’a pas su instituer. La parole a circulé, mais elle n’a pas performé. La procédure bien que dense, n’a pas permis là encore l’instauration d’une architecture de régulation efficiente.
|
||||
|
||||
En ce sens, la Convention n’a pas échoué parce qu’elle était utopique ; elle a échoué parce qu’elle n’a pas trouvé d’ancrage régulateur dans l’architecture politique réelle. Cet exemple montre bien comment un dispositif peut produire de la parole et de la visibilité, sans pour autant parvenir à instituer une régulation opérante. Ce n’est donc pas un lieu de confrontation sans conflit, mais une instance délibérative qui n’a pas donné suite. Et c’est ce type d’effacement — non spectaculaire, mais systémique — qui constitue aujourd’hui le symptôme d’une archéologie du politique désamarrée de ses obligations démocratiques.
|
||||
|
||||
Contrairement au discours du sens commun pointant la responsabilité du chef de l’État, nous pensons que le pouvoir s’est désincarné. Bien que les figures de l’autorité demeurent — titres, fonctions, attributs symboliques — elles ne rassemblent plus ni contestation structurée (opposant), ni reconnaissance affective (popularité), ni légitimation opérante (autorité). Pendant ce temps, la conflictualité n’a jamais autant submergé le tissu social : désaccords éthiques, désynchronisations temporelles, fractures territoriales, crises multifactorielles, paupérisation systémique, violences symboliques ou physiques. Et malgré nous, cette conflictualité ne trouve plus les lieux où s’exprimer sans exploser. Elle ne se problématise plus dans des dispositifs communs, mais éclate en formes de colère dispersées, parfois illisibles, parfois délégitimées avant même d’avoir trouvé son expression stabilisée. Tel fut le cas du mouvement des Gilets Jaunes.
|
||||
|
||||
Et pendant ce temps, les décisions, elles, se ramassent à la pelle : fermeture d’un service hospitalier, recentrage budgétaire, ajustement d’un seuil d’éligibilité, réforme à marche forcée du régime des retraites, réforme de l’assurance chômage, redéfinition d’indicateurs d’évaluation du marché de l’emploi, déremboursements médicaux, désindexation d’aide sociale, etc., etc. Ces décisions viennent, mais sans adresse explicite, sans exposition des arbitrages effectués, sans procès public, sans contradictoire. Elles sont le fruit d’instances spécifiques rendues opaques et qui n’apparaissent pas, ou n’assument pas leur fonction politique. Elles opèrent sous couvert de technique, mais agissent comme pouvoir — sans l’assumer publiquement.
|
||||
|
||||
Ainsi, ce n’est pas la *capacité d’agir* qui fait défaut — comme nous le voyons les régulations persistent — mais la *possibilité de rendre visible ce qui agit*. Ce qui tend à s’effacer, ce n’est pas le politique comme mécanisme de régulation, mais comme *espace de mise à l’épreuve*. Nous habitons un monde saturé de normes, mais privé de figures crédibles de justification. Les arbitrages se multiplient sans explication, sans délibération, sans lieu d’arbitrage démocratiquement établi.
|
||||
|
||||
Et c’est précisément cette disparition d’espaces de controverses et de confrontation — cette disparition des lieux où se mettait en forme le différend, où s’exposait le conflit, où se partageait le sensible — qui constitue une perte capitale. Car c’est par la mise en scène des dissensus que les sociétés humaines ont, pendant des siècles, pu penser ensemble ce qui les liait, les divisait, les orientait. C’est sur cette instance d’épreuve qu’étaient rendues visibles les visions du monde qui s’affrontaient, les justifications qui s’opposaient, les intérêts qui s’exprimaient. Supprimez la scène d’exposition — et ce n’est pas le pouvoir qui disparaît, mais la possibilité d’en débattre. De sorte que la régulation dans les faits ne s’interrompt jamais : c’est la possibilité même qu’elle devienne affaire publique qui s’efface. Quant à l’ordre des choses, il ne se dissout pas, il se mue. Et c’est notre capacité collective à le mettre en cause qui se délite faute de compréhension et de préhension.
|
||||
|
||||
Privés de lieux publics partagés, devenus propriétés privées, nous sommes aussi privés d’une mise en conflit visible et compréhensible. Pourtant, les tensions sont nombreuses, mais elles restent muettes, sans récit commun ni cadre d’expression. Ce qui nous divise cesse d’apparaître clairement. Ce qui nous déchire n’a plus de langage partagé performatif. Ce qui devrait susciter débat et polémique s’efface dans l’indifférence ou se réduit à une simple gestion technique de l’opinion et de la propagande. Le jeu politique ne dispute plus l’ordre du monde, car il semble ne plus le pouvoir, elle en devient seulement le décor figé, répétant sans contradiction les mêmes ritournelles idéologiques.
|
||||
|
||||
Or, sans polémique et sans cadre robuste de pensée, il n’y a plus de politique au sens fort. Il y a de la décision, de la gestion, de la réaction, du pilotage. Mais il n’y a plus d’espace où les fins pourraient être débattues, les normes interrogées, les tensions rendues visibles. Ce qui demeure, c’est une sorte de théâtre spectral — où le pouvoir mime encore ses rituels, mais sans adossement, sans prise, sans mise en jeu. Et ce décor fantomatique maintient en vie un imaginaire périmé : celui d’un pouvoir situé, identifiable, contestable. Mais cet imaginaire n’opère plus. Il flotte comme une relique, un fantasme d’époque révolue. C’est tout du moins ce que nous pensons.
|
||||
|
||||
Ce qui se prépare alors — sans être encore nommé —, c’est un changement de condition politique. Une métamorphose souterraine, silencieuse, mais décisive. Nous traversons un déplacement du sol même sur lequel reposait notre compréhension du pouvoir, du conflit, de la régulation. Et ce déplacement appelle un autre langage. Non pas un mot de plus dans une série — mais un geste de pensée qui permette de reconfigurer les coordonnées même à partir desquelles nous pourrions analyser ce qui fait tenir les mondes. Ce changement de condition politique n’est pas une abstraction. Il s’est incarné historiquement, idéologiquement, structurellement. Et l’un de ses vecteurs majeurs — rarement interrogé comme tel — fut le tournant néolibéral du XXᵉ siècle.
|
||||
|
||||
Avec son avènement, ce qui mute, c'est la fabrique même de la régulation. Ce qui s’est déplacé, ce sont les modalités par lesquelles un ordre devient opérant, ajusté, imposable — sans jamais se dire tel. Le néolibéralisme, en ce sens, décompose les conditions mêmes de la conflictualité démocratique. Il n’a pas réduit les règles : il a effacé les lieux de confrontation où l’on pouvait encore les contester. Il a opéré une reconfiguration des coordonnées fondamentales du politique : *qui agit ? selon quelles justifications ? selon quels formats ? Et où peut-on encore l’interroger ?*
|
||||
|
||||
Nous voici donc au bord d’un tournant : bien plus qu’un langage politique qui s’épuise, ce sont les gestes mêmes qui permettaient de nommer, de rendre visible, de mettre à l’épreuve les régulations. Il nous faut donc changer de focale. Non plus partir des régimes connus, des formes visibles du pouvoir, des catégories héritées. Mais remonter au plus près des gestes primitifs qui configurent toute forme de régulation : ce qui fonde, ce qui fait agir, et ce qui articule les deux dans des formes tangibles, contestables, visibles et viables qui pourraient expliquer le mouvement évolutif des sociétés.
|
||||
|
||||
Dans notre analyse, le moment néolibéral a précisément perturbé cette articulation. Il a introduit un brouillage entre l’origine du pouvoir et ses effets, entre ce qui autorise et ce qui contraint, entre ce qui se dit et ce qui agit. Et c’est dans ce brouillage que se loge aujourd’hui l’impensé du politique contemporain.
|
||||
|
||||
Pour en sortir, il faut retrouver les gestes fondamentaux à partir desquels un monde collectif peut encore être rendu lisible et vivable. Ce geste, nous le nommons ici : *archéologie de la régulation*. Si l’on veut comprendre ce qui se défait dans les régulations contemporaines — non pas de manière conjoncturelle, mais de façon structurelle —, il est impératif de remonter en amont des formes politiques connues, jusqu’aux forces sémantiques primitives que notre lexique transporte souvent à son insu.
|
||||
|
||||
C’est en reprenant le fil depuis ses origines étymologiques que nous pourrons reconstituer la force d’arrachement de ces termes, les faire parler à nouveau — non comme vestiges, mais comme opérateurs toujours actifs, toujours présents, sous des formes multiples et plurielles. Ce détour par la langue n’est pas un exercice érudit. C’est une tentative de ré-accorder le langage à l’expérience vécu, de ressaisir les prises fondamentales du pouvoir à travers leurs gestes constituants et fondateurs.
|
||||
|
||||
Ainsi, les suffixes en *-archie* et en *-cratie*, que nous avons évoqués plus haut comme désignations de régimes, sont bien plus que des marqueurs grammaticaux, ils condensent des opérations fondamentales du politique. Plus précisément, ils signalent deux gestes constitutifs et irréductibles dans toute structuration collective : *celui du fondement* (*arkhè*) *et celui de l’exercice* (*kratos*). Le premier désigne l’*origine légitime* ; le second, la *puissance agissante*. Mais dans leur réduction lexicale, ces deux gestes ont été figés et dissociés en formes de régime, perdant de vue leur fonction dynamique et conjointe dans tout ordre social : *fonder* et *faire agir.* C’est pour restituer leur opérativité conceptuelle que nous introduisons ici les termes d’*arcalité* et de *cratialité*.
|
||||
|
||||
Par *arcalité*, nous entendons tout ce qui fonctionne comme *principe de légitimation*, qu’il soit explicite ou tacite, sacré ou profane, traditionnel ou numérique, juridique ou narratif. Le terme, quitte à nous répéter, est construit à partir de la racine grecque *arkhè* (ἀρχή), qui — comme nous l’avons vu précédemment — désigne dans un même mouvement le commencement, le commandement et le fondement. Ce triple sens — d’origine, de légitimation et d’autorité — est au cœur des opérations symboliques à travers lesquelles les sociétés humaines tentent de rendre leur ordre acceptable, de justifier leurs hiérarchies, de naturaliser leurs choix.
|
||||
|
||||
Il ne s’agit donc pas d’une réalité substantielle, mais d’un acte d’instauration, d’un arc de légitimation, d’un marqueur de crédit : toute *arcalité* est un *geste de production de l’autorité*, qu’elle se fonde sur la révélation divine, sur la tradition des ancêtres, sur la puissance d’un nom de famille, sur la volonté générale, sur les données empiriques, sur des décrets ou des lois, sur l’efficience calculée ou sur la science algorithmique. Ce terme est polysémique dans son usage.
|
||||
|
||||
Dans la pensée grecque ancienne, l’*arkhè* n’était pas une figure du pouvoir, mais un principe d’intelligibilité du cosmos et de la cité. Pour Anaximandre, par exemple, l’*arkhè* est ce qui donne forme et cohérence au monde — ce par quoi tout commence et à quoi tout revient. Chez Aristote, elle devient aussi principe logique, cause première, source de mouvement. Transposée dans l’ordre politique, elle désigne *ce qui autorise un pouvoir à se dire tel, ce à partir de quoi il peut être reconnu, accepté, toléré, voire vénéré*. C’est dans ce sens que *la monarchie* (le pouvoir d’un seul) ou *l’oligarchie* (le pouvoir de quelques-uns) sont fondées sur un principe d’*arkhè*, qu’il s’agisse de droit divin, de naissance, de mérite ou de savoir. Mais une *arcalité* n’est pas forcément une doctrine : elle peut être une forme implicite d’*évidence sociale*, un *consentement tacite intériorisé*.
|
||||
|
||||
Alors pourquoi introduire le mot *arcalité*, au risque du néologisme ? Parce qu’à nos yeux, aucun autre terme ne permet de désigner avec suffisamment de précision, de plasticité et de portée opératoire ce qui constitue le noyau de légitimation des dispositifs de pouvoir. Le mot *autorité* est trop général. *Légitimité* est trop juridique. *Fondement* est trop théologique ou métaphysique. *Source* ou *origine* sont trop historicistes. Or, ce que nous cherchons à nommer, ce n’est pas ce qui est vrai, ni même ce qui est légitime en soi, mais *ce qui fonctionne comme légitimation dans un contexte donné* — ce qui est reconnu, assumé ou subi comme *ce à partir de quoi un pouvoir peut s’exercer* sans être immédiatement remis en cause.
|
||||
|
||||
L’*arcalité* est donc un concept transversal et multiple : elle traverse les époques, les cultures, les régimes — tout en changeant de visage et de figures. Elle peut prendre la forme d’un texte sacré, d’un contrat social, d’une Constitution, d’un mythe fondateur, d’une promesse technoscientifique, ou même d’un jeu d’indicateurs économiques. Elle peut être verticalement imposée ou horizontalement négociée. Elle peut être stabilisée dans le droit ou émerger dans la rue. Mais dans tous les cas, elle opère comme ce qui justifie le pouvoir, ce qui lui donne son aura d’évidence, ce qui naturalise ses opérations en les rendant pensables et acceptables.
|
||||
|
||||
Mais au-delà de ces premiers assertions, l’*arcalité* se présente aussi comme un outil épistémologique pour l’analyse des régimes de légitimation — elle peut servir comme *opérateur heuristique* pour lire les sociétés dans leurs *structures de croyance, de reconnaissance, de justification*. Elle permet d’analyser des situations aussi différentes que la réforme d’un système de retraite, le recours à une IA dans la sélection universitaire ou la fondation d’un État théocratique, non pas en fonction de leur contenu normatif, mais en fonction de *ce qui est supposé justifier leur existence*, *ce qui les autorise à s’imposer*.
|
||||
|
||||
Pour commencer à en saisir la portée, il nous faut sommairement déplier l’*arcalité* dans ses déclinaisons historiques et symboliques. Elle ne renvoie pas à un type de régime, mais à *ce qui autorise un pouvoir à s’exercer sans être récusé* : une scène de légitimation, explicite ou tacite. Ainsi, sous l’Ancien Régime, l’autorité du roi n’était pas justifiée par sa compétence, mais par une *sacralité divine incarnée dans le sang, la lignée, le rite* — une *arcalité théologico-politique* où l’*arkhè* se vivait du trône jusqu’à l’autel.
|
||||
|
||||
À Rome, l’*arcalité* pouvait se loger dans le *mos maiorum*, cette mémoire des ancêtres qui n’avait pas besoin de s’écrire pour s’imposer. Elle se transmettait par la répétition des gestes, la reprise des rituels, la reproduction des conduites fondatrices. Plus qu’un corpus de lois codifiées, c’était une continuité opérante, un fil invisible bien que contraignant, qui obligeait chacun non parce qu’un texte le disait, mais parce qu’une *tradition vivante* le réactivait dans chaque pratique. L’autorité se rejouait dans l’acte même, dans la persistance d’un style collectif, plus que dans une règle explicitement énoncée.
|
||||
|
||||
Avec la modernité politique, un autre geste s’invente : l’*arcalité* devient *récit contractuel*. Hobbes, Locke ou Rousseau projettent l’image d’un pacte inaugural entre égaux libres, d’un *contrat social* qui, bien qu’il n’ait jamais été historiquement conclu, institue l’ordre en lui donnant un mythe d’origine. Peu importe que ce contrat soit fictif : il agit comme s’il avait existé, et c’est précisément cette fiction qui fonde la légitimité moderne. L’*arcalité*, désormais, ne s’incarne plus seulement dans la répétition des pratiques, mais dans la *puissance narrative d’un contrat raconté*.
|
||||
|
||||
Aujourd’hui, d’autres *arcalités* se font jour, parfois là où on ne les attend pas.
|
||||
|
||||
Ainsi, dans la *Déclaration des droits de la Pachamama* (Bolivie, 2010), la Terre elle-même est instituée comme *source normative*. La planète, envisagée comme sujet de droit, devient figure d’*arkhè* : principe fondateur d’un ordre politique et juridique qui se justifie par la nature, le vivant et la mémoire écologique. Ici, l’*arcalité* prend la forme d’une *sacralisation de la Terre-mère*, traduite en langage constitutionnel, et ouvre la possibilité d’un autre type de fondement, ni théologique, ni contractuel, mais cosmologique.
|
||||
|
||||
Dans les technostructures contemporaines, comme les agences de notation, les protocoles de régulation algorithmique, ou les plateformes numériques, l’*arcalité* devient *data* : ce sont les chiffres, les indicateurs, les modèles prédictifs qui justifient l’action. L’autorité repose sur la *robustesse du calcul*, la *neutralité supposée du code*, la *précision des seuils*. Ce que Yuval Noah Harari (*Homo deus, une brève histoire du futur*, 2017) appelle le “*dataïsme”* en est une figure actualisée exemplaire, où le flux des données devient source même de vérités.
|
||||
|
||||
Enfin, dans certaines configurations capitalistiques contemporaines, l’*arcalité* prend une forme radicalement *autoréférentielle* : elle ne s’appuie plus sur une extériorité normative, ni même sur une validation juridique ou démocratique, mais sur la seule *performance passée érigée en légitimité présente*. C’est ce que l’on pourrait appeler une *arcalité autogénérative*, où le succès ne demande plus de justification externe — il en révèle sa propre preuve. Une entreprise qui attire des investissements massifs, une plateforme dont la valorisation boursière croît de manière exponentielle, une *startup* qui atteint le statut de “licorne” (plus d’un milliard de dollars de capitalisation), n’a plus besoin de se légitimer par un utilité sociale, une finalité collective ou un adossement institutionnel : *le simple fait d’avoir réussi* suffit à valider l’ensemble des choix stratégiques. Elle s’impose précisément parce qu’elle se donne à voir comme ayant *toujours déjà* fonctionné.
|
||||
|
||||
Mais l’*arcalité* dans ses multiples manifestations, si décisive soit-elle, ne suffit pas à faire tenir un monde. Car un ordre social, quel qu’il soit, ne repose jamais uniquement sur ses principes le légitimant. Il doit aussi pouvoir opérer, agir, décider, trancher, maintenir — parfois même contraindre ou punir. Il ne suffit pas qu’un pouvoir soit justifié ; encore faut-il qu’il se déploie, qu’il prenne forme dans des pratiques, des formats, des opérateurs. En somme, il ne suffit pas qu’un ordre se dise fondé ; il faut encore qu’il s’exerce.
|
||||
|
||||
Il faut pour cela un autre registre, un autre plan de la régulation : celui de l’action. C’est ici qu’intervient ce que nous nommons la *cratialité* — terme forgé comme pour les régimes en -cratie — à partir de *kratos* (κράτος), qui désigne en grec ancien la force, la puissance agissante, la capacité à faire advenir quelque chose dans le réel. Si l’*arcalité est ce qui autorise*, la *cratialité est ce qui opère*. L’une pose les conditions de validité, l’autre produit les effets. L’une fonde, l’autre exerce.
|
||||
|
||||
Par *cratialité*, nous entendons *ce qui agit concrètement dans le tissu des pratiques régulatrices*, ce qui produit des effets sans forcément passer par une scène légitime, visible, ou symboliquement codifiée. C’est la face opératoire du pouvoir, son versant dynamique et actif ; autrement dit : sa *capacité d’effectuation*. Tandis que l’*arcalité* pose, fonde, encadre, la *cratialité* module, infléchit, dévie, ajuste, parfois de manière souterraine, parfois de manière spectaculaire.
|
||||
|
||||
Ce qui justifie l’introduction du néologisme *cratialité*, c’est précisément l’absence, dans le lexique politique courant, d’un mot qui désigne la force agissante sans forme nécessairement instituée, le pouvoir qui s’exerce sans forcément se nommer. Il nous fallait un terme qui capture cette modalité infra-institutionnelle, trans-opérationnelle, souvent désintriquée de toute légitimation formelle, mais dont les effets façonnent les conduites, les agencements et les structures.
|
||||
|
||||
La *cratialité* constitue la *puissance agissante*, la *pulsation régulatrice*, le *versant infra-symbolique*. Elle n’obéit pas à un modèle de souveraineté ou de commandement, mais plutôt à une *logique d’opérativité distribuée*, qui se manifeste dans les actions physiques, mais aussi dans les procédures, les normes implicites, les scripts techniques, les interfaces, les indicateurs, les routines disciplinaires, les ajustements silencieux.
|
||||
|
||||
Mais la *cratialité*, pour autant qu’elle désigne une force agissante sans principe visible, ne saurait être une entité homogène ou univoque. Elle se manifeste toujours sous des formes différenciées, selon les milieux, les dispositifs, les instruments, les régimes d’effet, les temporalités d’action. On aurait tort de l’imaginer comme un pouvoir unifié : elle est plutôt un pluriel sans totalisation, un spectre de pratiques régulatrices qui modulent l’expérience collective à partir d’agencements situés, parfois massifs, parfois microscopiques.
|
||||
|
||||
Pour saisir ce que le concept de *cratialité* permet de penser, il faut l’observer là encore à travers des configurations historiques et contemporaines, où le pouvoir n’agit plus par commandement, mais par *formatage*. La *cratialité* désigne cette *capacité d’un agencement à produire des effets* — non par le droit ou l’autorité, mais par la scénographie, l’attendu, le code, le protocole, la procédure.
|
||||
|
||||
Sous l’Ancien Régime, par exemple, la cour de Versailles fonctionnait moins comme lieu de majesté que comme machinerie de régulation sociale. Le rituel monarchique — lever du roi, étiquette, spatialisation des corps — opérait comme une *cratialité incorporée*. Norbert Elias parlait de *processus de civilisation* ; ici, c’était d’un *pouvoir par intériorisation* qu’il s’agissait : réglage des affects, des gestes, des postures, sans violence apparente, avec une efficacité néanmoins redoutable.
|
||||
|
||||
Dans la Rome républicaine, la *cratialité*, ou puissance politique, s’incarnait dans des procédures précises et des institutions conçues pour éviter la concentration excessive du pouvoir : contre-pouvoirs, délais, rituels et magistratures étaient organisés selon une architecture politique équilibrée. Michel Villey a analysé le droit romain comme un art réaliste où la loi ne s’imposait pas arbitrairement du sommet, mais se construisait dans une pratique juridico-politique orientée vers la justice et adaptée aux circonstances. Cette organisation tempérait un pouvoir centralisé par un jeu complexe d’équilibres, où la règle naissait autant de la tradition et des usages que de l’autorité formelle.
|
||||
|
||||
Avec la modernité étatique, Michel Foucault a montré que le pouvoir se *biopolitise* : santé publique, statistiques, recensement, école obligatoire… la *cratialité* devient une *fabrique de conduites*, agissant en deçà de la loi, modulant les corps et les populations. Dès lors, le pouvoir ne commande plus : il structure la norme à travers des *dispositifs agissants*.
|
||||
|
||||
Dans de nombreux États postcoloniaux, la *cratialité* conserve en grande partie ses *formes héritées* du passé colonial. Cadastres construits selon les normes coloniales, routines bureaucratiques reproduisant les modalités administratives imposées, et scripts institutionnels continuent de réinscrire l’autorité coloniale dans le quotidien, infusant ainsi la gestion étatique d’une mémoire et d’une pratique dominantes. Cette régulation politique et administrative perdure, portée par l’*inertie d’une cratialité enracinée dans les structures héritées*.
|
||||
|
||||
À l’ère écologique, la *cratialité* passe par la *norme chiffrée* : marchés du carbone, seuils ISO, critères ESG. Le droit à polluer se négocie, se calcule, s’échange. Il s’agit moins de régulation morale que d’un *pilotage algorithmique* des équilibres. Bien que la Terre devienne une *arcalité*, sa régulation, elle, repose sur une *cratialité technico-financière*, opaque, désarrimée de tout débat public.
|
||||
|
||||
Enfin, dans les régimes numériques contemporains, la *cratialité* se dissout dans les infrastructures : IA décisionnelles, *scoring* social, plateformes automatisées. Comme l’écrit Shoshana Zuboff, nous sommes entrés dans l’ère du *capitalisme de surveillance*, où la gouvernance s’exerce sans discours et sans voie de recours. Byung-Chul Han y voit un “pouvoir transparent total” où celui-ci n’est plus énoncé — il agit, module, filtre et exclut.
|
||||
|
||||
Après ce tour d’horizon des *cratialités* à travers l’espace et le temps, s’il fallait justifier, en dernière instance, l’introduction d*e* ce concept, ce serait en raison d’un vide analytique que les cadres existants ne parviennent plus à combler. La question n’est pas tant de créer un terme de plus, que de rendre dicible une opération fondamentale du pouvoir : celle qui consiste à *agir sans autorité explicite*, à contraindre sans justification, à réguler sans discours. En ce sens, la *cratialité* ne s’oppose pas à l’*arcalité*, elles se complètent. Elles révèlent l’une à l’autre leur limite. Car il est possible d’agir sans légitimer, de structurer sans fonder, d’opérer sans lieu de confrontation ni narration instituante.
|
||||
|
||||
Le concept de *cratialité* nous offre le terme pour penser ce pouvoir qui ne se proclame plus, mais qui agit partout — dans les flux, les interfaces, les chaînes logistiques, les scripts numériques, les seuils budgétaires, les routines bureaucratiques. Il désigne la capacité d’un agencement à produire des effets, à modifier des comportements, à orienter des trajectoires, sans s’exposer à la délibération, sans se soumettre à l’explication, sans passer par l’énonciation d’un principe délibéré. C’est ce que Foucault entrevoyait dans la notion de *dispositif*, ce que Deleuze pressentait dans la *modulation*, ce que Simondon entrevoyait dans la *concrétisation technique* — mais qu’aucune de ces notions ne rassemble sous un registre proprement politique, assumant sa fonction régulatrice dans l’espace collectif.
|
||||
|
||||
C’est pourquoi la *cratialité* ne se confond ni avec la domination, ni avec le pouvoir institutionnel, ni même avec la technique. Elle traverse les formes, en tant qu’*opérativité sans fondement*. Elle n’explique pas pourquoi un ordre existe — elle permet de comprendre comment il s’exerce effectivement. Et c’est là sa puissance critique : déloger le politique de ses seules figures visibles, pour révéler les régulations silencieuses, les contraintes discrètes, les mécanismes délégués, les automatisations sans autorité véritablement légitime. Elle ouvre ainsi une contre-archéologie du pouvoir, attentive à ce qui agit sans se dire, à ce qui règle sans apparaître.
|
||||
|
||||
Sur le plan épistémologique, la *cratialité* permet donc un déplacement décisif : elle nous extrait du dualisme éculé entre légitimité et illégitimité, démocratie et anarchie, État et société civile. Elle invite à penser un entre-deux, un “tiers-opératoire” : là où le politique s’institue non par la Loi ou par le Chaos, mais par l’*agencement de dispositifs producteurs de réalité*. Ce pouvoir-là ne commande pas : il invente et configure. Il ne tranche pas : il jauge et module. Il ne s’expose pas : il infiltre et s’impose. Et c’est pourquoi il faut un mot pour le désigner — non pas pour l’isoler, mais pour en cartographier les régimes, les formats, les seuils d’acceptabilité.
|
||||
|
||||
Sur le plan critique, la *cratialité* permet aussi de distinguer l’effet de l’auteur : ce n’est pas parce que personne ne commande que rien ne s’impose. Ce n’est pas parce qu’une norme n’a pas été votée qu’elle n’opère pas, et à l’opposé, ce n’est pas parce qu’une norme est appliquée qu’elle opère nécessairement. C’est toute la différence entre l’ordre prescrit et l’ordre effectif — différence aujourd’hui cruciale, tant la puissance régulatrice des systèmes dépasse celle des gouvernements.
|
||||
|
||||
Enfin, sur le plan opératoire, la *cratialité* fournit une grille de lecture transversale des phénomènes contemporains : du design d’une application mobile à la réorganisation d’un hôpital, du protocole logistique d’un port à la normalisation de la parole publique, du filtrage des contenus en ligne à la gestion algorithmique de l’emploi. Partout où quelque chose agit sans se dire comme pouvoir, sans se nommer comme décision, sans s’assumer comme contrainte, la *cratialité* est à l’œuvre.
|
||||
|
||||
La *cratialité* ne dit pas ce qui est juste — elle *rend visible ce qui fait effet*. Elle ne produit pas une norme — elle révèle *ce qui règle sans normativité déclarée*. Et c’est à ce titre que le concept de *cratialité* est précieux : parce qu’il nous rend à nouveau capables de penser la régulation là où elle se dissimule, de discuter ce qui semblait inéluctable, de contester ce qui opérait sans discussion. En ce sens, elle ne ferme pas l’espace politique — elle l’ouvre de nouveau, en partant non plus des formes idéales, mais des forces effectives.
|
||||
|
||||
Mais si ces deux pôles — celui du fondement (*arcalité*) et celui de l’opération (*cratialité*) — peuvent être pensés séparément, ils ne fonctionnent jamais isolément dans les mondes réels. C’est là toute la limite d’une lecture en coupe : on peut analyser un principe, décrire une procédure, mais ce qui fait régulation, ce n’est jamais l’un sans l’autre. La légitimation sans effectuation tourne à la mystification ; l’effectuation sans légitimation vire à l’arbitraire. Ce n’est donc pas dans leur distinction, mais dans leur agencement — dans leur articulation concrète — que se joue le cœur du pouvoir régulateur.
|
||||
|
||||
Et c’est précisément ce point d’articulation que nous voulons nommer, penser et problématiser ici.
|
||||
|
||||
Si l’*arcalité* est *ce qui légitime*, et la *cratialité* *ce qui exerce*, alors l’*archicration* est le *nœud de leur articulation effective*. Elle désigne cette opération composite, toujours située, par laquelle un pouvoir donné fonde son action et agit selon son fondement — ou, inversement, agit tout en produisant sa propre légitimation — à la condition de se laisser exposer dans une scène d’épreuve. L’*archicration* est ce *point de nouage où se rejoignent la question du pourquoi* (*pourquoi ce pouvoir est-il reconnu ?*) *et celle du comment* (*comment ce pouvoir opère-t-il effectivement ?*), là où cette rencontre devient publiquement opposable. Non pas deux dimensions juxtaposées, mais deux régimes co-constitutifs de toute scène régulatrice.
|
||||
|
||||
Ainsi, de la fusion des deux termes (*arkhè* et *krateîn*) naît l’*archicration* : *l’acte de fonder en agissant, ou d’agir en fondant*. Factuellement, dans les régulations politiques concrètes, il n’y a jamais de pouvoir qui se contente de dire sans faire, ni d’agir sans justifier. Tout dispositif un tant soit peu structurant articule, à sa manière, un *arkhè* *qui justifie* et un *krateîn qui effectue*.
|
||||
|
||||
La force heuristique du concept d’*archicration*, c’est donc de réinscrire dans un même geste ce que la modernité politique avait tendance à dissocier : d’un côté, la légitimation (par le droit, le peuple, Dieu, la science…), de l’autre, l’effectuation (par les lois, les institutions, les techniques, les procédures…). Le pouvoir moderne s’est souvent construit sur une prétention à l’extériorité : l’action devait découler d’un principe, comme si l’on pouvait d’abord fonder, ensuite agir. Cette dissociation était une fiction structurante, utile pour l’ordre symbolique, mais inopérante pour une lecture du réel.
|
||||
|
||||
En vérité, les régulations contemporaines — et peut-être une grande partie des régulations passées, cela reste à être étayé — peuvent être relues comme des agencements archicratifs, plus ou moins explicites, plus ou moins stables, mais toujours composite : l’État-nation est une *archicration* qui articule une *arcalité constitutionnelle* (la souveraineté populaire) à des *cratialités institutionnelles* (le gouvernement, l’administration, la police, l’armée, etc.). Il en va de même pour les grandes plateformes numériques qui articulent des *arcalités technoscientifiques* (la promesse de l’innovation, la rationalité du code, la neutralité de l’algorithme) à des *cratialités infra-visibles* (le filtrage, l’instrumentation, la captation de données, la modulation du comportement). Même un ordre religieux — ou un régime dit théocratique — n’échappe pas à cette logique : il articule des *arcalités transcendantes* (le texte révélé, la Loi divine, les prophètes, le Messie) à des *cratialités rituelles et doctrinales* (l’interprétation, la sanction, le commandement, la discipline).
|
||||
|
||||
L’*archicration* ne désigne donc pas un régime parmi d’autres. Elle nomme, selon toute hypothèse, le nœud vivant d’une régulation politique qui accepte de se rendre visible, différée et contestable, dès lors qu’elle vise à perdurer. De sorte qu’un pouvoir qui échoue à fonder ce qu’il opère s’expose à l’arbitraire ; un pouvoir qui échoue à opérer ce qu’il fonde s’épuise dans l’impuissance. De même, le simulacre surgit quand l’*arcalité* se dissocie de toute effectuation ; l’instabilité s’installe quand la *cratialité* ne s’adosse à aucun principe partagé. Ce que nous appelons *archicration*, c’est donc ce point de tension active et scénique où s’articulent — ou se désarticulent — le fondement et l’opération, la forme et la force, la justification et l’exécution.
|
||||
|
||||
Or, c’est lorsque cette articulation devient asymétrique, disjointe, ou captée, que le politique mute en profondeur — non plus sous la forme d’un changement visible de régime, mais d’un glissement silencieux des coordonnées de la régulation. Et parmi les figures contemporaines de cette désarticulation archicrative, le moment néolibéral occupe une place stratégique, tant par son étendue historique que par sa profondeur de reconfiguration. Non qu’il ait aboli la régulation — il l’a redéployée. Non qu’il ait supprimé le politique — il l’a déplacé, technicisé, encodé, rendu algorithme.
|
||||
|
||||
À partir des années 1970, sous l’impulsion d’intellectuels comme Friedrich Hayek et Milton Friedman, relayés par les *Chicago Boys,* amplifiés par les politiques de Ronald Reagan et Margaret Thatcher, puis par les prescriptions des grandes organisations transnationales (FMI, Banque mondiale, OMC), le néolibéralisme s’est imposé sans conquête déclarée, par glissement, par ruse, par standardisation. Il n’a pas remplacé les institutions du politique, il les a désactivés en douceur ; il n’a pas détruit l’État, il a recodé ses prérogatives régulatrices selon une nouvelle logique : celles de l’efficience, du marché, du seuil, du calcul et d’un retour sur ses fonctions régaliennes.
|
||||
|
||||
D’un côté, cette idéologie a déplacé l’*arcalité* : le principe de fondement ne repose plus sur la souveraineté populaire, la loi, ou la délibération publique, mais sur l’*efficience marchande*. Le marché libre est présenté comme naturel, universel, neutre — et se promeut comme seul critère de légitimité. C’est ce que nous appelons ici *arcalité performative* : le fondement est produit par la performance elle-même. Le succès vaut justification. La croissance devient norme. L’indicateur remplace le débat.
|
||||
|
||||
D’un autre côté, elle redéfinit la *cratialité* : la régulation ne passe plus par des institutions politiques visibles, mais par une gouvernance par les normes, les seuils, les indicateurs, les standards, les classements (*rankings*) et les interfaces. La décision n’est plus revendiquée ; elle s’élabore en algorithme opérationnel. Le pouvoir module, ajuste, et encode. Ce que Wendy Brown a appelé, dans *Undoing the Demos*, la “rationalité néolibérale” ne se résume pas à un discours ou à une doctrine — c’est une *cratialité généralisée*, une *logique de fonctionnement incorporée* dans les infrastructures mêmes du quotidien, où toute alternative devient impensable.
|
||||
|
||||
Le mot d’ordre de “dérégulation” prôné par les tenants du néolibéralisme masquaient en réalité un déplacement massif de la régulation et non son annihilation. Ce qui a disparu peu à peu, ce ne sont pas les règles — elles pullulent — mais les scènes d’arbitrage où l’on pouvait encore en contester le sens, la pertinence, les effets. Les espaces publics de reconnaissance où elles pouvaient encore être mises à l’épreuve se sont effacés ou disséminés. Ainsi, chaque réforme — des retraites, de l’assurance chômage, de l’université, de l’hôpital ou de la fiscalité — est dès lors présentée non comme un choix idéologique, mais comme une évidence technico-financière, une réponse dite “rationnelle” à une contrainte supposée incontournable.
|
||||
|
||||
Ce qui s’impose alors est une reconfiguration radicale des conditions d’existence. Le pouvoir n’est ni aboli ni dissimulé — il est redistribué dans des registres et des protocoles privés, encodé dans des métriques non explicites, traduit dans des novlangues tout en se distillant dans des environnements régulateurs où la fabrique du politique a été méthodiquement neutralisée. Ce que le *néolibéralisme* installe, c’est donc une *pseudo-archicration sans scène*, là où la délibération démocratique est dessaisie. Il produit un agencement dans lequel la légitimation ne s’édicte plus, mais s’infiltre sous forme de performance antérieure ; dans lequel la régulation ne tranche plus publiquement, mais opère en souterrain, au travers d’indicateurs, de seuils, d’algorithmes, de traités supranationaux, de conventions de marché.
|
||||
|
||||
Arrêtons-nous un instant pour bien préciser les termes de notre propos. Par *scène*, nous entendons l’*instance d’épreuve où des* *forces*, des *acteurs*, des *registres* ou des *institutions* d’ordres différents *se confrontent sous règles explicites.* Il ne s’agit donc pas uniquement du seul théâtre institutionnel ; nous parlons ici d’un espace d’apparition conflictuelle, où puisse se rendre audible les dissensus (Jacques Rancière, *La Mésentente*, 1995).
|
||||
|
||||
Dès lors, penser l’*archicration*, c’est chercher à rendre lisible le point de tension vive où un ordre se constitue en tenant ensemble des forces hétérogènes. Ce que nous proposons ici n’est pas un modèle, mais un geste : un mode d’attention aux lignes d’opération du pouvoir, une manière de cartographier les régulations en acte, une tentative de réouverture d’espaces démocratiques. L’objectif étant de refonder la possibilité d’épreuves là où l’opacité et l’occultation s’est installée, et pour rouvrir un espace de visibilité critique là où l’efficacité s’auto-justifie et prétend se suffire à elle-même.
|
||||
|
||||
Sous notre prisme d’analyse, l’*archicration* s’éprouve comme une réalité concrète, un espace vivant traversé de tensions permanentes, où se cherche sans cesse un fragile *équilibre entre des formes d’assise, des forces productives et des vulnérabilités constitutives*. Or, cet équilibre n’a rien de garanti : il peut basculer, se rompre, se déformer.
|
||||
|
||||
Que faut-il entendre par là ? Certaines *configurations archicratives* tendent à surinvestir l’*affectation* — entendu par là, les dimensions idéologique, symbolique, émotionnelle qui prétendent donner sens et légitimer un ordre — tout en négligeant l’*effectuation,* soit la capacité de traduire ces principes en dispositifs opératoires stables et durables. L’exemple de la République jacobine de 1793 en fournit une illustration saisissante : exaltée par la fiction d’une souveraineté populaire absolue, elle n’a pas su consolider ses mécanismes de régulation pour perdurer. L’énergie de l’idéal a vite été dévorée par l’incapacité d’instaurer des équilibres viables, précipitant le régime dans la Terreur et l’épuisement institutionnel.
|
||||
|
||||
D’autres *archicrations*, à l’inverse, s’enferment dans l’*effectuation* en oubliant tout récit justificatif. L’Union européenne en fournit un cas exemplaire : sa gouvernance repose sur une accumulation de normes techniques, de seuils budgétaires, de critères de convergence, d’indicateurs chiffrés (3 % de déficit, 60 % de dette, pactes de stabilité, règles de concurrence, standards environnementaux...). Ce régime régulateur, d’une efficacité incontestable dans sa capacité à contraindre les États membres, opère par une effectuation puissante, mais trop souvent déliée d’un langage politique partagé. Les décisions se justifient au nom de la « rationalité économique » ou de la « soutenabilité financière », sans se traduire dans un récit de légitimité accessible aux citoyens. C’est cette asymétrie — abondance de dispositifs opératoires, rareté des justifications symboliques — qui alimente le sentiment d’un déficit démocratique chronique, relevé par de nombreux observateurs et confirmé par les épisodes de rejet populaire (référendum français de 2005, Brexit, poussées eurosceptiques). Ici, l’*archicration* s’épuise dans l’opération : elle agit, ajuste, module, mais peine à convaincre et à rassembler.
|
||||
|
||||
À l’autre extrême, certaines *archicrations* se construisent sur une *fausse fusion* entre fondement et opération. Les régimes autoritaires modernes, comme ceux de la Russie de Vladimir Poutine ou de la Turquie de Recep Tayyip Erdogan, en offrent des incarnations saisissantes. L’*arkhè* proclamé — la Nation, la Tradition, la Religion, parfois la Civilisation — est érigé en principe indiscutable, en vérité d’origine inattaquable. Mais cette légitimation abstraite fonctionne comme paravent et sert de couverture à des pratiques de régulation brutales, des répressions ciblées, des ajustements institutionnels opportunistes, des captations oligarchiques et des dispositifs de contrôle opaque. L’illusion d’une concordance parfaite entre fondement et action masque en réalité une dissociation : le fondement est vidé de son contenu normatif, réduit à une invocation rituelle, tandis que l’effectuation se déploie dans la violence, l’arbitraire ou la manipulation technique. Dans ce type d’*archicration captée*, l’absence de véritable contradictoire — presse muselée, opposition criminalisée, société civile réduite — empêche toute mise à l’épreuve du pouvoir. Au lieu d’être travaillé dans une tension féconde, l’équilibre *arcalité/cratialité* est réduit à un simulacre qui absolutise le principe pour mieux immuniser l’action.
|
||||
|
||||
C’est pourquoi penser la régulation politique à travers le prisme de l’*archicration*, ce n’est pas inventer un nouveau régime ni esquisser une utopie institutionnelle. C’est surtout déplacer le regard. Refuser de prendre les formes pour des essences, les régimes pour des totalités closes ou les normes pour des évidences. C’est ouvrir une autre cartographie du pouvoir, fondée non sur les déclarations, mais sur les agencements ; non seulement sur les formes héritées, mais aussi sur les opérations effectives. Une cartographie qui permette d’interroger chaque configuration politique en termes d’articulation concrète entre ce qui justifie (l’*arcalité*) et ce qui agit (la *cratialité*).
|
||||
|
||||
C’est là le cœur du geste archicratique : restituer aux sociétés la lisibilité critique de leurs propres agencements, là où le pouvoir cherche à effacer ses fondements, et sa régulation à se rendre inattaquable.
|
||||
|
||||
Mais allons plus loin. Si l’*archicration* désigne, dans une régulation, le moment où l’articulation mouvante de la légitimation et de l’opération devient scénique, explicite et publiquement opposable, alors l’*archicratie* peut être pensée comme la configuration historique dominante dans laquelle cette articulation s’effectue aujourd’hui sous condition de défiguration, de dissimulation et de dissémination.
|
||||
|
||||
Par *archicratie*, nous entendons une forme contemporaine de *gouvernementalité désancrée* — au sens où Michel Foucault a dégagé, dans ses cours au Collège de France, comme *méta-régime de rationalité politique* qui déportent l’exercice du pouvoir vers des dispositifs et des techniques (Sécurité, territoire, population, 1977–1978 ; Naissance de la biopolitique, 1978–1979) — dans laquelle l’arène politique est défaite, le fondement rendu flottant, et l’opération disséminée dans des dispositifs qui ne se laissent plus nommer, ni questionner. Il ne s’agit ni d’un type de régime, ni d’un idéal-type ; c’est une *condition régulatrice dépolitisée* — où l’*arcalité* est rendue *automatisée* (par et pour la data) et où, l’*archicration* étant *oblitérée*, la *cratialité* s’exerce *hors de toute instance publique* (algorithme, protocole, contrat, externalisation).
|
||||
|
||||
Cette entrée en matière aura eu comme visée de dégager le lieu exact où se joue aujourd’hui notre impuissance politique. Cette situation n’est pas un déficit de principes ni un excès de pouvoir ; c’est le dérèglement a-démocratique des régimes de régulation, rendu illisible faute de scènes délibératives et d’instruments partagés.
|
||||
|
||||
C’est en remontant aux rouages primitifs du pouvoir — l’*arcalité*, *polarité des formes d’affectation* ; la *cratialité*, *polarité des forces d’effectuation* — que nous déplaçons la question politique des figures du gouvernement vers les prises différenciées de sa régulation.
|
||||
|
||||
En nommant *archicration* cette *articulation dynamique* — souvent instable — *entre ce qui affecte le pouvoir et ce qui en déploie les effets*, nous avons pu construire une première grille de lecture propre aux configurations contemporaines, où les dispositifs agissent sans toujours se légitimer, et où les fondements proclamés — droits humains, droits du travail, droits du vivant — voient décroître leur force d’obligation effective, au profit d’agencements opératoires qui ne se légitiment plus qu’à la marge.
|
||||
|
||||
Ce que cette notion permet de mettre au jour, ne se limite pas au fonctionnement des régulations, mais aux difficultés croissantes à les voir être interrogées, éprouvées, exposées au contradictoire. Loin de n’être qu’une propriété secondaire, cette mise à distance de toute possibilité de contestation structurée constitue désormais un trait central de notre condition politique. Ce qui agit n’est plus exposé qu’à la marge de la discussion publique et ce qui régule dorénavant s’exerce de plus en plus en silence, dans l’ombre, à l’abri des regards, dans un retrait radical du débat sur ses formes, ses conditions, ses seuils, ses formats.
|
||||
|
||||
Mais ce geste inaugural — critique, archéologique, modélisateur — ne restera légitime et significatif que s’il s’éprouve. Et c’est justement à cette mise à l’épreuve que se consacreront les chapitres à venir. Car le paradigme archicratique n’est pas pur appareillage conceptuel, il est avant tout une méthode de dévoilement, une topologie des régimes régulateurs, un cadre opératoire pour penser la viabilité politique en situation.
|
||||
|
||||
Le chapitre I en établira le socle épistémologique rigoureux, en articulant les trois prises fondamentales du modèle — *arcalité, cratialité, archicration* — à une grammaire formelle et symbolique, pensée comme modèle falsifiable, susceptible de simulation et d’interprétation. Il posera la structure tripolaire comme condition d’une intelligibilité systémique, traversable tant par l’histoire que par la technique ou la psychologie collective.
|
||||
|
||||
Le chapitre II délaissera toute abstraction normative pour réinscrire le politique dans la généalogie profonde des régimes de co-viabilité. Il retracera, de manière située et incarnée, les formes empiriques de régulation, depuis les dispositifs totémiques mésolithiques jusqu’aux technorégulations cryptographiques contemporaines. Il tentera de montrer que toute société se constitue d’abord comme régime de *co-viabilité* régulée — dont certaines configurations seulement atteignent un seuil proprement archicratique — bien avant de se penser comme État, comme droit, ou comme nation.
|
||||
|
||||
Le chapitre III, d’inspiration philosophique, viendra tester l’*archicratie* comme outil de relecture des grandes pensées du pouvoir. Plutôt que de les aligner par écoles ou doctrines, il les confrontera à une grille archicratique : *quelle arcalité y opère ? Quelle cratialité s’y manifeste ? Quelle scène d’épreuve, d’opposition, ou de captation y est rendue possible ou empêchée ?* Ce faisant, il opérera un déplacement crucial passant de la justification du pouvoir à la morphologie des régimes de régulation.
|
||||
|
||||
Le chapitre IV, d’orientation techno-historique, incarnera ces tensions dans l’histoire matérielle même de la modernité. Il ne se contentera pas de relire les révolutions industrielles comme des ruptures productives, mais comme des reconfigurations archicratiques profondes. Chaque mutation technique — de la vapeur à l’électricité, du numérique à l’automatisme — a transformé la manière dont un pouvoir se légitime, agit, se distribue ou se dérobe. La technologie, dans sa manifestation même, devient une instance cratiale, et parfois une matrice d’*archicration* détournée, empêchée ou rendu invisible. C’est donc à une lecture de la régulation des bifurcations industrielles que ce chapitre s’attachera, afin d’en révéler les architectures implicites de pouvoir.
|
||||
|
||||
Enfin, le chapitre V affrontera la conflictualité maximale de notre temps : celle des tensions de co-viabilité. Il abordera, l’une après l’autre, les grandes scènes critiques — économique, écologique, sociale, médiatique, psychique, politique, technologique, géopolitique, cosmopolitique et culturelle — en les traitant non comme objets disciplinaires, mais comme *archicrations* problématiques. Chaque tension y sera relue comme une bifurcation possible : vers une régulation viable, une impasse pathologique ou une captation silencieuse. Ce chapitre constituera le point de bascule, le test ultime du paradigme archicratique : *permet-il de nous aider à discerner, sans dogme, ce qui tient un monde debout — ou le fait vaciller ?*
|
||||
|
||||
Cet essai-thèse convie à une lucidité active : observer l’effectivité du pouvoir, discerner l’articulation du fondement et de l’opération, rouvrir des formes de conflictualité légitime. Il propose une lecture critique et située des régulations qui nous gouvernent. Une manière d’observer ce qui se joue, non dans les proclamations du pouvoir, mais dans ses agencements concrets. Une méthode pour discerner les formes différenciées par lesquelles une société s’ajuste, se tient, ou s’effondre.
|
||||
|
||||
Ce livre a pour unique vocation de fonder le concept d’*archicratie* — articulé par le triptyque *arcalité* / *cratialité* / *archicration* — comme hypothèse heuristique pour lire les situations politiques réelles et situées. Les mentions de détectabilité, d’opérativité ou d’épreuve critique qui apparaissent çà et là n’engagent aucun protocole métrique dans le présent volume : elles signalent de simples repères de lecture et d’éventuelles pistes de recherche ultérieures. L’ouvrage n’édicte pas de méthode d’évaluation, n’agrège pas d’indicateurs, ne promet pas de simulation ; il propose une grammaire conceptuelle pour rendre intelligibles des configurations de pouvoir.
|
||||
|
||||
Penser l’*archicratie*, ce n’est pas restaurer une essence perdue, ni bâtir un système clos. C’est remettre la pensée politique là où elle devient la plus nécessaire : dans les frictions entre ce qui agit et ce qui justifie ; dans les écarts entre ce qui régule et ce qui est vécu ; dans les tensions entre ce qui tient ensemble et ce qui menace de rompre.
|
||||
|
||||
Ce que propose cette entrée en *archicratie*, c’est une autre orientation du regard : vers les conditions d’effectivité du pouvoir, vers les agencements où le fondement et l’opération cessent d’être dissociés, vers les formes nouvelles de conflictualité légitime et de co-viabilité existentielle.
|
||||
|
||||
C’est à cette cartographie critique, située, différenciée et incarnée des régulations contemporaines que nous allons maintenant nous atteler. Non pour clore le politique. Mais pour le rouvrir là où il se donne à penser, c’est-à-dire dans l’*archicration* — là où se nouent, toujours : ce qui fonde, ce qui agit, et ce qui fait tenir.
|
||||
Le 29 mai 2005, le référendum français sur le traité constitutionnel
|
||||
européen donne un résultat clair : le non l'emporte.
|
||||
|
||||
Pendant plusieurs semaines, la scène démocratique avait retrouvé une
|
||||
intensité rare. Le texte avait circulé dans les journaux, les réunions
|
||||
publiques, les partis, les syndicats, les familles, les plateaux de
|
||||
télévision, les tracts, les conversations ordinaires. On y parlait
|
||||
d'Europe, de souveraineté, de marché, de services publics, de droit
|
||||
social, de monnaie, de concurrence, de constitution. Des lignes de
|
||||
fracture traversaient les camps politiques habituels. La question était
|
||||
posée. Le corps électoral répondait. Le résultat était reconnu.
|
||||
|
||||
Rien ne paraît plus directement politique qu'un peuple appelé à se
|
||||
prononcer sur l'architecture institutionnelle dans laquelle il accepte,
|
||||
ou refuse, de s'inscrire.
|
||||
|
||||
Pourtant, deux ans plus tard, l'essentiel des dispositions
|
||||
institutionnelles revient par une autre voie : le traité de Lisbonne,
|
||||
ratifié en France par le Parlement réuni en Congrès à Versailles. Le
|
||||
vote n'a pas été juridiquement effacé. La démocratie ne s'est pas abolie
|
||||
pour autant. Une telle lecture manquerait le point le plus troublant. La
|
||||
scène avait eu lieu ; la parole collective avait été comptée ; le
|
||||
résultat avait été admis. Mais cette scène n'a pas suffi à infléchir
|
||||
durablement la trajectoire régulatrice dans laquelle elle prétendait
|
||||
intervenir.
|
||||
|
||||
Ce qui s'ouvre ici dépasse la crise de confiance ou la blessure
|
||||
symbolique laissée dans la mémoire démocratique française. Une fissure
|
||||
plus profonde apparaît : une scène peut engager une parole collective,
|
||||
produire un acte clair, recevoir une reconnaissance formelle, et manquer
|
||||
pourtant de la prise nécessaire pour transformer l'ordre où cet acte
|
||||
s'inscrit. Tout y était : un texte, une campagne, un vote, un résultat,
|
||||
une controverse. Ce qui vacille n'est pas la présence du politique, mais
|
||||
la capacité d'une scène politique à rejoindre les architectures
|
||||
effectives où se stabilisent les décisions.
|
||||
|
||||
Ce déplacement est moins spectaculaire qu'un effondrement. Il est plus
|
||||
difficile à saisir. Il ne dit pas que les institutions seraient vides,
|
||||
que les votes seraient sans valeur, que la parole collective serait
|
||||
devenue inutile. Il indique autre chose : le lieu où une volonté se
|
||||
formule ne coïncide plus toujours avec le lieu où les trajectoires se
|
||||
décident. Entre l'acte démocratique et la chaîne de ses effets
|
||||
s'interpose une épaisseur de normes, de procédures, de contraintes,
|
||||
d'engagements antérieurs, de cadres transnationaux, de rythmes
|
||||
institutionnels. La décision existe, mais elle rencontre un ordre déjà
|
||||
structuré, qui l'absorbe, la requalifie, la diffère ou la contourne.
|
||||
|
||||
La question politique ne sort pas intacte d'un tel écart.
|
||||
|
||||
Depuis lors, les noms se sont multipliés : démocratie illibérale,
|
||||
technocratie, ploutocratie, post-démocratie, gouvernement algorithmique,
|
||||
démocrature. Ils isolent des dérives réelles, des captures, des
|
||||
fermetures, des mutations. Ils disent quelque chose du présent. Leur
|
||||
profusion signale pourtant une gêne. Nous savons encore désigner des
|
||||
figures du pouvoir ; nous savons moins suivre les régulations qui les
|
||||
traversent, les prolongent, les dispersent ou les rendent partiellement
|
||||
inopérantes. Nous nommons des régimes, des déviations, des excès. Nous
|
||||
peinons davantage à comprendre comment une décision circule, où elle se
|
||||
transforme, par quels instruments elle prend effet, à quel moment elle
|
||||
devient presque impossible à reprendre.
|
||||
|
||||
Les catégories politiques classiques ne sont pas devenues fausses.
|
||||
L'État existe encore ; les lois se votent, les administrations
|
||||
instruisent, les tribunaux statuent, les parlements délibèrent, les
|
||||
élections ont lieu. Des gouvernements tombent, se maintiennent,
|
||||
négocient, arbitrent. Rien de tout cela n'a disparu. La difficulté vient
|
||||
précisément de là : ce qui demeure visible ne suffit plus à rendre
|
||||
lisible l'ensemble de ce qui agit.
|
||||
|
||||
Une part croissante de ce qui affecte les existences se joue dans des
|
||||
chaînes moins directement assignables : protocoles techniques, normes
|
||||
privées, standards internationaux, contraintes budgétaires, interfaces
|
||||
numériques, indicateurs, plateformes administratives. Ces éléments n'ont
|
||||
pas remplacé les institutions. Ils les traversent, les équipent, les
|
||||
prolongent, parfois les déplacent. Le pouvoir ne s'est pas absenté. Il a
|
||||
changé de régime d'apparition.
|
||||
|
||||
Ce changement s'annonce rarement comme rupture. Il ne prend pas toujours
|
||||
la forme d'un coup de force, d'une décision spectaculaire, d'une
|
||||
suspension déclarée des droits ou d'une disparition visible des
|
||||
institutions. Il s'installe dans la continuité ordinaire des dispositifs
|
||||
: amélioration des procédures, simplification des démarches,
|
||||
rationalisation des coûts, accélération des traitements, harmonisation
|
||||
des critères. Rien ne semble alors sortir du langage de la bonne
|
||||
administration. Pourtant, la décision ne paraît plus toujours comme
|
||||
décision. Elle prend la forme d'un seuil, d'un score, d'un barème, d'une
|
||||
notification, d'un recalcul, d'une mise en conformité, d'une priorité
|
||||
ajustée.
|
||||
|
||||
Le lieu du pouvoir se transforme. Sa possibilité d'adresse aussi.
|
||||
|
||||
On identifie encore des institutions, des responsables, des textes, des
|
||||
discours. Mais les effets décisifs passent par des médiations où les
|
||||
responsabilités se distribuent, où les critères se naturalisent, où les
|
||||
décisions se fragmentent en opérations successives. La visibilité d'un
|
||||
pouvoir ne le rend pas adressable. La connaissance d'une procédure ne la
|
||||
rend pas contestable. La trace d'une décision ne garantit aucune
|
||||
reprise. Un résultat se produit, se notifie, s'archive, s'oppose à une
|
||||
personne, sans que l'espace où il devrait être discuté soit réellement
|
||||
praticable.
|
||||
|
||||
Le référendum de 2005 donne une figure haute de ce problème : une scène
|
||||
démocratique majeure existe, mais sa prise se révèle limitée. Des scènes
|
||||
plus ordinaires en donnent chaque jour des formes plus discrètes. Elles
|
||||
ne frappent pas l'histoire avec la même intensité. Elles n'en disent pas
|
||||
moins quelque chose de l'époque, parce qu'elles touchent des vies avant
|
||||
même d'être reconnues comme politiques.
|
||||
|
||||
Une demande de titre de séjour se trouve suspendue par une plateforme
|
||||
pour anomalie de saisie. La personne concernée ne rencontre pas d'abord
|
||||
une décision, mais un espace en ligne, un formulaire, un message
|
||||
standard, un délai, une pièce déjà transmise, l'impossibilité de joindre
|
||||
le bon service. Elle ne sait pas toujours si son dossier est refusé, en
|
||||
attente, incomplet, mal orienté, perdu dans une file numérique. Ce qui
|
||||
est en jeu n'a pourtant rien d'abstrait : travail, logement, famille,
|
||||
circulation, statut. Une vie entière dépend d'une opération qui ne se
|
||||
présente jamais comme décision politique, mais comme traitement
|
||||
administratif.
|
||||
|
||||
Ailleurs, un versement s'interrompt après recalcul. Une orientation
|
||||
scolaire se joue dans une chaîne où se mêlent dossiers, attendus,
|
||||
capacités d'accueil et pondérations. Un crédit, une aide, une priorité
|
||||
médicale et un accès à un service dépendent de critères dont la logique
|
||||
demeure partiellement opaque pour ceux qu'elle affecte. Dans ces
|
||||
situations, quelque chose décide, classe, autorise, retarde ou exclut.
|
||||
Mais ce quelque chose ne comparaît pas toujours comme pouvoir. Il se
|
||||
donne comme procédure, traitement, calcul, nécessité.
|
||||
|
||||
La banalité de ces situations fait leur force. Elles appartiennent au
|
||||
quotidien des sociétés régulées : interfaces, seuils, justificatifs,
|
||||
files d'attente, classements, tableaux de bord, notifications. Rien n'y
|
||||
ressemble nécessairement à un ordre brutal. Aucun agent ne lève la voix.
|
||||
Aucune souveraineté ne se dresse en majesté. Pourtant, une existence s'y
|
||||
trouve infléchie, appauvrie, immobilisée, rendue plus dépendante, moins
|
||||
audible. La contrainte n'a pas besoin de s'annoncer comme commandement
|
||||
pour produire des effets politiques.
|
||||
|
||||
Demander qui gouverne demeure nécessaire. Mais la question arrive trop
|
||||
tard lorsque les chaînes par lesquelles la décision s'est déjà faite
|
||||
restent hors de portée. Il s'agit alors de suivre la manière dont une
|
||||
régulation tient, agit, se justifie, se rend praticable ou impraticable
|
||||
pour ceux qu'elle affecte. Une société ne se livre pas dans ses
|
||||
institutions déclarées ; elle se lit aussi dans les médiations qui
|
||||
organisent ses arbitrages, dans les formats qui rendent certaines vies
|
||||
visibles et d'autres secondaires, dans les procédures qui transforment
|
||||
des blessures en dossiers, des différends en anomalies, des conflits en
|
||||
traitements.
|
||||
|
||||
La question devient alors plus exigeante : qu'est-ce qu'un ordre qui
|
||||
tient ?
|
||||
|
||||
Tenir ne signifie pas durer. Une domination dure. Une injustice dure. Un
|
||||
ordre se maintient en détruisant ce qui le rend habitable. Il fonctionne
|
||||
longtemps tout en rendant certaines existences plus exposées, moins
|
||||
audibles, plus dépendantes de procédures qu'elles ne comprennent ni
|
||||
n'infléchissent. Il accumule des normes, produit des indicateurs, ouvre
|
||||
des recours, affiche des garanties, et réduit pourtant la possibilité
|
||||
effective de faire valoir ce qu'il abîme.
|
||||
|
||||
Tenir, dans le sens qui importe ici, désigne autre chose. Un monde tient
|
||||
politiquement lorsqu'il rend possible la coexistence de formes de vie
|
||||
dissemblables, vulnérables, inégalement exposées, traversées de
|
||||
contradictions, sans que certaines soient sacrifiées aux conditions
|
||||
imposées aux autres. Il tient lorsque les tensions qu'il produit ou
|
||||
reçoit ne sont pas aussitôt rejetées comme désordres, retards,
|
||||
résistances, coûts, défaillances individuelles. Il tient lorsque ce qui
|
||||
souffre apparaît, se formule, oblige une réponse, transforme quelque
|
||||
chose de l'ordre qui l'a rendu possible.
|
||||
|
||||
Cette possibilité fragile, nous la nommons co-viabilité.
|
||||
|
||||
Un monde co-viable n'est pas un monde apaisé. Il ne promet ni harmonie,
|
||||
ni paix civile, ni réconciliation finale. Il possède encore des formes
|
||||
pour porter ce qui le divise. Il sait encore reconnaître des tensions,
|
||||
les différer, les symboliser, les disputer, les reprendre, sans les
|
||||
abolir par force ni les abandonner à leur pure destruction. La
|
||||
co-viabilité nomme cette tenue précaire : non l'absence de conflit, mais
|
||||
la possibilité pour un conflit de ne pas devenir immédiatement ruine du
|
||||
commun.
|
||||
|
||||
Les sociétés ne tiennent pas parce qu'elles auraient supprimé ce qui les
|
||||
divise. Elles tiennent lorsqu'elles donnent forme à leurs divisions. Une
|
||||
souffrance devient objection. Un dommage devient litige. Une règle se
|
||||
rapporte à ses effets. Un pouvoir se trouve rappelé aux raisons qu'il
|
||||
invoque. Là où ces transformations deviennent impossibles, la
|
||||
conflictualité ne disparaît pas. Elle se déplace, s'épuise, se disperse
|
||||
ou revient sous des formes plus dures.
|
||||
|
||||
La politique commence aussi là : dans la possibilité de faire apparaître
|
||||
ce qui affecte, de le rendre partageable, de le mettre en tension, de le
|
||||
soumettre à une reprise. Un ordre peut posséder des institutions solides
|
||||
et perdre cette capacité. Il accumule normes, recours, consultations,
|
||||
rapports, indicateurs, tout en rendant difficile l'épreuve effective de
|
||||
ce qu'il produit. La scène demeure alors, mais quelque chose se retire
|
||||
d'elle. Elle continue d'accueillir des paroles, des procédures, des
|
||||
justifications ; elle ne parvient plus toujours à rejoindre les chaînes
|
||||
où les effets se décident.
|
||||
|
||||
La scène peut subsister comme forme et se vider comme puissance.
|
||||
|
||||
Le référendum de 2005 en a donné une figure. La Convention citoyenne
|
||||
pour le climat en offre une autre, plus récente, plus ambiguë. Après le
|
||||
mouvement des Gilets jaunes, cent cinquante citoyennes et citoyens tirés
|
||||
au sort sont réunis pour travailler sur la réduction des émissions de
|
||||
gaz à effet de serre dans un esprit de justice sociale. Des experts
|
||||
interviennent. Des auditions ont lieu. Les propositions sont discutées,
|
||||
formulées, rendues publiques. Pendant un moment, une scène prend forme :
|
||||
des citoyens non professionnels de la politique se saisissent d'enjeux
|
||||
que l'on croyait réservés aux administrations, aux experts, aux
|
||||
négociateurs, aux arbitrages gouvernementaux.
|
||||
|
||||
Ce moment n'a rien d'anecdotique. Il ouvre, même brièvement, une
|
||||
possibilité rare : des existences ordinaires entrent dans un problème
|
||||
systémique, apprennent ses contraintes, en éprouvent les tensions,
|
||||
formulent des propositions qui engagent l'organisation matérielle du
|
||||
commun. La scène n'était pas fictive. Elle n'était pas un décor. Des
|
||||
choses s'y sont dites, apprises, déplacées. Pourtant, sa traduction
|
||||
politique est restée partielle, sélective, affaiblie. La promesse d'une
|
||||
reprise « sans filtre » s'est défaite dans les arbitrages, les
|
||||
commissions, les reformulations, les retraits, les délais.
|
||||
|
||||
Là encore, la parole ne manque pas. Elle a même pris une forme
|
||||
travaillée, instruite, collective. Le problème tient à l'écart entre une
|
||||
comparution réelle et une capacité de transformation limitée. La scène
|
||||
existe ; elle produit du jugement, de l'attention, de la formulation.
|
||||
Mais elle ne dispose pas de la prise nécessaire pour modifier l'ordre
|
||||
qu'elle rend visible.
|
||||
|
||||
Les deux cas ne disent pas exactement la même chose. En 2005, une scène
|
||||
de souveraineté populaire produit un résultat dont la trajectoire
|
||||
institutionnelle ultérieure limite la portée. Avec la Convention
|
||||
citoyenne, une scène délibérative formule des propositions dont la
|
||||
reprise politique demeure fragile. Dans les deux cas, la démocratie
|
||||
n'est pas absente. La parole apparaît, la scène prend forme, l'acte
|
||||
existe. Pourtant, ces scènes peinent à rejoindre les circuits où les
|
||||
décisions prennent durablement forme.
|
||||
|
||||
L'épisode belge de 2010-2011 éclaire un autre versant du problème.
|
||||
Pendant de longs mois, l'État fédéral demeure sans gouvernement de plein
|
||||
exercice. Le pays ne s'effondre pas. Les administrations continuent
|
||||
d'agir. Les services publics fonctionnent. Les cadres normatifs
|
||||
persistent. L'événement ne rend pas le gouvernement superflu. Il montre
|
||||
qu'un ordre tient au-delà de la figure visible du commandement, par
|
||||
inerties, routines, structures administratives, compromis latents,
|
||||
agencements distribués. Là où l'on attendait la décision souveraine, on
|
||||
découvre l'épaisseur des continuités régulatrices.
|
||||
|
||||
Ainsi se dessine une difficulté plus vaste. Des scènes démocratiques
|
||||
existent sans infléchir suffisamment les trajectoires régulatrices ; des
|
||||
régulations fonctionnent sans scène politique pleinement lisible. Entre
|
||||
les deux, le politique ne disparaît pas. Il se dissocie. Ce qui fonde,
|
||||
ce qui opère et ce qui devrait être mis à l'épreuve ne coïncident plus.
|
||||
Les principes demeurent, les opérations continuent, les procédures
|
||||
subsistent, mais leur articulation devient incertaine.
|
||||
|
||||
Cette dissociation traverse les institutions et les vies. Elle apparaît
|
||||
dans l'école, l'hôpital, le travail, l'accès aux droits, la fiscalité,
|
||||
l'administration numérique, les plateformes, les politiques
|
||||
environnementales. Un dispositif affiche une finalité d'équité tout en
|
||||
produisant des effets de tri difficilement contestables. Une réforme se
|
||||
réclame de la justice tout en opérant par seuils budgétaires. Une
|
||||
plateforme promet la simplicité tout en rendant plus difficile
|
||||
l'explication d'un cas singulier. Un indicateur prétend éclairer une
|
||||
décision, puis finit par se substituer au jugement qu'il devait servir.
|
||||
|
||||
La violence propre de ces situations tient à leur apparence de
|
||||
normalité. Les droits ne sont pas toujours suspendus ; ils se
|
||||
conditionnent. Les recours demeurent, mais deviennent longs, étroits,
|
||||
techniques, décourageants. La parole circule, mais dans des formats qui
|
||||
l'épuisent. La scène ne se ferme pas brutalement ; elle se déplace, se
|
||||
sature, perd sa puissance. Le pouvoir n'a pas besoin de se dresser
|
||||
devant les sujets pour les orienter. Il les entoure de formes.
|
||||
|
||||
Les oppositions ordinaires deviennent alors insuffisantes : État ou
|
||||
marché, public ou privé, pouvoir fort ou pouvoir faible, décision
|
||||
politique ou exécution technique. Une régulation publique par ses effets
|
||||
peut devenir privée par ses instruments. Une finalité étatique peut
|
||||
s'accomplir par opération algorithmique. Une décision démocratiquement
|
||||
annoncée s'affaiblit dans son traitement administratif. Une mesure
|
||||
contestable en droit demeure presque inaccessible en pratique.
|
||||
|
||||
Ce que nous cherchons à penser se tient là : dans l'écart entre
|
||||
l'existence formelle d'une scène et sa capacité réelle de reprise ;
|
||||
entre la visibilité d'une procédure et son opposabilité ; entre la
|
||||
déclaration d'un principe et la chaîne qui le transforme en effet. Une
|
||||
démocratie conserve ses rites et perd une part de sa capacité à faire
|
||||
comparaître les régulations qui la traversent. Une administration
|
||||
multiplie les procédures et réduit la possibilité de discuter ce
|
||||
qu'elles produisent. Une société produit beaucoup d'information et peu
|
||||
de prise.
|
||||
|
||||
La visibilité montre ; la comparution oblige à répondre. Un résultat
|
||||
visible se publie, se mesure, se trace, se classe. Un résultat qui
|
||||
comparaît doit répondre de ses raisons, de ses critères, de ses
|
||||
responsabilités, de ses effets. Une société publie tout, mesure tout,
|
||||
classe tout, trace tout, et laisse encore ceux qu'elle affecte devant
|
||||
des résultats qu'ils ne parviennent pas à discuter. L'opacité
|
||||
contemporaine ne tient donc pas d'abord à l'absence de données. Elle
|
||||
tient à l'impossibilité pratique de rapporter ces données à une décision
|
||||
contestable.
|
||||
|
||||
La scène occupe ici une place centrale. Elle n'est pas un décor public,
|
||||
une cérémonie, un lieu de parole ou un espace d'expression. Elle est la
|
||||
forme dans laquelle une régulation devient adressable. Une scène existe
|
||||
lorsque ce qui fonde une régulation et ce qui l'opère peuvent être
|
||||
rapportés l'un à l'autre, exposés aux objections, confrontés à leurs
|
||||
effets, révisés dans un délai praticable. Sans cela, il y a
|
||||
communication, consultation, débat médiatique, participation, même
|
||||
controverse, sans véritable prise.
|
||||
|
||||
Or cette prise se fragilise. La parole circule, mais elle ne transforme
|
||||
pas toujours. Les diagnostics abondent, mais les leviers restent
|
||||
ailleurs. Les colères s'expriment, mais elles peinent à rejoindre les
|
||||
architectures qui produisent leurs objets. Les institutions répondent,
|
||||
mais à côté du lieu où le dommage se forme. Les scènes se multiplient en
|
||||
surface, tandis que les chaînes d'effectuation se déplacent hors
|
||||
d'elles.
|
||||
|
||||
Ce déplacement n'est pas né d'un événement unique. Il s'inscrit dans une
|
||||
histoire longue : transformation de l'État, mondialisation des chaînes
|
||||
économiques, montée des normes privées, financiarisation, construction
|
||||
européenne, numérisation administrative, extension des plateformes,
|
||||
gouvernement par indicateurs. Pris isolément, aucun de ces processus ne
|
||||
suffit. Ensemble, ils modifient la manière dont un ordre devient
|
||||
opérant.
|
||||
|
||||
Le néolibéralisme n'épuise pas cette histoire, mais il en intensifie une
|
||||
ligne majeure. Il a moins supprimé la régulation qu'il ne l'a déplacée
|
||||
vers des instruments plus diffus : normes de performance, indicateurs,
|
||||
évaluations comparatives, contraintes budgétaires, pilotage par
|
||||
objectifs. Sous le nom de dérégulation, il a souvent produit une autre
|
||||
régulation, moins immédiatement politique, plus procédurale, plus
|
||||
difficile à contester dans ses fondements mêmes.
|
||||
|
||||
Ce déplacement affecte la manière dont les décisions se justifient. Le
|
||||
marché tend à se présenter comme instance de vérité. La performance tend
|
||||
à valoir preuve. L'indicateur tend à valoir jugement. Ce qui réussit
|
||||
semble se justifier par le fait même de réussir. Le calcul ne se
|
||||
contente plus d'éclairer le politique ; il prétend le dispenser de
|
||||
comparaître.
|
||||
|
||||
Dans le même temps, les opérations se multiplient. Des seuils
|
||||
budgétaires orientent des politiques locales. Des standards
|
||||
transnationaux redéfinissent des marges nationales. Des plateformes
|
||||
organisent l'accès à des droits, des services, des emplois, des publics.
|
||||
Des algorithmes filtrent des demandes. Des classements modifient les
|
||||
conduites des institutions. Le pouvoir n'a pas besoin de prendre la
|
||||
forme d'un ordre souverain. Il agit par environnement, incitation,
|
||||
format, contrainte douce ou sèche, distribution des possibles.
|
||||
|
||||
Les instruments ne sont pas en cause par nature. Toute société suppose
|
||||
des médiations. Une procédure protège, un indicateur éclaire, une
|
||||
interface simplifie, un standard rend comparable. La difficulté commence
|
||||
lorsque ces médiations se détachent des raisons qui devraient les rendre
|
||||
discutables, ou lorsque leurs effets ne reviennent plus vers des scènes
|
||||
capables de les reprendre.
|
||||
|
||||
Une procédure devient politiquement fragile lorsqu'elle ne laisse plus
|
||||
apparaître ce qu'elle décide. Un indicateur devient dangereux lorsqu'il
|
||||
remplace le jugement qu'il devait informer. Une interface devient
|
||||
violente lorsqu'elle ferme les possibilités de reformulation du cas
|
||||
qu'elle prétend traiter. Un score devient pouvoir lorsqu'il classe sans
|
||||
ouvrir la scène où sa pertinence pourrait être discutée. La technique
|
||||
n'est pas l'ennemie du politique. Elle le devient lorsque son opération
|
||||
se substitue à l'épreuve.
|
||||
|
||||
Cette violence n'est pas toujours spectaculaire. Elle est lente,
|
||||
administrative, silencieuse. Elle tient à l'impossibilité de faire
|
||||
valoir une situation autrement que dans les cases prévues. Elle tient à
|
||||
la réduction d'une trajectoire à un statut, d'une vulnérabilité à une
|
||||
donnée, d'une objection à une anomalie, d'un droit à une mise à jour. Là
|
||||
où une vie demanderait à être entendue, le dispositif demande à être
|
||||
complété. Là où un conflit devrait être instruit, il devient incident de
|
||||
traitement.
|
||||
|
||||
Dès lors, la question n'est plus la stabilité d'un système, mais la
|
||||
forme de reprise qu'il offre à ce qu'il affecte. Un monde devient
|
||||
invivable lorsque les tensions qui le traversent n'apparaissent plus que
|
||||
comme désordres, retards, résistances, coûts, défaillances individuelles
|
||||
ou problèmes de gestion. Une société continue alors de fonctionner, tout
|
||||
en devenant moins capable de recevoir ce que son fonctionnement abîme.
|
||||
|
||||
Cette exigence dépasse la stabilité des systèmes. Elle demande que les
|
||||
tensions ne soient pas absorbées par des procédures, mais rendues
|
||||
visibles, adressables et discutables, afin que leurs effets transforment
|
||||
l'ordre qui les produit. Elle ne suppose pas que tout conflit trouve une
|
||||
solution. Elle suppose que le conflit ne soit pas privé d'emblée des
|
||||
formes par lesquelles il oblige le commun à se requalifier.
|
||||
|
||||
Cette exigence engage d'abord le temps. Un délai est nécessaire pour
|
||||
comprendre, formuler, contester, répondre, réviser. Or beaucoup de
|
||||
régulations contemporaines réduisent ce délai. Les décisions sont
|
||||
automatisées, les évaluations continues, les corrections anticipées, les
|
||||
comportements préemptés. Les systèmes absorbent les écarts avant qu'ils
|
||||
ne deviennent questions. La vitesse devient une forme de fermeture. Ce
|
||||
qui va trop vite pour être adressé finit par s'imposer comme évidence.
|
||||
|
||||
Elle engage aussi des formes de traduction. Les effets d'une régulation
|
||||
ne sont jamais immédiatement lisibles. Ils doivent être rapportés à des
|
||||
expériences, des preuves, des récits, des données, des objections, des
|
||||
institutions capables de les instruire. Sans cette traduction, les
|
||||
souffrances restent privées, les inégalités restent statistiques, les
|
||||
conflits restent dispersés. Une scène ne se contente pas d'exposer. Elle
|
||||
transforme ce qui arrive en matière partageable et opposable.
|
||||
|
||||
Elle engage enfin une prise sur les échelles. Beaucoup de régulations
|
||||
contemporaines produisent leurs effets loin des lieux où elles se
|
||||
décident. Une norme financière affecte une politique locale. Une chaîne
|
||||
logistique transforme un territoire éloigné. Un standard européen ou
|
||||
international redéfinit des marges nationales. Une plateforme globale
|
||||
ajuste des conduites singulières. Les lieux d'affectation et les lieux
|
||||
d'arbitrage se dissocient. Le conflit existe, mais il peine à trouver
|
||||
l'échelle où il pourrait être travaillé.
|
||||
|
||||
Cette dissociation explique une part de l'impuissance démocratique
|
||||
contemporaine. Les citoyens votent encore, débattent, manifestent,
|
||||
saisissent des institutions, interpellent des représentants. Mais une
|
||||
part des contraintes qui structurent les choix disponibles se situe
|
||||
ailleurs : marchés, traités, agences, plateformes, standards,
|
||||
architectures techniques, indicateurs, engagements antérieurs. L'espace
|
||||
de la décision visible ne contient plus l'ensemble des conditions de la
|
||||
décision réelle.
|
||||
|
||||
Aucune nostalgie ne doit s'en déduire. Aucun âge antérieur n'a offert
|
||||
des scènes pures, transparentes, pleinement démocratiques. Les formes
|
||||
historiques du politique ont toujours été traversées d'exclusions, de
|
||||
violences, de hiérarchies, de captations. Mais elles donnaient aux
|
||||
conflits des lieux plus identifiables : adversaires, procédures,
|
||||
rituels, temporalités, figures d'adresse. Notre difficulté tient à ce
|
||||
que la conflictualité demeure, parfois s'intensifie, tandis que ses
|
||||
prises deviennent plus fragmentées, plus intermittentes, plus difficiles
|
||||
à instituer.
|
||||
|
||||
Deux réflexes se présentent alors. Le premier cherche à restaurer les
|
||||
anciennes figures de l'autorité : souveraineté, État, loi, frontière,
|
||||
verticalité, commandement. Il espère retrouver dans la forme forte du
|
||||
pouvoir le moyen de contenir la dispersion contemporaine. Le second
|
||||
abandonne la question politique à la complexité des systèmes : flux,
|
||||
réseaux, marchés, données, plateformes, calculs. Il conclut que nul
|
||||
centre ne peut plus être retrouvé, et que toute contestation se dissout
|
||||
dans l'entrelacement des opérations. Ces deux réflexes manquent le même
|
||||
point. Ni le retour pur à la verticalité, ni l'acceptation de la
|
||||
dispersion ne permettront de penser la tenue d'un monde commun.
|
||||
|
||||
La focale doit changer. Plutôt que chercher le détenteur ultime du
|
||||
pouvoir ou se résigner à l'absence de centre, il s'agit de suivre les
|
||||
régulations dans leurs articulations : ce qui les rend recevables, ce
|
||||
par quoi elles agissent, ce qui permet de les éprouver. C'est à cette
|
||||
condition que l'on comprend pourquoi certaines décisions deviennent
|
||||
politiquement habitables, tandis que d'autres fonctionnent sans jamais
|
||||
être vraiment tenues.
|
||||
|
||||
Les mots en -archie et en -cratie donnent un premier indice de ce
|
||||
déplacement. Monarchie, oligarchie, technocratie, bureaucratie,
|
||||
démocratie : ces termes continuent de nommer des régimes, des formes,
|
||||
des principes ou des modalités d'exercice. Mais ils disent encore trop
|
||||
peu la manière concrète dont une régulation articule ce qui la fonde, ce
|
||||
qui l'opère et ce qui permet de l'éprouver.
|
||||
|
||||
L'arkhè renvoie au commencement, au commandement, au principe, à ce à
|
||||
partir de quoi un ordre se donne comme recevable. Le kratos renvoie à la
|
||||
force, à la puissance agissante, à la capacité d'effectuation. Mais un
|
||||
monde politique ne tient ni par le fondement seul, ni par l'opération
|
||||
seule. Un principe sans effectuation devient incantation. Une opération
|
||||
sans fondement exposable devient puissance difficilement adressable.
|
||||
L'un comme l'autre perdent leur qualité politique lorsqu'ils échappent à
|
||||
l'épreuve.
|
||||
|
||||
Ce qui manque alors n'est pas un nouveau nom de régime. C'est une
|
||||
grammaire de l'articulation. Il s'agit de distinguer ce qui donne à une
|
||||
régulation son horizon de recevabilité, ce qui la rend opératoire, ce
|
||||
qui l'expose à ses effets et la rend reprenable. Sans cette distinction,
|
||||
des ordres très différents se confondent : une règle juste mais
|
||||
inapplicable, une procédure efficace mais sans fondement exposable, une
|
||||
consultation visible mais sans capacité de transformation, une décision
|
||||
légitime dans son principe mais mutilée dans ses chaînes d'effectuation.
|
||||
|
||||
Trois prises doivent alors être distinguées : ce qui fonde une
|
||||
régulation, ce qui l'opère, ce qui permet d'exposer leur rapport à
|
||||
l'épreuve. Nous nommerons arcalité la première, cratialité la seconde,
|
||||
archicration la forme où fondement et opération deviennent ensemble
|
||||
discutables, contestables, révisables. Ces mots ne valent pas par leur
|
||||
nouveauté. Ils valent par le discernement qu'ils rendent possible :
|
||||
distinguer ce qui, autrement, reste confondu.
|
||||
|
||||
Un malentendu doit être écarté d'emblée. L'archicratie ne désigne pas un
|
||||
régime politique supplémentaire. Elle ne nomme ni une utopie
|
||||
institutionnelle, ni une nouvelle forme idéale de gouvernement, ni une
|
||||
solution générale aux conflits contemporains. Elle désigne une condition
|
||||
de tenue : le seuil à partir duquel une régulation peut encore être
|
||||
rapportée à ce qui la fonde, suivie dans ce qu'elle opère, reprise à
|
||||
partir de ce qu'elle affecte.
|
||||
|
||||
À l'inverse, la désarchicration désignera le processus par lequel cette
|
||||
articulation se défait. Les fondements deviennent incantatoires. Les
|
||||
opérations s'autonomisent. Les scènes d'épreuve se contractent, se
|
||||
simulent ou se déplacent hors de portée. La régulation continue parfois
|
||||
avec efficacité, mais sa tenue politique se fragilise. Elle fonctionne
|
||||
encore, tout en devenant plus difficile à comprendre, à adresser, à
|
||||
contester, à transformer.
|
||||
|
||||
Cette grammaire ne prétend pas absorber tout le politique. Elle ne
|
||||
remplace ni l'analyse de l'État, ni celle du droit, ni celle des classes
|
||||
sociales, ni celle des institutions, ni celle des techniques. Elle vaut
|
||||
là où elle produit un gain de discernement. Là où les catégories
|
||||
classiques suffisent, elle doit se retirer. Là où elle ne fait que
|
||||
renommer ce qui était déjà clair, elle devient inutile. Mais là où la
|
||||
légitimité déclarée, l'opération réelle et l'épreuve disponible se
|
||||
dissocient, elle permet de poser de meilleures questions.
|
||||
|
||||
Ces questions engagent le cœur de l'enquête : qu'est-ce qui fonde ici la
|
||||
régulation ? Par quels instruments agit-elle ? Où ses effets peuvent-ils
|
||||
être exposés, contestés, repris ? La procédure ouvre-t-elle une révision
|
||||
réelle ou une réponse formelle ? La visibilité donne-t-elle prise, ou
|
||||
remplace-t-elle la comparution ?
|
||||
|
||||
Ces questions ne résolvent pas les conflits. Elles les localisent. Elles
|
||||
empêchent de confondre la déclaration d'un principe avec son
|
||||
effectuation, l'efficacité d'un dispositif avec sa légitimité,
|
||||
l'existence d'une procédure avec une scène réelle d'épreuve. Elles
|
||||
déplacent la critique depuis la dénonciation du pouvoir vers l'analyse
|
||||
des conditions dans lesquelles une régulation devient, ou non,
|
||||
reprenable par ceux qu'elle affecte.
|
||||
|
||||
L'enquête part donc d'une inquiétude, mais elle ne s'y enferme pas. Si
|
||||
les scènes se fragilisent, elles peuvent aussi être repérées, défendues,
|
||||
réinstituées. Si les opérations se soustraient à l'épreuve, elles
|
||||
peuvent être ramenées vers des formes plus exigeantes de visibilité et
|
||||
d'opposabilité. Si les mots hérités ne suffisent plus toujours, ils
|
||||
peuvent être repris, déplacés, travaillés. La pensée politique ne
|
||||
commence pas lorsque le monde devient simple. Elle commence lorsque le
|
||||
monde devient difficile à tenir.
|
||||
|
||||
Cette grammaire devra être fondée, éprouvée dans la longue durée,
|
||||
confrontée aux grandes pensées du pouvoir, puis engagée dans la lecture
|
||||
des révolutions industrielles et des tensions contemporaines. Il ne
|
||||
s'agira pas d'appliquer un schéma à des objets déjà donnés, mais de
|
||||
suivre, à chaque fois, la manière dont une société différencie ce qui
|
||||
fonde, ce qui opère et ce qui reste reprenable. L'enquête traversera
|
||||
donc des formes anciennes et modernes, des institutions visibles et des
|
||||
médiations discrètes, des scènes politiques déclarées et des chaînes
|
||||
d'effectuation moins immédiatement lisibles.
|
||||
|
||||
Quelque chose, dans nos mondes contemporains, continue d'ordonner les
|
||||
conduites, de hiérarchiser les vies, de distribuer les ressources, de
|
||||
classer les existences, de prioriser les droits, d'ajuster les
|
||||
temporalités, sans toujours apparaître dans les formes où le politique
|
||||
savait se nommer. Ce quelque chose n'est pas extérieur aux institutions.
|
||||
Il les traverse, les prolonge, les déplace, parfois les contourne. Il ne
|
||||
remplace pas le pouvoir ; il en transforme les modes d'existence.
|
||||
|
||||
Suivre les régulations autrement devient alors la tâche de l'enquête :
|
||||
dans les lois qui les proclament, mais aussi dans les instruments qui
|
||||
les appliquent ; dans les discours qui les justifient, mais aussi dans
|
||||
les formats qui les rendent effectives ; dans les institutions qui les
|
||||
encadrent, mais aussi dans les scènes qui permettent ou empêchent leur
|
||||
reprise. La question n'est pas de savoir si le pouvoir existe encore.
|
||||
Elle est de savoir sous quelles conditions ce qui règle un monde peut
|
||||
encore être rendu intelligible, adressable et révisable.
|
||||
|
||||
L'enquête commence là : au point exact où les formes du pouvoir
|
||||
demeurent visibles, mais où les prises capables de les éprouver se
|
||||
retirent.
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
title: "Démarrage — Essai-thèse"
|
||||
edition: "archicratie"
|
||||
status: "modele_sociopolitique"
|
||||
level: 1
|
||||
version: "0.0.1"
|
||||
concepts: ["archicratie"]
|
||||
links:
|
||||
- type: "definition"
|
||||
target: "/glossaire/archicratie/"
|
||||
note: "Terme canonique."
|
||||
order: 0
|
||||
summary: "Page de test (structure)."
|
||||
---
|
||||
|
||||
import Callout from "../../components/Callout.astro";
|
||||
import Term from "../../components/Term.astro";
|
||||
|
||||
Ceci est une page de test pour valider la structure de l’**Essai-thèse**.
|
||||
|
||||
<Callout kind="definition" title="Entrée minimale">
|
||||
<p>
|
||||
<Term term="Archicratie" slug="archicratie" /> : régime où l’instance régulatrice est tenue d’exposer ses prises,
|
||||
ses critères et ses scènes d’épreuve.
|
||||
</p>
|
||||
</Callout>
|
||||
|
||||
<Callout kind="these" title="Ce que l’édition web doit rendre possible">
|
||||
<p>Une lecture à plusieurs niveaux, sans confusion entre les productions, et une citabilité stable.</p>
|
||||
</Callout>
|
||||
|
||||
<div class="level-2">
|
||||
<Callout kind="objection" title="Objection (niveau 2)">
|
||||
<p>Que gagne-t-on par rapport à une simple doctrine ? Réponse : la scène, la contrainte d’exposition, la pluralisation des prises.</p>
|
||||
</Callout>
|
||||
</div>
|
||||
|
||||
<div class="level-3">
|
||||
<Callout kind="limite" title="Limite (niveau 3)">
|
||||
<p>Tout schéma d’articulation doit préciser ses non-déductions (transpositions), sinon confusion Traité ↔ Archicratie.</p>
|
||||
</Callout>
|
||||
</div>
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "Démarrage — Atlas"
|
||||
edition: "atlas"
|
||||
status: "cartographie"
|
||||
level: 1
|
||||
version: "0.0.1"
|
||||
concepts: ["archicrates"]
|
||||
links: []
|
||||
order: 0
|
||||
summary: "Page de test (structure)."
|
||||
---
|
||||
|
||||
Ceci est une page de test pour valider la structure de l’**Atlas**.
|
||||
139
src/content/cas-ia/annexe-glossaire-audit.mdx
Normal file
139
src/content/cas-ia/annexe-glossaire-audit.mdx
Normal file
@@ -0,0 +1,139 @@
|
||||
---
|
||||
title: "Annexe — Glossaire archicratique pour l’audit des systèmes d’IA"
|
||||
edition: "cas-ia"
|
||||
status: "application"
|
||||
level: 1
|
||||
version: "0.1.0"
|
||||
concepts: []
|
||||
links: []
|
||||
order: 195
|
||||
summary: ""
|
||||
source:
|
||||
kind: docx
|
||||
path: "sources/docx/cas-ia/Cas_Pratique-Archicratie_et_gouvernance_des_systemes_IA-Annexe_Glossaire_Archicratique_Cas_IA.docx"
|
||||
---
|
||||
Cette annexe propose un bref glossaire des notions archicratiques mobilisées dans l’audit de Système F. Elle n’a pas vocation à réexposer la théorie dans toute son ampleur, mais à fournir au lecteur du cas pratique quelques repères opératoires pour suivre le fil des analyses.
|
||||
|
||||
## Arcalité
|
||||
|
||||
On appelle *arcalité* la *dimension de tout ordre régulateur qui concerne ses fondements* : *ce qui l’autorise, ce qui le rend légitime, ce qui lui donne droit de décider*. L’*arcalité* est faite de *récits*, de *principes*, de *valeurs*, de *visées explicites ou implicites* : lutter contre la fraude, protéger l’État social, garantir la sécurité, promouvoir la santé, favoriser le “talent”, préserver la liberté d’expression, etc.
|
||||
|
||||
Dans Système F, on distingue :
|
||||
|
||||
- une *arcalité déclarée* : chartes d’“IA digne de confiance”, discours politiques sur la lutte contre la fraude, documents de marketing qui promettent d’“objectiver” le risque ou d’“optimiser” la sélection ;
|
||||
|
||||
- une *arcalité implicite* : choix de variables (coûts de santé comme proxy des besoins, nationalité comme indicateur de risque), arbitrages entre faux positifs et faux négatifs, composition des jeux de données, sélection de critères de “talent” ou de “dangerosité”.
|
||||
|
||||
L’enjeu archicratique n’est pas de moraliser *a posteriori* ces choix, mais de les *faire comparer sur scène* : *exposer les axiomes silencieux* (“nous considérons qu’il est plus grave de laisser passer un fraudeur que de punir un innocent”, etc.), *et permettre qu’ils soient discutés, contestés, révisés*.
|
||||
|
||||
## Cratialité
|
||||
|
||||
La *cratialité* désigne la dimension opératoire du pouvoir régulateur : la manière dont il s’applique concrètement, par quels instruments, quelles chaînes techniques, quelles procédures. C’est le “*par quoi*” et le “*comment*”.
|
||||
|
||||
Dans Système F, la *cratialité* se déploie dans :
|
||||
|
||||
- les *données mobilisées* (dossiers fiscaux, historiques de soins, casiers judiciaires, CV, contenus de plateformes) ;
|
||||
|
||||
- les *pipelines* qui transforment des situations complexes en vecteurs numériques ;
|
||||
|
||||
- les *modèles* eux-mêmes (architectures, paramètres, fonctions de coût, seuils de décision) ;
|
||||
|
||||
- les *interfaces* (tableaux de bord, scores de risque, codes couleur, messages d’alerte) ;
|
||||
|
||||
- les *procédures d’intégration* (règles internes qui imposent de suivre la recommandation ou d’en justifier tout écart).
|
||||
|
||||
*Cratialité* n’est pas synonyme de “technique” : elle inclut aussi les *règles organisationnelles*, les *consignes*, les *scripts de travail*. L’audit archicratique ne se contente pas de repérer les algorithmes ; il suit les chaînes cratiales jusqu’aux guichets, aux tribunaux, aux services hospitaliers, aux départements RH, aux plateformes.
|
||||
|
||||
## Archicration
|
||||
|
||||
L’*archicration* est le troisième terme du triptyque : elle désigne la *scène d’épreuve où arcalité et cratialité sont amenées en visibilité, confrontées, mises à l’épreuve et, si nécessaire, transformées*.
|
||||
|
||||
Sans *archicration*, les fondements restent fantômes, et le pouvoir opératoire devient autarcique.
|
||||
|
||||
Une *archicration* digne de ce nom réunit quatre conditions minimales :
|
||||
|
||||
1. *Public(s) concerné(s) effectivement représentés* (et pas seulement des experts parlant “en leur nom”) ;
|
||||
|
||||
2. *Accès aux prises* : connaissance minimale des arcalités à l’œuvre (valeurs, finalités, proxies, fonctions de coût) et des cratialités (chaînes techniques, procédures, interfaces) ;
|
||||
|
||||
3. *Capacité de contestation* : possibilité d’interpeller les choix, de demander des modifications, de suspendre un dispositif ;
|
||||
|
||||
4. *Effet réel* : ce qui se dit sur la scène peut produire des transformations sur le système, pas seulement un “avis” consultatif.
|
||||
|
||||
Dans Système F, la plupart des dispositifs existants – comités d’éthique, audits de biais, formulaires de recours – n’atteignent pas ce seuil : ce sont des *archicrations fantômes*, qui donnent l’allure de la scène sans en avoir la puissance.
|
||||
|
||||
## Hypotopie, hypertopie, atopie
|
||||
|
||||
Ces trois termes qualifient la topologie des scènes où la régulation IA apparaît (ou disparaît).
|
||||
|
||||
- Une *hypotopie* est une *scène pauvre en prises, faiblement outillée, où les gens peuvent parler mais sans pouvoir effectivement infléchir la régulation*. Par exemple : un usager qui discute avec un agent de caisse sociale d’une décision prise en réalité par Système F, sans accès ni aux paramètres ni aux logs ; un formulaire de recours tellement opaque et lourd qu’il décourage toute contestation.
|
||||
|
||||
- Une *hypertopie* est une *scène surdotée à huit-clos* : tout s’y joue – paramètres, seuils, choix de déploiement – mais entre un nombre très limité d’acteurs (directions, ingénieurs, juristes, consultants). *Les comités de pilotage où l’on décide d’intégrer ou non Système F dans la chaîne de décision, les réunions de design des modèles sont souvent des hypertopies*.
|
||||
|
||||
- Une *atopie* est une *scène fantomatique* : elle mime le dispositif d’épreuve, sans donner prise réelle. Consultations publiques en ligne sans impact, “boîtes à idées” numériques, mécanismes de *feedback* qui alimentent surtout des métriques internes (satisfaction, engagement) sans reconfigurer la régulation.
|
||||
|
||||
L’épreuve topologique, dans le cas IA, consiste à cartographier où se trouvent les *hypotopies, hypertopies, atopies*, et à inventer des scènes nouvelles qui rééquilibrent cette topologie.
|
||||
|
||||
## Autarchicratie
|
||||
|
||||
L’*autarchicratie* désigne un régime où la régulation devient son propre souverain : les dispositifs de mesure, de modélisation et de contrôle se gouvernent eux-mêmes à partir de leurs propres métriques, et ne reconnaissent plus que très marginalement des scènes externes d’épreuve.
|
||||
|
||||
Dans Système F, l’*autarchicratie* prend plusieurs formes :
|
||||
|
||||
- *modèles évalués principalement par leurs métriques internes* (perte, précision, indicateurs d’équité) ;
|
||||
|
||||
- *dispositifs d’auto-audit et de reporting automatisé* qui servent de preuve de “responsabilité” sans ouvrir réellement le système à la contestation ;
|
||||
|
||||
- *boucles de rétroaction fermées* où les décisions passées alimentent les données futures (ceux que le système considère comme “à risque” seront davantage contrôlés, et donc produiront plus de “preuves” de risque).
|
||||
|
||||
L’*autarchicratie* est l’exact négatif de l’*archicratie* : *là où l’archicratie multiplie les scènes d’épreuve, l’autarchicratie les marginalise ou les simule*.
|
||||
|
||||
## Co-viabilité
|
||||
|
||||
Par *co-viabilité*, on entend la *capacité d’un ordre régulateur à rendre simultanément vivables plusieurs dimensions de l’existence* : sociale, écologique, symbolique, parfois économique.
|
||||
|
||||
Dans le cas IA :
|
||||
|
||||
- la *co-viabilité sociale* renvoie à l’*accès aux droits*, à la *protection contre l’arbitraire*, à la *dignité des personnes* (ne pas être réduit à un profil de risque opaque, pouvoir contester une décision qui affecte des prestations, des peines, des soins, un emploi) ;
|
||||
|
||||
- la *co-viabilité écologique* concerne les *coûts matériels de l’infrastructure* (consommation énergétique, pressions sur les ressources, effets territoriaux des centres de données et des centres d’extraction) et la *possibilité de les mettre en scène* ;
|
||||
|
||||
- la *co-viabilité symbolique* touche aux *représentations de la justice, du mérite, du risque, de la vérité*, et à la manière dont Système F contribue à les figer ou à les rouvrir.
|
||||
|
||||
L’*épreuve de co-viabilité* ne se limite donc pas à mesurer des “impacts” ; elle demande : *quels types de scènes faut-il instituer pour que ces dimensions puissent être mises en balance, arbitrées et révisées ?*
|
||||
|
||||
## Politique des épreuves viables
|
||||
|
||||
La *politique des épreuves viables* est le nom donné à une *orientation normative minimale* : plutôt que de définir un modèle de justice idéal, elle consiste à organiser les épreuves auxquelles les dispositifs régulateurs doivent se soumettre pour rester archicratiques.
|
||||
|
||||
Appliquée à l’IA, elle se traduit par une *série de gestes concrets* :
|
||||
|
||||
- *droit au différé contradictoire* pour les décisions appuyées par un système ;
|
||||
|
||||
- *journaux de justification* documentant les choix de modèles, de métriques, de proxies, d’usages ;
|
||||
|
||||
- *visas d’affectation* qui autorisent ou interdisent certains usages de scores dans des décisions critiques ;
|
||||
|
||||
- *coupe-circuits citoyens* permettant de suspendre un système en cas de dégâts massifs ;
|
||||
|
||||
- *tribunaux de l’algorithme*, assemblées d’affectation, budgets scéniques pour financer le temps de la délibération et de la traduction ;
|
||||
|
||||
- *révisions archicratives périodiques* s’accompagnant d’une *cartographie des scènes manquantes*.
|
||||
|
||||
Dans le cas de Système F, ces gestes ne sont pas des ornements : ils définissent le seuil en deçà duquel il n’est plus raisonnable de parler de gouvernance archicratique de l’IA, mais d’*autarchicratie numérique*.
|
||||
|
||||
## Système F
|
||||
|
||||
Enfin, *Système F* n’est pas le nom d’un produit commercial, mais celui d’une figure composite : un modèle de fondation (LLM / modèle multimodal) accessible par API, intégré dans des flux de travail décisionnels de la protection sociale, de la santé, de la justice, des ressources humaines, des plateformes numériques.
|
||||
|
||||
Il condense des caractéristiques empiriquement attestées :
|
||||
|
||||
- *usage de systèmes de scoring* pour cibler des contrôles de fraude, évaluer des risques pénaux, gérer des programmes de soins, filtrer des candidatures, modérer des contenus ;
|
||||
|
||||
- *insertion de modules d’IA dans des logiciels métier existants* ;
|
||||
|
||||
- *dépendance à des fournisseurs privés de services cloud et de modèles* ;
|
||||
|
||||
- *adoption de chartes d’“IA responsable” et de procédures d’audit parfois plus symboliques qu’effectives*.
|
||||
|
||||
L’intérêt de Système F n’est donc pas de décrire un futur hypothétique, mais de donner un nom commun à une configuration déjà largement engagée, afin de lui appliquer, sans esquive, l’ensemble des épreuves archicratiques.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user