diff --git a/.agents/agents/lardon-architect.md b/.agents/agents/lardon-architect.md deleted file mode 100644 index 76438cd..0000000 --- a/.agents/agents/lardon-architect.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: lardon-architect -description: Décisions architecturales persistantes et difficiles de Lardon3D. -model: gemini-3.6-flash ---- -Tu interviens uniquement lorsqu'une vraie décision architecturale persistante -est ambiguë. - -Étudie: -- invariants; -- compatibilité; -- persistance/migrations; -- fingerprints; -- invalidation; -- reprise; -- coût mémoire/CPU; -- évolution future. - -Préfère la solution minimale compatible avec l'architecture existante. - -Retour: -- décision; -- alternatives rejetées; -- conséquences; -- invariants à documenter. - -Ne code pas. diff --git a/.agents/agents/lardon-build.md b/.agents/agents/lardon-build.md deleted file mode 100644 index 52abd88..0000000 --- a/.agents/agents/lardon-build.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: lardon-build -description: Implémente une tranche Lardon3D déjà cadrée. -model: gemini-3.6-flash ---- -Tu es l'agent d'implémentation Lardon3D. - -Tu reçois un contrat, une tranche ou un diagnostic déjà cadré. - -Responsabilités: -- implémentation minimale et cohérente; -- respecter les invariants existants; -- C17/C++ selon les fichiers existants; -- lisibilité cible ~100 colonnes, maximum 120; -- pas de refactor hors périmètre; -- documentation mise à jour lorsque le contrat ou comportement change; -- uniquement les tests ciblés nécessaires pendant l'implémentation. - -Tu ne fais pas la validation indépendante finale. -Tu ne commit, push ou stage jamais. diff --git a/.agents/agents/lardon-concurrency.md b/.agents/agents/lardon-concurrency.md deleted file mode 100644 index 6f0f378..0000000 --- a/.agents/agents/lardon-concurrency.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: lardon-concurrency -description: Analyse spécialisée des problèmes réels de concurrence Lardon3D. -model: gemini-3.6-flash ---- -Tu interviens uniquement lorsqu'une vraie question de concurrence existe. - -Analyse: -- ownership; -- mutex/atomics; -- transactions; -- worker races; -- publication atomique; -- pause/cancel/recovery; -- deadlocks; -- duplicate creation; -- visibility entre threads. - -Ne fais pas d'audit général. -Ne modifie rien sauf demande explicite de l'orchestrateur après diagnostic. diff --git a/.agents/agents/lardon-diagnose.md b/.agents/agents/lardon-diagnose.md deleted file mode 100644 index df0c000..0000000 --- a/.agents/agents/lardon-diagnose.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: lardon-diagnose -description: Diagnostic borné des échecs dont la cause exacte est inconnue. -model: gemini-3.6-flash ---- -Tu diagnostiques un problème précis dans Lardon3D. - -Tu ne corriges rien. - -Exigence principale: -établir une chaîne causale concrète à partir du symptôme observé. - -Évite les hypothèses générales. -Inspecte uniquement le chemin nécessaire. - -Retour obligatoire: -- symptôme confirmé; -- cause CONFIRMÉE / PROBABLE / NON ÉTABLIE; -- chaîne causale; -- fichiers et lignes concernés; -- correction minimale recommandée; -- routage suivant. - -Arrête-toi dès qu'une cause concrète suffisante est établie. diff --git a/.agents/agents/lardon-docs.md b/.agents/agents/lardon-docs.md deleted file mode 100644 index 4f9ce91..0000000 --- a/.agents/agents/lardon-docs.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: lardon-docs -description: Maintient la documentation canonique de Lardon3D. -model: gemini-3.6-flash ---- -Tu es responsable de la documentation canonique de Lardon3D. - -La documentation est une partie du produit, pas une étape cosmétique finale. - -Tu dois: -- préserver la terminologie existante; -- documenter les contrats, invariants, persistance, fingerprints, - invalidation, recovery, bornes et concurrence lorsqu'ils changent; -- supprimer les promesses devenues obsolètes; -- vérifier que la documentation décrit le comportement réellement implémenté; -- éviter toute documentation artificielle pour un microfix sans changement de comportement. - -Une décision importante ne doit pas exister uniquement dans un rapport d'agent. diff --git a/.agents/agents/lardon-orchestrator.md b/.agents/agents/lardon-orchestrator.md deleted file mode 100644 index 4bb28c1..0000000 --- a/.agents/agents/lardon-orchestrator.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: lardon-orchestrator -description: Pilote les tickets Lardon3D et délègue le travail aux spécialistes. -model: gemini-3.6-flash ---- -Tu es l'orchestrateur principal de Lardon3D. - -Ta responsabilité est de piloter, pas de tout faire toi-même. - -Règles: -- comprendre le ticket et l'état réel du dépôt avant d'agir; -- considérer la documentation canonique comme la mémoire durable du projet; -- maintenir .goose/work/current_ticket.md et handoff.md lorsqu'un run devient long; -- déléguer aux agents spécialisés quand leur rôle correspond au travail; -- ne pas refaire toi-même un travail déjà délégué; -- ne pas lancer deux agents d'écriture sur les mêmes fichiers en parallèle; -- éviter les appels d'agents redondants; -- utiliser des tests ciblés pendant le développement et une validation globale seulement à la fin; -- continuer automatiquement tant qu'une action sûre et utile existe. - -Tu peux faire directement uniquement une correction locale, mécanique, évidente et petite. -Une modification substantielle doit aller à lardon-build. - -Ordre normal: -documentation/contrat -→ implémentation -→ tests ciblés -→ documentation alignée -→ tranche suivante -→ validation finale -→ review finale. - -Ne commit, ne push et ne stage jamais. diff --git a/.agents/agents/lardon-read.md b/.agents/agents/lardon-read.md deleted file mode 100644 index 31bea48..0000000 --- a/.agents/agents/lardon-read.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: lardon-read -description: Audit ciblé en lecture seule du code et de la documentation Lardon3D. -model: gemini-3.6-flash ---- -Tu es l'agent d'audit ciblé de Lardon3D. - -LECTURE SEULE. - -Ta mission: -- lire uniquement les fichiers nécessaires à la question; -- retrouver les contrats et invariants existants; -- identifier précisément les dépendances utiles; -- signaler les divergences documentation/code; -- rendre un résumé compact avec fichiers et preuves. - -Ne modifie aucun fichier. -Ne compile pas sauf si explicitement demandé. -Ne propose pas de refactor opportuniste. -Arrête l'audit lorsque les informations nécessaires sont établies. diff --git a/.agents/agents/lardon-review.md b/.agents/agents/lardon-review.md deleted file mode 100644 index e04a62a..0000000 --- a/.agents/agents/lardon-review.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: lardon-review -description: Review finale indépendante des tickets Lardon3D. -model: gemini-3.6-flash ---- -Tu es le reviewer final indépendant. - -Ne modifie rien. - -Review prioritaire: -- correctness; -- persistance et migrations; -- recovery; -- identité/fingerprints; -- invalidation; -- bornes; -- concurrence; -- déterminisme; -- tests; -- cohérence documentation/code; -- complexité inutile. - -Classe les findings par sévérité. -Ne transforme pas la review en nouvel audit général. - -Verdict final: -APPROVED -ou -CHANGES_REQUIRED avec findings précis. diff --git a/.agents/agents/lardon-tests.md b/.agents/agents/lardon-tests.md deleted file mode 100644 index 4c2ce30..0000000 --- a/.agents/agents/lardon-tests.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: lardon-tests -description: Validation indépendante des changements Lardon3D sans correction. -model: gemini-3.6-flash ---- -Tu es le validateur indépendant de Lardon3D. - -VALIDATION UNIQUEMENT. - -Tu peux: -- compiler; -- lancer les tests demandés; -- lancer sanitizers quand demandé; -- exécuter git diff --check; -- localiser exactement une assertion, cible ou commande en échec. - -Tu ne dois PAS: -- modifier le code; -- corriger les tests; -- diagnostiquer largement; -- implémenter une solution. - -En cas d'échec, rends: -- commande; -- test/cible; -- résultat attendu; -- résultat observé; -- localisation; -- reproductibilité; -- routage recommandé. - -Verdict: PASS ou FAIL. diff --git a/.agents/plugins/lardon-safety/hooks/hooks.json b/.agents/plugins/lardon-safety/hooks/hooks.json deleted file mode 100644 index 7e7323a..0000000 --- a/.agents/plugins/lardon-safety/hooks/hooks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "hooks": { - "PreToolUse": [ - { - "matcher": "developer__shell|developer__text_editor", - "hooks": [ - { - "type": "command", - "command": "python3 ${PLUGIN_ROOT}/scripts/policy.py" - } - ] - } - ], - "BeforeReadFile": [ - { - "hooks": [ - { - "type": "command", - "command": "python3 ${PLUGIN_ROOT}/scripts/policy.py" - } - ] - } - ] - } -} diff --git a/.agents/plugins/lardon-safety/plugin.json b/.agents/plugins/lardon-safety/plugin.json deleted file mode 100644 index ca7f7e0..0000000 --- a/.agents/plugins/lardon-safety/plugin.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "lardon-safety", - "version": "1.0.0", - "description": "Hard safety policy for Goose on Lardon3D" -} diff --git a/.agents/plugins/lardon-safety/scripts/policy.py b/.agents/plugins/lardon-safety/scripts/policy.py deleted file mode 100755 index 311bae2..0000000 --- a/.agents/plugins/lardon-safety/scripts/policy.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python3 - -import json -import re -import sys - -try: - payload = json.load(sys.stdin) -except Exception: - sys.exit(0) - -event = str(payload.get("event", "")) -tool = str(payload.get("tool_name", "")) -tool_input = payload.get("tool_input") or {} - -blob = json.dumps(tool_input, ensure_ascii=False) - -def deny(reason): - print(json.dumps({ - "decision": "block", - "reason": reason, - })) - sys.exit(0) - -# Explicitly forbidden areas. -if re.search(r'(^|[/\s"\'"])scan3d(?:/|["\'\s]|$)', blob): - deny("scan3d/** is outside the Lardon3D agent scope") - -if re.search(r'(^|[/\s"\'"])\.git(?:/|["\'\s]|$)', blob): - deny(".git/** must not be accessed directly") - -if event == "PreToolUse" and "shell" in tool: - command = str(tool_input.get("command", "")) - - forbidden = [ - r'(^|[;&|]\s*|\s)sudo(\s|$)', - r'(^|[;&|]\s*|\s)doas(\s|$)', - r'(^|[;&|]\s*|\s)(pacman|yay|paru)(\s|$)', - - r'\bgit\s+add\b', - r'\bgit\s+commit\b', - r'\bgit\s+push\b', - r'\bgit\s+reset\b', - r'\bgit\s+clean\b', - r'\bgit\s+checkout\b', - r'\bgit\s+restore\b', - r'\bgit\s+rebase\b', - r'\bgit\s+merge\b', - r'\bgit\s+switch\b', - - r'(^|[;&|]\s*|\s)rm(\s|$)', - r'(^|[;&|]\s*|\s)chmod(\s|$)', - r'(^|[;&|]\s*|\s)chown(\s|$)', - r'(^|[;&|]\s*|\s)(kill|pkill|killall)(\s|$)', - r'(^|[;&|]\s*|\s)(poweroff|reboot|shutdown)(\s|$)', - ] - - for pattern in forbidden: - if re.search(pattern, command): - deny(f"blocked by Lardon3D safety policy: {command}") - -sys.exit(0) diff --git a/include/lardon3d/acquisition_campaign.h b/include/lardon3d/acquisition_campaign.h index 931420d..387bd25 100644 --- a/include/lardon3d/acquisition_campaign.h +++ b/include/lardon3d/acquisition_campaign.h @@ -108,7 +108,12 @@ Lardon3DAcquisitionCampaignResult lardon3d_acquisition_campaign_discover( const Lardon3DAcquisitionCampaignRoot *roots, size_t root_count, Lardon3DAcquisitionCampaignDiscovery *discovery); -/* Pure planning over caller-supplied normalized source/evidence records. */ +/* + * Pure planning over caller-supplied normalized source/evidence records. + * The summary counts all evaluated pair evidence. If review proposals exceed + * MAX_PROPOSALS, the plan retains their deterministic source-pair-order prefix; + * proposal capacity never changes grouping or the scientific evidence rules. + */ Lardon3DAcquisitionCampaignResult lardon3d_acquisition_campaign_plan( const Lardon3DAcquisitionCampaignSource *sources, size_t source_count, const Lardon3DAcquisitionCampaignConfirmation *confirmations, size_t confirmation_count, diff --git a/src/acquisition_campaign.cpp b/src/acquisition_campaign.cpp index d9f1c9e..88997a0 100644 --- a/src/acquisition_campaign.cpp +++ b/src/acquisition_campaign.cpp @@ -57,13 +57,15 @@ bool same_stem(const char *left, const char *right) { return left_size == right_size && std::memcmp(left_name, right_name, left_size) == 0; } -bool add_proposal(Lardon3DAcquisitionCampaignPlan &plan, size_t left, size_t right, +void add_proposal(Lardon3DAcquisitionCampaignPlan &plan, size_t left, size_t right, Lardon3DAcquisitionCampaignProposalKind kind, const Lardon3DAcquisitionPairResult &pair) { - if (plan.proposal_count >= LARDON3D_ACQUISITION_CAMPAIGN_MAX_PROPOSALS) return false; + // Proposals are a bounded review aid, not acquisition identity or a scientific + // dataset-size limit. Retain the deterministic pair-order prefix while the + // complete summary and strong-partner analysis continue across every pair. + if (plan.proposal_count >= LARDON3D_ACQUISITION_CAMPAIGN_MAX_PROPOSALS) return; auto &proposal = plan.proposals[plan.proposal_count++]; proposal = {left, right, kind, pair}; - return true; } } // namespace @@ -204,20 +206,17 @@ extern "C" Lardon3DAcquisitionCampaignResult lardon3d_acquisition_campaign_plan( } else if (pair.decision == LARDON3D_ACQUISITION_SAME_ACQUISITION_CANDIDATE) { if (stem) { ++result.summary.candidate_pair_count; - if (!add_proposal(result, i, j, - LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_CANDIDATE, pair)) - return LARDON3D_ACQUISITION_CAMPAIGN_LIMIT_EXCEEDED; + add_proposal(result, i, j, + LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_CANDIDATE, pair); } else { ++result.summary.insufficient_pair_count; } } else if (pair.decision == LARDON3D_ACQUISITION_AMBIGUOUS) { ++result.summary.ambiguous_pair_count; - if (!add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_AMBIGUOUS, pair)) - return LARDON3D_ACQUISITION_CAMPAIGN_LIMIT_EXCEEDED; + add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_AMBIGUOUS, pair); } else if (pair.decision == LARDON3D_ACQUISITION_INSUFFICIENT && stem) { ++result.summary.candidate_pair_count; - if (!add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_CANDIDATE, pair)) - return LARDON3D_ACQUISITION_CAMPAIGN_LIMIT_EXCEEDED; + add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_CANDIDATE, pair); } else if (pair.decision == LARDON3D_ACQUISITION_INSUFFICIENT) { ++result.summary.insufficient_pair_count; } @@ -233,8 +232,7 @@ extern "C" Lardon3DAcquisitionCampaignResult lardon3d_acquisition_campaign_plan( if (pair.decision != LARDON3D_ACQUISITION_SAME_ACQUISITION_STRONG) continue; if (strong_count[i] > 1u || strong_count[j] > 1u) { ++result.summary.ambiguous_pair_count; - if (!add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_AMBIGUOUS, pair)) - return LARDON3D_ACQUISITION_CAMPAIGN_LIMIT_EXCEEDED; + add_proposal(result, i, j, LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_AMBIGUOUS, pair); } if (strong_count[i] == 1u && strong_count[j] == 1u && strong_partner[i] == j && strong_partner[j] == i && (assigned[i] || assigned[j])) diff --git a/tests/test_acquisition_campaign.cpp b/tests/test_acquisition_campaign.cpp index e5e18d8..6e20fd2 100644 --- a/tests/test_acquisition_campaign.cpp +++ b/tests/test_acquisition_campaign.cpp @@ -143,6 +143,34 @@ void planner_tests() { CHECK(weak_plan->proposals[0].kind == LARDON3D_ACQUISITION_CAMPAIGN_PROPOSAL_CANDIDATE); CHECK(std::strstr(weak[weak_plan->proposals[0].left_source_index].path, "/match.") != nullptr); CHECK(std::strstr(weak[weak_plan->proposals[0].right_source_index].path, "/match.") != nullptr); + + constexpr size_t saturated_source_count = 92; + constexpr size_t saturated_pair_count = + saturated_source_count * (saturated_source_count - 1u) / 2u; + static_assert(saturated_pair_count > LARDON3D_ACQUISITION_CAMPAIGN_MAX_PROPOSALS); + auto saturated = std::make_unique( + saturated_source_count); + for (size_t i = 0; i < saturated_source_count; ++i) { + char path[64]{}; + // Equal stems create review proposals only; they must never become identity + // or prevent the scientifically unresolved sources from remaining singletons. + std::snprintf(path, sizeof(path), "/saturated/%03zu/match.jpg", i); + source(saturated[i], path, nullptr, "shared-camera"); + } + auto saturated_plan = std::make_unique(); + CHECK(lardon3d_acquisition_campaign_plan(saturated.get(), saturated_source_count, + nullptr, 0, saturated_plan.get()) == + LARDON3D_ACQUISITION_CAMPAIGN_OK); + CHECK(saturated_plan->proposal_count == LARDON3D_ACQUISITION_CAMPAIGN_MAX_PROPOSALS); + CHECK(saturated_plan->summary.candidate_pair_count == saturated_pair_count); + CHECK(saturated_plan->summary.unresolved_source_count == saturated_source_count); + CHECK(saturated_plan->group_count == saturated_source_count); + CHECK(saturated_plan->summary.strong_group_count == 0u); + for (size_t i = 0; i < saturated_plan->group_count; ++i) { + CHECK(saturated_plan->groups[i].basis == LARDON3D_ACQUISITION_GROUP_SINGLETON); + CHECK(saturated_plan->groups[i].source_count == 1u); + CHECK(saturated_plan->groups[i].source_indices[0] == i); + } } void materialization_tests() {