Compare commits
7 Commits
f9d34110e4
...
bot/propos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be26b425d8 | ||
|
|
abf88e7037 | ||
| 04fee32fdb | |||
|
|
fbddf5c3fc | ||
|
|
bad748df3a | ||
| 0066cf8601 | |||
| 5d3473d66c |
@@ -130,8 +130,6 @@ jobs:
|
||||
echo "event=$EVENT_NAME label=${LABEL_NAME:-<empty>}"
|
||||
|
||||
if [[ "$EVENT_NAME" == "issues" ]]; then
|
||||
# Gitea peut fournir un payload "issues/labeled" sans label exploitable.
|
||||
# On ne skip QUE si le label est explicitement présent ET différent de state/approved.
|
||||
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
|
||||
@@ -218,6 +216,43 @@ jobs:
|
||||
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 }}
|
||||
@@ -233,6 +268,8 @@ jobs:
|
||||
-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";
|
||||
@@ -243,15 +280,21 @@ jobs:
|
||||
.split(/\s+/)
|
||||
.filter(Boolean);
|
||||
|
||||
const batchBranch = String(process.env.BATCH_BRANCH || "");
|
||||
const batchKey = String(process.env.BATCH_KEY || "");
|
||||
|
||||
const proposerOpen = Array.isArray(pulls)
|
||||
? pulls.filter((pr) => String(pr?.head?.ref || "").startsWith("bot/proposer-"))
|
||||
: [];
|
||||
|
||||
const current = proposerOpen.find((pr) => {
|
||||
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}`) ||
|
||||
@@ -262,10 +305,11 @@ jobs:
|
||||
|
||||
const out = [];
|
||||
|
||||
if (current) {
|
||||
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(current.html_url || current.url || ""))}`);
|
||||
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");
|
||||
@@ -277,6 +321,22 @@ jobs:
|
||||
process.stdout.write(out.join("\n") + (out.length ? "\n" : ""));
|
||||
NODE
|
||||
|
||||
- name: Guard on remote batch branch before heavy work
|
||||
run: |
|
||||
set -euo pipefail
|
||||
source /tmp/proposer.env
|
||||
[[ "${SKIP:-0}" != "1" ]] || { echo "Skipped"; exit 0; }
|
||||
|
||||
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:
|
||||
@@ -306,7 +366,13 @@ jobs:
|
||||
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 ticket already has an open proposer PR: ${OPEN_PR_URL:-"(URL unavailable)"}"
|
||||
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."
|
||||
@@ -328,6 +394,7 @@ jobs:
|
||||
;;
|
||||
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 }));
|
||||
@@ -384,8 +451,7 @@ jobs:
|
||||
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-${TARGET_PRIMARY_ISSUE}-${TS}"
|
||||
BR="$BATCH_BRANCH"
|
||||
echo "BRANCH=$BR" >> /tmp/proposer.env
|
||||
git checkout -b "$BR"
|
||||
|
||||
@@ -518,6 +584,27 @@ jobs:
|
||||
--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:
|
||||
@@ -557,13 +644,39 @@ jobs:
|
||||
|
||||
test -n "${FORGE_TOKEN:-}" || { echo "Missing FORGE_TOKEN"; exit 1; }
|
||||
|
||||
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
|
||||
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";
|
||||
@@ -581,6 +694,7 @@ jobs:
|
||||
...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");
|
||||
@@ -597,97 +711,6 @@ jobs:
|
||||
);
|
||||
'
|
||||
|
||||
echo "Creating proposer PR..."
|
||||
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 @/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")"
|
||||
|
||||
test -n "$PR_URL" || {
|
||||
echo "PR URL missing. Raw: $PR_JSON"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "PR created: $PR_URL"
|
||||
|
||||
for ISSUE in $TARGET_ISSUES; do
|
||||
export ISSUE PR_URL
|
||||
|
||||
node --input-type=module -e '
|
||||
import fs from "node:fs";
|
||||
|
||||
const issue = process.env.ISSUE || "";
|
||||
const url = process.env.PR_URL || "";
|
||||
const msg =
|
||||
`PR proposer créée pour le ticket #${issue} : ${url}\n\n` +
|
||||
`Le ticket est clôturé automatiquement ; la discussion peut se poursuivre dans la PR.`;
|
||||
|
||||
fs.writeFileSync(
|
||||
"/tmp/proposer.issue.close.comment.json",
|
||||
JSON.stringify({ body: msg })
|
||||
);
|
||||
'
|
||||
|
||||
echo "Commenting issue #$ISSUE ..."
|
||||
COMMENT_HTTP="$(curl -sS -o /tmp/proposer.comment.out.json -w '%{http_code}' -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 || true)"
|
||||
echo "Issue #$ISSUE comment HTTP=$COMMENT_HTTP"
|
||||
|
||||
if [[ ! "$COMMENT_HTTP" =~ ^2 ]]; then
|
||||
echo "Failed to comment issue #$ISSUE"
|
||||
cat /tmp/proposer.comment.out.json || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Closing issue #$ISSUE ..."
|
||||
CLOSE_HTTP="$(curl -sS -o /tmp/proposer.close.out.json -w '%{http_code}' -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"}' || true)"
|
||||
echo "Issue #$ISSUE close HTTP=$CLOSE_HTTP"
|
||||
|
||||
if [[ ! "$CLOSE_HTTP" =~ ^2 ]]; then
|
||||
echo "Failed to close issue #$ISSUE"
|
||||
cat /tmp/proposer.close.out.json || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Verifying issue #$ISSUE state ..."
|
||||
VERIFY_HTTP="$(curl -sS -o /tmp/proposer.verify.out.json -w '%{http_code}' \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Accept: application/json" \
|
||||
"$API_BASE/api/v1/repos/$OWNER/$REPO/issues/$ISSUE" || true)"
|
||||
echo "Issue #$ISSUE verify HTTP=$VERIFY_HTTP"
|
||||
|
||||
if [[ ! "$VERIFY_HTTP" =~ ^2 ]]; then
|
||||
echo "Failed to re-read issue #$ISSUE after close"
|
||||
cat /tmp/proposer.verify.out.json || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ISSUE_STATE="$(node --input-type=module -e '
|
||||
import fs from "node:fs";
|
||||
const j = JSON.parse(fs.readFileSync("/tmp/proposer.verify.out.json", "utf8"));
|
||||
console.log(String(j.state || ""));
|
||||
')"
|
||||
|
||||
echo "Issue #$ISSUE state=$ISSUE_STATE"
|
||||
[[ "$ISSUE_STATE" == "closed" ]] || {
|
||||
echo "Issue #$ISSUE is not closed after PATCH"
|
||||
cat /tmp/proposer.verify.out.json || true
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
echo "PR: $PR_URL"
|
||||
|
||||
PR_JSON="$(curl -fsS -X POST \
|
||||
-H "Authorization: token $FORGE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -710,8 +733,8 @@ jobs:
|
||||
const issue = process.env.ISSUE || "";
|
||||
const url = process.env.PR_URL || "";
|
||||
const msg =
|
||||
`PR proposer créée pour le ticket #${issue} : ${url}\n\n` +
|
||||
`Le ticket est clôturé automatiquement ; la discussion peut se poursuivre dans la PR.`;
|
||||
`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",
|
||||
@@ -730,6 +753,17 @@ jobs:
|
||||
-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"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"/archicrat-ia/prologue/": {
|
||||
"p-0-d7974f88": "p-0-e729df02",
|
||||
"p-17-b8c5bf21": "p-17-3deef56b",
|
||||
"p-22-a416d473": "p-22-5bfa283b",
|
||||
"p-23-d91a7b78": "p-23-0e7b37e9",
|
||||
"p-4-8ed4f807": "p-4-90b2a1cc",
|
||||
"p-5-85126fa5": "p-5-d788c546"
|
||||
"p-5-85126fa5": "p-5-d788c546",
|
||||
"p-7-64a0ca9c": "p-7-4efdb1d4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ Ce qui revient à dire que la question politique — au sens fort — n’a peut
|
||||
|
||||
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.
|
||||
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 pour autant systématiquement 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.
|
||||
|
||||
@@ -46,7 +46,7 @@ Cela ne veut pas dire que le politique ait disparu, mais plutôt qu’il tend pe
|
||||
|
||||
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.
|
||||
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 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.
|
||||
|
||||
@@ -56,9 +56,9 @@ Cela signifie que le politique s’est décousu de ses formes historiques. Il co
|
||||
|
||||
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 disjonction entre l’expérience vécue de la contrainte et le vocabulaire disponible pour l'exprimer 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.
|
||||
Cette impuissance démocratique à nommer, situer et orienter les formes réelles de la régulation — impuissance qui tend à se généraliser — apparaît parfois au grand jour dans des situations d’apparente clarté, ce qui est peut‑être le plus troublant. En témoigne un exemple rendu brûlant par l’actualité française de 2025 : la proposition de ce qu’on a appelé la taxe Zucman. Formulée par l’économiste Gabriel Zucman, cette mesure prévoit l’instauration d’un impôt minimal annuel sur le patrimoine des ultra-riches, en France comme à l’échelle mondiale, au‑delà d’un seuil d’environ 100 millions d’euros. Le taux proposé, de l’ordre de 2% de la valeur totale du patrimoine net, qu’il soit liquide ou non (actions non cotées, participations, biens immobiliers), soulève toutefois d’importants problèmes 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user