diff --git a/.adop/adop_candidate-intake-note_ci-001.json b/.adop/adop_candidate-intake-note_ci-001.json deleted file mode 100644 index 73c74ec..0000000 --- a/.adop/adop_candidate-intake-note_ci-001.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-001", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "pre-commit-hooks", - "candidate_shape": "atomic", - "category": "hooks", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "bundle common repository hygiene checks", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "repo-hygiene-hooks", - "root_cause_hypothesis": "bundle common repository hygiene checks", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-002.json b/.adop/adop_candidate-intake-note_ci-002.json deleted file mode 100644 index 33affe0..0000000 --- a/.adop/adop_candidate-intake-note_ci-002.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-002", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "ruff", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "repo-local Python lint and format gate", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "lint-python", - "root_cause_hypothesis": "repo-local Python lint and format gate", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-003.json b/.adop/adop_candidate-intake-note_ci-003.json deleted file mode 100644 index 67e9d64..0000000 --- a/.adop/adop_candidate-intake-note_ci-003.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-003", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "pre-commit", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "centralize commit-time checks", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "commit-gates", - "root_cause_hypothesis": "centralize commit-time checks", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-004.json b/.adop/adop_candidate-intake-note_ci-004.json deleted file mode 100644 index 19cafd1..0000000 --- a/.adop/adop_candidate-intake-note_ci-004.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-004", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "check-jsonschema", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "validate workflow and automation schema usage", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "workflow-schema-check", - "root_cause_hypothesis": "validate workflow and automation schema usage", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-005.json b/.adop/adop_candidate-intake-note_ci-005.json deleted file mode 100644 index 3e43aec..0000000 --- a/.adop/adop_candidate-intake-note_ci-005.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-005", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "mypy", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a typed regression gate for runtime modules", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "type-check-python", - "root_cause_hypothesis": "add a typed regression gate for runtime modules", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-006.json b/.adop/adop_candidate-intake-note_ci-006.json deleted file mode 100644 index 6bab8e5..0000000 --- a/.adop/adop_candidate-intake-note_ci-006.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-006", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "pytest-xdist", - "candidate_shape": "atomic", - "category": "plugin", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "parallelize the test matrix workload", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "parallel-pytest", - "root_cause_hypothesis": "parallelize the test matrix workload", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-007.json b/.adop/adop_candidate-intake-note_ci-007.json deleted file mode 100644 index 59618c5..0000000 --- a/.adop/adop_candidate-intake-note_ci-007.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-007", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "eslint", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a Node-side lint gate example", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "js-lint", - "root_cause_hypothesis": "add a Node-side lint gate example", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-008.json b/.adop/adop_candidate-intake-note_ci-008.json deleted file mode 100644 index 705b2c5..0000000 --- a/.adop/adop_candidate-intake-note_ci-008.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-008", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "prettier", - "candidate_shape": "atomic", - "category": "formatter", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a cross-ecosystem formatter example", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "js-format", - "root_cause_hypothesis": "add a cross-ecosystem formatter example", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-009.json b/.adop/adop_candidate-intake-note_ci-009.json deleted file mode 100644 index 0f218ca..0000000 --- a/.adop/adop_candidate-intake-note_ci-009.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-009", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "actionlint", - "candidate_shape": "atomic", - "category": "action", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "validate GitHub Actions with a dedicated checker", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "workflow-action-check", - "root_cause_hypothesis": "validate GitHub Actions with a dedicated checker", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-010.json b/.adop/adop_candidate-intake-note_ci-010.json deleted file mode 100644 index 5bdaf94..0000000 --- a/.adop/adop_candidate-intake-note_ci-010.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-010", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "shellcheck", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "lint shell surfaces in scripts and workflows", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "shell-script-lint", - "root_cause_hypothesis": "lint shell surfaces in scripts and workflows", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-011.json b/.adop/adop_candidate-intake-note_ci-011.json deleted file mode 100644 index 7a992e6..0000000 --- a/.adop/adop_candidate-intake-note_ci-011.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-011", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "trivy", - "candidate_shape": "atomic", - "category": "scanner", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a security scan tool example", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "repo-security-scan", - "root_cause_hypothesis": "add a security scan tool example", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-012.json b/.adop/adop_candidate-intake-note_ci-012.json deleted file mode 100644 index c986a5e..0000000 --- a/.adop/adop_candidate-intake-note_ci-012.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-012", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "renovate", - "candidate_shape": "atomic", - "category": "bot", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "track a SaaS bot style tool with repo-local config", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "dependency-refresh-bot", - "root_cause_hypothesis": "track a SaaS bot style tool with repo-local config", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-013.json b/.adop/adop_candidate-intake-note_ci-013.json deleted file mode 100644 index 46fcacc..0000000 --- a/.adop/adop_candidate-intake-note_ci-013.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-013", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "hadolint", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a Dockerfile lint surface example", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "dockerfile-lint", - "root_cause_hypothesis": "add a Dockerfile lint surface example", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-014.json b/.adop/adop_candidate-intake-note_ci-014.json deleted file mode 100644 index 26a97f4..0000000 --- a/.adop/adop_candidate-intake-note_ci-014.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-014", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "markdownlint-cli2", - "candidate_shape": "atomic", - "category": "cli", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "add a docs lint tool example", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "markdown-lint", - "root_cause_hypothesis": "add a docs lint tool example", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-015.json b/.adop/adop_candidate-intake-note_ci-015.json deleted file mode 100644 index eb92395..0000000 --- a/.adop/adop_candidate-intake-note_ci-015.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-015", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "vscode-eslint", - "candidate_shape": "atomic", - "category": "extension", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "track an editor extension style tool", - "intended_lane": "assistance", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "editor-eslint-integration", - "root_cause_hypothesis": "track an editor extension style tool", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_candidate-intake-note_ci-016.json b/.adop/adop_candidate-intake-note_ci-016.json deleted file mode 100644 index 800b8a3..0000000 --- a/.adop/adop_candidate-intake-note_ci-016.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "ai_compatibility": "unknown", - "artifact_id": "ci-016", - "artifact_type": "candidate-intake-note", - "candidate_or_tool": "dependabot", - "candidate_shape": "atomic", - "category": "bot", - "cost": "unknown", - "created_at": "2026-06-18", - "current_disposition": "proposed", - "data_flow": { - "data_types": [ - "unknown" - ], - "destination": "unknown", - "opt_in": true - }, - "intake_reason": "compare another dependency bot style tool", - "intended_lane": "operations", - "license": "unknown", - "next_action": "compare candidate set", - "platform": "any", - "recording_mode": "guided", - "recording_source": "quick-intake", - "related_scene": "dependency-bot-alt", - "root_cause_hypothesis": "compare another dependency bot style tool", - "schema_version": 1, - "source": "github", - "status": "active", - "version": "unknown" -} diff --git a/.adop/adop_comparison-note_cmp-003.json b/.adop/adop_comparison-note_cmp-003.json deleted file mode 100644 index f2931a4..0000000 --- a/.adop/adop_comparison-note_cmp-003.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "pre-commit", - "artifact_id": "cmp-003", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "pre-commit", - "local-scripts" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "pre-commit", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-003" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "commit-gates", - "root_cause_hypothesis": "the use case 'commit-gates' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "pre-commit", - "selection_reason": "selected pre-commit for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-004.json b/.adop/adop_comparison-note_cmp-004.json deleted file mode 100644 index 0bcfd29..0000000 --- a/.adop/adop_comparison-note_cmp-004.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "check-jsonschema", - "artifact_id": "cmp-004", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "check-jsonschema", - "manual-schema-review" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "check-jsonschema", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-004" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "workflow-schema-check", - "root_cause_hypothesis": "the use case 'workflow-schema-check' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "check-jsonschema", - "selection_reason": "selected check-jsonschema for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-005.json b/.adop/adop_comparison-note_cmp-005.json deleted file mode 100644 index 67d6251..0000000 --- a/.adop/adop_comparison-note_cmp-005.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "mypy", - "artifact_id": "cmp-005", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "mypy", - "no-type-gate" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "mypy", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-005" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "type-check-python", - "root_cause_hypothesis": "the use case 'type-check-python' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "mypy", - "selection_reason": "selected mypy for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-006.json b/.adop/adop_comparison-note_cmp-006.json deleted file mode 100644 index 5ff6f6a..0000000 --- a/.adop/adop_comparison-note_cmp-006.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "pytest-xdist", - "artifact_id": "cmp-006", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "pytest-xdist", - "serial-pytest" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "pytest-xdist", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-006" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "parallel-pytest", - "root_cause_hypothesis": "the use case 'parallel-pytest' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "pytest-xdist", - "selection_reason": "selected pytest-xdist for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-007.json b/.adop/adop_comparison-note_cmp-007.json deleted file mode 100644 index dd9df04..0000000 --- a/.adop/adop_comparison-note_cmp-007.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "eslint", - "artifact_id": "cmp-007", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "eslint", - "no-js-lint" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "eslint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-007" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "js-lint", - "root_cause_hypothesis": "the use case 'js-lint' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "eslint", - "selection_reason": "selected eslint for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-008.json b/.adop/adop_comparison-note_cmp-008.json deleted file mode 100644 index 2c3dfc3..0000000 --- a/.adop/adop_comparison-note_cmp-008.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "prettier", - "artifact_id": "cmp-008", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "prettier", - "manual-formatting" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "prettier", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-008" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "js-format", - "root_cause_hypothesis": "the use case 'js-format' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "prettier", - "selection_reason": "selected prettier for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-009.json b/.adop/adop_comparison-note_cmp-009.json deleted file mode 100644 index 3a0c1ee..0000000 --- a/.adop/adop_comparison-note_cmp-009.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "actionlint", - "artifact_id": "cmp-009", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "actionlint", - "yaml-review-only" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "actionlint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-009" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "workflow-action-check", - "root_cause_hypothesis": "the use case 'workflow-action-check' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "actionlint", - "selection_reason": "selected actionlint for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-010.json b/.adop/adop_comparison-note_cmp-010.json deleted file mode 100644 index cb46ce3..0000000 --- a/.adop/adop_comparison-note_cmp-010.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "shellcheck", - "artifact_id": "cmp-010", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "shellcheck", - "manual-shell-review" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "shellcheck", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-010" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "shell-script-lint", - "root_cause_hypothesis": "the use case 'shell-script-lint' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "shellcheck", - "selection_reason": "selected shellcheck for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-011.json b/.adop/adop_comparison-note_cmp-011.json deleted file mode 100644 index 5e177b1..0000000 --- a/.adop/adop_comparison-note_cmp-011.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "trivy", - "artifact_id": "cmp-011", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "trivy", - "manual-security-review" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "trivy", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-011" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "repo-security-scan", - "root_cause_hypothesis": "the use case 'repo-security-scan' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "trivy", - "selection_reason": "selected trivy for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-012.json b/.adop/adop_comparison-note_cmp-012.json deleted file mode 100644 index 820a224..0000000 --- a/.adop/adop_comparison-note_cmp-012.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "renovate", - "artifact_id": "cmp-012", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "renovate", - "manual-dependency-updates" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "renovate", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-012" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "dependency-refresh-bot", - "root_cause_hypothesis": "the use case 'dependency-refresh-bot' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "renovate", - "selection_reason": "selected renovate for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-013.json b/.adop/adop_comparison-note_cmp-013.json deleted file mode 100644 index 8778613..0000000 --- a/.adop/adop_comparison-note_cmp-013.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "hadolint", - "artifact_id": "cmp-013", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "hadolint", - "manual-dockerfile-review" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "hadolint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-013" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "dockerfile-lint", - "root_cause_hypothesis": "the use case 'dockerfile-lint' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "hadolint", - "selection_reason": "selected hadolint for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-014.json b/.adop/adop_comparison-note_cmp-014.json deleted file mode 100644 index b6be43f..0000000 --- a/.adop/adop_comparison-note_cmp-014.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "markdownlint-cli2", - "artifact_id": "cmp-014", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "markdownlint-cli2", - "manual-markdown-review" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "markdownlint-cli2", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-014" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "markdown-lint", - "root_cause_hypothesis": "the use case 'markdown-lint' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "markdownlint-cli2", - "selection_reason": "selected markdownlint-cli2 for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-015.json b/.adop/adop_comparison-note_cmp-015.json deleted file mode 100644 index 98cbf92..0000000 --- a/.adop/adop_comparison-note_cmp-015.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "vscode-eslint", - "artifact_id": "cmp-015", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "vscode-eslint", - "no-editor-extension" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "vscode-eslint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-015" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "editor-eslint-integration", - "root_cause_hypothesis": "the use case 'editor-eslint-integration' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "vscode-eslint", - "selection_reason": "selected vscode-eslint for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_comparison-note_cmp-016.json b/.adop/adop_comparison-note_cmp-016.json deleted file mode 100644 index 970c9d9..0000000 --- a/.adop/adop_comparison-note_cmp-016.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "adoption_unit": "dependabot", - "artifact_id": "cmp-016", - "artifact_type": "comparison-note", - "candidate_shape": "atomic", - "compared_candidates": [ - "dependabot", - "manual-updates" - ], - "compatibility_diagnosis": [ - { - "adoption_unit": "dependabot", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": null, - "decomposition_decision": "as-is", - "derived_from": [ - "ci-016" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" - ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", - "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", - "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "dependency-bot-alt", - "root_cause_hypothesis": "the use case 'dependency-bot-alt' still depends on ad hoc operator judgment", - "schema_version": 1, - "selected_candidate": "dependabot", - "selection_reason": "selected dependabot for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - } -} diff --git a/.adop/adop_coupling-note_cp-001.json b/.adop/adop_coupling-note_cp-001.json deleted file mode 100644 index 50a00ea..0000000 --- a/.adop/adop_coupling-note_cp-001.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "artifact_id": "cp-001", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "coupling_type": "config", - "note": "dependency declaration and [tool.ruff] settings", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "invocation", - "note": "local ruff hooks", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "invocation", - "note": "pre-commit job exercises ruff hooks", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-002.json b/.adop/adop_coupling-note_cp-002.json deleted file mode 100644 index dca514a..0000000 --- a/.adop/adop_coupling-note_cp-002.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "artifact_id": "cp-002", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "coupling_type": "config", - "note": "dev dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "note": "primary hook registry", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "invocation", - "note": "pre-commit run --all-files", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-003.json b/.adop/adop_coupling-note_cp-003.json deleted file mode 100644 index 767b203..0000000 --- a/.adop/adop_coupling-note_cp-003.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "artifact_id": "cp-003", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "coupling_type": "config", - "note": "shared repository hygiene hook set", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-004.json b/.adop/adop_coupling-note_cp-004.json deleted file mode 100644 index c2fa319..0000000 --- a/.adop/adop_coupling-note_cp-004.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "artifact_id": "cp-004", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "coupling_type": "config", - "note": "dev dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "note": "workflow and renovate schema hooks", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-006.json b/.adop/adop_coupling-note_cp-006.json deleted file mode 100644 index 73a6d58..0000000 --- a/.adop/adop_coupling-note_cp-006.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "artifact_id": "cp-006", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "coupling_type": "config", - "note": "dev dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "invocation", - "note": "pytest -n auto in CI", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-007.json b/.adop/adop_coupling-note_cp-007.json deleted file mode 100644 index 3cf2314..0000000 --- a/.adop/adop_coupling-note_cp-007.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-007", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-008.json b/.adop/adop_coupling-note_cp-008.json deleted file mode 100644 index d837808..0000000 --- a/.adop/adop_coupling-note_cp-008.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-008", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-009.json b/.adop/adop_coupling-note_cp-009.json deleted file mode 100644 index 02fae68..0000000 --- a/.adop/adop_coupling-note_cp-009.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-009", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-010.json b/.adop/adop_coupling-note_cp-010.json deleted file mode 100644 index 58e62cb..0000000 --- a/.adop/adop_coupling-note_cp-010.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-010", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-011.json b/.adop/adop_coupling-note_cp-011.json deleted file mode 100644 index df660ba..0000000 --- a/.adop/adop_coupling-note_cp-011.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "artifact_id": "cp-011", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "audit.manifest.yml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-012.json b/.adop/adop_coupling-note_cp-012.json deleted file mode 100644 index b60846c..0000000 --- a/.adop/adop_coupling-note_cp-012.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-012", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "adop-overlay.md", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-013.json b/.adop/adop_coupling-note_cp-013.json deleted file mode 100644 index cc44404..0000000 --- a/.adop/adop_coupling-note_cp-013.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-013", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-014.json b/.adop/adop_coupling-note_cp-014.json deleted file mode 100644 index baa765a..0000000 --- a/.adop/adop_coupling-note_cp-014.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-014", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-015.json b/.adop/adop_coupling-note_cp-015.json deleted file mode 100644 index ec557ec..0000000 --- a/.adop/adop_coupling-note_cp-015.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-015", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-016.json b/.adop/adop_coupling-note_cp-016.json deleted file mode 100644 index 1dc63bb..0000000 --- a/.adop/adop_coupling-note_cp-016.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "artifact_id": "cp-016", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-017.json b/.adop/adop_coupling-note_cp-017.json deleted file mode 100644 index ae5bef8..0000000 --- a/.adop/adop_coupling-note_cp-017.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-017", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-018.json b/.adop/adop_coupling-note_cp-018.json deleted file mode 100644 index 5ff780f..0000000 --- a/.adop/adop_coupling-note_cp-018.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-018", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-019.json b/.adop/adop_coupling-note_cp-019.json deleted file mode 100644 index b11fe2d..0000000 --- a/.adop/adop_coupling-note_cp-019.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-019", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "coupling_type": "reference", - "path": "eslint.config.js", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "package.json", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-020.json b/.adop/adop_coupling-note_cp-020.json deleted file mode 100644 index da4de89..0000000 --- a/.adop/adop_coupling-note_cp-020.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-020", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "coupling_type": "reference", - "path": ".trivyignore", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "package.json", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-021.json b/.adop/adop_coupling-note_cp-021.json deleted file mode 100644 index 31bd87e..0000000 --- a/.adop/adop_coupling-note_cp-021.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "artifact_id": "cp-021", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-022.json b/.adop/adop_coupling-note_cp-022.json deleted file mode 100644 index c16b061..0000000 --- a/.adop/adop_coupling-note_cp-022.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-022", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "coupling_type": "config", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": "package.json", - "removal_cost": "edit" - }, - { - "coupling_type": "reference", - "path": "renovate.json", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-023.json b/.adop/adop_coupling-note_cp-023.json deleted file mode 100644 index 4dc62f0..0000000 --- a/.adop/adop_coupling-note_cp-023.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-023", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "coupling_type": "reference", - "path": ".prettierignore", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "package.json", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-024.json b/.adop/adop_coupling-note_cp-024.json deleted file mode 100644 index 1082e76..0000000 --- a/.adop/adop_coupling-note_cp-024.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-024", - "artifact_type": "coupling-note", - "candidate_or_tool": "shellcheck", - "couplings": [ - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "coupling_type": "invocation", - "path": "scripts/repo-smoke.sh", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "shell-script-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-025.json b/.adop/adop_coupling-note_cp-025.json deleted file mode 100644 index 15bc4e3..0000000 --- a/.adop/adop_coupling-note_cp-025.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-025", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "coupling_type": "reference", - "path": ".trivyignore", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-026.json b/.adop/adop_coupling-note_cp-026.json deleted file mode 100644 index 05118f0..0000000 --- a/.adop/adop_coupling-note_cp-026.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "artifact_id": "cp-026", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "coupling_type": "reference", - "path": "renovate.json", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-027.json b/.adop/adop_coupling-note_cp-027.json deleted file mode 100644 index d54ad89..0000000 --- a/.adop/adop_coupling-note_cp-027.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "artifact_id": "cp-027", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "coupling_type": "reference", - "path": "Dockerfile.tooling-example", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-028.json b/.adop/adop_coupling-note_cp-028.json deleted file mode 100644 index e79ff28..0000000 --- a/.adop/adop_coupling-note_cp-028.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-028", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "coupling_type": "reference", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "clean" - }, - { - "coupling_type": "config", - "path": "package.json", - "removal_cost": "edit" - }, - { - "coupling_type": "config", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-029.json b/.adop/adop_coupling-note_cp-029.json deleted file mode 100644 index 4761970..0000000 --- a/.adop/adop_coupling-note_cp-029.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "artifact_id": "cp-029", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "coupling_type": "reference", - "path": ".vscode/extensions.json", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-030.json b/.adop/adop_coupling-note_cp-030.json deleted file mode 100644 index abfd81e..0000000 --- a/.adop/adop_coupling-note_cp-030.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "artifact_id": "cp-030", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "coupling_type": "config", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-031.json b/.adop/adop_coupling-note_cp-031.json deleted file mode 100644 index 876e71f..0000000 --- a/.adop/adop_coupling-note_cp-031.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "artifact_id": "cp-031", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "build/pytest-run-006/test_scan_matches_pre_commit_u0/.github/workflows/ci.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "build/pytest-run-scanmeta/test_scan_matches_pre_commit_u0/.github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-032.json b/.adop/adop_coupling-note_cp-032.json deleted file mode 100644 index d71b460..0000000 --- a/.adop/adop_coupling-note_cp-032.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "artifact_id": "cp-032", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": ".vscode/extensions.json", - "removal_cost": "clean" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": ".vscode/settings.json", - "removal_cost": "clean" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": "build/pytest-run-scanmeta/test_scan_detects_vscode_eslin0/.vscode/settings.json", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-033.json b/.adop/adop_coupling-note_cp-033.json deleted file mode 100644 index ad7d5f9..0000000 --- a/.adop/adop_coupling-note_cp-033.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "artifact_id": "cp-033", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierrc.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-034.json b/.adop/adop_coupling-note_cp-034.json deleted file mode 100644 index b54cb9e..0000000 --- a/.adop/adop_coupling-note_cp-034.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "artifact_id": "cp-034", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-035.json b/.adop/adop_coupling-note_cp-035.json deleted file mode 100644 index 38939ad..0000000 --- a/.adop/adop_coupling-note_cp-035.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-035", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "scanner config surface", - "path": ".trivyignore", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-036.json b/.adop/adop_coupling-note_cp-036.json deleted file mode 100644 index df535df..0000000 --- a/.adop/adop_coupling-note_cp-036.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-036", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-037.json b/.adop/adop_coupling-note_cp-037.json deleted file mode 100644 index 044be51..0000000 --- a/.adop/adop_coupling-note_cp-037.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "artifact_id": "cp-037", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-038.json b/.adop/adop_coupling-note_cp-038.json deleted file mode 100644 index df7a599..0000000 --- a/.adop/adop_coupling-note_cp-038.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-038", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-039.json b/.adop/adop_coupling-note_cp-039.json deleted file mode 100644 index a3aa779..0000000 --- a/.adop/adop_coupling-note_cp-039.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-039", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-040.json b/.adop/adop_coupling-note_cp-040.json deleted file mode 100644 index 65d91d9..0000000 --- a/.adop/adop_coupling-note_cp-040.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "artifact_id": "cp-040", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-041.json b/.adop/adop_coupling-note_cp-041.json deleted file mode 100644 index a88bf06..0000000 --- a/.adop/adop_coupling-note_cp-041.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "artifact_id": "cp-041", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": ".vscode/extensions.json", - "removal_cost": "clean" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": ".vscode/settings.json", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-042.json b/.adop/adop_coupling-note_cp-042.json deleted file mode 100644 index c7e6d20..0000000 --- a/.adop/adop_coupling-note_cp-042.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "artifact_id": "cp-042", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierrc.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-043.json b/.adop/adop_coupling-note_cp-043.json deleted file mode 100644 index 17cdce7..0000000 --- a/.adop/adop_coupling-note_cp-043.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "artifact_id": "cp-043", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-044.json b/.adop/adop_coupling-note_cp-044.json deleted file mode 100644 index 1b58402..0000000 --- a/.adop/adop_coupling-note_cp-044.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-044", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "scanner config surface", - "path": ".trivyignore", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-045.json b/.adop/adop_coupling-note_cp-045.json deleted file mode 100644 index 08823fa..0000000 --- a/.adop/adop_coupling-note_cp-045.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-045", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-046.json b/.adop/adop_coupling-note_cp-046.json deleted file mode 100644 index dd9f1ce..0000000 --- a/.adop/adop_coupling-note_cp-046.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "artifact_id": "cp-046", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-047.json b/.adop/adop_coupling-note_cp-047.json deleted file mode 100644 index 919ece6..0000000 --- a/.adop/adop_coupling-note_cp-047.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-047", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-048.json b/.adop/adop_coupling-note_cp-048.json deleted file mode 100644 index 8a0293b..0000000 --- a/.adop/adop_coupling-note_cp-048.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-048", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-049.json b/.adop/adop_coupling-note_cp-049.json deleted file mode 100644 index de859e6..0000000 --- a/.adop/adop_coupling-note_cp-049.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "artifact_id": "cp-049", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "audit.manifest.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - }, - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": "shared/python/adop.egg-info/PKG-INFO", - "removal_cost": "clean" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-050.json b/.adop/adop_coupling-note_cp-050.json deleted file mode 100644 index 51a22f1..0000000 --- a/.adop/adop_coupling-note_cp-050.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-050", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-051.json b/.adop/adop_coupling-note_cp-051.json deleted file mode 100644 index 65b2e7f..0000000 --- a/.adop/adop_coupling-note_cp-051.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-051", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-052.json b/.adop/adop_coupling-note_cp-052.json deleted file mode 100644 index f4c6224..0000000 --- a/.adop/adop_coupling-note_cp-052.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-052", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-053.json b/.adop/adop_coupling-note_cp-053.json deleted file mode 100644 index ee72d97..0000000 --- a/.adop/adop_coupling-note_cp-053.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "artifact_id": "cp-053", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-054.json b/.adop/adop_coupling-note_cp-054.json deleted file mode 100644 index acaeb76..0000000 --- a/.adop/adop_coupling-note_cp-054.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-054", - "artifact_type": "coupling-note", - "candidate_or_tool": "shellcheck", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "invocation", - "detection_source": "config-mention", - "path": "scripts/repo-smoke.sh", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "shell-script-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-055.json b/.adop/adop_coupling-note_cp-055.json deleted file mode 100644 index 3dc89d8..0000000 --- a/.adop/adop_coupling-note_cp-055.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-055", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "confidence": "low", - "coupling_type": "reference", - "detection_source": "text-reference", - "path": "Dockerfile.tooling-example", - "removal_cost": "clean" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-056.json b/.adop/adop_coupling-note_cp-056.json deleted file mode 100644 index 7708f52..0000000 --- a/.adop/adop_coupling-note_cp-056.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "artifact_id": "cp-056", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "audit.manifest.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-057.json b/.adop/adop_coupling-note_cp-057.json deleted file mode 100644 index 32f8717..0000000 --- a/.adop/adop_coupling-note_cp-057.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-057", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-058.json b/.adop/adop_coupling-note_cp-058.json deleted file mode 100644 index f33deeb..0000000 --- a/.adop/adop_coupling-note_cp-058.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-058", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "inline hadolint directives", - "path": "Dockerfile.tooling-example", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-059.json b/.adop/adop_coupling-note_cp-059.json deleted file mode 100644 index df1bdef..0000000 --- a/.adop/adop_coupling-note_cp-059.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "artifact_id": "cp-059", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-060.json b/.adop/adop_coupling-note_cp-060.json deleted file mode 100644 index a5deb4e..0000000 --- a/.adop/adop_coupling-note_cp-060.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-060", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-061.json b/.adop/adop_coupling-note_cp-061.json deleted file mode 100644 index 08f3ba3..0000000 --- a/.adop/adop_coupling-note_cp-061.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "artifact_id": "cp-061", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "config-mention", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-062.json b/.adop/adop_coupling-note_cp-062.json deleted file mode 100644 index 733d5c7..0000000 --- a/.adop/adop_coupling-note_cp-062.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-062", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-063.json b/.adop/adop_coupling-note_cp-063.json deleted file mode 100644 index 4bd3ec8..0000000 --- a/.adop/adop_coupling-note_cp-063.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-063", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-064.json b/.adop/adop_coupling-note_cp-064.json deleted file mode 100644 index 34128bc..0000000 --- a/.adop/adop_coupling-note_cp-064.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-064", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-065.json b/.adop/adop_coupling-note_cp-065.json deleted file mode 100644 index 4b9deab..0000000 --- a/.adop/adop_coupling-note_cp-065.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-065", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-066.json b/.adop/adop_coupling-note_cp-066.json deleted file mode 100644 index d8f713b..0000000 --- a/.adop/adop_coupling-note_cp-066.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-066", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-067.json b/.adop/adop_coupling-note_cp-067.json deleted file mode 100644 index a9f0585..0000000 --- a/.adop/adop_coupling-note_cp-067.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-067", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-068.json b/.adop/adop_coupling-note_cp-068.json deleted file mode 100644 index 0ebc923..0000000 --- a/.adop/adop_coupling-note_cp-068.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "artifact_id": "cp-068", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-069.json b/.adop/adop_coupling-note_cp-069.json deleted file mode 100644 index e05202b..0000000 --- a/.adop/adop_coupling-note_cp-069.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "artifact_id": "cp-069", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierrc.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-070.json b/.adop/adop_coupling-note_cp-070.json deleted file mode 100644 index 0ed070d..0000000 --- a/.adop/adop_coupling-note_cp-070.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-070", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-071.json b/.adop/adop_coupling-note_cp-071.json deleted file mode 100644 index 0ab8a6d..0000000 --- a/.adop/adop_coupling-note_cp-071.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-071", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-072.json b/.adop/adop_coupling-note_cp-072.json deleted file mode 100644 index e1ba0e5..0000000 --- a/.adop/adop_coupling-note_cp-072.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-072", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "inline hadolint directives", - "path": "Dockerfile.tooling-example", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-073.json b/.adop/adop_coupling-note_cp-073.json deleted file mode 100644 index 5551b06..0000000 --- a/.adop/adop_coupling-note_cp-073.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-073", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "scanner config surface", - "path": ".trivyignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-074.json b/.adop/adop_coupling-note_cp-074.json deleted file mode 100644 index c4189c2..0000000 --- a/.adop/adop_coupling-note_cp-074.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "artifact_id": "cp-074", - "artifact_type": "coupling-note", - "candidate_or_tool": "shellcheck", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "medium", - "coupling_type": "invocation", - "detection_source": "config-mention", - "path": "scripts/repo-smoke.sh", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "shell-script-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-075.json b/.adop/adop_coupling-note_cp-075.json deleted file mode 100644 index 1bce056..0000000 --- a/.adop/adop_coupling-note_cp-075.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-075", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-076.json b/.adop/adop_coupling-note_cp-076.json deleted file mode 100644 index ec06183..0000000 --- a/.adop/adop_coupling-note_cp-076.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-076", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-077.json b/.adop/adop_coupling-note_cp-077.json deleted file mode 100644 index 2722ca2..0000000 --- a/.adop/adop_coupling-note_cp-077.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-077", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-078.json b/.adop/adop_coupling-note_cp-078.json deleted file mode 100644 index ba47262..0000000 --- a/.adop/adop_coupling-note_cp-078.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-078", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-079.json b/.adop/adop_coupling-note_cp-079.json deleted file mode 100644 index 4801b77..0000000 --- a/.adop/adop_coupling-note_cp-079.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-079", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-080.json b/.adop/adop_coupling-note_cp-080.json deleted file mode 100644 index 04c3e26..0000000 --- a/.adop/adop_coupling-note_cp-080.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-080", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-081.json b/.adop/adop_coupling-note_cp-081.json deleted file mode 100644 index ef33507..0000000 --- a/.adop/adop_coupling-note_cp-081.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-081", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-082.json b/.adop/adop_coupling-note_cp-082.json deleted file mode 100644 index 75e2ddb..0000000 --- a/.adop/adop_coupling-note_cp-082.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-082", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-083.json b/.adop/adop_coupling-note_cp-083.json deleted file mode 100644 index d99b081..0000000 --- a/.adop/adop_coupling-note_cp-083.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-083", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-084.json b/.adop/adop_coupling-note_cp-084.json deleted file mode 100644 index 0882401..0000000 --- a/.adop/adop_coupling-note_cp-084.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "artifact_id": "cp-084", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-085.json b/.adop/adop_coupling-note_cp-085.json deleted file mode 100644 index 75b214c..0000000 --- a/.adop/adop_coupling-note_cp-085.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "artifact_id": "cp-085", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierrc.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-086.json b/.adop/adop_coupling-note_cp-086.json deleted file mode 100644 index c6d5713..0000000 --- a/.adop/adop_coupling-note_cp-086.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-086", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-087.json b/.adop/adop_coupling-note_cp-087.json deleted file mode 100644 index 534fe34..0000000 --- a/.adop/adop_coupling-note_cp-087.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-087", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-088.json b/.adop/adop_coupling-note_cp-088.json deleted file mode 100644 index 3f8cd7f..0000000 --- a/.adop/adop_coupling-note_cp-088.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-088", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "inline hadolint directives", - "path": "Dockerfile.tooling-example", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-089.json b/.adop/adop_coupling-note_cp-089.json deleted file mode 100644 index 5f1bd07..0000000 --- a/.adop/adop_coupling-note_cp-089.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-089", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "scanner config surface", - "path": ".trivyignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-090.json b/.adop/adop_coupling-note_cp-090.json deleted file mode 100644 index 2156142..0000000 --- a/.adop/adop_coupling-note_cp-090.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-090", - "artifact_type": "coupling-note", - "candidate_or_tool": "shellcheck", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "inline shellcheck directive", - "path": "scripts/repo-smoke.sh", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "shell-script-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-091.json b/.adop/adop_coupling-note_cp-091.json deleted file mode 100644 index a4166a0..0000000 --- a/.adop/adop_coupling-note_cp-091.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-091", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-092.json b/.adop/adop_coupling-note_cp-092.json deleted file mode 100644 index f1c2b9c..0000000 --- a/.adop/adop_coupling-note_cp-092.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-092", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-093.json b/.adop/adop_coupling-note_cp-093.json deleted file mode 100644 index 6da40eb..0000000 --- a/.adop/adop_coupling-note_cp-093.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-093", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-094.json b/.adop/adop_coupling-note_cp-094.json deleted file mode 100644 index ef3f404..0000000 --- a/.adop/adop_coupling-note_cp-094.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-094", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "commit-gates", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-095.json b/.adop/adop_coupling-note_cp-095.json deleted file mode 100644 index 33fb0dc..0000000 --- a/.adop/adop_coupling-note_cp-095.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-095", - "artifact_type": "coupling-note", - "candidate_or_tool": "pre-commit-hooks", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-hygiene-hooks", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-096.json b/.adop/adop_coupling-note_cp-096.json deleted file mode 100644 index ef4876d..0000000 --- a/.adop/adop_coupling-note_cp-096.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-096", - "artifact_type": "coupling-note", - "candidate_or_tool": "check-jsonschema", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "hook repository declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-schema-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-097.json b/.adop/adop_coupling-note_cp-097.json deleted file mode 100644 index 49b0742..0000000 --- a/.adop/adop_coupling-note_cp-097.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-097", - "artifact_type": "coupling-note", - "candidate_or_tool": "ruff", - "couplings": [ - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "lint-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-098.json b/.adop/adop_coupling-note_cp-098.json deleted file mode 100644 index 30d7f75..0000000 --- a/.adop/adop_coupling-note_cp-098.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-098", - "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "config-mention", - "note": "hook entry declaration", - "path": ".pre-commit-config.yaml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "tool configuration section", - "path": "pyproject.toml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-099.json b/.adop/adop_coupling-note_cp-099.json deleted file mode 100644 index 61a7805..0000000 --- a/.adop/adop_coupling-note_cp-099.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-099", - "artifact_type": "coupling-note", - "candidate_or_tool": "pytest-xdist", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "pyproject.toml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/ci.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "parallel-pytest", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-100.json b/.adop/adop_coupling-note_cp-100.json deleted file mode 100644 index 8dc79e7..0000000 --- a/.adop/adop_coupling-note_cp-100.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "artifact_id": "cp-100", - "artifact_type": "coupling-note", - "candidate_or_tool": "eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": "eslint.config.js", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-101.json b/.adop/adop_coupling-note_cp-101.json deleted file mode 100644 index 618886c..0000000 --- a/.adop/adop_coupling-note_cp-101.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "artifact_id": "cp-101", - "artifact_type": "coupling-note", - "candidate_or_tool": "prettier", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".prettierrc.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "js-format", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-102.json b/.adop/adop_coupling-note_cp-102.json deleted file mode 100644 index e14a1ba..0000000 --- a/.adop/adop_coupling-note_cp-102.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "artifact_id": "cp-102", - "artifact_type": "coupling-note", - "candidate_or_tool": "markdownlint-cli2", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "tool-owned config surface", - "path": ".markdownlint-cli2.jsonc", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "dependency declaration", - "path": "package.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow run command", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "markdown-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-103.json b/.adop/adop_coupling-note_cp-103.json deleted file mode 100644 index c7906e5..0000000 --- a/.adop/adop_coupling-note_cp-103.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-103", - "artifact_type": "coupling-note", - "candidate_or_tool": "actionlint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "workflow-action-check", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-104.json b/.adop/adop_coupling-note_cp-104.json deleted file mode 100644 index b723031..0000000 --- a/.adop/adop_coupling-note_cp-104.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-104", - "artifact_type": "coupling-note", - "candidate_or_tool": "hadolint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "inline hadolint directives", - "path": "Dockerfile.tooling-example", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dockerfile-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-105.json b/.adop/adop_coupling-note_cp-105.json deleted file mode 100644 index 7c481fa..0000000 --- a/.adop/adop_coupling-note_cp-105.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-105", - "artifact_type": "coupling-note", - "candidate_or_tool": "trivy", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "scanner config surface", - "path": ".trivyignore", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "repo-security-scan", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-106.json b/.adop/adop_coupling-note_cp-106.json deleted file mode 100644 index b7f7f4f..0000000 --- a/.adop/adop_coupling-note_cp-106.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-106", - "artifact_type": "coupling-note", - "candidate_or_tool": "shellcheck", - "couplings": [ - { - "confidence": "high", - "coupling_type": "invocation", - "detection_source": "invocation-pattern", - "note": "workflow action usage", - "path": ".github/workflows/tool-surface-examples.yml", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "config-mention", - "note": "inline shellcheck directive", - "path": "scripts/repo-smoke.sh", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "shell-script-lint", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-107.json b/.adop/adop_coupling-note_cp-107.json deleted file mode 100644 index beaabcd..0000000 --- a/.adop/adop_coupling-note_cp-107.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-107", - "artifact_type": "coupling-note", - "candidate_or_tool": "renovate", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": "renovate.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-refresh-bot", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-108.json b/.adop/adop_coupling-note_cp-108.json deleted file mode 100644 index cfa9c7d..0000000 --- a/.adop/adop_coupling-note_cp-108.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "artifact_id": "cp-108", - "artifact_type": "coupling-note", - "candidate_or_tool": "dependabot", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "dependency bot config surface", - "path": ".github/dependabot.yml", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "dependency-bot-alt", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_coupling-note_cp-109.json b/.adop/adop_coupling-note_cp-109.json deleted file mode 100644 index ba7c088..0000000 --- a/.adop/adop_coupling-note_cp-109.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "artifact_id": "cp-109", - "artifact_type": "coupling-note", - "candidate_or_tool": "vscode-eslint", - "couplings": [ - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace extension recommendation", - "path": ".vscode/extensions.json", - "removal_cost": "edit" - }, - { - "confidence": "high", - "coupling_type": "config", - "detection_source": "surface-rule", - "note": "workspace editor settings", - "path": ".vscode/settings.json", - "removal_cost": "edit" - } - ], - "created_at": "2026-06-18", - "related_scene": "editor-eslint-integration", - "schema_version": 1, - "status": "active" -} diff --git a/.adop/adop_trial-packet_tr-002.json b/.adop/adop_trial-packet_tr-002.json deleted file mode 100644 index 85a23a9..0000000 --- a/.adop/adop_trial-packet_tr-002.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "ruff", - "artifact_id": "tr-002", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "ruff", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-002" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "pyproject.toml:[tool.ruff]", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "lint-python", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "lint-python", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-003.json b/.adop/adop_trial-packet_tr-003.json deleted file mode 100644 index 1ec3d32..0000000 --- a/.adop/adop_trial-packet_tr-003.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "pre-commit", - "artifact_id": "tr-003", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "pre-commit", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-003" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".pre-commit-config.yaml", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "commit-gates", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "commit-gates", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-004.json b/.adop/adop_trial-packet_tr-004.json deleted file mode 100644 index 9673677..0000000 --- a/.adop/adop_trial-packet_tr-004.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "check-jsonschema", - "artifact_id": "tr-004", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "check-jsonschema", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-004" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".pre-commit-config.yaml", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "workflow-schema-check", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "workflow-schema-check", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-005.json b/.adop/adop_trial-packet_tr-005.json deleted file mode 100644 index 9ac38bd..0000000 --- a/.adop/adop_trial-packet_tr-005.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "mypy", - "artifact_id": "tr-005", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "mypy", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-005" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "pyproject.toml:[tool.mypy]", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "type-check-python", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "type-check-python", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-006.json b/.adop/adop_trial-packet_tr-006.json deleted file mode 100644 index 1cd1f0b..0000000 --- a/.adop/adop_trial-packet_tr-006.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "pytest-xdist", - "artifact_id": "tr-006", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "pytest-xdist", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-006" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".github/workflows/ci.yml:test", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "parallel-pytest", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "parallel-pytest", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-007.json b/.adop/adop_trial-packet_tr-007.json deleted file mode 100644 index 84a1c19..0000000 --- a/.adop/adop_trial-packet_tr-007.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "eslint", - "artifact_id": "tr-007", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "eslint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-007" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "package.json:lint:js", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "js-lint", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "js-lint", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-008.json b/.adop/adop_trial-packet_tr-008.json deleted file mode 100644 index da30430..0000000 --- a/.adop/adop_trial-packet_tr-008.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "prettier", - "artifact_id": "tr-008", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "prettier", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-008" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".prettierrc.json", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "js-format", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "js-format", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-009.json b/.adop/adop_trial-packet_tr-009.json deleted file mode 100644 index dba9f93..0000000 --- a/.adop/adop_trial-packet_tr-009.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "actionlint", - "artifact_id": "tr-009", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "actionlint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-009" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".github/workflows/tool-surface-examples.yml", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "workflow-action-check", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "workflow-action-check", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-010.json b/.adop/adop_trial-packet_tr-010.json deleted file mode 100644 index 83dba12..0000000 --- a/.adop/adop_trial-packet_tr-010.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "shellcheck", - "artifact_id": "tr-010", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "shellcheck", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-010" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "scripts/repo-smoke.sh", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "shell-script-lint", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "shell-script-lint", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-011.json b/.adop/adop_trial-packet_tr-011.json deleted file mode 100644 index d499435..0000000 --- a/.adop/adop_trial-packet_tr-011.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "trivy", - "artifact_id": "tr-011", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "trivy", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-011" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".trivyignore", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "repo-security-scan", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "repo-security-scan", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-012.json b/.adop/adop_trial-packet_tr-012.json deleted file mode 100644 index 5900101..0000000 --- a/.adop/adop_trial-packet_tr-012.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "renovate", - "artifact_id": "tr-012", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "renovate", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-012" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "renovate.json", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "dependency-refresh-bot", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "dependency-refresh-bot", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-013.json b/.adop/adop_trial-packet_tr-013.json deleted file mode 100644 index 93978e4..0000000 --- a/.adop/adop_trial-packet_tr-013.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "hadolint", - "artifact_id": "tr-013", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "hadolint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-013" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": "Dockerfile.tooling-example", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "dockerfile-lint", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "dockerfile-lint", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-014.json b/.adop/adop_trial-packet_tr-014.json deleted file mode 100644 index fc8f0ce..0000000 --- a/.adop/adop_trial-packet_tr-014.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "markdownlint-cli2", - "artifact_id": "tr-014", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "markdownlint-cli2", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-014" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".markdownlint-cli2.jsonc", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "markdown-lint", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "markdown-lint", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-015.json b/.adop/adop_trial-packet_tr-015.json deleted file mode 100644 index 76e2074..0000000 --- a/.adop/adop_trial-packet_tr-015.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "vscode-eslint", - "artifact_id": "tr-015", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "vscode-eslint", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-015" - ], - "evaluation_gate": "at least one useful finding", - "executor": "maintainer", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded review input", - "landing_target": ".vscode/extensions.json", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "structured review notes", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "editor-eslint-integration", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "review-assist", - "trigger": "manual review request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual review request", - "reopen_condition": "", - "scene": "editor-eslint-integration", - "start_threshold": "", - "stop_condition": "at least one useful finding" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/.adop/adop_trial-packet_tr-016.json b/.adop/adop_trial-packet_tr-016.json deleted file mode 100644 index 15d59e7..0000000 --- a/.adop/adop_trial-packet_tr-016.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "adoption_unit": "dependabot", - "artifact_id": "tr-016", - "artifact_type": "trial-packet", - "candidate_shape": "atomic", - "code_level_compatibility": { - "dependency_fit": "", - "failure_mode_fit": "", - "input_surface_fit": "provided", - "mutation_boundary_fit": "provided", - "output_contract_fit": "provided", - "verification_fit": "provided", - "workflow_fit": "" - }, - "compatibility_diagnosis": [ - { - "adoption_unit": "dependabot", - "authority_safe": "safe when reviewed before writeback", - "controlability": "bounded trial is possible", - "input_fit": "simple operator input is sufficient", - "no_impact_feasibility": "artifact root can stay outside target project", - "output_fit": "result can be reviewed before writeback", - "recommended_fit_lane": "trial-ready", - "scene_fit_to_project": "fits one bounded scene lane" - } - ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", - "decomposition_decision": "as-is", - "dependency_note": "", - "derived_from": [ - "cmp-016" - ], - "evaluation_gate": "clear compare outcome", - "executor": "ci", - "failure_mode_hypothesis": [], - "fallback": "hold", - "input_surface": "bounded comparison input", - "landing_target": ".github/dependabot.yml", - "lane": "assistance", - "mutation_boundary": "no write", - "no_impact_envelope": { - "allowed": [ - "file read", - "metadata read", - "git read", - "artifact write outside target project", - "temp clone outside target project" - ], - "forbidden": [ - "dependency install inside target project", - "branch mutation inside target project", - "tracked file write inside target project", - "generated artifact write inside target project" - ] - }, - "output_contract": "comparison summary", - "recommended_fit_lane": "trial-ready", - "recording_mode": "guided", - "recording_source": "quick-trial", - "related_scene": "dependency-bot-alt", - "sandbox_type": "read-only sandbox", - "schema_version": 1, - "status": "active", - "target_project_profile": { - "allowed_input_surfaces": [ - "tool output", - "manual notes" - ], - "allowed_mutation_boundary": "no write", - "artifact_surfaces": [ - "review-note", - "trial-artifact" - ], - "authority_boundary": "human approved writeback only", - "main_language": "unknown", - "operator_phase": "evaluation", - "runtime": [ - "manual" - ], - "verification_methods": [ - "human review" - ] - }, - "trial_type": "read-only", - "trigger": "manual comparison request", - "trigger_canonical_record": { - "friction_class": "", - "observable_signal": "manual comparison request", - "reopen_condition": "", - "scene": "dependency-bot-alt", - "start_threshold": "", - "stop_condition": "clear compare outcome" - }, - "verification_method": "human review", - "writeback_target": "trial-result and judgment-report" -} diff --git a/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to .github/CODE_OF_CONDUCT.md diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index afe86be..e3ba79b 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: ADOP support guide - url: https://github.com/maruwork/adop/blob/main/SUPPORT.md + url: https://github.com/maruwork/adop/blob/main/.github/SUPPORT.md about: Use support guidance before opening a general question. diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..be776fa --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,29 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| --- | --- | +| latest | yes | + +## Trust Model + +ADOP is a local, single-operator CLI. Its inputs are treated as operator-trusted, and its boundaries are scoped accordingly: + +- **Operator-controlled inputs are trusted.** `@file` JSON arguments (e.g. `--couplings-json @path`), `render-html --output`, and `init --overlay` / `--artifact-root` read or write wherever the operator points them, by design. Do not pass untrusted/attacker-supplied values to these flags. +- **The artifact-root boundary is opt-in.** It is enforced only when `--target-project-root` is given without `--allow-project-impact`, and it protects against *writing into the target project's tree during a trial*. Read commands (`list`, `show`, `couplings`, `scan`) intentionally read whatever root they are pointed at. +- **`adop_sync` requires a trusted canonical source.** It copies the files named in that source's `adop.json`. Manifest paths are validated to be project-relative (no `..`, no absolute/drive-relative roots) so a manifest cannot direct writes outside `--target`, but you should still only sync from a canonical repo you trust. +- **`scan` is bounded.** It skips files larger than 5 MB and does not follow symlinked directories. +- **Durability is local-filesystem only.** Atomic writes rely on `os.replace` + `fsync` on the same volume. On network/SMB mounts those guarantees are weaker; keep the artifact root on a local filesystem if crash durability matters. + +Running ADOP against fully untrusted inputs (e.g. a CI job feeding attacker-controlled `@file` paths or a cloned repo whose `adop.json` you have not reviewed) is outside the supported model. + +## Reporting a Vulnerability + +Use [GitHub private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) to submit security issues. + +Private disclosure is supported from day one. + +Include the affected version, a description of the issue, and reproduction steps if available. + +Response is best effort with no fixed SLA at this stage. diff --git a/SUPPORT.md b/.github/SUPPORT.md similarity index 100% rename from SUPPORT.md rename to .github/SUPPORT.md diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 50bce35..c3288fa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,10 +7,3 @@ updates: labels: - "dependabot" - "dependencies" - - package-ecosystem: "npm" - directory: "/" - schedule: - interval: "weekly" - labels: - - "dependabot" - - "dependencies" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc7010e..83fbf6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,21 +98,21 @@ jobs: if ($overlay -notmatch "## Approved Use Scenes") { throw "overlay missing Approved Use Scenes" } if ($overlay -notmatch "## Prohibited Use Scenes") { throw "overlay missing Prohibited Use Scenes" } Set-Content -Path pyproject.toml -Value @' -[project] -name = "consumer-smoke" -version = "0.0.1" + [project] + name = "consumer-smoke" + version = "0.0.1" -[tool.ruff] -line-length = 88 -'@ + [tool.ruff] + line-length = 88 + '@ Set-Content -Path .pre-commit-config.yaml -Value @' -repos: - - repo: local - hooks: - - id: ruff-check - entry: python -m ruff check - language: system -'@ + repos: + - repo: local + hooks: + - id: ruff-check + entry: python -m ruff check + language: system + '@ Set-Content -Path data-flow.json -Value '{"destination":"local","data_types":["code"],"opt_in":true}' adop render-html --artifact-root .adop --output dashboard-empty.html $emptyHtml = Get-Content dashboard-empty.html -Raw diff --git a/.github/workflows/tool-surface-examples.yml b/.github/workflows/tool-surface-examples.yml deleted file mode 100644 index b918457..0000000 --- a/.github/workflows/tool-surface-examples.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Tool Surface Examples - -on: - workflow_dispatch: - -jobs: - mixed-tooling: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Node - uses: actions/setup-node@v4 - with: - node-version: "22" - - - name: Install JS tooling - run: npm install - - - name: Run ESLint - run: npm run lint:js - - - name: Run markdownlint-cli2 - run: npm run lint:md - - - name: Run Prettier - run: npm run format:check - - - name: Run actionlint - uses: rhysd/actionlint@v1 - - - name: Run Hadolint - uses: hadolint/hadolint-action@v3.1.0 - with: - dockerfile: Dockerfile.tooling-example - - - name: Run ShellCheck - uses: reviewdog/action-shellcheck@v1 - with: - path: scripts/repo-smoke.sh - - - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@0.31.0 - with: - scan-type: fs - scan-ref: . diff --git a/.gitignore b/.gitignore index 792eb50..4643035 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,13 @@ docs/governance/ docs/publication/ docs/AI_AGENT_RUNTIME_TOKEN_OPTIMIZATION.md workspace/ + +# coverage output +.coverage +.coverage.* +htmlcov/ +coverage.xml + +# this canonical repo does not track its own root adoption records +# (root-only: a tracked worked example under examples/**/.adop/ is allowed) +/.adop/ diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc deleted file mode 100644 index 293f2bd..0000000 --- a/.markdownlint-cli2.jsonc +++ /dev/null @@ -1,12 +0,0 @@ -{ - // markdownlint-cli2 config for ADOP repo surface examples - "config": { - "default": true, - "line-length": false - }, - "globs": [ - "README.md", - "docs/**/*.md", - "adop-overlay.md" - ] -} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf2a9b6..6174baa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,6 @@ repos: rev: 0.37.3 hooks: - id: check-github-workflows - - id: check-renovate - repo: local hooks: diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 761dc01..0000000 --- a/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -# prettier ignore for ADOP repo surface examples -build/ -dist/ -workspace/ -archive/ -common/ diff --git a/.prettierrc.json b/.prettierrc.json deleted file mode 100644 index 3f29208..0000000 --- a/.prettierrc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "printWidth": 100, - "singleQuote": false, - "trailingComma": "es5" -} diff --git a/.trivyignore b/.trivyignore deleted file mode 100644 index 92ad7d0..0000000 --- a/.trivyignore +++ /dev/null @@ -1,2 +0,0 @@ -# trivy ignore file for ADOP repo surface examples -# Add advisory IDs here only when the repo explicitly accepts the risk. diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index b308e58..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "recommendations": [ - "dbaeumer.vscode-eslint" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index af2f5f1..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "eslint.validate": [ - "javascript" - ], - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - } -} diff --git a/Dockerfile.tooling-example b/Dockerfile.tooling-example deleted file mode 100644 index 7167b10..0000000 --- a/Dockerfile.tooling-example +++ /dev/null @@ -1,7 +0,0 @@ -# hadolint global ignore=DL3008 -FROM python:3.13-slim - -WORKDIR /app -COPY pyproject.toml README.md /app/ - -CMD ["python", "--version"] diff --git a/README.md b/README.md index 62d3350..d90e963 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,13 @@ Teams that: Each evaluation is tracked as a **scene lane** rooted at `related_scene`, with the chosen tool and adoption unit carried by the artifacts inside that lane. Evaluating `ruff` as a linter is therefore a separate lane from evaluating `ruff` as a formatter. Each lane moves through up to 11 states: ``` -watch → proposed → blocked → trial-ready → in-trial - → promote / hold / reject → deprecated → migrating → archived +watch → proposed → trial-ready → in-trial → promote / hold / reject ``` +`proposed` may branch to `blocked` (and back); `reject` is reachable before a trial +(`proposed` / `blocked`) or after a `hold`. The retirement tail after `promote` is +`deprecated → migrating → archived`. + State is always derived from what is written on disk. There is no daemon, no database, no central server. ## What It Looks Like @@ -43,6 +46,8 @@ $ adop next Artifacts are plain JSON files in `.adop/`. They are append-only — nothing is deleted or overwritten. `adop lint` validates the full record. +**Correcting a mistake:** because the store is append-only, you do not edit or delete a wrong artifact — you append a newer one that supersedes it. The latest artifact of each type per scene wins (e.g. record a fresh `coupling` snapshot, or move the lane forward with the next lifecycle command). The erroneous record stays visible as history, which is the point. + ## Setup **Requires Python 3.11, 3.12, or 3.13.** @@ -178,7 +183,7 @@ When preview lanes are injected this way, the page warns that those rows are sam - `adop-governance-dashboard-template.html`: canonical HTML dashboard template - `docs/design/`: design notes and schema reference - `docs/ADOP_GENERIC_QUICKSTART.md`: fastest path to understand and verify ADOP -- `SUPPORT.md`: pre-issue checklist and support contact routes +- `.github/SUPPORT.md`: pre-issue checklist and support contact routes ## Full Reading Order @@ -191,7 +196,7 @@ When preview lanes are injected this way, the page warns that those rows are sam 7. `shared/python/adop_types.py` 8. `shared/python/adop_cli.py` 9. `docs/design/adop-lifecycle-schema-design.md` -10. `SUPPORT.md` +10. `.github/SUPPORT.md` ## Authority Boundary @@ -202,11 +207,11 @@ Each project maintains its own overlay file (scaffolded by `adop init`) that hol ## Repository Community Files - `LICENSE` -- `CONTRIBUTING.md` -- `SECURITY.md` -- `CODE_OF_CONDUCT.md` - `CHANGELOG.md` -- `SUPPORT.md` +- `.github/CONTRIBUTING.md` +- `.github/SECURITY.md` +- `.github/CODE_OF_CONDUCT.md` +- `.github/SUPPORT.md` - `.github/ISSUE_TEMPLATE/` - `.github/PULL_REQUEST_TEMPLATE.md` - `.github/CODEOWNERS` diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 0892812..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,17 +0,0 @@ -# Security Policy - -## Supported Versions - -| Version | Supported | -| --- | --- | -| latest | yes | - -## Reporting a Vulnerability - -Use [GitHub private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability) to submit security issues. - -Private disclosure is supported from day one. - -Include the affected version, a description of the issue, and reproduction steps if available. - -Response is best effort with no fixed SLA at this stage. diff --git a/adop-overlay.md b/adop-overlay.md index b985660..d04585a 100644 --- a/adop-overlay.md +++ b/adop-overlay.md @@ -4,11 +4,14 @@ **Common authority**: https://github.com/maruwork/adop **ADOP version synced**: 0.1.1 (canonical repo itself) +This repository is the canonical ADOP source. It does not track its own tool-adoption +scenes here; consumer projects create their own `.adop/` and overlay via `adop init`. + --- ## Artifact Root -Adoption artifacts for this project live at: +Adoption artifacts for a consumer project live at: ``` .adop/ @@ -26,7 +29,7 @@ ADOP runtime files (`adop_*.py`, `common.py`) are at: shared/python/ ``` -Last verified against canonical: 2026-06-18 +Last verified against canonical: 2026-06-19 Check drift: not applicable here because this repository is the canonical source --- @@ -35,22 +38,7 @@ Check drift: not applicable here because this repository is the canonical source | Scene Lane | Tool | Current State | Last Activity | |---|---|---|---| -| `lint-python` | `ruff` | `in-trial` | 2026-06-18 | -| `commit-gates` | `pre-commit` | `in-trial` | 2026-06-18 | -| `repo-hygiene-hooks` | `pre-commit-hooks` | `in-trial` | 2026-06-18 | -| `workflow-schema-check` | `check-jsonschema` | `in-trial` | 2026-06-18 | -| `type-check-python` | `mypy` | `in-trial` | 2026-06-18 | -| `parallel-pytest` | `pytest-xdist` | `in-trial` | 2026-06-18 | -| `js-lint` | `eslint` | `in-trial` | 2026-06-18 | -| `js-format` | `prettier` | `in-trial` | 2026-06-18 | -| `workflow-action-check` | `actionlint` | `in-trial` | 2026-06-18 | -| `shell-script-lint` | `shellcheck` | `in-trial` | 2026-06-18 | -| `repo-security-scan` | `trivy` | `in-trial` | 2026-06-18 | -| `dependency-refresh-bot` | `renovate` | `in-trial` | 2026-06-18 | -| `dockerfile-lint` | `hadolint` | `in-trial` | 2026-06-18 | -| `markdown-lint` | `markdownlint-cli2` | `in-trial` | 2026-06-18 | -| `editor-eslint-integration` | `vscode-eslint` | `in-trial` | 2026-06-18 | -| `dependency-bot-alt` | `dependabot` | `in-trial` | 2026-06-18 | +| none | none | this canonical repo records no project-local adoption scenes | n/a | Live view: `python shared/python/adop_cli.py summary --artifact-root .adop` @@ -60,11 +48,11 @@ Live view: `python shared/python/adop_cli.py summary --artifact-root .adop` | Stage | Who | How | |---|---|---| -| Raise a candidate | repo maintainer | edit repo config, note intended tool, then create ADOP intake | -| Run comparison | repo maintainer | compare against status quo or lighter alternative in the matching scene lane | -| Open trial | repo maintainer + CI | wire config into `pyproject.toml`, `.pre-commit-config.yaml`, or `.github/workflows/ci.yml` | -| Close trial | repo maintainer | use CI evidence plus coupling review before `promote` / `hold` / `reject` | -| Define landing target | repo maintainer | fix the exact owning file or workflow location before promotion | +| Raise a candidate | repo maintainer | open an issue / note the intended tool, then `adop quick-intake` | +| Run comparison | repo maintainer | `adop quick-compare` against the status quo or a lighter alternative | +| Open trial | repo maintainer | `adop quick-trial` with a bounded, no-write mode | +| Close trial | repo maintainer | `adop quick-close-trial` with observed evidence | +| Define landing target | repo maintainer | fix the exact owning file/workflow before promotion | --- @@ -72,12 +60,12 @@ Live view: `python shared/python/adop_cli.py summary --artifact-root .adop` | Item | Current project answer | |---|---| -| Adoption class | mixed: this repo records both recurring operations integration examples and creation-assistance governance behavior | -| Layer to strengthen | mechanism | -| Phase | self-improvement loop | -| What serves as authority | `.adop/` artifacts, repo-tracked config/workflow files, and human-reviewed promotion decisions | -| What does not serve as authority | preview sample rows, ad-hoc tool output, unrecorded scan output, or undocumented convenience use | -| Fail-close / escalation / verification | unresolved or unknown cases stay `hold` / `reject`; repo maintainer decides status changes; verify with `adop lint`, coupling review, and CI evidence before promote | +| Adoption class | n/a — canonical tool repository, not a consumer project | +| Layer to strengthen | mechanism (the ADOP CLI itself) | +| Phase | core build / self-improvement loop | +| What serves as authority | `shared/python/` runtime, repo-tracked config/workflow files, human-reviewed decisions | +| What does not serve as authority | ad-hoc tool output, unrecorded scan output, undocumented convenience use | +| Fail-close / escalation / verification | unresolved cases stay unadopted; maintainer decides; verify with `pytest`, `adop lint`, and CI | --- @@ -85,7 +73,7 @@ Live view: `python shared/python/adop_cli.py summary --artifact-root .adop` | Scene | Tool | Allowed use | Landing target | Notes | |---|---|---|---|---| -| none yet | none | no recurring tool use is promoted yet in this repository | n/a | every recorded scene is still `in-trial` as of 2026-06-18 | +| none yet | none | no recurring external-tool use is promoted in this repository | n/a | dev toolchain (ruff/mypy/pytest/pre-commit) is project tooling, not an ADOP-tracked scene | --- @@ -93,41 +81,17 @@ Live view: `python shared/python/adop_cli.py summary --artifact-root .adop` | Scene | Tool | Prohibited or blocked use | Reason | Reopen condition | |---|---|---|---|---| -| all scenes until promoted | all recorded tools | treat any in-trial tool as approved recurring authority | no scene has been promoted yet | a recorded `promotion-note` plus updated landing target authority | -| preview sample rows | sample-only lanes | use layout-check sample rows as if they were project decisions | samples are not canonical records | render HTML without sample rows, or rely only on recorded lanes | -| unrecorded convenience use | any external tool | keep using a tool repeatedly without leaving ADOP artifacts | breaks the explicit adoption record and authority boundary | create the missing intake / compare / trial / coupling artifacts | +| unrecorded convenience use | any external tool | repeated use without leaving ADOP artifacts | breaks the explicit adoption record | create the missing intake / compare / trial artifacts | --- ## Landing Target Authority -Promoted tools land at: - | Target | Owner | Notes | |---|---|---| -| `pyproject.toml` | repo maintainer | dependency declarations plus `ruff` / `mypy` settings | -| `.pre-commit-config.yaml` | repo maintainer | `pre-commit`, `pre-commit-hooks`, `check-jsonschema`, local `ruff`, local `mypy` hooks | -| `.github/workflows/ci.yml` | repo maintainer | CI surface for `pre-commit` and `pytest -n auto` | -| `package.json` / `eslint.config.js` / `.prettierignore` | repo maintainer | Node-side examples for `eslint` and `prettier` | -| `.github/workflows/tool-surface-examples.yml` | repo maintainer | example workflow for `actionlint`, `shellcheck`, `trivy`, `eslint`, and `prettier` | -| `scripts/repo-smoke.sh` | repo maintainer | shell script surface used by `shellcheck` | -| `.trivyignore` | repo maintainer | repo-local security scan exception surface for `trivy` | -| `renovate.json` | repo maintainer | bot-style config surface for `renovate` | -| `Dockerfile.tooling-example` | repo maintainer | Dockerfile surface for `hadolint` | -| `.markdownlint-cli2.jsonc` | repo maintainer | Markdown lint config surface for `markdownlint-cli2` | -| `.vscode/extensions.json` / `.vscode/settings.json` | repo maintainer | editor integration surface for `vscode-eslint` | -| `.github/dependabot.yml` | repo maintainer | dependency bot config surface for `dependabot` | - ---- - -## Pending Project Decisions - -| Item | Status | -|---|---| -| Verify the new dev-tool surface on a networked GitHub runner | pending | -| Decide whether Node / bot / security scenes should be promoted or kept as audit-only examples | pending | -| Verify whether low-confidence `text-reference` hits are strict enough to keep for this repo, or should be filtered further | pending | -| Treat editor-local settings such as `.vscode/settings.json` as first-class coupling surfaces even when the tool id is implicit | fixed in generic scan on 2026-06-18 | +| `pyproject.toml` | repo maintainer | build metadata, dependencies, ruff / mypy / pytest / coverage config | +| `.pre-commit-config.yaml` | repo maintainer | ruff, ruff-format, mypy, and workflow-schema hooks | +| `.github/workflows/ci.yml` | repo maintainer | black-box install + source-tree test matrix | --- diff --git a/adop.json b/adop.json index 2aef184..bbc8fc1 100644 --- a/adop.json +++ b/adop.json @@ -5,6 +5,7 @@ "runtime_files": [ "shared/python/adop_artifacts.py", "shared/python/adop_cli.py", + "shared/python/adop_html.py", "shared/python/adop_ids.py", "shared/python/adop_state_machine.py", "shared/python/adop_summary.py", @@ -12,5 +13,8 @@ "shared/python/adop_types.py", "shared/python/adop_validation.py", "shared/python/common.py" + ], + "template_files": [ + "shared/templates/adop-governance-dashboard-template.html" ] } diff --git a/docs/ADOP_GENERIC_QUICKSTART.md b/docs/ADOP_GENERIC_QUICKSTART.md index fbaed30..d53d72f 100644 --- a/docs/ADOP_GENERIC_QUICKSTART.md +++ b/docs/ADOP_GENERIC_QUICKSTART.md @@ -103,6 +103,9 @@ must track drift against the canonical. ADOP provides `shared/python/adop_sync.p # check drift in a project's copy python shared/python/adop_sync.py check --target path/to/project/ +# seed or update a single project's copy in one shot +python shared/python/adop_sync.py apply --target path/to/project/ + # register a project (stored in sync-registry.json, gitignored) python shared/python/adop_sync.py register --target path/to/project/ diff --git a/docs/design/adop-design-notes.md b/docs/design/adop-design-notes.md index 0e1be61..329122d 100644 --- a/docs/design/adop-design-notes.md +++ b/docs/design/adop-design-notes.md @@ -37,13 +37,18 @@ Because artifacts are JSON, multiple artifact roots can be aggregated across pro - Functions as a personal SSOT for tool adoption history - "What is being used in which project right now" is visible in one place +This is implemented by the read-only `aggregate` command: + +``` +adop aggregate --root path/to/proj-a/.adop --root path/to/proj-b/.adop +``` + +It prints each root's scene lanes, tool, and current lifecycle state (or `--json`). + --- ## Confirmed Design Decisions 1. **`watch` lives inside ADOP** — Interest records belong in the SSOT too. `watch` is the entry point; transition to `proposed` starts formal evaluation. 2. **`migrating` is an independent state** — Distinct from `deprecated` (decision made, work not yet started). It is a standalone state representing active migration work in progress. - -## Open Design Questions - -1. **Re-evaluation route for `reject`** — Keep it as a terminal state, or allow conditional transitions back to `watch` or `proposed`? +3. **`reject` is terminal for a scene lane** — Re-evaluation does not reopen a rejected lane; it must use a new `related_scene`. A candidate can be rejected before any trial (from `proposed` / `blocked`) or after a pause (from `hold`) via `adop reject`, in addition to a `reject` verdict at trial close. diff --git a/docs/design/adop-lifecycle-schema-design.md b/docs/design/adop-lifecycle-schema-design.md index c18f9b2..9406b4e 100644 --- a/docs/design/adop-lifecycle-schema-design.md +++ b/docs/design/adop-lifecycle-schema-design.md @@ -246,6 +246,28 @@ Prefixes for new artifact types to be added to `ARTIFACT_ID_PREFIX` in `adop_typ --- +## 5b. Schema Versioning + +Artifacts carry `schema_version`. Two constants govern durability across adop +upgrades (`adop_types.py`): + +- `SCHEMA_VERSION` — the version new artifacts are **written** at. +- `MIN_READABLE_SCHEMA_VERSION` — the oldest version still **readable**. + +Rules enforced in `validate_artifact_schema`: + +- `MIN_READABLE_SCHEMA_VERSION <= version <= SCHEMA_VERSION` → valid. +- `version > SCHEMA_VERSION` → rejected with an explicit "written by a newer adop; + upgrade adop to read it" message (not a generic invalid), so a still-good record + is not mistaken for corruption. +- anything else (non-int, `< MIN_READABLE_SCHEMA_VERSION`) → invalid. + +**Compatibility contract:** schema changes are **additive-only** — a new version +may add optional fields but must not remove or repurpose existing ones. This keeps +every already-written, append-only artifact valid after an adop upgrade; no +in-place migration (which would violate append-only) is required. Bumping +`SCHEMA_VERSION` without raising `MIN_READABLE_SCHEMA_VERSION` preserves old records. + ## 6. Fixed Decisions | Topic | Decision | diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 6007606..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,9 +0,0 @@ -// eslint config for the ADOP repo surface example -export default [ - { - files: ["scripts/**/*.js"], - rules: { - "no-unused-vars": "warn" - } - } -]; diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..319bee8 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,36 @@ +# ADOP Examples + +A small, self-contained worked example so you can see what ADOP records look like +without scaffolding your own. It is **read-only sample data** — not this +repository's own adoption decisions. + +## `walkthrough/` + +One scene lane (`lint-ci`) evaluating `ruff`, driven through a full bounded cycle +and left on `hold`, plus one coupling snapshot. Seven artifacts under +`walkthrough/.adop/`: + +| Artifact | Meaning | +|---|---| +| `candidate-intake-note` (ci-001) | the tool was proposed for one use case | +| `comparison-note` (cmp-001) | `ruff` selected over `pylint` for a bounded trial | +| `trial-packet` (tr-001) | the trial's scope, executor, and no-write boundary | +| `trial-result` + `judgment-report` (tr-001) | observed effect and the close decision | +| `hold-note` (hl-001) | trial paused (verdict: hold) with a reopen condition | +| `coupling-note` (cp-001) | where `ruff` is entangled in project files | + +### View it + +```bash +adop summary --artifact-root examples/walkthrough/.adop +adop status --artifact-root examples/walkthrough/.adop +adop couplings --artifact-root examples/walkthrough/.adop +adop lint --artifact-root examples/walkthrough/.adop +adop render-html --artifact-root examples/walkthrough/.adop --output workspace/html-preview/example.html +``` + +(Run from the repo root, or use `python shared/python/adop_cli.py …` if `adop` +is not installed as a command.) + +To explore your own project instead, run `adop init` there — it creates a fresh +`.adop/` and overlay. diff --git a/examples/walkthrough/.adop/adop_candidate-intake-note_ci-001.json b/examples/walkthrough/.adop/adop_candidate-intake-note_ci-001.json new file mode 100644 index 0000000..17752a1 --- /dev/null +++ b/examples/walkthrough/.adop/adop_candidate-intake-note_ci-001.json @@ -0,0 +1,31 @@ +{ + "ai_compatibility": "any", + "artifact_id": "ci-001", + "artifact_type": "candidate-intake-note", + "candidate_or_tool": "ruff", + "candidate_shape": "atomic", + "category": "cli", + "cost": "free", + "created_at": "2026-06-20", + "current_disposition": "proposed", + "data_flow": { + "data_types": [ + "code" + ], + "destination": "local", + "opt_in": true + }, + "intake_reason": "evaluate a faster linter than the status quo", + "intended_lane": "assistance", + "license": "MIT", + "next_action": "compare candidate set", + "platform": "any", + "recording_mode": "guided", + "recording_source": "quick-intake", + "related_scene": "lint-ci", + "root_cause_hypothesis": "evaluate a faster linter than the status quo", + "schema_version": 1, + "source": "doc", + "status": "active", + "version": "0.5.0" +} diff --git a/.adop/adop_comparison-note_cmp-001.json b/examples/walkthrough/.adop/adop_comparison-note_cmp-001.json similarity index 85% rename from .adop/adop_comparison-note_cmp-001.json rename to examples/walkthrough/.adop/adop_comparison-note_cmp-001.json index 6550f6f..c5c30d1 100644 --- a/.adop/adop_comparison-note_cmp-001.json +++ b/examples/walkthrough/.adop/adop_comparison-note_cmp-001.json @@ -1,15 +1,15 @@ { - "adoption_unit": "pre-commit-hooks", + "adoption_unit": "ruff", "artifact_id": "cmp-001", "artifact_type": "comparison-note", "candidate_shape": "atomic", "compared_candidates": [ - "pre-commit-hooks", - "ad-hoc-shell-checks" + "ruff", + "pylint" ], "compatibility_diagnosis": [ { - "adoption_unit": "pre-commit-hooks", + "adoption_unit": "ruff", "authority_safe": "safe when reviewed before writeback", "controlability": "bounded trial is possible", "input_fit": "simple operator input is sufficient", @@ -19,7 +19,7 @@ "scene_fit_to_project": "fits one bounded scene lane" } ], - "created_at": "2026-06-18", + "created_at": "2026-06-20", "decision_owner": null, "decomposition_decision": "as-is", "derived_from": [ @@ -70,11 +70,11 @@ "recording_mode": "guided", "recording_source": "quick-compare", "rejected_reasons": [], - "related_scene": "repo-hygiene-hooks", - "root_cause_hypothesis": "the use case 'repo-hygiene-hooks' still depends on ad hoc operator judgment", + "related_scene": "lint-ci", + "root_cause_hypothesis": "the use case 'lint-ci' still depends on ad hoc operator judgment", "schema_version": 1, - "selected_candidate": "pre-commit-hooks", - "selection_reason": "selected pre-commit-hooks for bounded trial", + "selected_candidate": "ruff", + "selection_reason": "selected ruff for bounded trial", "status": "active", "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", "target_project_profile": { diff --git a/.adop/adop_coupling-note_cp-005.json b/examples/walkthrough/.adop/adop_coupling-note_cp-001.json similarity index 59% rename from .adop/adop_coupling-note_cp-005.json rename to examples/walkthrough/.adop/adop_coupling-note_cp-001.json index d30016a..e45e3a9 100644 --- a/.adop/adop_coupling-note_cp-005.json +++ b/examples/walkthrough/.adop/adop_coupling-note_cp-001.json @@ -1,23 +1,23 @@ { - "artifact_id": "cp-005", + "artifact_id": "cp-001", "artifact_type": "coupling-note", - "candidate_or_tool": "mypy", + "candidate_or_tool": "ruff", "couplings": [ { "coupling_type": "config", - "note": "dependency declaration and [tool.mypy] settings", + "note": "ruff settings live here", "path": "pyproject.toml", "removal_cost": "edit" }, { "coupling_type": "invocation", - "note": "local mypy hook", + "note": "ruff runs as a hook", "path": ".pre-commit-config.yaml", "removal_cost": "edit" } ], - "created_at": "2026-06-18", - "related_scene": "type-check-python", + "created_at": "2026-06-20", + "related_scene": "lint-ci", "schema_version": 1, "status": "active" } diff --git a/examples/walkthrough/.adop/adop_hold-note_hl-001.json b/examples/walkthrough/.adop/adop_hold-note_hl-001.json new file mode 100644 index 0000000..67dc7a4 --- /dev/null +++ b/examples/walkthrough/.adop/adop_hold-note_hl-001.json @@ -0,0 +1,17 @@ +{ + "artifact_id": "hl-001", + "artifact_type": "hold-note", + "created_at": "2026-06-20", + "decision_owner": "lead", + "derived_from": [ + "tr-001" + ], + "evidence_refs": [], + "hold_reason": "bounded trial was useful but still needs narrowing", + "recording_mode": "guided", + "recording_source": "quick-close-trial", + "related_scene": "lint-ci", + "reopen_condition": "retry after narrowing the use case or threshold", + "schema_version": 1, + "status": "closed" +} diff --git a/.adop/adop_comparison-note_cmp-002.json b/examples/walkthrough/.adop/adop_judgment-report_tr-001.json similarity index 56% rename from .adop/adop_comparison-note_cmp-002.json rename to examples/walkthrough/.adop/adop_judgment-report_tr-001.json index e95e7c1..f788700 100644 --- a/.adop/adop_comparison-note_cmp-002.json +++ b/examples/walkthrough/.adop/adop_judgment-report_tr-001.json @@ -1,12 +1,11 @@ { "adoption_unit": "ruff", - "artifact_id": "cmp-002", - "artifact_type": "comparison-note", + "artifact_id": "tr-001", + "artifact_type": "judgment-report", + "candidate_or_tool": "ruff", "candidate_shape": "atomic", - "compared_candidates": [ - "ruff", - "flake8" - ], + "closed_at": "2026-06-20", + "code_level_compatibility_summary": "input=bounded comparison input; output=comparison summary; mutation=no write; verification=human review", "compatibility_diagnosis": [ { "adoption_unit": "ruff", @@ -19,36 +18,15 @@ "scene_fit_to_project": "fits one bounded scene lane" } ], - "created_at": "2026-06-18", - "decision_owner": null, + "created_at": "2026-06-20", + "decision_owner": "lead", "decomposition_decision": "as-is", "derived_from": [ - "ci-002" - ], - "discovered_subtargets": [], - "discriminator": [ - "scene-fit", - "authority-safe", - "controlability" + "tr-001" ], - "drawbacks": [], - "filter_assessment": { - "authority_safe": { - "constraint": "review before writeback", - "reason": "safe when reviewed before writeback", - "status": "conditional" - }, - "controlability": { - "constraint": null, - "reason": "bounded trial possible", - "status": "pass" - }, - "scene_fit": { - "constraint": null, - "reason": "one bounded scene lane", - "status": "pass" - } - }, + "judgment_reason": "bounded trial was useful but still needs narrowing", + "landing_target": "ci/lint", + "next_action": "narrow the use case and rerun", "no_impact_envelope": { "allowed": [ "file read", @@ -64,19 +42,23 @@ "generated artifact write inside target project" ] }, - "non_tool_alternative": "tighten the manual checklist before adding a tool", + "observed_effect_summary": "~30% faster on the sample, but approval scope still needs narrowing", + "preventive_action": [ + "clarify the bounded scene before the next trial" + ], "recommended_fit_lane": "trial-ready", - "recommended_next_candidate": "", "recording_mode": "guided", - "recording_source": "quick-compare", - "rejected_reasons": [], - "related_scene": "lint-python", - "root_cause_hypothesis": "the use case 'lint-python' still depends on ad hoc operator judgment", + "recording_source": "quick-close-trial", + "recurring_control_decision": "later", + "related_artifacts": [ + "tr-001", + "tr-001" + ], + "related_scene": "lint-ci", + "reopen_condition": "retry after narrowing the use case or threshold", + "root_cause_hypothesis": "the bounded scene 'lint-ci' needed a more explicit helper path", "schema_version": 1, - "selected_candidate": "ruff", - "selection_reason": "selected ruff for bounded trial", - "status": "active", - "structural_gap": "current workflow lacks a bounded evaluation lane for this scene", + "status": "closed", "target_project_profile": { "allowed_input_surfaces": [ "tool output", @@ -96,5 +78,10 @@ "verification_methods": [ "human review" ] - } + }, + "verdict": "hold", + "why_this_problem_recurred": "the use case still depends on ad hoc operator judgment", + "writeback_artifact": [ + "trial-result and judgment-report" + ] } diff --git a/.adop/adop_trial-packet_tr-001.json b/examples/walkthrough/.adop/adop_trial-packet_tr-001.json similarity index 90% rename from .adop/adop_trial-packet_tr-001.json rename to examples/walkthrough/.adop/adop_trial-packet_tr-001.json index 26580e5..820e57d 100644 --- a/.adop/adop_trial-packet_tr-001.json +++ b/examples/walkthrough/.adop/adop_trial-packet_tr-001.json @@ -1,5 +1,5 @@ { - "adoption_unit": "pre-commit-hooks", + "adoption_unit": "ruff", "artifact_id": "tr-001", "artifact_type": "trial-packet", "candidate_shape": "atomic", @@ -14,7 +14,7 @@ }, "compatibility_diagnosis": [ { - "adoption_unit": "pre-commit-hooks", + "adoption_unit": "ruff", "authority_safe": "safe when reviewed before writeback", "controlability": "bounded trial is possible", "input_fit": "simple operator input is sufficient", @@ -24,8 +24,8 @@ "scene_fit_to_project": "fits one bounded scene lane" } ], - "created_at": "2026-06-18", - "decision_owner": "repo-maintainer", + "created_at": "2026-06-20", + "decision_owner": "lead", "decomposition_decision": "as-is", "dependency_note": "", "derived_from": [ @@ -36,7 +36,7 @@ "failure_mode_hypothesis": [], "fallback": "hold", "input_surface": "bounded comparison input", - "landing_target": ".pre-commit-config.yaml", + "landing_target": "ci/lint", "lane": "assistance", "mutation_boundary": "no write", "no_impact_envelope": { @@ -58,7 +58,7 @@ "recommended_fit_lane": "trial-ready", "recording_mode": "guided", "recording_source": "quick-trial", - "related_scene": "repo-hygiene-hooks", + "related_scene": "lint-ci", "sandbox_type": "read-only sandbox", "schema_version": 1, "status": "active", @@ -88,7 +88,7 @@ "friction_class": "", "observable_signal": "manual comparison request", "reopen_condition": "", - "scene": "repo-hygiene-hooks", + "scene": "lint-ci", "start_threshold": "", "stop_condition": "clear compare outcome" }, diff --git a/examples/walkthrough/.adop/adop_trial-result_tr-001.json b/examples/walkthrough/.adop/adop_trial-result_tr-001.json new file mode 100644 index 0000000..932c82a --- /dev/null +++ b/examples/walkthrough/.adop/adop_trial-result_tr-001.json @@ -0,0 +1,29 @@ +{ + "artifact_id": "tr-001", + "artifact_type": "trial-result", + "closed_at": "2026-06-20", + "code_level_compatibility_summary": "input=bounded comparison input; output=comparison summary; mutation=no write; verification=human review", + "created_at": "2026-06-20", + "decision_owner": "lead", + "derived_from": [ + "tr-001" + ], + "evidence_refs": [], + "failure_observed": [], + "input_surface": "bounded comparison input", + "landing_target": "ci/lint", + "mutation_boundary": "no write", + "observed_effect": "~30% faster on the sample, but approval scope still needs narrowing", + "output_contract": "comparison summary", + "recording_mode": "guided", + "recording_source": "quick-close-trial", + "related_scene": "lint-ci", + "sandbox_type": "read-only sandbox", + "schema_version": 1, + "status": "closed", + "trial_type": "read-only", + "verification_method": "human review", + "writeback_performed": [ + "trial-result and judgment-report" + ] +} diff --git a/package.json b/package.json deleted file mode 100644 index 33e3e05..0000000 --- a/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "adop-tool-surface-examples", - "private": true, - "type": "module", - "description": "Cross-ecosystem tool surface examples for ADOP recovery checks", - "scripts": { - "lint:js": "eslint eslint.config.js", - "format:check": "prettier --check package.json .prettierrc.json .prettierignore eslint.config.js .github/workflows/tool-surface-examples.yml scripts/repo-smoke.sh", - "lint:md": "markdownlint-cli2 README.md docs/**/*.md adop-overlay.md" - }, - "devDependencies": { - "eslint": "^9.29.0", - "markdownlint-cli2": "^0.18.1", - "prettier": "^3.6.2" - } -} diff --git a/pyproject.toml b/pyproject.toml index b560ef0..8b63677 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dev = [ "check-jsonschema>=0.37,<1", "mypy>=1.16,<2", "pre-commit>=4,<5", + "pytest-cov>=5,<7", "pytest-xdist>=3.8,<4", "ruff>=0.12,<1", ] @@ -58,6 +59,10 @@ line-length = 100 [tool.ruff.lint] select = ["E", "F", "I"] +# E501 (line-too-long) is ignored: most long lines are help text, lifecycle +# command templates, and URLs where wrapping hurts readability. `line-length` +# above is kept so `ruff format` still reflows real code to 100 columns. +ignore = ["E501"] [tool.mypy] python_version = "3.11" @@ -65,3 +70,22 @@ files = ["shared/python", "tests"] ignore_missing_imports = true warn_unused_configs = true pretty = true + +[tool.coverage.run] +source = ["shared/python"] +branch = true + +[tool.coverage.report] +# Coverage gate: run `python -m pytest --cov` (pytest-cov is a dev dependency). +# Floor set below the measured ~84%: each module's dual try/except import block +# leaves its relative-import (`from .x`) half uncovered when modules are imported +# as top-level in tests, and `__main__`/defensive OSError branches are untestable. +# 80 is a real regression guard, not a vanity target. +show_missing = true +fail_under = 80 + +[tool.pytest.ini_options] +testpaths = ["tests"] +python_files = ["test_*.py"] +python_functions = ["test_*"] +addopts = "-q" diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index d4b4c23..0000000 --- a/pytest.ini +++ /dev/null @@ -1,7 +0,0 @@ -[pytest] -# ADOP test suite. Run from the repo root with: python -m pytest -# conftest.py prepends shared/python/ to sys.path so runtime modules import cleanly. -testpaths = tests -python_files = test_*.py -python_functions = test_* -addopts = -q diff --git a/renovate.json b/renovate.json deleted file mode 100644 index f631c9b..0000000 --- a/renovate.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:best-practices" - ], - "labels": [ - "dependencies", - "renovate" - ], - "packageRules": [ - { - "matchManagers": ["pip_requirements", "github-actions", "npm"], - "groupName": "tooling updates" - } - ] -} diff --git a/scripts/repo-smoke.sh b/scripts/repo-smoke.sh deleted file mode 100644 index 7d31dab..0000000 --- a/scripts/repo-smoke.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# shellcheck shell=bash - -set -euo pipefail - -echo "ADOP repo smoke example" -test -f pyproject.toml -test -f package.json diff --git a/shared/python/adop_artifacts.py b/shared/python/adop_artifacts.py index 9b963b6..3265444 100644 --- a/shared/python/adop_artifacts.py +++ b/shared/python/adop_artifacts.py @@ -6,17 +6,44 @@ import json import os import sys +import time from pathlib import Path -from typing import Any +from typing import Any, Callable -from typing import Callable +from adop_ids import next_sequential_id, parse_numeric_id +from adop_types import JUDGMENT_REPORT, SCHEMA_VERSION, TRIAL_PACKET -try: - from .adop_ids import next_sequential_id, parse_numeric_id - from .adop_types import JUDGMENT_REPORT, SCHEMA_VERSION, TRIAL_PACKET -except ImportError: # pragma: no cover - script import path - from adop_ids import next_sequential_id, parse_numeric_id - from adop_types import JUDGMENT_REPORT, SCHEMA_VERSION, TRIAL_PACKET +# A write completes in well under a second; a lock older than this can only be +# the orphan of a crashed/killed writer, so it is safe to reclaim instead of +# blocking the artifact name forever (round-2 audit, orphaned-lock lockout). +_LOCK_STALE_SECONDS: float = 30.0 + + +def _acquire_lock(lock_path: Path, display_name: str) -> int: + """Exclusively create the lock file, reclaiming a clearly-stale orphan once. + + A contended lock surfaces as FileExistsError on POSIX but as PermissionError + on Windows (the existing/pending-delete lock cannot be opened O_EXCL). Both + mean "another writer holds it", so both are mapped to AdopArtifactError, which + write_next_sequential_artifact retries on instead of crashing (Windows race). + """ + try: + return os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + except (FileExistsError, PermissionError) as exc: + try: + age = time.time() - lock_path.stat().st_mtime + except OSError: + age = 0.0 + if age <= _LOCK_STALE_SECONDS: + raise AdopArtifactError(f"artifact write already in progress: {display_name}") from exc + try: + lock_path.unlink() + except OSError: + pass + try: + return os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) + except (FileExistsError, PermissionError) as exc2: + raise AdopArtifactError(f"artifact write already in progress: {display_name}") from exc2 class AdopArtifactError(ValueError): @@ -102,7 +129,9 @@ def artifact_path(root: Path, artifact_type: str, artifact_id: str) -> Path: return ensure_artifact_root(root) / artifact_filename(artifact_type, artifact_id) -def write_artifact(root: Path, artifact_type: str, artifact_id: str, payload: dict[str, Any]) -> Path: +def write_artifact( + root: Path, artifact_type: str, artifact_id: str, payload: dict[str, Any] +) -> Path: path = artifact_path(root, artifact_type, artifact_id) body = json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n" lock_path = path.with_name(f".{path.name}.lock") @@ -110,10 +139,7 @@ def write_artifact(root: Path, artifact_type: str, artifact_id: str, payload: di # would later poison load_all_artifacts (residual B36). Write to a temp file # in the same directory, fsync, then os.replace (atomic on the same volume). tmp_path = path.with_name(f"{path.name}.{os.getpid()}.tmp") - try: - fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) - except FileExistsError as exc: - raise AdopArtifactError(f"artifact write already in progress: {path.name}") from exc + fd = _acquire_lock(lock_path, path.name) try: os.close(fd) if path.exists(): @@ -124,10 +150,18 @@ def write_artifact(root: Path, artifact_type: str, artifact_id: str, payload: di os.fsync(handle.fileno()) os.replace(tmp_path, path) finally: + # Cleanup must not raise in finally and mask the real result; on Windows + # an unlink can transiently fail with PermissionError under contention. if tmp_path.exists(): - tmp_path.unlink() + try: + tmp_path.unlink() + except OSError: + pass if lock_path.exists(): - lock_path.unlink() + try: + lock_path.unlink() + except OSError: + pass return path @@ -157,10 +191,7 @@ def write_artifact_group( path = resolved / artifact_filename(artifact_type, artifact_id) lock_path = path.with_name(f".{path.name}.lock") tmp_path = path.with_name(f"{path.name}.{os.getpid()}.tmp") - try: - fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY) - except FileExistsError as exc: - raise AdopArtifactError(f"artifact write already in progress: {path.name}") from exc + fd = _acquire_lock(lock_path, path.name) # Register the lock for cleanup immediately — before any raise below — # so a collision cannot leak a stale .lock file. locks.append(lock_path) @@ -249,7 +280,7 @@ def _filename_type(path: Path) -> str | None: stem = path.stem if not stem.startswith("adop_"): return None - middle = stem[len("adop_"):] + middle = stem[len("adop_") :] if "_" not in middle: return None return middle.rsplit("_", 1)[0] @@ -276,7 +307,10 @@ def load_all_artifacts(root: Path) -> list[dict[str, Any]]: body_type = payload.get("artifact_type") name_type = _filename_type(path) if body_type is None: - print(f"[adop] artifact missing artifact_type, classified by filename: {path}", file=sys.stderr) + print( + f"[adop] artifact missing artifact_type, classified by filename: {path}", + file=sys.stderr, + ) elif name_type is not None and body_type != name_type: print( f"[adop] artifact_type mismatch (filename={name_type}, body={body_type}): {path}", @@ -310,7 +344,9 @@ def _id_sort_key(item: dict[str, Any]) -> tuple[int, int, str]: return (0, number, raw) -def latest_by_type(root: Path, artifact_type: str, *, scene: str | None = None) -> dict[str, Any] | None: +def latest_by_type( + root: Path, artifact_type: str, *, scene: str | None = None +) -> dict[str, Any] | None: items = find_by_type(root, artifact_type) if scene is not None: items = [item for item in items if item.get("related_scene") == scene] @@ -323,7 +359,9 @@ def latest_by_type(root: Path, artifact_type: str, *, scene: str | None = None) def _find_unique_by_id(root: Path, artifact_type: str, artifact_id: str) -> dict[str, Any] | None: if not artifact_id: return None - matches = [item for item in find_by_type(root, artifact_type) if item.get("artifact_id") == artifact_id] + matches = [ + item for item in find_by_type(root, artifact_type) if item.get("artifact_id") == artifact_id + ] if len(matches) > 1: raise AdopArtifactError(f"multiple {artifact_type} artifacts share id {artifact_id}") return matches[0] if matches else None @@ -337,7 +375,9 @@ def find_judgment_report(root: Path, trial_id: str) -> dict[str, Any] | None: return _find_unique_by_id(root, JUDGMENT_REPORT, trial_id) -def json_response(command: str, status: str, artifact_refs: list[str], errors: list[str]) -> dict[str, Any]: +def json_response( + command: str, status: str, artifact_refs: list[str], errors: list[str] +) -> dict[str, Any]: return { "schema_version": SCHEMA_VERSION, "command": command, diff --git a/shared/python/adop_cli.py b/shared/python/adop_cli.py index cec5cc9..15d1197 100644 --- a/shared/python/adop_cli.py +++ b/shared/python/adop_cli.py @@ -5,7 +5,6 @@ import argparse import fnmatch -import io import json import os import re @@ -16,137 +15,69 @@ from pathlib import Path from typing import Any -try: - from .common import fix_stdout_encoding - from . import adop_artifacts as artifacts - from .adop_html import render_dashboard_html - from .adop_ids import next_sequential_id, parse_numeric_id - from .adop_state_machine import comparison_ready_for_trial, promote_gate_errors - from .adop_summary import build_summary, get_scene_states - from .adop_types import ( - ARCHIVE_NOTE, - BLOCKED_NOTE, - CANDIDATE_INTAKE_NOTE, - CANDIDATE_SHAPES, - COMPARISON_NOTE, - COUPLING_NOTE, - COUPLING_TYPES, - DECOMPOSITION_DECISION, - DECOMPOSITION_DECISIONS, - DEPRECATION_NOTE, - DISPOSITIONS, - EVALUATION_GATE, - EXECUTOR, - FALLBACKS, - FILTER_NAMES, - FILTER_STATUSES, - FIT_LANES, - HOLD_NOTE, - JUDGMENT_REPORT, - LANDING_TARGET, - LANES, - MIGRATION_NOTE, - OBSERVED_EFFECT, - PLATFORMS, - PROMOTION_NOTE, - PROPOSED, - RECURRING_CONTROL_DECISIONS, - REJECT_NOTE, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - SANDBOX_TYPES, - SCHEMA_VERSION, - STRUCTURAL_GAP, - TRIAL_PACKET, - TRIAL_RESULT, - TRIAL_TYPES, - VERDICTS, - WATCH_NOTE, - ) - from .adop_validation import ( - AdopValidationError, - lint_artifact_root, - unknown_tool_attribute_fields, - today_iso, - validate_archive_note_payload, - validate_blocked_note_payload, - validate_close_payload, - validate_comparison_payload, - validate_coupling_note_payload, - validate_deprecation_note_payload, - validate_filter_assessment, - validate_intake_payload, - validate_migration_note_payload, - validate_no_impact_trial_mode, - validate_trial_packet_payload, - validate_watch_note_payload, - ) -except ImportError: # pragma: no cover - script import path - from common import fix_stdout_encoding - - import adop_artifacts as artifacts - from adop_html import render_dashboard_html - from adop_ids import next_sequential_id, parse_numeric_id - from adop_state_machine import comparison_ready_for_trial, promote_gate_errors - from adop_summary import build_summary, get_scene_states - from adop_types import ( - ARCHIVE_NOTE, - BLOCKED_NOTE, - CANDIDATE_INTAKE_NOTE, - CANDIDATE_SHAPES, - COMPARISON_NOTE, - COUPLING_NOTE, - COUPLING_TYPES, - DECOMPOSITION_DECISION, - DECOMPOSITION_DECISIONS, - DEPRECATION_NOTE, - DISPOSITIONS, - EVALUATION_GATE, - EXECUTOR, - FALLBACKS, - FILTER_NAMES, - FILTER_STATUSES, - FIT_LANES, - JUDGMENT_REPORT, - LANDING_TARGET, - LANES, - MIGRATION_NOTE, - HOLD_NOTE, - OBSERVED_EFFECT, - PLATFORMS, - PROMOTION_NOTE, - PROPOSED, - RECURRING_CONTROL_DECISIONS, - REJECT_NOTE, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - SANDBOX_TYPES, - SCHEMA_VERSION, - STRUCTURAL_GAP, - TRIAL_PACKET, - TRIAL_RESULT, - TRIAL_TYPES, - VERDICTS, - WATCH_NOTE, - ) - from adop_validation import ( - AdopValidationError, - lint_artifact_root, - unknown_tool_attribute_fields, - today_iso, - validate_archive_note_payload, - validate_blocked_note_payload, - validate_close_payload, - validate_comparison_payload, - validate_coupling_note_payload, - validate_deprecation_note_payload, - validate_filter_assessment, - validate_intake_payload, - validate_migration_note_payload, - validate_no_impact_trial_mode, - validate_trial_packet_payload, - validate_watch_note_payload, - ) +import adop_artifacts as artifacts +from adop_html import render_dashboard_html +from adop_ids import next_sequential_id, parse_numeric_id +from adop_state_machine import comparison_ready_for_trial, promote_gate_errors +from adop_summary import build_summary, get_scene_states +from adop_types import ( + ARCHIVE_NOTE, + BLOCKED_NOTE, + CANDIDATE_INTAKE_NOTE, + CANDIDATE_SHAPES, + COMPARISON_NOTE, + COUPLING_NOTE, + COUPLING_TYPES, + DECOMPOSITION_DECISION, + DECOMPOSITION_DECISIONS, + DEPRECATION_NOTE, + EVALUATION_GATE, + EXECUTOR, + FALLBACKS, + FILTER_NAMES, + FILTER_STATUSES, + FIT_LANES, + HOLD_NOTE, + JUDGMENT_REPORT, + LANDING_TARGET, + LANES, + MIGRATION_NOTE, + OBSERVED_EFFECT, + PLATFORMS, + PROMOTION_NOTE, + PROPOSED, + RECURRING_CONTROL_DECISIONS, + REJECT_NOTE, + REMOVAL_COSTS, + ROOT_CAUSE_HYPOTHESIS, + SANDBOX_TYPES, + SCHEMA_VERSION, + STRUCTURAL_GAP, + TRIAL_PACKET, + TRIAL_RESULT, + TRIAL_TYPES, + VERDICTS, + WATCH_NOTE, +) +from adop_validation import ( + AdopValidationError, + lint_artifact_root, + today_iso, + unknown_tool_attribute_fields, + validate_archive_note_payload, + validate_blocked_note_payload, + validate_close_payload, + validate_comparison_payload, + validate_coupling_note_payload, + validate_deprecation_note_payload, + validate_filter_assessment, + validate_intake_payload, + validate_migration_note_payload, + validate_no_impact_trial_mode, + validate_trial_packet_payload, + validate_watch_note_payload, +) +from common import fix_stdout_encoding fix_stdout_encoding() @@ -279,31 +210,48 @@ # Next-command templates keyed by lifecycle state. _NEXT_FOR_STATE: dict[str, str] = { - "watch": 'adop quick-intake --scene {scene} --candidate --source doc --why-now ""', - "proposed": 'adop quick-compare --scene {scene} --candidate --candidate --selected ', - "trial-ready": 'adop quick-trial --scene {scene} --mode review-assist --executor --decision-owner --landing-target ', - "blocked": 'adop unblock --scene {scene} --why-unblocked ""', - "hold": 'adop quick-compare --scene {scene} --candidate --candidate --selected # resume trial; or: adop deprecate if no longer needed', - "reject": 'adop deprecate --scene {scene} --deprecation-reason "rejected at trial" # or: adop archive if fully closed', - "deprecated": 'adop migrate --scene {scene} --migration-target --migration-plan ""', - "migrating": 'adop archive --scene {scene} --end-date ', + "watch": 'adop quick-intake --scene {scene} --candidate --source doc --why-now ""', + "proposed": "adop quick-compare --scene {scene} --candidate --candidate --selected ", + "trial-ready": "adop quick-trial --scene {scene} --mode review-assist --executor --decision-owner --landing-target ", + "blocked": 'adop unblock --scene {scene} --why-unblocked ""', + "hold": "adop quick-compare --scene {scene} --candidate --candidate --selected # resume trial; or: adop reject if no longer needed", + "deprecated": 'adop migrate --scene {scene} --migration-target --migration-plan ""', + "migrating": "adop archive --scene {scene} --end-date ", } -_CONFIG_FILE_NAMES: frozenset[str] = frozenset({ - "pyproject.toml", "setup.cfg", "setup.py", "tox.ini", - ".pre-commit-config.yaml", ".flake8", - "requirements.txt", "requirements-dev.txt", "requirements-test.txt", -}) +_CONFIG_FILE_NAMES: frozenset[str] = frozenset( + { + "pyproject.toml", + "setup.cfg", + "setup.py", + "tox.ini", + ".pre-commit-config.yaml", + ".flake8", + "requirements.txt", + "requirements-dev.txt", + "requirements-test.txt", + } +) # Node ecosystem: package.json declares deps + scripts; lock files are generated artifacts. -_NODE_DEP_FILES: frozenset[str] = frozenset({ - "package.json", "package-lock.json", "pnpm-lock.yaml", "yarn.lock", -}) +_NODE_DEP_FILES: frozenset[str] = frozenset( + { + "package.json", + "package-lock.json", + "pnpm-lock.yaml", + "yarn.lock", + } +) _TOOL_SURFACE_RULES: dict[str, tuple[dict[str, Any], ...]] = { "actionlint": ( { - "patterns": (".actionlint.yaml", ".actionlint.yml", ".github/actionlint.yaml", ".github/actionlint.yml"), + "patterns": ( + ".actionlint.yaml", + ".actionlint.yml", + ".github/actionlint.yaml", + ".github/actionlint.yml", + ), "coupling_type": "config", "removal_cost": "edit", "note": "tool-owned config surface", @@ -320,8 +268,14 @@ "eslint": ( { "patterns": ( - "eslint.config.js", "eslint.config.cjs", "eslint.config.mjs", - ".eslintrc", ".eslintrc.json", ".eslintrc.yaml", ".eslintrc.yml", ".eslintrc.js", + "eslint.config.js", + "eslint.config.cjs", + "eslint.config.mjs", + ".eslintrc", + ".eslintrc.json", + ".eslintrc.yaml", + ".eslintrc.yml", + ".eslintrc.js", ".eslintignore", ), "coupling_type": "config", @@ -337,7 +291,7 @@ }, { "patterns": (".vscode/settings.json",), - "contains_any": ("\"eslint.validate\"", "\"source.fixall.eslint\"", "\"eslint."), + "contains_any": ('"eslint.validate"', '"source.fixall.eslint"', '"eslint.'), "coupling_type": "config", "removal_cost": "edit", "note": "workspace editor settings", @@ -360,7 +314,12 @@ ), "markdownlint-cli2": ( { - "patterns": (".markdownlint-cli2.jsonc", ".markdownlint-cli2.json", ".markdownlint-cli2.yaml", ".markdownlint-cli2.yml"), + "patterns": ( + ".markdownlint-cli2.jsonc", + ".markdownlint-cli2.json", + ".markdownlint-cli2.yaml", + ".markdownlint-cli2.yml", + ), "coupling_type": "config", "removal_cost": "edit", "note": "tool-owned config surface", @@ -377,8 +336,14 @@ "prettier": ( { "patterns": ( - ".prettierrc", ".prettierrc.json", ".prettierrc.yaml", ".prettierrc.yml", - ".prettierignore", "prettier.config.js", "prettier.config.cjs", "prettier.config.mjs", + ".prettierrc", + ".prettierrc.json", + ".prettierrc.yaml", + ".prettierrc.yml", + ".prettierignore", + "prettier.config.js", + "prettier.config.cjs", + "prettier.config.mjs", ), "coupling_type": "config", "removal_cost": "edit", @@ -387,7 +352,12 @@ ), "renovate": ( { - "patterns": ("renovate.json", "renovate.json5", ".github/renovate.json", ".github/renovate.json5"), + "patterns": ( + "renovate.json", + "renovate.json5", + ".github/renovate.json", + ".github/renovate.json5", + ), "coupling_type": "config", "removal_cost": "edit", "note": "dependency bot config surface", @@ -403,7 +373,13 @@ ), "trivy": ( { - "patterns": (".trivyignore", "trivy.yaml", "trivy.yml", ".trivy/config.yaml", ".trivy/config.yml"), + "patterns": ( + ".trivyignore", + "trivy.yaml", + "trivy.yml", + ".trivy/config.yaml", + ".trivy/config.yml", + ), "coupling_type": "config", "removal_cost": "edit", "note": "scanner config surface", @@ -419,7 +395,7 @@ }, { "patterns": (".vscode/settings.json",), - "contains_any": ("\"eslint.validate\"", "\"source.fixall.eslint\"", "\"eslint."), + "contains_any": ('"eslint.validate"', '"source.fixall.eslint"', '"eslint.'), "coupling_type": "config", "removal_cost": "edit", "note": "workspace editor settings", @@ -427,10 +403,27 @@ ), } -_SCAN_SKIP_DIRS: frozenset[str] = frozenset({ - ".git", ".hg", "__pycache__", ".pytest_cache", ".mypy_cache", - ".venv", "venv", "env", "node_modules", ".adop", "build", "dist", -}) +# Skip files larger than this when scanning for couplings: a coupling is a +# config/import/invocation reference, never a multi-MB blob, so reading huge +# files in full would only risk OOM (round-2 audit R4). +_MAX_SCAN_FILE_BYTES: int = 5_000_000 + +_SCAN_SKIP_DIRS: frozenset[str] = frozenset( + { + ".git", + ".hg", + "__pycache__", + ".pytest_cache", + ".mypy_cache", + ".venv", + "venv", + "env", + "node_modules", + ".adop", + "build", + "dist", + } +) _PLATFORM_ALIASES: dict[str, str] = { "win": "windows", @@ -511,9 +504,9 @@ def _next_step(scene: str, state: str, root: Path, items: list[dict[str, Any]]) """Return the recommended next CLI command for the given scene/state.""" if state == "in-trial": pkts = [ - i for i in items - if i.get("artifact_type") == TRIAL_PACKET - and str(i.get("related_scene", "")) == scene + i + for i in items + if i.get("artifact_type") == TRIAL_PACKET and str(i.get("related_scene", "")) == scene ] trial_id = str(pkts[-1]["artifact_id"]) if pkts else "tr-001" return ( @@ -534,7 +527,9 @@ def _prepare_artifact_root(args: argparse.Namespace) -> Path: try: return artifacts.ensure_artifact_root( Path(root_arg), - target_project_root=Path(args.target_project_root) if getattr(args, "target_project_root", None) else None, + target_project_root=Path(args.target_project_root) + if getattr(args, "target_project_root", None) + else None, allow_project_impact=bool(getattr(args, "allow_project_impact", False)), ) except artifacts.AdopBoundaryError as exc: @@ -658,8 +653,7 @@ def _tool_search_aliases(tool: str) -> list[str]: def _text_mentions_tool(text_lower: str, aliases: list[str]) -> bool: return any( - re.search(rf"(? dict[str, Any] for rule in rules: patterns = tuple(str(pattern).lower() for pattern in rule.get("patterns", ())) prefixes = tuple(str(prefix).lower() for prefix in rule.get("path_prefixes", ())) - matches_path = rel_lower in patterns or any(Path(rel_lower).name.startswith(prefix) for prefix in prefixes) + matches_path = rel_lower in patterns or any( + Path(rel_lower).name.startswith(prefix) for prefix in prefixes + ) if not matches_path: continue contains_any = tuple(str(token).lower() for token in rule.get("contains_any", ())) @@ -718,13 +714,14 @@ def _surface_rule_match(tool: str, rel: str, text_lower: str) -> dict[str, Any] return None -def _text_mentions_tool_in_context(tool: str, rel: str, text_lower: str, aliases: list[str]) -> bool: +def _text_mentions_tool_in_context( + tool: str, rel: str, text_lower: str, aliases: list[str] +) -> bool: tool_key = tool.lower().strip() rel_lower = rel.lower() if tool_key == "renovate": return bool( - re.search(r"(? dict normalized_aliases = {alias.replace("-", "_").replace(".", "_") for alias in aliases} if isinstance(tool_table, dict) and any(alias in tool_table for alias in normalized_aliases): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="tool configuration section", detection_source="config-mention", confidence="high", @@ -774,9 +773,15 @@ def _structured_pyproject_match(rel: str, text: str, aliases: list[str]) -> dict if isinstance(requires, list): dependency_lists.append(requires) - if any(_dependency_string_mentions_tool(item, aliases) for group in dependency_lists for item in group): + if any( + _dependency_string_mentions_tool(item, aliases) + for group in dependency_lists + for item in group + ): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="dependency declaration", detection_source="config-mention", confidence="high", @@ -784,7 +789,9 @@ def _structured_pyproject_match(rel: str, text: str, aliases: list[str]) -> dict return None -def _structured_package_json_match(rel: str, text: str, aliases: list[str], tool: str) -> dict[str, Any] | None: +def _structured_package_json_match( + rel: str, text: str, aliases: list[str], tool: str +) -> dict[str, Any] | None: if rel.lower() != "package.json": return None try: @@ -802,7 +809,9 @@ def _structured_package_json_match(rel: str, text: str, aliases: list[str], tool for mapping in dependency_maps: if isinstance(mapping, dict) and any(str(key).lower() in alias_set for key in mapping): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="dependency declaration", detection_source="config-mention", confidence="high", @@ -814,9 +823,13 @@ def _structured_package_json_match(rel: str, text: str, aliases: list[str], tool if not isinstance(value, str): continue value_lower = value.lower() - if _looks_like_pytest_xdist_invocation(tool, value_lower) or _text_mentions_tool_in_context(tool, rel, value_lower, aliases): + if _looks_like_pytest_xdist_invocation( + tool, value_lower + ) or _text_mentions_tool_in_context(tool, rel, value_lower, aliases): return _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", note="package script invocation", detection_source="invocation-pattern", confidence="high", @@ -824,20 +837,30 @@ def _structured_package_json_match(rel: str, text: str, aliases: list[str], tool return None -def _structured_precommit_match(rel: str, text_lower: str, aliases: list[str]) -> dict[str, Any] | None: +def _structured_precommit_match( + rel: str, text_lower: str, aliases: list[str] +) -> dict[str, Any] | None: if rel.lower() not in (".pre-commit-config.yaml", ".pre-commit-config.yml"): return None - if "github.com/pre-commit/pre-commit-hooks" in text_lower and any(alias == "pre-commit-hooks" for alias in aliases): + if "github.com/pre-commit/pre-commit-hooks" in text_lower and any( + alias == "pre-commit-hooks" for alias in aliases + ): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="hook repository declaration", detection_source="config-mention", confidence="high", ) - if "github.com/python-jsonschema/check-jsonschema" in text_lower and any(alias == "check-jsonschema" for alias in aliases): + if "github.com/python-jsonschema/check-jsonschema" in text_lower and any( + alias == "check-jsonschema" for alias in aliases + ): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="hook repository declaration", detection_source="config-mention", confidence="high", @@ -849,7 +872,9 @@ def _structured_precommit_match(rel: str, text_lower: str, aliases: list[str]) - continue if _text_mentions_tool(line, aliases): return _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", note="hook entry declaration", detection_source="config-mention", confidence="high", @@ -873,7 +898,9 @@ def _package_scripts_matching_tool(target: Path, aliases: list[str], tool: str) if not isinstance(name, str) or not isinstance(value, str): continue value_lower = value.lower() - if _looks_like_pytest_xdist_invocation(tool, value_lower) or _text_mentions_tool_in_context(tool, "package.json", value_lower, aliases): + if _looks_like_pytest_xdist_invocation(tool, value_lower) or _text_mentions_tool_in_context( + tool, "package.json", value_lower, aliases + ): matched.add(name.lower()) return matched @@ -904,7 +931,9 @@ def _workflow_command_lines(text_lower: str) -> list[str]: return commands -def _structured_workflow_match(target: Path, rel: str, text_lower: str, aliases: list[str], tool: str) -> dict[str, Any] | None: +def _structured_workflow_match( + target: Path, rel: str, text_lower: str, aliases: list[str], tool: str +) -> dict[str, Any] | None: if not rel.lower().startswith(".github/workflows/"): return None @@ -920,22 +949,32 @@ def _structured_workflow_match(target: Path, rel: str, text_lower: str, aliases: if line.startswith(("- uses:", "uses:")): if any(pattern in line for pattern in action_patterns.get(tool.lower().strip(), ())): return _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", note="workflow action usage", detection_source="invocation-pattern", confidence="high", ) for line in _workflow_command_lines(text_lower): - if any(re.search(rf"\bnpm\s+run\s+{re.escape(script)}\b", line) for script in matching_scripts): + if any( + re.search(rf"\bnpm\s+run\s+{re.escape(script)}\b", line) for script in matching_scripts + ): return _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", note="workflow run command", detection_source="invocation-pattern", confidence="high", ) - if _looks_like_pytest_xdist_invocation(tool, line) or _text_mentions_tool_in_context(tool, rel, line, aliases): + if _looks_like_pytest_xdist_invocation(tool, line) or _text_mentions_tool_in_context( + tool, rel, line, aliases + ): return _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", note="workflow run command", detection_source="invocation-pattern", confidence="high", @@ -943,11 +982,17 @@ def _structured_workflow_match(target: Path, rel: str, text_lower: str, aliases: return None -def _structured_shell_script_match(path: Path, rel: str, text_lower: str, tool: str) -> dict[str, Any] | None: +def _structured_shell_script_match( + path: Path, rel: str, text_lower: str, tool: str +) -> dict[str, Any] | None: if tool.lower().strip() == "shellcheck" and path.suffix in (".sh", ".bash"): - if any(raw_line.strip().startswith("# shellcheck ") for raw_line in text_lower.splitlines()): + if any( + raw_line.strip().startswith("# shellcheck ") for raw_line in text_lower.splitlines() + ): return _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", note="inline shellcheck directive", detection_source="config-mention", confidence="high", @@ -955,7 +1000,9 @@ def _structured_shell_script_match(path: Path, rel: str, text_lower: str, tool: return None -def _structured_config_match(target: Path, path: Path, rel: str, text: str, text_lower: str, aliases: list[str], tool: str) -> dict[str, Any] | None: +def _structured_config_match( + target: Path, path: Path, rel: str, text: str, text_lower: str, aliases: list[str], tool: str +) -> dict[str, Any] | None: return ( _structured_pyproject_match(rel, text, aliases) or _structured_package_json_match(rel, text, aliases, tool) @@ -994,6 +1041,8 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ for path, rel in _iter_scan_files(target, excludes): try: + if path.stat().st_size > _MAX_SCAN_FILE_BYTES: + continue text = path.read_text(encoding="utf-8", errors="ignore") except OSError: continue @@ -1004,7 +1053,9 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ couplings.append(surface_match) continue - structured_match = _structured_config_match(target, path, rel, text, text_lower, aliases, tool) + structured_match = _structured_config_match( + target, path, rel, text, text_lower, aliases, tool + ) if structured_match: couplings.append(structured_match) continue @@ -1013,7 +1064,9 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ if re.search(rf"(?m)^\s*(?:import|from)\s+{re.escape(tool_mod)}\b", text): couplings.append( _build_detected_coupling( - rel, "import", "edit", + rel, + "import", + "edit", detection_source="python-import", confidence="high", ) @@ -1023,7 +1076,9 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ if path.name in ("package-lock.json", "pnpm-lock.yaml", "yarn.lock"): couplings.append( _build_detected_coupling( - rel, "config", "clean", + rel, + "config", + "clean", note="generated lock file", detection_source="config-mention", confidence="medium", @@ -1032,47 +1087,85 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ else: couplings.append( _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", detection_source="config-mention", confidence="medium", ) ) - elif path.name in _CONFIG_FILE_NAMES or path.suffix in (".yml", ".yaml", ".toml", ".cfg", ".ini"): - if _text_mentions_tool_in_context(tool, rel, text_lower, aliases) or _looks_like_pytest_xdist_invocation(tool, text_lower): - if path.name in ("requirements.txt", "requirements-dev.txt", "requirements-test.txt"): + elif path.name in _CONFIG_FILE_NAMES or path.suffix in ( + ".yml", + ".yaml", + ".toml", + ".cfg", + ".ini", + ): + if _text_mentions_tool_in_context( + tool, rel, text_lower, aliases + ) or _looks_like_pytest_xdist_invocation(tool, text_lower): + if path.name in ( + "requirements.txt", + "requirements-dev.txt", + "requirements-test.txt", + ): couplings.append( _build_detected_coupling( - rel, "config", "clean", + rel, + "config", + "clean", note="dependency declaration", detection_source="config-mention", confidence="medium", ) ) else: - detection_source = "invocation-pattern" if _looks_like_pytest_xdist_invocation(tool, text_lower) else "config-mention" + detection_source = ( + "invocation-pattern" + if _looks_like_pytest_xdist_invocation(tool, text_lower) + else "config-mention" + ) confidence = "high" if detection_source == "invocation-pattern" else "medium" couplings.append( _build_detected_coupling( - rel, "config", "edit", + rel, + "config", + "edit", detection_source=detection_source, confidence=confidence, ) ) - elif path.suffix in (".sh", ".bash", ".ps1", ".bat", ".cmd") or path.name in ("Makefile", "makefile"): - if _text_mentions_tool_in_context(tool, rel, text_lower, aliases) or _looks_like_pytest_xdist_invocation(tool, text_lower): + elif path.suffix in (".sh", ".bash", ".ps1", ".bat", ".cmd") or path.name in ( + "Makefile", + "makefile", + ): + if _text_mentions_tool_in_context( + tool, rel, text_lower, aliases + ) or _looks_like_pytest_xdist_invocation(tool, text_lower): detection_source = "invocation-pattern" confidence = "high" couplings.append( _build_detected_coupling( - rel, "invocation", "edit", + rel, + "invocation", + "edit", detection_source=detection_source, confidence=confidence, ) ) - elif _text_mentions_tool_in_context(tool, rel, text_lower, aliases) and path.suffix not in (".pyc", ".pyo", ".lock", ".md", ".rst", ".txt"): + elif _text_mentions_tool_in_context(tool, rel, text_lower, aliases) and path.suffix not in ( + ".pyc", + ".pyo", + ".lock", + ".md", + ".rst", + ".txt", + ): couplings.append( _build_detected_coupling( - rel, "reference", "clean", + rel, + "reference", + "clean", detection_source="text-reference", confidence="low", ) @@ -1080,7 +1173,9 @@ def _scan_target_for_tool(target: Path, tool: str, excludes: list[str]) -> list[ return couplings -def _write_coupling_note(root: Path, *, scene: str, tool: str, couplings: list[dict[str, Any]]) -> Path: +def _write_coupling_note( + root: Path, *, scene: str, tool: str, couplings: list[dict[str, Any]] +) -> Path: artifact_id = next_sequential_id(root, "cp") payload = { "schema_version": SCHEMA_VERSION, @@ -1163,7 +1258,9 @@ def _simple_close_preset(verdict: str) -> dict[str, Any]: "next_action": "promote with explicit writeback review", "recurring_control_decision": RECURRING_CONTROL_DECISIONS[2], "reopen_condition": "", - "preventive_actions": ["capture the reusable writeback pattern in project-local guidance"], + "preventive_actions": [ + "capture the reusable writeback pattern in project-local guidance" + ], "why_this_problem_recurred": "the use case had no stable helper path before the bounded trial", } if verdict == VERDICTS[1]: @@ -1181,7 +1278,9 @@ def _simple_close_preset(verdict: str) -> dict[str, Any]: "next_action": "keep the use case on manual handling", "recurring_control_decision": RECURRING_CONTROL_DECISIONS[1], "reopen_condition": "retry only if a different bounded scene appears", - "preventive_actions": ["record the rejected pattern so the same trial is not repeated blindly"], + "preventive_actions": [ + "record the rejected pattern so the same trial is not repeated blindly" + ], "why_this_problem_recurred": "the candidate did not fit the bounded scene well enough", } raise AdopValidationError(f"unsupported quick close verdict: {verdict}", 2) @@ -1189,7 +1288,6 @@ def _simple_close_preset(verdict: str) -> dict[str, Any]: def _comparison_filter_args(parser: argparse.ArgumentParser) -> None: for cli_name in ("scene-fit", "authority-safe", "controlability"): - key = cli_name.replace("-", "_") parser.add_argument(f"--{cli_name}-status", required=True, choices=FILTER_STATUSES) parser.add_argument(f"--{cli_name}-reason", required=True) parser.add_argument(f"--{cli_name}-constraint", default=None) @@ -1221,7 +1319,6 @@ def _project_boundary_args(parser: argparse.ArgumentParser) -> None: def _build_filter_assessment(args: argparse.Namespace) -> dict[str, dict[str, str | None]]: data: dict[str, dict[str, str | None]] = {} for key in FILTER_NAMES: - cli_name = key.replace("_", "-") data[key] = { "status": getattr(args, f"{key}_status"), "reason": getattr(args, f"{key}_reason"), @@ -1275,7 +1372,9 @@ def _build_parser() -> argparse.ArgumentParser: quick_intake.add_argument("--source", required=True) _scene_arg(quick_intake, required=True, help_text="scene lane to record") quick_intake.add_argument("--why-now", required=True) - quick_intake.add_argument("--candidate-shape", default=CANDIDATE_SHAPES[0], choices=CANDIDATE_SHAPES) + quick_intake.add_argument( + "--candidate-shape", default=CANDIDATE_SHAPES[0], choices=CANDIDATE_SHAPES + ) quick_intake.add_argument("--lane", default=LANES[1], choices=LANES) quick_intake.add_argument("--root-cause-hypothesis", default="") quick_intake.add_argument("--platform", default="") @@ -1296,11 +1395,18 @@ def _build_parser() -> argparse.ArgumentParser: _scene_arg(quick_compare, required=True, help_text="scene lane to compare within") quick_compare.add_argument("--candidate", dest="candidates", action="append", required=True) quick_compare.add_argument("--selected", required=True) - quick_compare.add_argument("--candidate-shape", default=CANDIDATE_SHAPES[0], choices=CANDIDATE_SHAPES) + quick_compare.add_argument( + "--candidate-shape", default=CANDIDATE_SHAPES[0], choices=CANDIDATE_SHAPES + ) quick_compare.add_argument("--adoption-unit") quick_compare.add_argument("--root-cause-hypothesis", default="") - quick_compare.add_argument("--structural-gap", default="current workflow lacks a bounded evaluation lane for this scene") - quick_compare.add_argument("--non-tool-alternative", default="tighten the manual checklist before adding a tool") + quick_compare.add_argument( + "--structural-gap", + default="current workflow lacks a bounded evaluation lane for this scene", + ) + quick_compare.add_argument( + "--non-tool-alternative", default="tighten the manual checklist before adding a tool" + ) quick_compare.add_argument("--target-project-profile-json", default="") quick_compare.add_argument("--compatibility-diagnosis-json", default="") quick_compare.add_argument("--no-impact-envelope-json", default="") @@ -1313,7 +1419,9 @@ def _build_parser() -> argparse.ArgumentParser: quick_trial.add_argument("--artifact-root", default=_DEFAULT_ARTIFACT_ROOT, metavar="DIR") _project_boundary_args(quick_trial) _scene_arg(quick_trial, required=True, help_text="scene lane to open a trial for") - quick_trial.add_argument("--mode", required=True, choices=("review-assist", "read-only-comparison")) + quick_trial.add_argument( + "--mode", required=True, choices=("review-assist", "read-only-comparison") + ) quick_trial.add_argument("--executor", required=True) quick_trial.add_argument("--decision-owner", required=True) quick_trial.add_argument("--landing-target", required=True) @@ -1340,7 +1448,9 @@ def _build_parser() -> argparse.ArgumentParser: quick_close.add_argument("--next-action", default="") quick_close.add_argument("--reopen-condition", default="") quick_close.add_argument("--recurring-control-decision", default="") - quick_close.add_argument("--preventive-action", dest="preventive_actions", action="append", default=[]) + quick_close.add_argument( + "--preventive-action", dest="preventive_actions", action="append", default=[] + ) quick_close.add_argument("--why-this-problem-recurred", default="") intake = subparsers.add_parser( @@ -1378,7 +1488,9 @@ def _build_parser() -> argparse.ArgumentParser: compare.add_argument("--candidate-shape", required=True, choices=CANDIDATE_SHAPES) compare.add_argument("--decomposition-decision", required=True, choices=DECOMPOSITION_DECISIONS) compare.add_argument("--adoption-unit", required=True) - compare.add_argument("--discovered-subtarget", dest="discovered_subtargets", action="append", default=[]) + compare.add_argument( + "--discovered-subtarget", dest="discovered_subtargets", action="append", default=[] + ) compare.add_argument("--recommended-next-candidate", default="") compare.add_argument("--target-project-profile-json", required=True) compare.add_argument("--compatibility-diagnosis-json", required=True) @@ -1427,10 +1539,14 @@ def _build_parser() -> argparse.ArgumentParser: close.add_argument("--evidence-ref", dest="evidence_refs", action="append", default=[]) close.add_argument("--reopen-condition", default="") close.add_argument("--next-action", required=True) - close.add_argument("--recurring-control-decision", required=True, choices=RECURRING_CONTROL_DECISIONS) + close.add_argument( + "--recurring-control-decision", required=True, choices=RECURRING_CONTROL_DECISIONS + ) close.add_argument("--decision-owner", default=None) close.add_argument("--root-cause-hypothesis", required=True) - close.add_argument("--preventive-action", dest="preventive_actions", action="append", required=True) + close.add_argument( + "--preventive-action", dest="preventive_actions", action="append", required=True + ) close.add_argument("--why-this-problem-recurred", required=True) summary = subparsers.add_parser( @@ -1508,7 +1624,9 @@ def _build_parser() -> argparse.ArgumentParser: _project_boundary_args(watch_cmd) watch_cmd.add_argument("--candidate", required=True) watch_cmd.add_argument("--interest-reason", required=True) - _scene_arg(watch_cmd, required=False, default="", help_text="optional scene lane if already known") + _scene_arg( + watch_cmd, required=False, default="", help_text="optional scene lane if already known" + ) block_cmd = subparsers.add_parser( "block", @@ -1532,6 +1650,16 @@ def _build_parser() -> argparse.ArgumentParser: _scene_arg(unblock_cmd, required=True, help_text="scene lane to unblock") unblock_cmd.add_argument("--why-unblocked", required=True) + reject_cmd = subparsers.add_parser( + "reject", + help="reject a candidate before trial, or after a hold, without a trial verdict", + description="Create a reject-note for a proposed / blocked / hold scene lane. Terminal for the scene.", + ) + reject_cmd.add_argument("--artifact-root", default=_DEFAULT_ARTIFACT_ROOT, metavar="DIR") + _project_boundary_args(reject_cmd) + _scene_arg(reject_cmd, required=True, help_text="scene lane to reject") + reject_cmd.add_argument("--reject-reason", required=True) + deprecate_cmd = subparsers.add_parser( "deprecate", help="begin retirement of a promoted tool", @@ -1541,7 +1669,9 @@ def _build_parser() -> argparse.ArgumentParser: _project_boundary_args(deprecate_cmd) _scene_arg(deprecate_cmd, required=True, help_text="scene lane to retire") deprecate_cmd.add_argument("--retirement-reason", required=True) - deprecate_cmd.add_argument("--replacement-candidate", dest="replacement_candidates", action="append", required=True) + deprecate_cmd.add_argument( + "--replacement-candidate", dest="replacement_candidates", action="append", required=True + ) deprecate_cmd.add_argument("--timeline", required=True) migrate_cmd = subparsers.add_parser( @@ -1586,7 +1716,10 @@ def _build_parser() -> argparse.ArgumentParser: _scene_arg(couple_cmd, required=True, help_text="scene lane this coupling snapshot belongs to") couple_cmd.add_argument("--tool", required=True) couple_cmd.add_argument( - "--couple", dest="couples", action="append", default=[], + "--couple", + dest="couples", + action="append", + default=[], metavar="PATH|TYPE|COST[|NOTE]", help="one coupling entry, pipe-delimited; repeatable", ) @@ -1631,7 +1764,13 @@ def _build_parser() -> argparse.ArgumentParser: scan_cmd.add_argument("--artifact-root", default=_DEFAULT_ARTIFACT_ROOT, metavar="DIR") scan_cmd.add_argument("--target", required=True, metavar="DIR", help="directory to scan") scan_cmd.add_argument("--tool", required=True, help="tool name to detect (case-insensitive)") - _scene_arg(scan_cmd, required=False, default=None, metavar="SCENE", help_text="optional scene lane label for the resulting coupling note") + _scene_arg( + scan_cmd, + required=False, + default=None, + metavar="SCENE", + help_text="optional scene lane label for the resulting coupling note", + ) scan_cmd.add_argument( "--exclude", dest="excludes", @@ -1640,7 +1779,9 @@ def _build_parser() -> argparse.ArgumentParser: metavar="PATH", help="path prefix or glob to skip; repeatable", ) - scan_cmd.add_argument("--record", action="store_true", help="write the detected coupling set as a coupling-note") + scan_cmd.add_argument( + "--record", action="store_true", help="write the detected coupling set as a coupling-note" + ) scan_cmd.add_argument("--json", action="store_true", help="emit raw JSON coupling list") next_cmd = subparsers.add_parser( @@ -1650,6 +1791,24 @@ def _build_parser() -> argparse.ArgumentParser: ) next_cmd.add_argument("--artifact-root", default=_DEFAULT_ARTIFACT_ROOT, metavar="DIR") + aggregate_cmd = subparsers.add_parser( + "aggregate", + help="aggregate lifecycle states across multiple artifact roots (read-only)", + description=( + "Cross-project portfolio view: for each --root, list its scene lanes, " + "the tool, and the current lifecycle state. Read-only; writes nothing." + ), + ) + aggregate_cmd.add_argument( + "--root", + dest="roots", + action="append", + required=True, + metavar="DIR", + help="artifact root to include; repeatable", + ) + aggregate_cmd.add_argument("--json", action="store_true") + return parser @@ -1698,8 +1857,12 @@ def _handle_compare(args: argparse.Namespace) -> dict[str, Any]: parent = artifacts.latest_by_type(root, CANDIDATE_INTAKE_NOTE, scene=args.scene) if not parent: raise AdopValidationError("candidate-intake-note for scene not found", 5) - target_project_profile = _parse_json_arg(args.target_project_profile_json, "target_project_profile_json") - compatibility_diagnosis = _parse_json_arg(args.compatibility_diagnosis_json, "compatibility_diagnosis_json") + target_project_profile = _parse_json_arg( + args.target_project_profile_json, "target_project_profile_json" + ) + compatibility_diagnosis = _parse_json_arg( + args.compatibility_diagnosis_json, "compatibility_diagnosis_json" + ) no_impact_envelope = ( _parse_json_arg(args.no_impact_envelope_json, "no_impact_envelope_json") if args.no_impact_envelope_json @@ -1710,7 +1873,11 @@ def _handle_compare(args: argparse.Namespace) -> dict[str, Any]: if isinstance(item, dict) and str(item.get("adoption_unit")) == args.adoption_unit: recommended_fit_lane = str(item.get("recommended_fit_lane", "")) break - if not recommended_fit_lane and compatibility_diagnosis and isinstance(compatibility_diagnosis[0], dict): + if ( + not recommended_fit_lane + and compatibility_diagnosis + and isinstance(compatibility_diagnosis[0], dict) + ): recommended_fit_lane = str(compatibility_diagnosis[0].get("recommended_fit_lane", "")) artifact_id = next_sequential_id(root, "cmp") derived_from = [parent["artifact_id"]] @@ -1819,6 +1986,7 @@ def _handle_start_trial(args: argparse.Namespace) -> dict[str, Any]: "dependency_note": "", "failure_mode_hypothesis": [], } + def _build_packet_payload(artifact_id: str) -> dict[str, Any]: payload = dict(base_payload) payload["artifact_id"] = artifact_id @@ -1840,7 +2008,10 @@ def _handle_close_trial(args: argparse.Namespace) -> dict[str, Any]: if not packet: raise AdopValidationError("trial-packet not found", 5) # Detect double-close before building any payloads (exit 5 = readiness gate). - if any(str(r.get("artifact_id")) == args.trial_id for r in artifacts.find_by_type(root, TRIAL_RESULT)): + if any( + str(r.get("artifact_id")) == args.trial_id + for r in artifacts.find_by_type(root, TRIAL_RESULT) + ): raise AdopValidationError(f"trial {args.trial_id} already closed", 5) close_payload = { "verdict": args.verdict, @@ -1864,13 +2035,16 @@ def _handle_close_trial(args: argparse.Namespace) -> dict[str, Any]: } validate_close_payload(close_payload) if args.verdict == "promote": - latest_intake = artifacts.latest_by_type(root, CANDIDATE_INTAKE_NOTE, scene=str(packet.get("related_scene", ""))) + latest_intake = artifacts.latest_by_type( + root, CANDIDATE_INTAKE_NOTE, scene=str(packet.get("related_scene", "")) + ) if not latest_intake: raise AdopValidationError("promote requires candidate-intake-note history", 7) unknowns = unknown_tool_attribute_fields(latest_intake) if unknowns: raise AdopValidationError( - "promote requires known tool attributes in the latest intake: " + ", ".join(unknowns), + "promote requires known tool attributes in the latest intake: " + + ", ".join(unknowns), 7, ) promote_errors = promote_gate_errors(packet, close_payload) @@ -2086,16 +2260,26 @@ def _handle_unblock(args: argparse.Namespace) -> dict[str, Any]: "intended_lane": LANES[1], "intake_reason": args.why_unblocked, "current_disposition": PROPOSED, - "candidate_shape": str(prior_intake.get("candidate_shape", "unknown")) if prior_intake else "unknown", + "candidate_shape": str(prior_intake.get("candidate_shape", "unknown")) + if prior_intake + else "unknown", "next_action": "compare candidate set", ROOT_CAUSE_HYPOTHESIS: args.why_unblocked, "derived_from": [blocked["artifact_id"]], - "platform": str((prior_intake or {}).get("platform", _default_tool_attributes()["platform"])), + "platform": str( + (prior_intake or {}).get("platform", _default_tool_attributes()["platform"]) + ), "license": str((prior_intake or {}).get("license", _default_tool_attributes()["license"])), "cost": str((prior_intake or {}).get("cost", _default_tool_attributes()["cost"])), "version": str((prior_intake or {}).get("version", _default_tool_attributes()["version"])), - "category": str((prior_intake or {}).get("category", _default_tool_attributes()["category"])), - "ai_compatibility": str((prior_intake or {}).get("ai_compatibility", _default_tool_attributes()["ai_compatibility"])), + "category": str( + (prior_intake or {}).get("category", _default_tool_attributes()["category"]) + ), + "ai_compatibility": str( + (prior_intake or {}).get( + "ai_compatibility", _default_tool_attributes()["ai_compatibility"] + ) + ), "data_flow": (prior_intake or {}).get("data_flow", _default_tool_attributes()["data_flow"]), } validate_intake_payload(payload) @@ -2103,14 +2287,54 @@ def _handle_unblock(args: argparse.Namespace) -> dict[str, Any]: return artifacts.json_response("unblock", "ok", [path.name], []) +def _handle_reject(args: argparse.Namespace) -> dict[str, Any]: + root = _prepare_artifact_root(args) + _ensure_scene_not_rejected(root, args.scene, command="reject") + if get_scene_states(root).get(args.scene) == "in-trial": + raise AdopValidationError( + "scene is in an open trial; reject it with 'close-trial --verdict reject' instead", 7 + ) + hold = artifacts.latest_by_type(root, HOLD_NOTE, scene=args.scene) + blocked = artifacts.latest_by_type(root, BLOCKED_NOTE, scene=args.scene) + comparison = artifacts.latest_by_type(root, COMPARISON_NOTE, scene=args.scene) + intake = artifacts.latest_by_type(root, CANDIDATE_INTAKE_NOTE, scene=args.scene) + parent = hold or blocked or comparison or intake + if not parent: + raise AdopValidationError("no intake/blocked/hold history for scene to reject", 5) + tool = (comparison or {}).get("selected_candidate") or str( + (intake or {}).get("candidate_or_tool", "") + ) + artifact_id = next_sequential_id(root, "rj") + payload = { + "schema_version": SCHEMA_VERSION, + "artifact_type": REJECT_NOTE, + "artifact_id": artifact_id, + "status": "closed", + "created_at": today_iso(), + "recording_mode": "explicit", + "recording_source": "manual-cli", + "related_scene": args.scene, + "candidate_or_tool": tool, + "derived_from": [parent["artifact_id"]], + "reject_reason": args.reject_reason, + "drawbacks": [], + } + path = artifacts.write_artifact(root, REJECT_NOTE, artifact_id, payload) + return artifacts.json_response("reject", "ok", [path.name], []) + + def _handle_deprecate(args: argparse.Namespace) -> dict[str, Any]: root = _prepare_artifact_root(args) parent = artifacts.latest_by_type(root, PROMOTION_NOTE, scene=args.scene) if not parent: - raise AdopValidationError("promotion-note for scene not found; tool must be promoted before deprecation", 5) + raise AdopValidationError( + "promotion-note for scene not found; tool must be promoted before deprecation", 5 + ) cmp = artifacts.latest_by_type(root, COMPARISON_NOTE, scene=args.scene) intake = artifacts.latest_by_type(root, CANDIDATE_INTAKE_NOTE, scene=args.scene) - tool_name = (cmp or {}).get("selected_candidate") or str((intake or {}).get("candidate_or_tool", "")) + tool_name = (cmp or {}).get("selected_candidate") or str( + (intake or {}).get("candidate_or_tool", "") + ) artifact_id = next_sequential_id(root, "dp") payload = { "schema_version": SCHEMA_VERSION, @@ -2134,7 +2358,9 @@ def _handle_migrate(args: argparse.Namespace) -> dict[str, Any]: root = _prepare_artifact_root(args) parent = artifacts.latest_by_type(root, DEPRECATION_NOTE, scene=args.scene) if not parent: - raise AdopValidationError("deprecation-note for scene not found; tool must be deprecated before migration", 5) + raise AdopValidationError( + "deprecation-note for scene not found; tool must be deprecated before migration", 5 + ) artifact_id = next_sequential_id(root, "mg") payload = { "schema_version": SCHEMA_VERSION, @@ -2160,7 +2386,8 @@ def _handle_archive(args: argparse.Namespace) -> dict[str, Any]: parent = migration or deprecation if not parent: raise AdopValidationError( - "deprecation-note or migration-note for scene not found; tool must be deprecated before archiving", 5 + "deprecation-note or migration-note for scene not found; tool must be deprecated before archiving", + 5, ) artifact_id = next_sequential_id(root, "ar") payload: dict[str, Any] = { @@ -2223,7 +2450,8 @@ def _worst_removal_cost(couplings: list[dict[str, Any]]) -> str: def _latest_coupling_notes(root: Path, scene: str | None) -> list[dict[str, Any]]: """Latest coupling-note per (tool, scene) — each note is a full snapshot.""" notes = [ - item for item in artifacts.load_all_artifacts(root) + item + for item in artifacts.load_all_artifacts(root) if item.get("artifact_type") == COUPLING_NOTE and (scene is None or str(item.get("related_scene", "")) == scene) ] @@ -2317,9 +2545,14 @@ def _handle_quick_compare(args: argparse.Namespace) -> dict[str, Any]: args.recommended_next_candidate = "" args.owner = None args.adoption_unit = args.adoption_unit or args.selected - args.root_cause_hypothesis = args.root_cause_hypothesis or f"the use case '{args.scene}' still depends on ad hoc operator judgment" + args.root_cause_hypothesis = ( + args.root_cause_hypothesis + or f"the use case '{args.scene}' still depends on ad hoc operator judgment" + ) if not args.target_project_profile_json: - args.target_project_profile_json = json.dumps(_simple_project_profile_default(), ensure_ascii=False) + args.target_project_profile_json = json.dumps( + _simple_project_profile_default(), ensure_ascii=False + ) if not args.compatibility_diagnosis_json: args.compatibility_diagnosis_json = json.dumps( _simple_compatibility_diagnosis_default(args.adoption_unit), @@ -2416,9 +2649,17 @@ def _handle_init(args: argparse.Namespace) -> str: overlay_path = Path(args.overlay) created_overlay = not overlay_path.exists() if created_overlay: + # Create the overlay's parent directory so a nested --overlay path does not + # crash with a raw FileNotFoundError from copy/write. + if overlay_path.parent != Path(""): + overlay_path.parent.mkdir(parents=True, exist_ok=True) candidates = ( Path(__file__).parent.parent / "templates" / "project-local-adop-overlay-template.md", - Path(sys.prefix) / "share" / "adop" / "templates" / "project-local-adop-overlay-template.md", + Path(sys.prefix) + / "share" + / "adop" + / "templates" + / "project-local-adop-overlay-template.md", ) for candidate in candidates: if candidate.exists(): @@ -2428,14 +2669,18 @@ def _handle_init(args: argparse.Namespace) -> str: overlay_path.write_text(_OVERLAY_INIT_STUB, encoding="utf-8") lines = ["ADOP initialized.", ""] - lines.append(f" Artifact root : {root}/{' (created)' if created_root else ' (already exists)'}") - lines.append(f" Overlay file : {overlay_path}{' (created)' if created_overlay else ' (already exists)'}") + lines.append( + f" Artifact root : {root}/{' (created)' if created_root else ' (already exists)'}" + ) + lines.append( + f" Overlay file : {overlay_path}{' (created)' if created_overlay else ' (already exists)'}" + ) lines += [ "", "Next steps:", ' adop watch --candidate --interest-reason "watching to evaluate"', ' adop quick-intake --candidate --source doc --scene --why-now ""', - ' adop status', + " adop status", ] return "\n".join(lines) @@ -2477,7 +2722,11 @@ def _handle_status(args: argparse.Namespace) -> str: lines.append(f" {tool} @ {sc}: {len(entries)} file(s), max detachment: {worst}") else: tool_hint = next( - (str(i.get("candidate_or_tool", "")) for i in items if i.get("candidate_or_tool")), + ( + str(i.get("candidate_or_tool", "")) + for i in items + if i.get("candidate_or_tool") + ), "", ) scene_hint = next(iter(sorted(scene_states)), "") @@ -2500,21 +2749,28 @@ def _handle_scan(args: argparse.Namespace) -> tuple[int, str]: target = Path(args.target) if not target.is_dir(): raise AdopValidationError(f"scan target is not a directory: {target}", 2) - excludes = [_normalize_scan_pattern(raw) for raw in getattr(args, "excludes", []) if str(raw).strip()] + excludes = [ + _normalize_scan_pattern(raw) for raw in getattr(args, "excludes", []) if str(raw).strip() + ] scene = getattr(args, "scene", None) or "" couplings = _scan_target_for_tool(target, args.tool, excludes) artifact_ref = "" if not couplings: if getattr(args, "record", False): - return 0, f"No references to '{args.tool}' found in {target}/\nNo coupling note was written." + return ( + 0, + f"No references to '{args.tool}' found in {target}/\nNo coupling note was written.", + ) return 0, f"No references to '{args.tool}' found in {target}/" if getattr(args, "record", False): if getattr(args, "scene", None) is None: raise AdopValidationError("scan --record requires --scene", 2) root = _prepare_artifact_root(args) - artifact_ref = _write_coupling_note(root, scene=args.scene, tool=args.tool, couplings=couplings).name + artifact_ref = _write_coupling_note( + root, scene=args.scene, tool=args.tool, couplings=couplings + ).name if getattr(args, "json", False): if artifact_ref: @@ -2544,19 +2800,92 @@ def _handle_scan(args: argparse.Namespace) -> tuple[int, str]: lines += ["", f"Recorded coupling snapshot: {artifact_ref}"] return 0, "\n".join(lines) - lines += ["", "Scan output is advisory only. No canonical artifact is written until `adop couple` or `adop scan --record` runs.", ""] + lines += [ + "", + "Scan output is advisory only. No canonical artifact is written until `adop couple` or `adop scan --record` runs.", + "", + ] direct = [f"adop scan --target {args.target} --tool {args.tool}"] if getattr(args, "scene", None): direct.append(f"--scene {scene}") for excluded in excludes: direct.append(f"--exclude {excluded}") direct.append("--record") - lines += ["Record directly next time:", f" {' '.join(direct)}", "", "Or create a manual snapshot:", f" adop couple --scene {scene} --tool {args.tool} \\"] + lines += [ + "Record directly next time:", + f" {' '.join(direct)}", + "", + "Or create a manual snapshot:", + f" adop couple --scene {scene} --tool {args.tool} \\", + ] for entry in couplings: np = f"|{entry['note']}" if entry.get("note") else "" - lines.append(f" --couple '{entry['path']}|{entry['coupling_type']}|{entry['removal_cost']}{np}' \\") - lines += ["", "Or as JSON:", f" adop scan --target {args.target} --tool {args.tool} --json > couplings.json"] - lines.append(f" adop couple --scene {scene} --tool {args.tool} --couplings-json @couplings.json") + lines.append( + f" --couple '{entry['path']}|{entry['coupling_type']}|{entry['removal_cost']}{np}' \\" + ) + lines += [ + "", + "Or as JSON:", + f" adop scan --target {args.target} --tool {args.tool} --json > couplings.json", + ] + lines.append( + f" adop couple --scene {scene} --tool {args.tool} --couplings-json @couplings.json" + ) + return 0, "\n".join(lines) + + +def _aggregate_scene_tool(items: list[dict[str, Any]], scene: str) -> str: + """Best tool name for a scene: comparison's selected_candidate, else latest candidate_or_tool.""" + scene_items = sorted( + (i for i in items if str(i.get("related_scene", "")) == scene), + key=_artifact_numeric_sort_key, + ) + for item in reversed(scene_items): + tool = item.get("selected_candidate") or item.get("candidate_or_tool") + if tool: + return str(tool) + return "-" + + +def _handle_aggregate(args: argparse.Namespace) -> tuple[int, dict[str, Any] | str]: + portfolio: list[dict[str, Any]] = [] + for raw in args.roots: + root = Path(raw) + if not root.exists(): + portfolio.append( + {"root": str(root), "scene": None, "tool": None, "state": "MISSING_ROOT"} + ) + continue + items = artifacts.load_all_artifacts(root) + for scene, state in sorted(get_scene_states(root).items()): + portfolio.append( + { + "root": str(root), + "scene": scene, + "tool": _aggregate_scene_tool(items, scene), + "state": state, + } + ) + if getattr(args, "json", False): + return 0, { + "schema_version": SCHEMA_VERSION, + "command": "aggregate", + "status": "ok", + "count": len(portfolio), + "portfolio": portfolio, + } + if not portfolio: + return 0, "ADOP Portfolio\n(no scenes across the given roots)" + lines = ["ADOP Portfolio (across roots)"] + current_root = None + for row in portfolio: + if row["root"] != current_root: + current_root = row["root"] + lines.append(f"\n{current_root}/") + if row["scene"] is None: + lines.append(f" ! {row['state']}") + else: + lines.append(f" {row['scene']}: {row['state']} ({row['tool']})") return 0, "\n".join(lines) @@ -2569,7 +2898,15 @@ def _handle_next(args: argparse.Namespace) -> str: return 'No records yet — start: adop quick-intake --candidate --source doc --scene --why-now ""' terminal = {"promote", "archived", "reject"} - priority = {"in-trial": 0, "proposed": 1, "trial-ready": 2, "watch": 3, "blocked": 4, "deprecated": 5, "migrating": 6} + priority = { + "in-trial": 0, + "proposed": 1, + "trial-ready": 2, + "watch": 3, + "blocked": 4, + "deprecated": 5, + "migrating": 6, + } active = sorted( [(sc, st) for sc, st in scene_states.items() if st not in terminal], key=lambda x: (priority.get(x[1], 99), x[0]), @@ -2793,6 +3130,9 @@ def main(argv: list[str] | None = None) -> int: if args.command == "unblock": _emit(_handle_unblock(args)) return 0 + if args.command == "reject": + _emit(_handle_reject(args)) + return 0 if args.command == "deprecate": _emit(_handle_deprecate(args)) return 0 @@ -2825,6 +3165,13 @@ def main(argv: list[str] | None = None) -> int: if args.command == "next": print(_handle_next(args)) return 0 + if args.command == "aggregate": + exit_code, payload = _handle_aggregate(args) + if isinstance(payload, str): + print(payload) + else: + _emit(payload) + return exit_code raise AdopValidationError(f"unsupported command: {args.command}", 2) except AdopValidationError as exc: _emit(artifacts.json_response(args.command, "error", [], [str(exc)])) diff --git a/shared/python/adop_html.py b/shared/python/adop_html.py index acfe1f9..6a484c1 100644 --- a/shared/python/adop_html.py +++ b/shared/python/adop_html.py @@ -9,14 +9,9 @@ from pathlib import Path from typing import Any -try: - from .adop_artifacts import AdopArtifactError, load_all_artifacts - from .adop_ids import parse_numeric_id - from .adop_summary import get_scene_states -except ImportError: # pragma: no cover - script import path - from adop_artifacts import AdopArtifactError, load_all_artifacts - from adop_ids import parse_numeric_id - from adop_summary import get_scene_states +from adop_artifacts import AdopArtifactError, load_all_artifacts +from adop_ids import parse_numeric_id +from adop_summary import get_scene_states _TEMPLATE_NAME = "adop-governance-dashboard-template.html" @@ -173,7 +168,11 @@ ], "artifacts": [ {"type": "trial-result", "id": "tr-003", "purpose": "captures doc generation outcome"}, - {"type": "promotion-note", "id": "pm-003", "purpose": "marks the doc decision as promoted"}, + { + "type": "promotion-note", + "id": "pm-003", + "purpose": "marks the doc decision as promoted", + }, ], "raw_artifacts": [], "timeline": [], @@ -198,7 +197,11 @@ {"label": "Observed effect", "value": "deployment annotations remained bounded"}, ], "artifacts": [ - {"type": "promotion-note", "id": "pm-004", "purpose": "records the approved observability decision"}, + { + "type": "promotion-note", + "id": "pm-004", + "purpose": "records the approved observability decision", + }, ], "raw_artifacts": [], "timeline": [], @@ -222,7 +225,11 @@ {"label": "Trial readiness", "value": "executor, gate, and boundary are defined"}, ], "artifacts": [ - {"type": "trial-packet", "id": "tr-004", "purpose": "declares trial scope and controls"}, + { + "type": "trial-packet", + "id": "tr-004", + "purpose": "declares trial scope and controls", + }, ], "raw_artifacts": [], "timeline": [], @@ -321,7 +328,11 @@ {"label": "Why now", "value": "dependency update burden keeps recurring"}, ], "artifacts": [ - {"type": "candidate-intake-note", "id": "ci-004", "purpose": "records the proposed decision"}, + { + "type": "candidate-intake-note", + "id": "ci-004", + "purpose": "records the proposed decision", + }, ], "raw_artifacts": [], "timeline": [], @@ -333,7 +344,7 @@ "decision": "Observed need; not yet narrowed to a trial decision", "landing_target": "docs/editorial", "control_model": "No execution authority granted", - "last_evidence": "Watch note wa-001", + "last_evidence": "Watch note wt-001", "kind_meta": "cli / MIT / prose lint", "why_it_matters": "ADOP can show interest without pretending a decision exists.", "why_this_state": "Only watch-level evidence exists, so the review remains before intake.", @@ -342,10 +353,13 @@ "allowed": ["watch entry"], "forbidden": ["trial start", "writeback authority"], "rationale": [ - {"label": "Interest reason", "value": "editorial consistency is becoming a recurring problem"}, + { + "label": "Interest reason", + "value": "editorial consistency is becoming a recurring problem", + }, ], "artifacts": [ - {"type": "watch-note", "id": "wa-001", "purpose": "records early interest only"}, + {"type": "watch-note", "id": "wt-001", "purpose": "records early interest only"}, ], "raw_artifacts": [], "timeline": [], @@ -369,7 +383,11 @@ {"label": "Archive reason", "value": "migration away from tslint is complete"}, ], "artifacts": [ - {"type": "archive-note", "id": "ar-001", "purpose": "records final closure of the decision"}, + { + "type": "archive-note", + "id": "ar-001", + "purpose": "records final closure of the decision", + }, ], "raw_artifacts": [], "timeline": [], @@ -390,10 +408,17 @@ "allowed": ["historical reference", "planned migration work"], "forbidden": ["treating this decision as the current approved standard"], "rationale": [ - {"label": "Retirement reason", "value": "the successor decision is replacing the old lint path"}, + { + "label": "Retirement reason", + "value": "the successor decision is replacing the old lint path", + }, ], "artifacts": [ - {"type": "deprecation-note", "id": "dp-001", "purpose": "records the retirement decision"}, + { + "type": "deprecation-note", + "id": "dp-001", + "purpose": "records the retirement decision", + }, ], "raw_artifacts": [], "timeline": [], @@ -414,10 +439,17 @@ "allowed": ["historical lookup"], "forbidden": ["reopening the same rejected scene"], "rationale": [ - {"label": "Reject reason", "value": "cost and fit did not justify adoption for this scene"}, + { + "label": "Reject reason", + "value": "cost and fit did not justify adoption for this scene", + }, ], "artifacts": [ - {"type": "reject-note", "id": "rj-001", "purpose": "records terminal rejection for this use case"}, + { + "type": "reject-note", + "id": "rj-001", + "purpose": "records terminal rejection for this use case", + }, ], "raw_artifacts": [], "timeline": [], @@ -448,10 +480,7 @@ def _load_template() -> str: def _strip_runtime_metadata(item: dict[str, Any]) -> dict[str, Any]: - return { - key: value for key, value in item.items() - if key not in {"_path", "_adop_path"} - } + return {key: value for key, value in item.items() if key not in {"_path", "_adop_path"}} def _latest(items: list[dict[str, Any]], artifact_type: str) -> dict[str, Any] | None: @@ -484,18 +513,22 @@ def _lane_meta(scene_items: list[dict[str, Any]]) -> str: return "decision record" flow = intake.get("data_flow") or {} destination = _pick_first(flow.get("destination"), "unspecified flow") - return " / ".join([ - _pick_first(intake.get("category"), "category ?"), - _pick_first(intake.get("license"), "license ?"), - destination, - ]) + return " / ".join( + [ + _pick_first(intake.get("category"), "category ?"), + _pick_first(intake.get("license"), "license ?"), + destination, + ] + ) def _scene_label(scene: str) -> str: words = [part for part in scene.replace("_", "-").split("-") if part] if not words: return scene - return " ".join(word.upper() if len(word) <= 3 else word[:1].upper() + word[1:] for word in words) + return " ".join( + word.upper() if len(word) <= 3 else word[:1].upper() + word[1:] for word in words + ) def _landing_target(scene_items: list[dict[str, Any]]) -> str: @@ -556,7 +589,7 @@ def _decision_text(state: str, scene_items: list[dict[str, Any]], landing_target return "Trial is running and waiting for a decision." if state == "blocked": return _pick_first( - (_latest(scene_items, "blocked-note") or {}).get("blocking_reason"), + (_latest(scene_items, "blocked-note") or {}).get("block_reason"), "Blocked until someone makes an explicit unblock decision.", ) if state == "proposed": @@ -564,15 +597,36 @@ def _decision_text(state: str, scene_items: list[dict[str, Any]], landing_target if state == "watch": return "On the radar, but no active decision exists yet." if state == "deprecated": - return "A retirement path is recorded for this decision." + return _pick_first( + (_latest(scene_items, "deprecation-note") or {}).get("retirement_reason"), + "A retirement path is recorded for this decision.", + ) if state == "migrating": - return "Replacement work is actively in progress." + target = _pick_first((_latest(scene_items, "migration-note") or {}).get("migration_target")) + return ( + f"Replacement in progress; migrating to {target}." + if target != "-" + else "Replacement work is actively in progress." + ) if state == "archived": + archive = _latest(scene_items, "archive-note") or {} + end = _pick_first(archive.get("end_date")) + successor = _pick_first(archive.get("successor_tool")) + if end != "-" and successor != "-": + return f"Closed on {end}; succeeded by {successor}." + if end != "-": + return f"Closed and archived on {end}." return "This decision is closed and archived." if state == "hold": - return "The trial closed on hold, and the decision is paused." + return _pick_first( + (_latest(scene_items, "hold-note") or {}).get("hold_reason"), + "The trial closed on hold, and the decision is paused.", + ) if state == "reject": - return "The trial closed with a reject decision." + return _pick_first( + (_latest(scene_items, "reject-note") or {}).get("reject_reason"), + "This decision ended in rejection for this use case.", + ) return "A lifecycle state is recorded." @@ -591,33 +645,79 @@ def _control_model(scene_items: list[dict[str, Any]]) -> str: return f"allowed {len(allowed)} / forbidden {len(forbidden)} controls recorded" packet = _latest(scene_items, "trial-packet") if packet: - return ", ".join([ - part for part in ( - _pick_first(packet.get("mutation_boundary")), - _pick_first(packet.get("verification_method")), + return ( + ", ".join( + [ + part + for part in ( + _pick_first(packet.get("mutation_boundary")), + _pick_first(packet.get("verification_method")), + ) + if part != "-" + ] ) - if part != "-" - ]) or "trial boundary recorded" + or "trial boundary recorded" + ) return "No usage limits are recorded yet." def _allowed_forbidden(scene_items: list[dict[str, Any]]) -> tuple[list[str], list[str]]: - judgment = _latest(scene_items, "judgment-report") - if judgment: - envelope = judgment.get("no_impact_envelope") or {} + # Prefer the closed-trial judgment envelope; fall back to the open trial + # packet's envelope so in-trial / trial-ready lanes still show real limits. + for artifact_type in ("judgment-report", "trial-packet"): + source = _latest(scene_items, artifact_type) + if not source: + continue + envelope = source.get("no_impact_envelope") or {} allowed = [str(item) for item in envelope.get("allowed") or []] forbidden = [str(item) for item in envelope.get("forbidden") or []] - return allowed, forbidden - state = "" - if scene_items: - state = str(scene_items[-1].get("status", "")) + if allowed or forbidden: + return allowed, forbidden return ( - ["No explicit allow-list recorded yet"] if state else ["No explicit allow-list recorded yet"], + ["No explicit allow-list recorded yet"], ["No explicit deny-list recorded yet"], ) def _rationale(scene_items: list[dict[str, Any]]) -> list[dict[str, str]]: + # Retirement-tail notes win over the (older) promote judgment-report: once a + # lane is deprecated/migrating/archived, the relevant reasoning is the + # retirement note, not the promotion that preceded it. + archive = _latest(scene_items, "archive-note") + if archive: + rows = [ + ("End date", archive.get("end_date")), + ("Successor tool", archive.get("successor_tool")), + ] + surfaced = [ + {"label": label, "value": _pick_first(value)} + for label, value in rows + if _pick_first(value) != "-" + ] + return surfaced or [ + {"label": "Archived", "value": "This decision is closed and kept as history."} + ] + migration = _latest(scene_items, "migration-note") + if migration: + return [ + {"label": "Migration target", "value": _pick_first(migration.get("migration_target"))}, + {"label": "Migration plan", "value": _pick_first(migration.get("migration_plan"))}, + ] + deprecation = _latest(scene_items, "deprecation-note") + if deprecation: + rows = [ + ("Retirement reason", deprecation.get("retirement_reason")), + ( + "Replacement candidates", + ", ".join(str(x) for x in deprecation.get("replacement_candidates") or []), + ), + ("Timeline", deprecation.get("timeline")), + ] + return [ + {"label": label, "value": _pick_first(value)} + for label, value in rows + if _pick_first(value) != "-" + ] judgment = _latest(scene_items, "judgment-report") if judgment: rows = [ @@ -625,30 +725,48 @@ def _rationale(scene_items: list[dict[str, Any]]) -> list[dict[str, str]]: ("Observed effect", judgment.get("observed_effect_summary")), ("Root-cause hypothesis", judgment.get("root_cause_hypothesis")), ("Why this problem recurred", judgment.get("why_this_problem_recurred")), - ("Preventive action", ", ".join(str(x) for x in judgment.get("preventive_action") or [])), + ( + "Preventive action", + ", ".join(str(x) for x in judgment.get("preventive_action") or []), + ), ("Recurring control decision", judgment.get("recurring_control_decision")), ] - return [{"label": label, "value": _pick_first(value)} for label, value in rows if _pick_first(value) != "-"] - intake = _latest(scene_items, "candidate-intake-note") + return [ + {"label": label, "value": _pick_first(value)} + for label, value in rows + if _pick_first(value) != "-" + ] + reject_note = _latest(scene_items, "reject-note") + if reject_note: + return [{"label": "Reject reason", "value": _pick_first(reject_note.get("reject_reason"))}] blocked = _latest(scene_items, "blocked-note") if blocked: - return [{"label": "Block reason", "value": _pick_first(blocked.get("blocking_reason"), blocked.get("reason"))}] + return [{"label": "Block reason", "value": _pick_first(blocked.get("block_reason"))}] + intake = _latest(scene_items, "candidate-intake-note") if intake: return [ - {"label": "Why now", "value": _pick_first(intake.get("reason"))}, - {"label": "Root-cause hypothesis", "value": _pick_first(intake.get("root_cause_hypothesis"))}, + {"label": "Why now", "value": _pick_first(intake.get("intake_reason"))}, + { + "label": "Root-cause hypothesis", + "value": _pick_first(intake.get("root_cause_hypothesis")), + }, ] + watch = _latest(scene_items, "watch-note") + if watch: + return [{"label": "Interest reason", "value": _pick_first(watch.get("interest_reason"))}] return [{"label": "Status", "value": "No reason fields are available yet for this decision."}] def _artifacts(scene_items: list[dict[str, Any]]) -> list[dict[str, str]]: rows = [] for item in sorted(scene_items, key=_id_sort_key): - rows.append({ - "type": str(item.get("artifact_type", "-")), - "id": str(item.get("artifact_id", "-")), - "purpose": _artifact_purpose(item), - }) + rows.append( + { + "type": str(item.get("artifact_type", "-")), + "id": str(item.get("artifact_id", "-")), + "purpose": _artifact_purpose(item), + } + ) return rows @@ -680,7 +798,9 @@ def _artifact_purpose(item: dict[str, Any]) -> str: def _timeline(scene_items: list[dict[str, Any]], state: str) -> list[dict[str, Any]]: - has = lambda artifact_type: _latest(scene_items, artifact_type) is not None + def has(artifact_type: str) -> bool: + return _latest(scene_items, artifact_type) is not None + return [ { "step": "Intake", @@ -710,14 +830,18 @@ def _timeline(scene_items: list[dict[str, Any]], state: str) -> list[dict[str, A ] -def _lane_summary(scene_items: list[dict[str, Any]], state: str, lane: dict[str, Any]) -> dict[str, str]: +def _lane_summary( + scene_items: list[dict[str, Any]], state: str, lane: dict[str, Any] +) -> dict[str, str]: judgment = _latest(scene_items, "judgment-report") next_text = _default_next_text(state, lane["landing_target"]) summary = { "headline": f"{lane['tool']} is in the {_state_label(state)} state for the {lane['scene']} use case.", "why_it_matters": "This keeps tool decisions reviewable instead of rediscovering them later.", "why_this_state": _decision_text(state, scene_items, str(lane["landing_target"])), - "what_happens_next": next_text if state == "promote" else _pick_first(judgment.get("next_action") if judgment else "", next_text), + "what_happens_next": next_text + if state == "promote" + else _pick_first(judgment.get("next_action") if judgment else "", next_text), "change_condition": _default_change_condition(state), } if judgment and judgment.get("judgment_reason"): @@ -779,87 +903,216 @@ def _next_command(scene: str, state: str, scene_items: list[dict[str, Any]]) -> def _command_details(state: str, scene: str) -> list[dict[str, str]]: if state == "watch": return [ - {"label": "Why this command", "value": "The tool is only being watched. Intake is the first command that turns interest into a recorded review."}, - {"label": "Use it when", "value": "You have decided the tool should enter formal review for this use case."}, - {"label": "Do not use it when", "value": "The team is still only collecting ideas and is not ready to open a real review."}, - {"label": "Result", "value": "ADOP records why the review is starting and moves this decision out of watch-only status."}, + { + "label": "Why this command", + "value": "The tool is only being watched. Intake is the first command that turns interest into a recorded review.", + }, + { + "label": "Use it when", + "value": "You have decided the tool should enter formal review for this use case.", + }, + { + "label": "Do not use it when", + "value": "The team is still only collecting ideas and is not ready to open a real review.", + }, + { + "label": "Result", + "value": "ADOP records why the review is starting and moves this decision out of watch-only status.", + }, ] if state == "proposed": return [ - {"label": "Why this command", "value": "The candidate exists, but ADOP still needs one selected option before trial planning can be trusted."}, - {"label": "Use it when", "value": "You are ready to narrow the review to one chosen candidate for this use case."}, - {"label": "Do not use it when", "value": "You still need to gather candidates or the selected tool is not decided yet."}, - {"label": "Result", "value": "ADOP records the chosen candidate and prepares the decision for bounded trial setup."}, + { + "label": "Why this command", + "value": "The candidate exists, but ADOP still needs one selected option before trial planning can be trusted.", + }, + { + "label": "Use it when", + "value": "You are ready to narrow the review to one chosen candidate for this use case.", + }, + { + "label": "Do not use it when", + "value": "You still need to gather candidates or the selected tool is not decided yet.", + }, + { + "label": "Result", + "value": "ADOP records the chosen candidate and prepares the decision for bounded trial setup.", + }, ] if state == "trial-ready": return [ - {"label": "Why this command", "value": "Comparison is complete, but ADOP still needs the trial boundary, owner, and target before execution begins."}, - {"label": "Use it when", "value": "The team is ready to start the bounded trial under explicit ownership and scope."}, - {"label": "Do not use it when", "value": "The trial owner, execution mode, or allowed use area is still undecided."}, - {"label": "Result", "value": "ADOP records the trial plan and moves the review into an executable trial phase."}, + { + "label": "Why this command", + "value": "Comparison is complete, but ADOP still needs the trial boundary, owner, and target before execution begins.", + }, + { + "label": "Use it when", + "value": "The team is ready to start the bounded trial under explicit ownership and scope.", + }, + { + "label": "Do not use it when", + "value": "The trial owner, execution mode, or allowed use area is still undecided.", + }, + { + "label": "Result", + "value": "ADOP records the trial plan and moves the review into an executable trial phase.", + }, ] if state == "in-trial": return [ - {"label": "Why this command", "value": "Trial output exists, but ADOP still needs the final decision to close the review properly."}, - {"label": "Use it when", "value": "The bounded trial has finished and you are ready to record promote, hold, or reject."}, - {"label": "Do not use it when", "value": "Evidence is still incomplete or the team has not decided the verdict yet."}, - {"label": "Result", "value": "ADOP records the outcome and moves the decision into its next explicit lifecycle state."}, + { + "label": "Why this command", + "value": "Trial output exists, but ADOP still needs the final decision to close the review properly.", + }, + { + "label": "Use it when", + "value": "The bounded trial has finished and you are ready to record promote, hold, or reject.", + }, + { + "label": "Do not use it when", + "value": "Evidence is still incomplete or the team has not decided the verdict yet.", + }, + { + "label": "Result", + "value": "ADOP records the outcome and moves the decision into its next explicit lifecycle state.", + }, ] if state == "blocked": return [ - {"label": "Why this command", "value": "This decision is blocked. Unblock exists to record that the blocking condition is actually resolved."}, - {"label": "Use it when", "value": "The blocker has been cleared and you need to record what changed."}, - {"label": "Do not use it when", "value": "The blocker still exists, or you are only reviewing the situation without resolving it."}, - {"label": "Result", "value": "ADOP removes the blocked state and reopens the decision for the next review step."}, + { + "label": "Why this command", + "value": "This decision is blocked. Unblock exists to record that the blocking condition is actually resolved.", + }, + { + "label": "Use it when", + "value": "The blocker has been cleared and you need to record what changed.", + }, + { + "label": "Do not use it when", + "value": "The blocker still exists, or you are only reviewing the situation without resolving it.", + }, + { + "label": "Result", + "value": "ADOP removes the blocked state and reopens the decision for the next review step.", + }, ] if state == "hold": return [ - {"label": "Why this command", "value": "The earlier trial paused. Comparison is the clean way to restart the review with a fresh narrowed choice."}, - {"label": "Use it when", "value": "You want to resume the paused review and choose the next candidate path."}, - {"label": "Do not use it when", "value": "The review should remain paused or a blocker must be resolved first."}, - {"label": "Result", "value": "ADOP records the resumed comparison and moves the decision back toward a bounded trial."}, + { + "label": "Why this command", + "value": "The earlier trial paused. Comparison is the clean way to restart the review with a fresh narrowed choice.", + }, + { + "label": "Use it when", + "value": "You want to resume the paused review and choose the next candidate path.", + }, + { + "label": "Do not use it when", + "value": "The review should remain paused or a blocker must be resolved first.", + }, + { + "label": "Result", + "value": "ADOP records the resumed comparison and moves the decision back toward a bounded trial.", + }, ] if state == "deprecated": return [ - {"label": "Why this command", "value": "Retirement has been decided, but ADOP still needs the actual migration target and plan."}, - {"label": "Use it when", "value": "A replacement path is agreed and the team is ready to record migration work."}, - {"label": "Do not use it when", "value": "The replacement target or migration plan is still unclear."}, - {"label": "Result", "value": "ADOP records the replacement plan and marks the decision as actively being replaced."}, + { + "label": "Why this command", + "value": "Retirement has been decided, but ADOP still needs the actual migration target and plan.", + }, + { + "label": "Use it when", + "value": "A replacement path is agreed and the team is ready to record migration work.", + }, + { + "label": "Do not use it when", + "value": "The replacement target or migration plan is still unclear.", + }, + { + "label": "Result", + "value": "ADOP records the replacement plan and marks the decision as actively being replaced.", + }, ] if state == "migrating": return [ - {"label": "Why this command", "value": "Replacement work is already underway. Archive is the final step that closes the old decision."}, - {"label": "Use it when", "value": "Migration is complete and the retired decision should be kept only as history."}, - {"label": "Do not use it when", "value": "The old tool is still in use or migration work is not actually finished."}, - {"label": "Result", "value": "ADOP closes the decision and retains it only as historical record."}, + { + "label": "Why this command", + "value": "Replacement work is already underway. Archive is the final step that closes the old decision.", + }, + { + "label": "Use it when", + "value": "Migration is complete and the retired decision should be kept only as history.", + }, + { + "label": "Do not use it when", + "value": "The old tool is still in use or migration work is not actually finished.", + }, + { + "label": "Result", + "value": "ADOP closes the decision and retains it only as historical record.", + }, ] if state == "reject": return [ - {"label": "Why there is no command", "value": "A reject decision is terminal for this use case."}, - {"label": "Use a new review when", "value": "A materially different evaluation must begin later under a new use case key."}, + { + "label": "Why there is no command", + "value": "A reject decision is terminal for this use case.", + }, + { + "label": "Use a new review when", + "value": "A materially different evaluation must begin later under a new use case key.", + }, ] if state == "archived": return [ - {"label": "Why there is no command", "value": "This decision is already closed and kept only for historical reference."}, - {"label": "Use a new review when", "value": "A materially new evaluation needs to start instead of reviving old history."}, + { + "label": "Why there is no command", + "value": "This decision is already closed and kept only for historical reference.", + }, + { + "label": "Use a new review when", + "value": "A materially new evaluation needs to start instead of reviving old history.", + }, ] if state == "promote": return [ - {"label": "Why there is no command", "value": "The decision is already approved, so no normal lifecycle command is required now."}, - {"label": "What to do instead", "value": "Keep use inside the approved area and recorded rules until retirement is needed."}, + { + "label": "Why there is no command", + "value": "The decision is already approved, so no normal lifecycle command is required now.", + }, + { + "label": "What to do instead", + "value": "Keep use inside the approved area and recorded rules until retirement is needed.", + }, ] return [ - {"label": "Why this command", "value": "This is the next recorded lifecycle move for the decision."}, + { + "label": "Why this command", + "value": "This is the next recorded lifecycle move for the decision.", + }, {"label": "Result", "value": "ADOP records the next explicit state change."}, ] def _retirement_command_details() -> list[dict[str, str]]: return [ - {"label": "Why this command", "value": "The tool is still approved. This command exists only to record that retirement has formally started."}, - {"label": "Use it when", "value": "The team has decided the approved tool should stop being the active path."}, - {"label": "Do not use it when", "value": "You only want to pause usage temporarily or there is not yet a real retirement decision."}, - {"label": "Result", "value": "ADOP records the retirement start and moves the decision into the removal phase."}, + { + "label": "Why this command", + "value": "The tool is still approved. This command exists only to record that retirement has formally started.", + }, + { + "label": "Use it when", + "value": "The team has decided the approved tool should stop being the active path.", + }, + { + "label": "Do not use it when", + "value": "You only want to pause usage temporarily or there is not yet a real retirement decision.", + }, + { + "label": "Result", + "value": "ADOP records the retirement start and moves the decision into the removal phase.", + }, ] @@ -887,7 +1140,7 @@ def _command_surface(scene: str, state: str, scene_items: list[dict[str, Any]]) "retirement_command_label": "Retirement command", "retirement_command_note": "This command changes the decision from approved into retirement tracking.", "retirement_command_details": _retirement_command_details(), - "retirement_command": f'adop deprecate --scene {scene} --deprecation-reason ""', + "retirement_command": f'adop deprecate --scene {scene} --retirement-reason "" --replacement-candidate "" --timeline ""', "retirement_command_copyable": True, } ) @@ -915,7 +1168,9 @@ def _default_change_condition(state: str) -> str: if state == "promote": return "A retirement, migration, or archive note would move this decision beyond promoted." if state in {"trial-ready", "in-trial"}: - return "A decision record will decide whether this review becomes promoted, hold, or reject." + return ( + "A decision record will decide whether this review becomes promoted, hold, or reject." + ) if state == "blocked": return "An unblock path plus renewed intake would change this decision." if state == "watch": @@ -933,11 +1188,15 @@ def _build_lane(scene: str, scene_items: list[dict[str, Any]], state: str) -> di tool = _lane_tool(scene_items) landing_target = _landing_target(scene_items) command_surface = _command_surface(scene, state, scene_items) - summary = _lane_summary(scene_items, state, { - "tool": tool, - "scene": scene, - "landing_target": landing_target, - }) + summary = _lane_summary( + scene_items, + state, + { + "tool": tool, + "scene": scene, + "landing_target": landing_target, + }, + ) allowed, forbidden = _allowed_forbidden(scene_items) return { "scene": scene, @@ -949,7 +1208,9 @@ def _build_lane(scene: str, scene_items: list[dict[str, Any]], state: str) -> di "state_meaning": _state_meaning(state), "tone": _state_tone(state), "decision": _decision_text(state, scene_items, landing_target), - "next_step": _default_next_text(state, landing_target) if not summary["what_happens_next"] else summary["what_happens_next"], + "next_step": _default_next_text(state, landing_target) + if not summary["what_happens_next"] + else summary["what_happens_next"], "landing_target": landing_target, "control_model": _control_model(scene_items), "last_evidence": _last_evidence(scene_items), @@ -963,7 +1224,9 @@ def _build_lane(scene: str, scene_items: list[dict[str, Any]], state: str) -> di "forbidden": forbidden, "rationale": _rationale(scene_items), "artifacts": _artifacts(scene_items), - "raw_artifacts": [_strip_runtime_metadata(item) for item in sorted(scene_items, key=_id_sort_key)], + "raw_artifacts": [ + _strip_runtime_metadata(item) for item in sorted(scene_items, key=_id_sort_key) + ], "timeline": _timeline(scene_items, state), "is_sample": False, **command_surface, @@ -977,15 +1240,29 @@ def _sample_lane(base: dict[str, Any]) -> dict[str, Any]: lane["state_label"] = _state_label(str(lane["state"])) lane["state_meaning"] = _state_meaning(str(lane["state"])) lane["tone"] = _state_tone(str(lane["state"])) - lane["headline"] = f"{lane['tool']} is in the {lane['state_label']} state for the {lane['scene']} use case." + lane["headline"] = ( + f"{lane['tool']} is in the {lane['state_label']} state for the {lane['scene']} use case." + ) lane["next_step"] = _default_next_text(str(lane["state"]), str(lane.get("landing_target", "-"))) lane.update(_command_surface(str(lane["scene"]), str(lane["state"]), [])) lane["timeline"] = lane.get("timeline") or [ {"step": "Intake", "body": "Sample decision for layout stress testing.", "done": True}, {"step": "Comparison", "body": "Sample decision for layout stress testing.", "done": True}, - {"step": "Trial", "body": "Sample decision for layout stress testing.", "done": lane["state"] in {"trial-ready", "in-trial", "promote"}}, - {"step": "Judgment", "body": "Sample decision for layout stress testing.", "done": lane["state"] == "promote"}, - {"step": "Operational state", "body": f"Sample decision is in the {lane['state_label']} state.", "done": True}, + { + "step": "Trial", + "body": "Sample decision for layout stress testing.", + "done": lane["state"] in {"trial-ready", "in-trial", "promote"}, + }, + { + "step": "Judgment", + "body": "Sample decision for layout stress testing.", + "done": lane["state"] == "promote", + }, + { + "step": "Operational state", + "body": f"Sample decision is in the {lane['state_label']} state.", + "done": True, + }, ] lane["is_sample"] = True return lane @@ -1017,7 +1294,11 @@ def build_dashboard_payload( items = load_all_artifacts(root) scene_states = get_scene_states(root) lanes = [ - _build_lane(scene, [item for item in items if str(item.get("related_scene", "")).strip() == scene], state) + _build_lane( + scene, + [item for item in items if str(item.get("related_scene", "")).strip() == scene], + state, + ) for scene, state in scene_states.items() ] lanes.sort(key=lambda lane: (_STATE_SORT.get(str(lane["state"]), 99), str(lane["scene"]))) @@ -1025,11 +1306,16 @@ def build_dashboard_payload( if sample_board_count > len(lanes): existing_scenes = {str(lane["scene"]) for lane in lanes} available = [ - candidate for candidate in _SAMPLE_LANES + candidate + for candidate in _SAMPLE_LANES if str(candidate["scene"]) not in existing_scenes ] - historical_candidates = [candidate for candidate in available if _is_historical(str(candidate["state"]))] - active_candidates = [candidate for candidate in available if not _is_historical(str(candidate["state"]))] + historical_candidates = [ + candidate for candidate in available if _is_historical(str(candidate["state"])) + ] + active_candidates = [ + candidate for candidate in available if not _is_historical(str(candidate["state"])) + ] chosen: list[dict[str, Any]] = [] has_historical_real = any(_is_historical(str(lane["state"])) for lane in lanes) @@ -1128,13 +1414,8 @@ def render_dashboard_html( ) payload_json = json.dumps(payload, ensure_ascii=False) payload_json = ( - payload_json - .replace("&", "\\u0026") - .replace("<", "\\u003c") - .replace(">", "\\u003e") + payload_json.replace("&", "\\u0026").replace("<", "\\u003c").replace(">", "\\u003e") ) - return ( - template - .replace("__ADOP_DASHBOARD_TITLE__", escape(title)) - .replace("__ADOP_DASHBOARD_PAYLOAD__", payload_json) + return template.replace("__ADOP_DASHBOARD_TITLE__", escape(title)).replace( + "__ADOP_DASHBOARD_PAYLOAD__", payload_json ) diff --git a/shared/python/adop_ids.py b/shared/python/adop_ids.py index 36b7b51..716789b 100644 --- a/shared/python/adop_ids.py +++ b/shared/python/adop_ids.py @@ -6,7 +6,6 @@ import re from pathlib import Path - # Accept 3-or-more digits: format_id zero-pads to 3 but ids >= 1000 widen to 4+. # Keeping a fixed \d{3} here would make parse_numeric_id reject pm-1000 and break # next_sequential_id / latest ordering past 999 (residual B12). diff --git a/shared/python/adop_state_machine.py b/shared/python/adop_state_machine.py index fb19153..42d2e50 100644 --- a/shared/python/adop_state_machine.py +++ b/shared/python/adop_state_machine.py @@ -5,10 +5,7 @@ from typing import Any -try: - from .adop_types import FILTER_NAMES, IN_TRIAL, TRIAL_READY -except ImportError: # pragma: no cover - script import path - from adop_types import FILTER_NAMES, IN_TRIAL, TRIAL_READY +from adop_types import FILTER_NAMES, IN_TRIAL, TRIAL_READY def comparison_ready_for_trial(comparison: dict[str, Any]) -> bool: @@ -22,7 +19,9 @@ def comparison_ready_for_trial(comparison: dict[str, Any]) -> bool: return True -def infer_effective_trial_state(packet: dict[str, Any], judgment_report: dict[str, Any] | None) -> str: +def infer_effective_trial_state( + packet: dict[str, Any], judgment_report: dict[str, Any] | None +) -> str: if judgment_report: return str(judgment_report.get("verdict", IN_TRIAL)) return IN_TRIAL diff --git a/shared/python/adop_summary.py b/shared/python/adop_summary.py index ac2f5e8..e31bfc2 100644 --- a/shared/python/adop_summary.py +++ b/shared/python/adop_summary.py @@ -7,68 +7,37 @@ from pathlib import Path from typing import Any -try: - from .adop_artifacts import find_by_type, find_judgment_report, load_all_artifacts - from .adop_ids import parse_numeric_id - from .adop_state_machine import infer_effective_trial_state - from .adop_types import ( - ARCHIVE_NOTE, - ARCHIVED, - BLOCKED_NOTE, - BLOCKED_STATE, - CANDIDATE_INTAKE_NOTE, - COMPARISON_NOTE, - DECOMPOSITION_DECISION, - DEPRECATED, - DEPRECATION_NOTE, - HOLD_NOTE, - IN_TRIAL, - JUDGMENT_REPORT, - COUPLING_NOTE, - MIGRATING, - MIGRATION_NOTE, - PROMOTION_NOTE, - PROPOSED, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - STRUCTURAL_GAP, - SUMMARY_STATES, - TRIAL_PACKET, - TRIAL_READY, - WATCH, - WATCH_NOTE, - ) -except ImportError: # pragma: no cover - script import path - from adop_artifacts import find_by_type, find_judgment_report, load_all_artifacts - from adop_ids import parse_numeric_id - from adop_state_machine import infer_effective_trial_state - from adop_types import ( - ARCHIVE_NOTE, - ARCHIVED, - BLOCKED_NOTE, - BLOCKED_STATE, - CANDIDATE_INTAKE_NOTE, - COMPARISON_NOTE, - DECOMPOSITION_DECISION, - DEPRECATED, - DEPRECATION_NOTE, - HOLD_NOTE, - IN_TRIAL, - JUDGMENT_REPORT, - COUPLING_NOTE, - MIGRATING, - MIGRATION_NOTE, - PROMOTION_NOTE, - PROPOSED, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - STRUCTURAL_GAP, - SUMMARY_STATES, - TRIAL_PACKET, - TRIAL_READY, - WATCH, - WATCH_NOTE, - ) +from adop_artifacts import load_all_artifacts +from adop_ids import parse_numeric_id +from adop_state_machine import infer_effective_trial_state +from adop_types import ( + ARCHIVE_NOTE, + ARCHIVED, + BLOCKED_NOTE, + BLOCKED_STATE, + CANDIDATE_INTAKE_NOTE, + COMPARISON_NOTE, + COUPLING_NOTE, + DECOMPOSITION_DECISION, + DEPRECATED, + DEPRECATION_NOTE, + HOLD_NOTE, + IN_TRIAL, + JUDGMENT_REPORT, + MIGRATING, + MIGRATION_NOTE, + PROMOTION_NOTE, + PROPOSED, + REJECT_NOTE, + REMOVAL_COSTS, + ROOT_CAUSE_HYPOTHESIS, + STRUCTURAL_GAP, + SUMMARY_STATES, + TRIAL_PACKET, + TRIAL_READY, + WATCH, + WATCH_NOTE, +) # Hold / reject are reused by both intake and trial buckets, so they are spelled # as literals here rather than imported, matching the existing intake_dispositions set. @@ -82,6 +51,11 @@ INTAKE_STATES: tuple[str, ...] = (PROPOSED, TRIAL_READY, HOLD, REJECT) TRIAL_STATES: tuple[str, ...] = (IN_TRIAL, PROMOTE, HOLD, REJECT) +# `structural_gap` ("current workflow lacks a bounded evaluation lane") is a +# comparison-time diagnostic. Once a scene reaches in-trial or any later state +# the lane exists, so showing the gap there is stale and self-contradictory. +_PRE_TRIAL_GAP_STATES: frozenset[str] = frozenset({WATCH, PROPOSED, BLOCKED_STATE, TRIAL_READY}) + # Extended lifecycle states are tracked by dedicated note types, not by intake # disposition or trial verdict. State = the latest note of this type per scene. EXTENDED_STATE_NOTES: tuple[tuple[str, str], ...] = ( @@ -130,18 +104,29 @@ def _resolve_scene_states(root: Path, items: list[dict[str, Any]]) -> dict[str, def of_type(scene: str, artifact_type: str) -> list[dict[str, Any]]: matched = [ - item for item in items + item + for item in items if item.get("artifact_type") == artifact_type and str(item.get("related_scene", "")).strip() == scene ] matched.sort(key=_id_sort_key) return matched - scenes = sorted({ - str(item.get("related_scene", "")).strip() + scenes = sorted( + { + str(item.get("related_scene", "")).strip() + for item in items + if str(item.get("related_scene", "")).strip() + } + ) + + # Resolve judgment verdicts from the already-loaded items instead of + # re-reading the whole artifact root once per scene (was O(scenes × files)). + judgment_by_id = { + str(item.get("artifact_id", "")): item for item in items - if str(item.get("related_scene", "")).strip() - }) + if item.get("artifact_type") == JUDGMENT_REPORT + } resolved: dict[str, str] = {} for scene in scenes: @@ -153,11 +138,13 @@ def of_type(scene: str, artifact_type: str) -> list[dict[str, Any]]: resolved[scene] = DEPRECATED elif of_type(scene, PROMOTION_NOTE): resolved[scene] = "promote" + elif of_type(scene, REJECT_NOTE): + resolved[scene] = REJECT elif _scene_resumed_after_hold(scene, of_type): resolved[scene] = TRIAL_READY elif of_type(scene, TRIAL_PACKET): packet = of_type(scene, TRIAL_PACKET)[-1] - judgment = find_judgment_report(root, str(packet.get("artifact_id", ""))) + judgment = judgment_by_id.get(str(packet.get("artifact_id", ""))) resolved[scene] = str(judgment.get("verdict", IN_TRIAL)) if judgment else IN_TRIAL elif _scene_is_blocked(scene, items, of_type): resolved[scene] = BLOCKED_STATE @@ -170,7 +157,10 @@ def of_type(scene: str, artifact_type: str) -> list[dict[str, Any]]: resolved[scene] = WATCH for note in items: - if note.get("artifact_type") == WATCH_NOTE and not str(note.get("related_scene", "")).strip(): + if ( + note.get("artifact_type") == WATCH_NOTE + and not str(note.get("related_scene", "")).strip() + ): resolved.setdefault(f"(watch) {note.get('candidate_or_tool', '-')}", WATCH) return resolved @@ -239,7 +229,11 @@ def build_summary(root: Path, *, scene: str | None = None, status: str | None = # Count latest intake per (scene, tool) pair to avoid double-counting when # quick-intake is run multiple times for the same candidate. latest_intake: dict[tuple[str, str], dict] = {} - for intake in find_by_type(root, CANDIDATE_INTAKE_NOTE): + intakes = sorted( + (i for i in items if i.get("artifact_type") == CANDIDATE_INTAKE_NOTE), + key=_id_sort_key, + ) + for intake in intakes: if scene and intake.get("related_scene") != scene: continue intake_scene = str(intake.get("related_scene", "")) @@ -253,7 +247,10 @@ def build_summary(root: Path, *, scene: str | None = None, status: str | None = if intake_state in intake_dispositions: intake_counts[intake_state].append(str(intake.get("candidate_or_tool", "-"))) - for packet in find_by_type(root, TRIAL_PACKET): + summary_judgment_by_id = { + str(i.get("artifact_id", "")): i for i in items if i.get("artifact_type") == JUDGMENT_REPORT + } + for packet in (i for i in items if i.get("artifact_type") == TRIAL_PACKET): if scene and packet.get("related_scene") != scene: continue trial_id = packet.get("artifact_id") @@ -262,7 +259,7 @@ def build_summary(root: Path, *, scene: str | None = None, status: str | None = # rather than emit a phantom "None" row (residual B44). continue trial_id = str(trial_id) - judgment = find_judgment_report(root, trial_id) + judgment = summary_judgment_by_id.get(trial_id) effective = infer_effective_trial_state(packet, judgment) label = str(packet.get("related_scene", trial_id)) if effective in SUMMARY_STATES: @@ -283,13 +280,15 @@ def build_summary(root: Path, *, scene: str | None = None, status: str | None = for note in notes: note_scene = str(note.get("related_scene", "")).strip() tool = str(note.get("candidate_or_tool", "-")) - key = note_scene or f"tool:{tool}" - latest_notes[key] = note # later id wins (append-only history) + note_key = note_scene or f"tool:{tool}" + latest_notes[note_key] = note # later id wins (append-only history) for note in latest_notes.values(): note_scene = str(note.get("related_scene", "")).strip() if scene and note_scene != scene: continue - lifecycle_counts[state_name].append(note_scene or str(note.get("candidate_or_tool", "-"))) + lifecycle_counts[state_name].append( + note_scene or str(note.get("candidate_or_tool", "-")) + ) def _render_section(title: str, states: tuple[str, ...], counts: dict[str, list[str]]) -> None: lines.append(title) @@ -317,7 +316,9 @@ def _render_section(title: str, states: tuple[str, ...], counts: dict[str, list[ _render_section("Intake Dispositions", INTAKE_STATES, intake_counts) _render_section("Trial States", TRIAL_STATES, trial_counts) - _render_section("Lifecycle Notes", tuple(state for state, _ in EXTENDED_STATE_NOTES), lifecycle_counts) + _render_section( + "Lifecycle Notes", tuple(state for state, _ in EXTENDED_STATE_NOTES), lifecycle_counts + ) # Tool entanglement: latest coupling-note per scene/tool snapshot, headline = # worst detachment cost. status filter does not apply (coupling is not a state). @@ -332,7 +333,7 @@ def _render_section(title: str, states: tuple[str, ...], counts: dict[str, list[ latest_couplings[(note_scene, str(note.get("candidate_or_tool", "")))] = note if latest_couplings: lines.append("Tool Entanglement") - for (note_scene, tool) in sorted(latest_couplings): + for note_scene, tool in sorted(latest_couplings): entries = latest_couplings[(note_scene, tool)].get("couplings", []) worst = max( (str(e.get("removal_cost", REMOVAL_COSTS[0])) for e in entries), @@ -363,26 +364,20 @@ def _render_section(title: str, states: tuple[str, ...], counts: dict[str, list[ judgment = latest_judgments.get(scene_name, {}) hypothesis = str( - judgment.get(ROOT_CAUSE_HYPOTHESIS) - or comparison.get(ROOT_CAUSE_HYPOTHESIS) - or "" + judgment.get(ROOT_CAUSE_HYPOTHESIS) or comparison.get(ROOT_CAUSE_HYPOTHESIS) or "" ).strip() if hypothesis: root_cause_lines.append(f"- {scene_name}: {hypothesis}") structural_gap = str(comparison.get(STRUCTURAL_GAP, "")).strip() - if structural_gap: + if structural_gap and scene_states.get(scene_name) in _PRE_TRIAL_GAP_STATES: structural_gap_lines.append(f"- {scene_name}: {structural_gap}") decomposition_decision = str( - judgment.get(DECOMPOSITION_DECISION) - or comparison.get(DECOMPOSITION_DECISION) - or "" + judgment.get(DECOMPOSITION_DECISION) or comparison.get(DECOMPOSITION_DECISION) or "" ).strip() adoption_unit = str( - judgment.get("adoption_unit") - or comparison.get("adoption_unit") - or "" + judgment.get("adoption_unit") or comparison.get("adoption_unit") or "" ).strip() if decomposition_decision or adoption_unit: decomposition_lines.append( @@ -390,16 +385,16 @@ def _render_section(title: str, states: tuple[str, ...], counts: dict[str, list[ ) recommended_fit_lane = str( - judgment.get("recommended_fit_lane") - or comparison.get("recommended_fit_lane") - or "" + judgment.get("recommended_fit_lane") or comparison.get("recommended_fit_lane") or "" ).strip() if recommended_fit_lane: fit_lane_lines.append(f"- {scene_name}: {recommended_fit_lane}") preventive_actions = judgment.get("preventive_action", []) if isinstance(preventive_actions, list) and preventive_actions: - preventive_action_lines.append(f"- {scene_name}: {'; '.join(str(item) for item in preventive_actions)}") + preventive_action_lines.append( + f"- {scene_name}: {'; '.join(str(item) for item in preventive_actions)}" + ) if root_cause_lines: lines.append("Root-Cause Signals") diff --git a/shared/python/adop_sync.py b/shared/python/adop_sync.py index cd6c968..2e6f516 100644 --- a/shared/python/adop_sync.py +++ b/shared/python/adop_sync.py @@ -19,6 +19,7 @@ list [--source ] Show registered targets and their current sync status. """ + from __future__ import annotations import argparse @@ -26,7 +27,7 @@ import json import shutil import sys -from pathlib import Path +from pathlib import Path, PurePosixPath, PureWindowsPath _REGISTRY_FILE = "sync-registry.json" _MANIFEST_FILE = "adop.json" @@ -40,14 +41,20 @@ def _load_manifest(source: Path) -> dict: path = source / _MANIFEST_FILE if not path.exists(): sys.exit(f"error: {path} not found — run from the ADOP canonical root") - return json.loads(path.read_text(encoding="utf-8")) + try: + return json.loads(path.read_text(encoding="utf-8")) + except (json.JSONDecodeError, OSError, UnicodeDecodeError) as exc: + sys.exit(f"error: {path} is not readable JSON: {exc}") def _load_registry(source: Path) -> list[str]: reg = source / _REGISTRY_FILE if not reg.exists(): return [] - return json.loads(reg.read_text(encoding="utf-8")).get("targets", []) + try: + return json.loads(reg.read_text(encoding="utf-8")).get("targets", []) + except (json.JSONDecodeError, OSError, UnicodeDecodeError) as exc: + sys.exit(f"error: {reg} is not readable JSON: {exc}") def _save_registry(source: Path, targets: list[str]) -> None: @@ -56,24 +63,48 @@ def _save_registry(source: Path, targets: list[str]) -> None: ) +def _managed_files(manifest: dict) -> list[str]: + """Files sync must keep in step: runtime modules plus declared templates. + + Each entry must be a project-relative path with no '..' and no absolute root, + so a hostile/clone manifest cannot make apply/push write outside --target. + """ + files = list(manifest.get("runtime_files", [])) + list(manifest.get("template_files", [])) + for rel in files: + norm = str(rel).replace("\\", "/") + # Reject leading-slash/drive-relative ("/etc/passwd"), Windows drive + # ("C:\\..."), and any "../" traversal. is_absolute() alone is not enough: + # on Windows "/etc/passwd" is drive-relative, not absolute. + if ( + norm.startswith("/") + or PureWindowsPath(rel).drive + or PurePosixPath(norm).is_absolute() + or ".." in PurePosixPath(norm).parts + ): + sys.exit(f"error: manifest path must be project-relative without '..': {rel}") + return files + + def _check_one(source: Path, target: Path, manifest: dict) -> list[dict]: - """Check each runtime file; dst preserves the canonical relative path.""" + """Check each managed file; dst preserves the canonical relative path.""" results = [] - for rel in manifest["runtime_files"]: + for rel in _managed_files(manifest): src = source / rel - dst = target / rel # preserve full relative path (e.g. shared/python/adop_cli.py) + dst = target / rel # preserve full relative path (e.g. shared/python/adop_cli.py) if not src.exists(): results.append({"file": rel, "status": "MISSING_IN_SOURCE"}) elif not dst.exists(): results.append({"file": rel, "status": "MISSING", "src": str(src), "dst": str(dst)}) else: ok = _file_hash(src) == _file_hash(dst) - results.append({ - "file": rel, - "status": "OK" if ok else "DIFF", - "src": str(src), - "dst": str(dst), - }) + results.append( + { + "file": rel, + "status": "OK" if ok else "DIFF", + "src": str(src), + "dst": str(dst), + } + ) return results @@ -177,21 +208,32 @@ def main() -> int: sub = parser.add_subparsers(dest="command", metavar="command") def _src(p: argparse.ArgumentParser) -> None: - p.add_argument("--source", default=".", metavar="DIR", - help="ADOP canonical root containing adop.json (default: .)") + p.add_argument( + "--source", + default=".", + metavar="DIR", + help="ADOP canonical root containing adop.json (default: .)", + ) def _tgt(p: argparse.ArgumentParser) -> None: - p.add_argument("--target", required=True, metavar="DIR", - help="Project root (runtime files placed at /shared/python/)") + p.add_argument( + "--target", + required=True, + metavar="DIR", + help="Project root (runtime files placed at /shared/python/)", + ) p = sub.add_parser("check", help="compare hashes, report DIFF") - _src(p); _tgt(p) + _src(p) + _tgt(p) p = sub.add_parser("apply", help="copy differing/missing files to target") - _src(p); _tgt(p) + _src(p) + _tgt(p) p = sub.add_parser("register", help="add target to local registry") - _src(p); _tgt(p) + _src(p) + _tgt(p) p = sub.add_parser("push", help="apply to all registered targets") _src(p) diff --git a/shared/python/adop_types.py b/shared/python/adop_types.py index fb25e46..8cff1e8 100644 --- a/shared/python/adop_types.py +++ b/shared/python/adop_types.py @@ -5,23 +5,28 @@ from typing import Final +# SCHEMA_VERSION is the version new artifacts are WRITTEN at. Older artifacts +# stay readable down to MIN_READABLE_SCHEMA_VERSION: schema changes are +# additive-only (new optional fields), so a record written by an older adop +# remains valid after an upgrade — the append-only store outlives the schema. SCHEMA_VERSION: Final[int] = 1 +MIN_READABLE_SCHEMA_VERSION: Final[int] = 1 ARTIFACT_TYPES: Final[tuple[str, ...]] = ( "candidate-intake-note", # [0] - "comparison-note", # [1] - "trial-packet", # [2] - "trial-result", # [3] - "reject-note", # [4] - "promotion-note", # [5] - "judgment-report", # [6] - "watch-note", # [7] - "blocked-note", # [8] - "deprecation-note", # [9] - "migration-note", # [10] - "archive-note", # [11] - "coupling-note", # [12] — orthogonal metadata, NOT a lifecycle state - "hold-note", # [13] — trial closed with hold verdict (distinct from reject-note) + "comparison-note", # [1] + "trial-packet", # [2] + "trial-result", # [3] + "reject-note", # [4] + "promotion-note", # [5] + "judgment-report", # [6] + "watch-note", # [7] + "blocked-note", # [8] + "deprecation-note", # [9] + "migration-note", # [10] + "archive-note", # [11] + "coupling-note", # [12] — orthogonal metadata, NOT a lifecycle state + "hold-note", # [13] — trial closed with hold verdict (distinct from reject-note) ) ARTIFACT_ID_PREFIX: Final[dict[str, str]] = { @@ -44,20 +49,20 @@ # Tool-to-file coupling vocabulary (declared entanglement; see coupling-note). # COUPLING_TYPES = HOW the tool is entangled with a file. COUPLING_TYPES: Final[tuple[str, ...]] = ( - "config", # tool configuration lives in the file - "import", # source imports the tool as a dependency - "invocation", # file calls/runs the tool (CI step, script, hook) - "generated", # file is generated/owned by the tool - "data-write", # tool writes runtime data into the file - "reference", # file hardcodes a path/name reference to the tool + "config", # tool configuration lives in the file + "import", # source imports the tool as a dependency + "invocation", # file calls/runs the tool (CI step, script, hook) + "generated", # file is generated/owned by the tool + "data-write", # tool writes runtime data into the file + "reference", # file hardcodes a path/name reference to the tool ) # REMOVAL_COSTS = the "癒着度": how hard it is to detach the tool from the file. COUPLING_DETECTION_SOURCES: Final[tuple[str, ...]] = ( - "surface-rule", # matched a known tool-owned file surface - "python-import", # matched a Python import / from import - "config-mention", # matched a config or dependency declaration mention - "invocation-pattern", # matched a command / workflow invocation pattern - "text-reference", # matched a plain text reference only + "surface-rule", # matched a known tool-owned file surface + "python-import", # matched a Python import / from import + "config-mention", # matched a config or dependency declaration mention + "invocation-pattern", # matched a command / workflow invocation pattern + "text-reference", # matched a plain text reference only ) COUPLING_CONFIDENCE_LEVELS: Final[tuple[str, ...]] = ( "high", @@ -65,9 +70,9 @@ "low", ) REMOVAL_COSTS: Final[tuple[str, ...]] = ( - "clean", # remove/disable with no edits to other content - "edit", # requires targeted edits to the file - "entangled", # pervasive; removal is risky or large + "clean", # remove/disable with no edits to other content + "edit", # requires targeted edits to the file + "entangled", # pervasive; removal is risky or large ) PLATFORMS: Final[tuple[str, ...]] = ( "windows", @@ -104,7 +109,12 @@ FILTER_NAMES: Final[tuple[str, ...]] = ("scene_fit", "authority_safe", "controlability") FILTER_STATUSES: Final[tuple[str, ...]] = ("pass", "conditional", "fail") CANDIDATE_SHAPES: Final[tuple[str, ...]] = ("atomic", "composite", "unknown") -DECOMPOSITION_DECISIONS: Final[tuple[str, ...]] = ("as-is", "split-required", "reference-only", "reject") +DECOMPOSITION_DECISIONS: Final[tuple[str, ...]] = ( + "as-is", + "split-required", + "reference-only", + "reject", +) FIT_LANES: Final[tuple[str, ...]] = ("reference-only", "compare", "trial-ready", "reject") TRIAL_TYPES: Final[tuple[str, ...]] = ( "read-only", @@ -130,20 +140,22 @@ FALLBACKS: Final[tuple[str, ...]] = ("fail-close", "warn", "hold", "reject") RECURRING_CONTROL_DECISIONS: Final[tuple[str, ...]] = ("yes", "no", "later") SUMMARY_STATES: Final[tuple[str, ...]] = ( - "watch", # [0] - "proposed", # [1] - "blocked", # [2] - "trial-ready", # [3] - "in-trial", # [4] - "promote", # [5] - "hold", # [6] - "reject", # [7] + "watch", # [0] + "proposed", # [1] + "blocked", # [2] + "trial-ready", # [3] + "in-trial", # [4] + "promote", # [5] + "hold", # [6] + "reject", # [7] "deprecated", # [8] - "migrating", # [9] - "archived", # [10] + "migrating", # [9] + "archived", # [10] ) -WRITE_TRIAL_TYPES: Final[frozenset[str]] = frozenset({"isolated-write", "task-scoped", "phase-scoped"}) +WRITE_TRIAL_TYPES: Final[frozenset[str]] = frozenset( + {"isolated-write", "task-scoped", "phase-scoped"} +) NON_PROMOTE_VERDICTS: Final[frozenset[str]] = frozenset({"hold", "reject"}) # Named artifact type constants — index-bound to ARTIFACT_TYPES (keep in sync with tuple order) @@ -180,15 +192,15 @@ OBSERVED_EFFECT: Final[str] = "observed_effect" # Named state constants — Wave C (in-trial / trial-ready SSOT unification) -TRIAL_READY: Final[str] = FIT_LANES[2] # "trial-ready" — tuple is SSOT +TRIAL_READY: Final[str] = FIT_LANES[2] # "trial-ready" — tuple is SSOT IN_TRIAL: Final[str] = SUMMARY_STATES[4] # "in-trial" — tuple is SSOT # Named state constants for extended lifecycle states -WATCH: Final[str] = SUMMARY_STATES[0] # "watch" +WATCH: Final[str] = SUMMARY_STATES[0] # "watch" BLOCKED_STATE: Final[str] = SUMMARY_STATES[2] # "blocked" DEPRECATED: Final[str] = SUMMARY_STATES[8] # "deprecated" -MIGRATING: Final[str] = SUMMARY_STATES[9] # "migrating" -ARCHIVED: Final[str] = SUMMARY_STATES[10] # "archived" +MIGRATING: Final[str] = SUMMARY_STATES[9] # "migrating" +ARCHIVED: Final[str] = SUMMARY_STATES[10] # "archived" def empty_filter_assessment() -> dict[str, dict[str, str | None]]: diff --git a/shared/python/adop_validation.py b/shared/python/adop_validation.py index 87b2533..945847b 100644 --- a/shared/python/adop_validation.py +++ b/shared/python/adop_validation.py @@ -7,110 +7,58 @@ from pathlib import Path from typing import Any -try: - from .adop_artifacts import latest_by_type, load_all_artifacts - from .adop_types import ( - ARCHIVE_NOTE, - ARTIFACT_ID_PREFIX, - ARTIFACT_TYPES, - AUTHORITY_SAFE, - BLOCKED_NOTE, - CANDIDATE_INTAKE_NOTE, - CANDIDATE_SHAPES, - COMPARISON_NOTE, - CONTROLABILITY, - COUPLING_CONFIDENCE_LEVELS, - COUPLING_DETECTION_SOURCES, - COUPLING_NOTE, - COUPLING_TYPES, - COSTS, - DATA_FLOW_DESTINATIONS, - DECOMPOSITION_DECISION, - DECOMPOSITION_DECISIONS, - DEPRECATION_NOTE, - DISPOSITIONS, - EVALUATION_GATE, - EXECUTOR, - FALLBACKS, - FILTER_NAMES, - FILTER_STATUSES, - FIT_LANES, - HOLD_NOTE, - JUDGMENT_REPORT, - LANES, - MIGRATION_NOTE, - NON_PROMOTE_VERDICTS, - OBSERVED_EFFECT, - PROMOTION_NOTE, - PLATFORMS, - RECORDING_MODES, - RECORDING_SOURCES, - RECURRING_CONTROL_DECISIONS, - REJECT_NOTE, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - SANDBOX_TYPES, - SCHEMA_VERSION, - STRUCTURAL_GAP, - TRIAL_PACKET, - TRIAL_RESULT, - TRIAL_TYPES, - VERDICTS, - WATCH_NOTE, - WRITE_TRIAL_TYPES, - ) -except ImportError: # pragma: no cover - script import path - from adop_artifacts import latest_by_type, load_all_artifacts - from adop_types import ( - ARCHIVE_NOTE, - ARTIFACT_ID_PREFIX, - ARTIFACT_TYPES, - AUTHORITY_SAFE, - BLOCKED_NOTE, - CANDIDATE_INTAKE_NOTE, - CANDIDATE_SHAPES, - COMPARISON_NOTE, - CONTROLABILITY, - COUPLING_CONFIDENCE_LEVELS, - COUPLING_DETECTION_SOURCES, - COUPLING_NOTE, - COUPLING_TYPES, - COSTS, - DATA_FLOW_DESTINATIONS, - DECOMPOSITION_DECISION, - DECOMPOSITION_DECISIONS, - DEPRECATION_NOTE, - DISPOSITIONS, - EVALUATION_GATE, - EXECUTOR, - FALLBACKS, - FILTER_NAMES, - FILTER_STATUSES, - FIT_LANES, - HOLD_NOTE, - JUDGMENT_REPORT, - LANES, - MIGRATION_NOTE, - NON_PROMOTE_VERDICTS, - OBSERVED_EFFECT, - PROMOTION_NOTE, - PLATFORMS, - RECORDING_MODES, - RECORDING_SOURCES, - RECURRING_CONTROL_DECISIONS, - REJECT_NOTE, - REMOVAL_COSTS, - ROOT_CAUSE_HYPOTHESIS, - SANDBOX_TYPES, - SCHEMA_VERSION, - STRUCTURAL_GAP, - TRIAL_PACKET, - TRIAL_RESULT, - TRIAL_TYPES, - VERDICTS, - WATCH_NOTE, - WRITE_TRIAL_TYPES, - ) +from adop_artifacts import latest_by_type, load_all_artifacts +from adop_types import ( + ARCHIVE_NOTE, + ARTIFACT_ID_PREFIX, + ARTIFACT_TYPES, + AUTHORITY_SAFE, + BLOCKED_NOTE, + CANDIDATE_INTAKE_NOTE, + CANDIDATE_SHAPES, + COMPARISON_NOTE, + CONTROLABILITY, + COSTS, + COUPLING_CONFIDENCE_LEVELS, + COUPLING_DETECTION_SOURCES, + COUPLING_NOTE, + COUPLING_TYPES, + DATA_FLOW_DESTINATIONS, + DECOMPOSITION_DECISION, + DECOMPOSITION_DECISIONS, + DEPRECATION_NOTE, + DISPOSITIONS, + EVALUATION_GATE, + EXECUTOR, + FALLBACKS, + FILTER_NAMES, + FILTER_STATUSES, + FIT_LANES, + HOLD_NOTE, + JUDGMENT_REPORT, + LANES, + MIGRATION_NOTE, + MIN_READABLE_SCHEMA_VERSION, + NON_PROMOTE_VERDICTS, + OBSERVED_EFFECT, + PLATFORMS, + PROMOTION_NOTE, + RECORDING_MODES, + RECORDING_SOURCES, + RECURRING_CONTROL_DECISIONS, + REJECT_NOTE, + REMOVAL_COSTS, + ROOT_CAUSE_HYPOTHESIS, + SANDBOX_TYPES, + SCHEMA_VERSION, + STRUCTURAL_GAP, + TRIAL_PACKET, + TRIAL_RESULT, + TRIAL_TYPES, + VERDICTS, + WATCH_NOTE, + WRITE_TRIAL_TYPES, +) class AdopValidationError(ValueError): @@ -126,7 +74,9 @@ def require_non_empty(value: str | None, field_name: str) -> None: raise AdopValidationError(f"{field_name} is required", 2) -def validate_choice(value: str, field_name: str, allowed: tuple[str, ...], *, exit_code: int = 3) -> None: +def validate_choice( + value: str, field_name: str, allowed: tuple[str, ...], *, exit_code: int = 3 +) -> None: if value not in allowed: raise AdopValidationError(f"{field_name} must be one of {allowed}", exit_code) @@ -142,7 +92,9 @@ def validate_filter_assessment(filter_assessment: dict[str, Any]) -> None: section = filter_assessment[key] if not isinstance(section, dict): raise AdopValidationError(f"filter_assessment.{key} must be object") - validate_choice(str(section.get("status", "")), f"filter_assessment.{key}.status", FILTER_STATUSES) + validate_choice( + str(section.get("status", "")), f"filter_assessment.{key}.status", FILTER_STATUSES + ) require_non_empty(section.get("reason"), f"filter_assessment.{key}.reason") @@ -161,15 +113,27 @@ def validate_target_project_profile(profile: dict[str, Any]) -> None: raise AdopValidationError("target_project_profile must be object", 2) require_non_empty(profile.get("main_language"), "target_project_profile.main_language") _require_string_list(profile.get("runtime"), "target_project_profile.runtime") - _require_string_list(profile.get("artifact_surfaces"), "target_project_profile.artifact_surfaces") - require_non_empty(profile.get("authority_boundary"), "target_project_profile.authority_boundary") + _require_string_list( + profile.get("artifact_surfaces"), "target_project_profile.artifact_surfaces" + ) + require_non_empty( + profile.get("authority_boundary"), "target_project_profile.authority_boundary" + ) require_non_empty(profile.get("operator_phase"), "target_project_profile.operator_phase") - _require_string_list(profile.get("allowed_input_surfaces"), "target_project_profile.allowed_input_surfaces") - require_non_empty(profile.get("allowed_mutation_boundary"), "target_project_profile.allowed_mutation_boundary") - _require_string_list(profile.get("verification_methods"), "target_project_profile.verification_methods") + _require_string_list( + profile.get("allowed_input_surfaces"), "target_project_profile.allowed_input_surfaces" + ) + require_non_empty( + profile.get("allowed_mutation_boundary"), "target_project_profile.allowed_mutation_boundary" + ) + _require_string_list( + profile.get("verification_methods"), "target_project_profile.verification_methods" + ) -def validate_compatibility_diagnosis(items: Any, *, require_adoption_unit: str | None = None) -> list[dict[str, Any]]: +def validate_compatibility_diagnosis( + items: Any, *, require_adoption_unit: str | None = None +) -> list[dict[str, Any]]: if not isinstance(items, list) or not items: raise AdopValidationError("compatibility_diagnosis must contain at least one item", 2) validated: list[dict[str, Any]] = [] @@ -192,7 +156,9 @@ def validate_compatibility_diagnosis(items: Any, *, require_adoption_unit: str | FIT_LANES, ) validated.append(item) - if require_adoption_unit and not any(str(item.get("adoption_unit")) == require_adoption_unit for item in validated): + if require_adoption_unit and not any( + str(item.get("adoption_unit")) == require_adoption_unit for item in validated + ): raise AdopValidationError("compatibility_diagnosis must include adoption_unit entry", 2) return validated @@ -277,7 +243,12 @@ def validate_comparison_payload(payload: dict[str, Any]) -> None: DECOMPOSITION_DECISION, DECOMPOSITION_DECISIONS, ) - for field in (ROOT_CAUSE_HYPOTHESIS, STRUCTURAL_GAP, "non_tool_alternative", "selection_reason"): + for field in ( + ROOT_CAUSE_HYPOTHESIS, + STRUCTURAL_GAP, + "non_tool_alternative", + "selection_reason", + ): require_non_empty(payload.get(field), field) require_non_empty(payload.get("adoption_unit"), "adoption_unit") validate_target_project_profile(payload.get("target_project_profile", {})) @@ -289,7 +260,11 @@ def validate_comparison_payload(payload: dict[str, Any]) -> None: if recommended_fit_lane: validate_choice(recommended_fit_lane, "recommended_fit_lane", FIT_LANES) matched = next( - (item for item in diagnoses if str(item.get("adoption_unit")) == str(payload.get("adoption_unit", ""))), + ( + item + for item in diagnoses + if str(item.get("adoption_unit")) == str(payload.get("adoption_unit", "")) + ), None, ) if matched and str(matched.get("recommended_fit_lane")) != recommended_fit_lane: @@ -330,7 +305,10 @@ def validate_trial_packet_payload(payload: dict[str, Any]) -> None: "landing_target", ): require_non_empty(payload.get(field), field) - if "write" in payload["trial_type"] and "isolated write sandbox" not in payload["sandbox_type"]: + if ( + payload["trial_type"] in WRITE_TRIAL_TYPES + and "isolated write sandbox" not in payload["sandbox_type"] + ): raise AdopValidationError("write trial requires isolated write sandbox", 13) for field in ( "candidate_shape", @@ -366,9 +344,17 @@ def validate_close_payload(payload: dict[str, Any]) -> None: why = payload.get("why_this_problem_recurred", "") require_non_empty(why, "why_this_problem_recurred") preventive = payload.get("preventive_action", []) - if not isinstance(preventive, list) or not preventive or not all(str(item).strip() for item in preventive): + if ( + not isinstance(preventive, list) + or not preventive + or not all(str(item).strip() for item in preventive) + ): raise AdopValidationError("preventive_action must contain at least one non-empty item", 2) - validate_choice(payload.get("recurring_control_decision", ""), "recurring_control_decision", RECURRING_CONTROL_DECISIONS) + validate_choice( + payload.get("recurring_control_decision", ""), + "recurring_control_decision", + RECURRING_CONTROL_DECISIONS, + ) validate_choice(payload.get("candidate_shape", ""), "candidate_shape", CANDIDATE_SHAPES) validate_choice( payload.get(DECOMPOSITION_DECISION, ""), @@ -437,7 +423,9 @@ def validate_coupling_entries(value: Any, field_name: str = "couplings") -> None if not isinstance(entry, dict): raise AdopValidationError(f"{where} must be an object", 2) require_non_empty(entry.get("path"), f"{where}.path") - validate_choice(str(entry.get("coupling_type", "")), f"{where}.coupling_type", COUPLING_TYPES) + validate_choice( + str(entry.get("coupling_type", "")), f"{where}.coupling_type", COUPLING_TYPES + ) validate_choice(str(entry.get("removal_cost", "")), f"{where}.removal_cost", REMOVAL_COSTS) if "detection_source" in entry: validate_choice( @@ -460,8 +448,20 @@ def validate_coupling_note_payload(payload: dict[str, Any]) -> None: def validate_artifact_schema(item: dict[str, Any]) -> None: - if item.get("schema_version") != SCHEMA_VERSION: + version = item.get("schema_version") + if ( + not isinstance(version, int) + or isinstance(version, bool) + or version < MIN_READABLE_SCHEMA_VERSION + ): raise AdopValidationError("schema_version invalid", 11) + if version > SCHEMA_VERSION: + # Forward-incompatible: a newer adop wrote this. Say so plainly instead of + # the generic "invalid" so the operator upgrades adop rather than deleting + # a record that is actually fine. + raise AdopValidationError( + f"schema_version {version} was written by a newer adop; upgrade adop to read it", 11 + ) artifact_type = item.get("artifact_type") if artifact_type not in ARTIFACT_TYPES: raise AdopValidationError(f"artifact_type invalid: {artifact_type}", 11) @@ -590,9 +590,13 @@ def lint_artifact_root(root: Path) -> list[str]: issues.append(f"promotion-note {item.get('artifact_id')} missing derived_from") if not item.get("landing_target"): issues.append(f"promotion-note {item.get('artifact_id')} missing landing_target") - intake = latest_by_type(root, CANDIDATE_INTAKE_NOTE, scene=str(item.get("related_scene", ""))) + intake = latest_by_type( + root, CANDIDATE_INTAKE_NOTE, scene=str(item.get("related_scene", "")) + ) if not intake: - issues.append(f"promotion-note {item.get('artifact_id')} missing candidate-intake-note history") + issues.append( + f"promotion-note {item.get('artifact_id')} missing candidate-intake-note history" + ) else: unknowns = unknown_tool_attribute_fields(intake) if unknowns: @@ -627,18 +631,19 @@ def lint_artifact_root(root: Path) -> list[str]: for parent_id in item.get("derived_from", []): expected_ok = any( - f"{parent_type}::{parent_id}" in by_id - for parent_type in ARTIFACT_TYPES + f"{parent_type}::{parent_id}" in by_id for parent_type in ARTIFACT_TYPES ) if not expected_ok: issues.append(f"derived_from missing target: {parent_id}") - seen_trial_ids = {str(i["artifact_id"]) for i in items if i.get("artifact_type") == TRIAL_PACKET} + seen_trial_ids = { + str(i["artifact_id"]) for i in items if i.get("artifact_type") == TRIAL_PACKET + } # Trials that have been closed: a trial-result exists that derives from the packet. closed_trial_ids: set[str] = set() for item in items: if item.get("artifact_type") == TRIAL_RESULT: - for parent_id in (item.get("derived_from") or []): + for parent_id in item.get("derived_from") or []: closed_trial_ids.add(str(parent_id)) for item in items: diff --git a/shared/templates/adop-governance-dashboard-template.html b/shared/templates/adop-governance-dashboard-template.html index 3743a16..80776b4 100644 --- a/shared/templates/adop-governance-dashboard-template.html +++ b/shared/templates/adop-governance-dashboard-template.html @@ -772,6 +772,45 @@ background: #f8fafc; } + .to-top { + position: fixed; + right: 24px; + bottom: 24px; + z-index: 30; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 10px 14px; + border: 1px solid var(--navy); + border-radius: 8px; + background: var(--navy); + color: #f5f7fa; + font: inherit; + font-weight: 700; + cursor: pointer; + box-shadow: var(--card-shadow); + opacity: 0; + transform: translateY(8px); + transition: opacity .18s ease, transform .18s ease; + pointer-events: none; + } + + .to-top.is-visible { + opacity: 1; + transform: translateY(0); + pointer-events: auto; + } + + .to-top:hover, + .to-top:focus-visible { + background: var(--navy-2); + outline: none; + } + + @media (max-width: 640px) { + .to-top { right: 14px; bottom: 14px; padding: 9px 12px; } + } + @media (max-width: 1180px) { .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } .hero-meta, @@ -1177,6 +1216,8 @@

Raw record files for this decision

+ + diff --git a/tests/conftest.py b/tests/conftest.py index a6df005..3d487a5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,9 +14,8 @@ if str(PYTHON_DIR) not in sys.path: sys.path.insert(0, str(PYTHON_DIR)) -import pytest # noqa: E402 - import adop_artifacts as artifacts # noqa: E402 +import pytest # noqa: E402 from adop_cli import main # noqa: E402 @@ -51,35 +50,93 @@ def promote_scene(run, root: str, *, scene: str = "lint", tool: str = "pylint") Leaves the scene in the `promote` state so retirement transitions can run. """ - assert run( - "quick-intake", "--artifact-root", root, - "--candidate", tool, "--source", "doc", - "--use-case", scene, "--why-now", "need bounded trial", - "--platform", "any", - "--license", "MIT", - "--cost", "free", - "--version", "1.0.0", - "--category", "cli", - "--ai-compatibility", "any", - "--data-flow-json", '{"destination":"local","data_types":["code"],"opt_in":true}', - ) == 0 - assert run( - "quick-compare", "--artifact-root", root, "--use-case", scene, - "--candidate", tool, "--candidate", "other-tool", "--selected", tool, - ) == 0 - assert run( - "quick-trial", "--artifact-root", root, "--use-case", scene, - "--mode", "read-only-comparison", "--executor", "ci", - "--decision-owner", "lead", "--landing-target", "ci/lint", - ) == 0 - assert run( - "quick-close-trial", "--artifact-root", root, - "--trial-id", "tr-001", "--verdict", "promote", - "--observed-effect", "works", - "--judgment-reason", "trial produced reusable value", - "--next-action", "promote into the lint workflow", - "--recurring-control-decision", "yes", - "--root-cause-hypothesis", "lint evaluation needed a stable reusable helper", - "--preventive-action", "document the approved lint usage scene", - "--why-this-problem-recurred", "the team had no explicit adoption record before the trial", - ) == 0 + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + tool, + "--source", + "doc", + "--use-case", + scene, + "--why-now", + "need bounded trial", + "--platform", + "any", + "--license", + "MIT", + "--cost", + "free", + "--version", + "1.0.0", + "--category", + "cli", + "--ai-compatibility", + "any", + "--data-flow-json", + '{"destination":"local","data_types":["code"],"opt_in":true}', + ) + == 0 + ) + assert ( + run( + "quick-compare", + "--artifact-root", + root, + "--use-case", + scene, + "--candidate", + tool, + "--candidate", + "other-tool", + "--selected", + tool, + ) + == 0 + ) + assert ( + run( + "quick-trial", + "--artifact-root", + root, + "--use-case", + scene, + "--mode", + "read-only-comparison", + "--executor", + "ci", + "--decision-owner", + "lead", + "--landing-target", + "ci/lint", + ) + == 0 + ) + assert ( + run( + "quick-close-trial", + "--artifact-root", + root, + "--trial-id", + "tr-001", + "--verdict", + "promote", + "--observed-effect", + "works", + "--judgment-reason", + "trial produced reusable value", + "--next-action", + "promote into the lint workflow", + "--recurring-control-decision", + "yes", + "--root-cause-hypothesis", + "lint evaluation needed a stable reusable helper", + "--preventive-action", + "document the approved lint usage scene", + "--why-this-problem-recurred", + "the team had no explicit adoption record before the trial", + ) + == 0 + ) diff --git a/tests/test_aggregate.py b/tests/test_aggregate.py new file mode 100644 index 0000000..d366a7a --- /dev/null +++ b/tests/test_aggregate.py @@ -0,0 +1,98 @@ +"""Cross-project aggregation: a read-only portfolio view across artifact roots.""" + +from __future__ import annotations + +import io +import json +from contextlib import redirect_stdout +from pathlib import Path + +from adop_cli import main + + +def _run_capture(*argv: str) -> tuple[int, str]: + buf = io.StringIO() + with redirect_stdout(buf): + rc = main(list(argv)) + return rc, buf.getvalue() + + +def test_aggregate_spans_multiple_roots(tmp_path): + a = str(tmp_path / "proj-a") + b = str(tmp_path / "proj-b") + assert ( + main( + [ + "quick-intake", + "--artifact-root", + a, + "--candidate", + "ruff", + "--source", + "doc", + "--use-case", + "lint", + "--why-now", + "x", + ] + ) + == 0 + ) + assert ( + main( + [ + "watch", + "--artifact-root", + b, + "--candidate", + "vale", + "--interest-reason", + "r", + "--use-case", + "docs", + ] + ) + == 0 + ) + rc, out = _run_capture("aggregate", "--root", a, "--root", b, "--json") + assert rc == 0 + payload = json.loads(out) + rows = payload["portfolio"] + by_scene = {(Path(r["root"]).name, r["scene"]): r for r in rows} + assert ("proj-a", "lint") in by_scene + assert by_scene[("proj-a", "lint")]["state"] == "proposed" + assert by_scene[("proj-a", "lint")]["tool"] == "ruff" + assert ("proj-b", "docs") in by_scene + assert by_scene[("proj-b", "docs")]["state"] == "watch" + + +def test_aggregate_missing_root_is_flagged(tmp_path): + rc, out = _run_capture("aggregate", "--root", str(tmp_path / "nope"), "--json") + assert rc == 0 + assert json.loads(out)["portfolio"][0]["state"] == "MISSING_ROOT" + + +def test_aggregate_text_output_groups_by_root(tmp_path): + a = str(tmp_path / "p") + assert ( + main( + [ + "quick-intake", + "--artifact-root", + a, + "--candidate", + "ruff", + "--source", + "doc", + "--use-case", + "lint", + "--why-now", + "x", + ] + ) + == 0 + ) + rc, out = _run_capture("aggregate", "--root", a) + assert rc == 0 + assert "ADOP Portfolio" in out + assert "lint: proposed (ruff)" in out diff --git a/tests/test_artifact_root_errors.py b/tests/test_artifact_root_errors.py index 74775ba..42b048d 100644 --- a/tests/test_artifact_root_errors.py +++ b/tests/test_artifact_root_errors.py @@ -17,8 +17,9 @@ def test_uncreatable_root_returns_io_error_not_traceback(run, root, capsys): parent_file.write_text("x", encoding="utf-8") bad_root = str(parent_file / "sub") # mkdir under a file -> OSError - code = run("watch", "--artifact-root", bad_root, - "--candidate", "ruff", "--interest-reason", "speed") + code = run( + "watch", "--artifact-root", bad_root, "--candidate", "ruff", "--interest-reason", "speed" + ) assert code == 11 out = capsys.readouterr().out @@ -33,9 +34,19 @@ def test_boundary_violation_returns_14(run, root, capsys): inside = str(project / "artifacts") code = run( - "quick-intake", "--artifact-root", inside, - "--target-project-root", str(project), - "--candidate", "x", "--source", "doc", "--use-case", "u", "--why-now", "w", + "quick-intake", + "--artifact-root", + inside, + "--target-project-root", + str(project), + "--candidate", + "x", + "--source", + "doc", + "--use-case", + "u", + "--why-now", + "w", ) assert code == 14 @@ -59,3 +70,80 @@ def test_lint_on_empty_root_exits_10(run, tmp_path, capsys): assert code == 10 out = capsys.readouterr().out assert "empty" in out + + +def _watch_payload(artifact_id: str) -> dict: + return { + "schema_version": 1, + "artifact_type": "watch-note", + "artifact_id": artifact_id, + "status": "active", + "created_at": "2026-01-01", + "candidate_or_tool": "x", + "interest_reason": "y", + } + + +def test_stale_lock_is_reclaimed(tmp_path): + import os + import time as _t + + import adop_artifacts as A + + A.ensure_artifact_root(tmp_path) + name = A.artifact_filename("watch-note", "wt-001") + lock = tmp_path / f".{name}.lock" + lock.write_text("") + old = _t.time() - 120 # far older than the stale threshold + os.utime(lock, (old, old)) + path = A.write_artifact(tmp_path, "watch-note", "wt-001", _watch_payload("wt-001")) + assert path.exists() + assert not lock.exists() # reclaimed and cleaned up + + +def test_fresh_lock_blocks(tmp_path): + import adop_artifacts as A + import pytest + + A.ensure_artifact_root(tmp_path) + name = A.artifact_filename("watch-note", "wt-002") + lock = tmp_path / f".{name}.lock" + lock.write_text("") # fresh lock (current mtime) + with pytest.raises(A.AdopArtifactError): + A.write_artifact(tmp_path, "watch-note", "wt-002", _watch_payload("wt-002")) + + +def test_concurrent_id_minting_no_duplicates(tmp_path): + """Many writers minting sequential ids under contention must not collide.""" + from concurrent.futures import ThreadPoolExecutor + + import adop_artifacts as A + + A.ensure_artifact_root(tmp_path) + + def mint(_n): + def factory(artifact_id): + return { + "schema_version": 1, + "artifact_type": "watch-note", + "artifact_id": artifact_id, + "status": "active", + "created_at": "2026-01-01", + "candidate_or_tool": "x", + "interest_reason": "y", + } + + artifact_id, _path = A.write_next_sequential_artifact(tmp_path, "watch-note", "wt", factory) + return artifact_id + + # Keep contention real but bounded: enough concurrent writers to exercise the + # lock + retry path (incl. the Windows PermissionError mapping) without letting + # a starved worker on a loaded CI runner exhaust the 64-attempt retry budget. + workers = 5 + with ThreadPoolExecutor(max_workers=workers) as ex: + ids = list(ex.map(mint, range(workers))) + + assert len(ids) == workers + assert len(set(ids)) == workers, f"duplicate ids minted: {sorted(ids)}" + written = sorted(p.name for p in tmp_path.glob("adop_watch-note_*.json")) + assert len(written) == workers diff --git a/tests/test_coupling.py b/tests/test_coupling.py index 843b06a..6b65f84 100644 --- a/tests/test_coupling.py +++ b/tests/test_coupling.py @@ -10,9 +10,8 @@ import json from pathlib import Path -import pytest - import adop_summary +import pytest from adop_validation import AdopValidationError, validate_coupling_note_payload COUPLING_NOTE = "coupling-note" @@ -24,11 +23,20 @@ def _summary(root: str, **kwargs) -> str: # --- create + report ------------------------------------------------------- + def test_couple_records_full_snapshot(run, root, latest): code = run( - "couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "pyproject.toml|config|edit|ruff config", - "--couple", "ci.yml|invocation|clean", + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "pyproject.toml|config|edit|ruff config", + "--couple", + "ci.yml|invocation|clean", ) assert code == 0 note = latest(root, COUPLING_NOTE, scene="lint") @@ -36,15 +44,27 @@ def test_couple_records_full_snapshot(run, root, latest): assert note["candidate_or_tool"] == "ruff" assert len(note["couplings"]) == 2 assert note["couplings"][0] == { - "path": "pyproject.toml", "coupling_type": "config", - "removal_cost": "edit", "note": "ruff config", + "path": "pyproject.toml", + "coupling_type": "config", + "removal_cost": "edit", + "note": "ruff config", } def test_couplings_report_headline_is_worst_removal_cost(run, root, capsys): - run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "ci.yml|invocation|clean", - "--couple", "src/legacy.py|reference|entangled") + run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "ci.yml|invocation|clean", + "--couple", + "src/legacy.py|reference|entangled", + ) capsys.readouterr() code = run("couplings", "--artifact-root", root) assert code == 0 @@ -55,8 +75,17 @@ def test_couplings_report_headline_is_worst_removal_cost(run, root, capsys): def test_couplings_json_report(run, root, capsys): - run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "pyproject.toml|config|edit") + run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "pyproject.toml|config|edit", + ) capsys.readouterr() run("couplings", "--artifact-root", root, "--json") payload = json.loads(capsys.readouterr().out) @@ -69,11 +98,22 @@ def test_couplings_json_report(run, root, capsys): def test_couple_via_json_input(run, root, latest): - couplings = json.dumps([ - {"path": "Makefile", "coupling_type": "invocation", "removal_cost": "clean"}, - ]) - code = run("couple", "--artifact-root", root, "--use-case", "build", "--tool", "make", - "--couplings-json", couplings) + couplings = json.dumps( + [ + {"path": "Makefile", "coupling_type": "invocation", "removal_cost": "clean"}, + ] + ) + code = run( + "couple", + "--artifact-root", + root, + "--use-case", + "build", + "--tool", + "make", + "--couplings-json", + couplings, + ) assert code == 0 note = latest(root, COUPLING_NOTE, scene="build") assert note["couplings"][0]["path"] == "Makefile" @@ -81,12 +121,33 @@ def test_couple_via_json_input(run, root, latest): # --- snapshot semantics ---------------------------------------------------- + def test_latest_coupling_note_wins(run, root, capsys): """Each couple call is a full snapshot; the report uses only the latest.""" - run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "a.py|reference|edit", "--couple", "b.py|reference|edit") - run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "a.py|reference|clean") # decoupled b.py, a.py now clean + run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "a.py|reference|edit", + "--couple", + "b.py|reference|edit", + ) + run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "a.py|reference|clean", + ) # decoupled b.py, a.py now clean capsys.readouterr() run("couplings", "--artifact-root", root) out = capsys.readouterr().out @@ -103,54 +164,90 @@ def test_couplings_empty_report(run, root, capsys): # --- validation ------------------------------------------------------------ + def test_validate_rejects_empty_couplings(): with pytest.raises(AdopValidationError): - validate_coupling_note_payload({ - "related_scene": "lint", "candidate_or_tool": "ruff", "couplings": [], - }) + validate_coupling_note_payload( + { + "related_scene": "lint", + "candidate_or_tool": "ruff", + "couplings": [], + } + ) def test_validate_rejects_bad_coupling_type(): with pytest.raises(AdopValidationError): - validate_coupling_note_payload({ - "related_scene": "lint", "candidate_or_tool": "ruff", - "couplings": [{"path": "x", "coupling_type": "bogus", "removal_cost": "edit"}], - }) + validate_coupling_note_payload( + { + "related_scene": "lint", + "candidate_or_tool": "ruff", + "couplings": [{"path": "x", "coupling_type": "bogus", "removal_cost": "edit"}], + } + ) def test_validate_rejects_bad_removal_cost(): with pytest.raises(AdopValidationError): - validate_coupling_note_payload({ - "related_scene": "lint", "candidate_or_tool": "ruff", - "couplings": [{"path": "x", "coupling_type": "config", "removal_cost": "bogus"}], - }) + validate_coupling_note_payload( + { + "related_scene": "lint", + "candidate_or_tool": "ruff", + "couplings": [{"path": "x", "coupling_type": "config", "removal_cost": "bogus"}], + } + ) def test_validate_accepts_detection_metadata(): - validate_coupling_note_payload({ - "related_scene": "lint", - "candidate_or_tool": "ruff", - "couplings": [{ - "path": "pyproject.toml", - "coupling_type": "config", - "removal_cost": "edit", - "detection_source": "surface-rule", - "confidence": "high", - }], - }) + validate_coupling_note_payload( + { + "related_scene": "lint", + "candidate_or_tool": "ruff", + "couplings": [ + { + "path": "pyproject.toml", + "coupling_type": "config", + "removal_cost": "edit", + "detection_source": "surface-rule", + "confidence": "high", + } + ], + } + ) def test_bad_couple_flag_format_returns_validation_error(run, root): - code = run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "missing-fields") + code = run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "missing-fields", + ) assert code == 2 # PATH|TYPE|COST required # --- summary integration --------------------------------------------------- + def test_summary_tool_entanglement_section(run, root): - run("couple", "--artifact-root", root, "--use-case", "lint", "--tool", "ruff", - "--couple", "pyproject.toml|config|edit", "--couple", "x.py|reference|entangled") + run( + "couple", + "--artifact-root", + root, + "--use-case", + "lint", + "--tool", + "ruff", + "--couple", + "pyproject.toml|config|edit", + "--couple", + "x.py|reference|entangled", + ) text = _summary(root) assert "Tool Entanglement" in text assert "- ruff @ lint: 2 file(s), detachment: entangled" in text diff --git a/tests/test_html_render.py b/tests/test_html_render.py index 900379b..e2c0b20 100644 --- a/tests/test_html_render.py +++ b/tests/test_html_render.py @@ -25,9 +25,12 @@ def test_render_html_writes_first_time_guidance_and_promote_command_guard(run, r rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), - "--scene", "lint-ci", + "--artifact-root", + root, + "--output", + str(output), + "--scene", + "lint-ci", ) assert rc == 0 @@ -36,7 +39,10 @@ def test_render_html_writes_first_time_guidance_and_promote_command_guard(run, r lane = payload["lanes"][0] assert 'data-adop-template="governance-dashboard-v2"' in text - assert "ADOP shows which external tools are under review, approved, blocked, retired, or kept only as history." in text + assert ( + "ADOP shows which external tools are under review, approved, blocked, retired, or kept only as history." + in text + ) assert "Use this page to understand status" in text assert "Run commands only when you are changing a decision" in text assert "You are a reader if you only need to understand status" in text @@ -67,8 +73,10 @@ def test_render_html_empty_state_includes_start_commands(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), + "--artifact-root", + root, + "--output", + str(output), ) assert rc == 0 @@ -79,11 +87,19 @@ def test_render_html_empty_state_includes_start_commands(run, root): assert payload["metrics"]["recorded_lanes"] == 0 assert payload["metrics"]["preview_sample_lanes"] == 0 assert payload["empty_state_commands"][0]["command"] == "adop init" - assert payload["empty_state_commands"][1]["command"].startswith("adop quick-intake --candidate --source doc --scene ") - assert payload["empty_state_commands"][2]["command"].startswith("adop quick-compare --scene ") - assert payload["empty_state_commands"][3]["command"].startswith("adop quick-trial --scene ") + assert payload["empty_state_commands"][1]["command"].startswith( + "adop quick-intake --candidate --source doc --scene " + ) + assert payload["empty_state_commands"][2]["command"].startswith( + "adop quick-compare --scene " + ) + assert payload["empty_state_commands"][3]["command"].startswith( + "adop quick-trial --scene " + ) assert payload["empty_state_commands"][4]["command"] == "adop status" - assert payload["empty_state_commands"][5]["command"].startswith("adop render-html --artifact-root .adop") + assert payload["empty_state_commands"][5]["command"].startswith( + "adop render-html --artifact-root .adop" + ) def test_render_html_can_warn_when_sample_lanes_are_mixed_in(run, root): @@ -92,16 +108,22 @@ def test_render_html_can_warn_when_sample_lanes_are_mixed_in(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), - "--sample-board-count", "10", + "--artifact-root", + root, + "--output", + str(output), + "--sample-board-count", + "10", ) assert rc == 0 text = output.read_text(encoding="utf-8") payload = _extract_payload(text) - assert "Preview only: 9 sample decisions are shown for layout checking. Do not treat them as project records." in text + assert ( + "Preview only: 9 sample decisions are shown for layout checking. Do not treat them as project records." + in text + ) assert "Preview sample" in text assert payload["metrics"]["managed_lanes"] == 10 assert payload["metrics"]["recorded_lanes"] == 1 @@ -122,10 +144,14 @@ def test_render_html_explains_why_a_blocked_command_should_be_used(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), - "--scene", "dep-alerts", - "--sample-board-count", "10", + "--artifact-root", + root, + "--output", + str(output), + "--scene", + "dep-alerts", + "--sample-board-count", + "10", ) assert rc == 0 @@ -137,7 +163,9 @@ def test_render_html_explains_why_a_blocked_command_should_be_used(run, root): assert lane["next_command"].startswith("adop unblock --scene dep-alerts") assert lane["next_command_details"][0]["value"].startswith("This decision is blocked.") assert "The blocker has been cleared and you need to record what changed." in text - assert "ADOP removes the blocked state and reopens the decision for the next review step." in text + assert ( + "ADOP removes the blocked state and reopens the decision for the next review step." in text + ) def test_render_html_includes_large_board_controls_and_limits(run, root): @@ -146,9 +174,12 @@ def test_render_html_includes_large_board_controls_and_limits(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), - "--sample-board-count", "100", + "--artifact-root", + root, + "--output", + str(output), + "--sample-board-count", + "100", ) assert rc == 0 @@ -165,7 +196,10 @@ def test_render_html_includes_large_board_controls_and_limits(run, root): assert "Historical" in text assert "Show more current decisions" in text assert "Show more past decisions" in text - assert "Read the table first. The detail view explains the decision. The command box is separate and only matters for the person changing status." in text + assert ( + "Read the table first. The detail view explains the decision. The command box is separate and only matters for the person changing status." + in text + ) assert "Layout-check examples, not project decisions" in text assert "Needed next from owner" in text assert "Latest update" in text @@ -184,8 +218,10 @@ def test_render_html_keeps_accessibility_hooks_for_modal_and_copy(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), + "--artifact-root", + root, + "--output", + str(output), ) assert rc == 0 @@ -195,7 +231,7 @@ def test_render_html_keeps_accessibility_hooks_for_modal_and_copy(run, root): assert 'aria-label="Copy primary command"' in text assert 'aria-label="Copy retirement command"' in text assert 'event.key === "Escape"' in text - assert 'uiState.lastFocusedRow.focus()' in text + assert "uiState.lastFocusedRow.focus()" in text assert 'document.execCommand("copy")' in text @@ -205,8 +241,10 @@ def test_render_html_separates_reader_and_operator_guidance(run, root): rc = run( "render-html", - "--artifact-root", root, - "--output", str(output), + "--artifact-root", + root, + "--output", + str(output), ) assert rc == 0 @@ -216,7 +254,550 @@ def test_render_html_separates_reader_and_operator_guidance(run, root): assert "For readers" in text assert "For operators" in text assert "Operator only" in text - assert "You are the operator only if you are the person who records approve, hold, reject, unblock, retire, or trial changes in ADOP." in text + assert ( + "You are the operator only if you are the person who records approve, hold, reject, unblock, retire, or trial changes in ADOP." + in text + ) assert "Open operator action" in text assert payload["reader_steps"][0] == "Check the summary counts" assert payload["operator_steps"][0] == "Open the decision you need to change" + + +def test_in_trial_lane_shows_packet_envelope(run, root): + from adop_html import build_dashboard_payload + + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "T", + "--source", + "doc", + "--use-case", + "t", + "--why-now", + "x", + ) + == 0 + ) + assert ( + run( + "quick-compare", + "--artifact-root", + root, + "--use-case", + "t", + "--candidate", + "T", + "--candidate", + "U", + "--selected", + "T", + ) + == 0 + ) + assert ( + run( + "quick-trial", + "--artifact-root", + root, + "--use-case", + "t", + "--mode", + "review-assist", + "--executor", + "ci", + "--decision-owner", + "o", + "--landing-target", + "x", + ) + == 0 + ) + lane = next( + lane for lane in build_dashboard_payload(Path(root))["lanes"] if lane["scene"] == "t" + ) + assert lane["allowed"] != ["No explicit allow-list recorded yet"] + assert "file read" in lane["allowed"] + assert any("inside target project" in f for f in lane["forbidden"]) + + +def test_blocked_lane_shows_block_reason(run, root): + from adop_html import build_dashboard_payload + + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "B", + "--source", + "doc", + "--use-case", + "b", + "--why-now", + "x", + ) + == 0 + ) + assert ( + run( + "block", + "--artifact-root", + root, + "--use-case", + "b", + "--block-reason", + "LICENSE_UNDECIDED", + "--unblock-condition", + "legal ok", + "--owner", + "o", + ) + == 0 + ) + lane = next( + lane for lane in build_dashboard_payload(Path(root))["lanes"] if lane["scene"] == "b" + ) + assert "LICENSE_UNDECIDED" in lane["decision"] + assert any("LICENSE_UNDECIDED" in str(r["value"]) for r in lane["rationale"]) + + +def test_proposed_lane_shows_why_now(run, root): + from adop_html import build_dashboard_payload + + assert ( + run( + "intake", + "--artifact-root", + root, + "--candidate", + "P", + "--candidate-shape", + "atomic", + "--source", + "doc", + "--scene", + "p", + "--lane", + "assistance", + "--reason", + "WHYNOW_TEXT", + "--root-cause-hypothesis", + "DIFFERENT_RCH", + "--platform", + "any", + "--license", + "MIT", + "--cost", + "free", + "--version", + "1.0", + "--category", + "cli", + "--ai-compatibility", + "any", + "--data-flow-json", + '{"destination":"local","data_types":["code"],"opt_in":true}', + ) + == 0 + ) + lane = next( + lane for lane in build_dashboard_payload(Path(root))["lanes"] if lane["scene"] == "p" + ) + why_now = next(r["value"] for r in lane["rationale"] if r["label"] == "Why now") + assert why_now == "WHYNOW_TEXT" + + +def test_no_wrong_deprecation_flag_anywhere(): + py = Path("shared/python") + cli = (py / "adop_cli.py").read_text(encoding="utf-8") + html = (py / "adop_html.py").read_text(encoding="utf-8") + assert "--deprecation-reason" not in cli + assert "--deprecation-reason" not in html + assert "--retirement-reason" in html + assert "wa-001" not in html + + +def test_modal_open_moves_focus_and_traps(run, root): + from adop_html import render_dashboard_html + + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "A", + "--source", + "doc", + "--use-case", + "a", + "--why-now", + "x", + ) + == 0 + ) + html = render_dashboard_html(Path(root)) + open_fn = html.split("function openLaneDetail")[1].split("function closeLaneDetail")[0] + assert 'document.getElementById("detail-close").focus()' in open_fn + assert "trapModalTab" in html + + +def test_watch_lane_shows_interest_reason(run, root): + from adop_html import build_dashboard_payload + + assert ( + run( + "watch", + "--artifact-root", + root, + "--candidate", + "vale", + "--interest-reason", + "EDITORIAL_MARK", + "--use-case", + "docs-style", + ) + == 0 + ) + lane = next( + lane + for lane in build_dashboard_payload(Path(root))["lanes"] + if lane["scene"] == "docs-style" + ) + assert any("EDITORIAL_MARK" in str(r["value"]) for r in lane["rationale"]) + + +def test_pre_trial_reject_shows_reason_not_trial(run, root): + from adop_html import build_dashboard_payload + + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "snyk", + "--source", + "doc", + "--use-case", + "dep-x", + "--why-now", + "risk", + ) + == 0 + ) + assert ( + run( + "reject", + "--artifact-root", + root, + "--use-case", + "dep-x", + "--reject-reason", + "COST_FIT_MARK", + ) + == 0 + ) + lane = next( + lane for lane in build_dashboard_payload(Path(root))["lanes"] if lane["scene"] == "dep-x" + ) + assert "trial closed" not in lane["decision"].lower() + assert "COST_FIT_MARK" in lane["decision"] or any( + "COST_FIT_MARK" in str(r["value"]) for r in lane["rationale"] + ) + + +def test_scan_skips_oversized_file(run, root, tmp_path): + target = tmp_path / "big" + target.mkdir() + (target / "huge.cfg").write_bytes(b"eslint\n" + b"x" * (6 * 1024 * 1024)) + (target / "small.cfg").write_text("eslint config here\n") + from adop_cli import _scan_target_for_tool + + couplings = _scan_target_for_tool(target, "eslint", []) + paths = {c["path"] for c in couplings} + assert "huge.cfg" not in paths # oversized file skipped + + +def _visible_blob(root_str, scene): + import json as _j + + from adop_html import build_dashboard_payload + + lane = next( + lane for lane in build_dashboard_payload(Path(root_str))["lanes"] if lane["scene"] == scene + ) + return _j.dumps({"decision": lane["decision"], "rationale": lane["rationale"]}), lane + + +def test_deprecated_lane_surfaces_retirement_reason(run, root): + promote_scene(run, root, scene="lint", tool="pylint") + assert ( + run( + "deprecate", + "--artifact-root", + root, + "--use-case", + "lint", + "--retirement-reason", + "RETIRE_MARK", + "--replacement-candidate", + "ruff", + "--timeline", + "Q3", + ) + == 0 + ) + blob, _ = _visible_blob(root, "lint") + assert "RETIRE_MARK" in blob + + +def test_migrating_lane_surfaces_migration(run, root): + promote_scene(run, root, scene="lint", tool="pylint") + run( + "deprecate", + "--artifact-root", + root, + "--use-case", + "lint", + "--retirement-reason", + "r", + "--replacement-candidate", + "ruff", + "--timeline", + "Q3", + ) + assert ( + run( + "migrate", + "--artifact-root", + root, + "--use-case", + "lint", + "--migration-target", + "MIGTGT_MARK", + "--migration-plan", + "MIGPLAN_MARK", + ) + == 0 + ) + blob, _ = _visible_blob(root, "lint") + assert "MIGTGT_MARK" in blob or "MIGPLAN_MARK" in blob + + +def test_archived_lane_surfaces_end_date(run, root): + promote_scene(run, root, scene="lint", tool="pylint") + run( + "deprecate", + "--artifact-root", + root, + "--use-case", + "lint", + "--retirement-reason", + "r", + "--replacement-candidate", + "ruff", + "--timeline", + "Q3", + ) + assert ( + run( + "archive", + "--artifact-root", + root, + "--use-case", + "lint", + "--end-date", + "2026-07-01", + "--successor-tool", + "SUCC_MARK", + ) + == 0 + ) + blob, _ = _visible_blob(root, "lint") + assert "2026-07-01" in blob or "SUCC_MARK" in blob + + +def test_hold_lane_decision_mentions_hold(run, root): + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "mypy", + "--source", + "doc", + "--use-case", + "h", + "--why-now", + "x", + ) + == 0 + ) + assert ( + run( + "quick-compare", + "--artifact-root", + root, + "--use-case", + "h", + "--candidate", + "mypy", + "--candidate", + "y", + "--selected", + "mypy", + ) + == 0 + ) + assert ( + run( + "quick-trial", + "--artifact-root", + root, + "--use-case", + "h", + "--mode", + "review-assist", + "--executor", + "ci", + "--decision-owner", + "l", + "--landing-target", + "ci", + ) + == 0 + ) + assert ( + run( + "quick-close-trial", + "--artifact-root", + root, + "--trial-id", + "tr-001", + "--verdict", + "hold", + "--observed-effect", + "HOLDREASON_MARK", + ) + == 0 + ) + _, lane = _visible_blob(root, "h") + assert "HOLDREASON_MARK" in lane["decision"] or any( + "HOLDREASON_MARK" in str(r["value"]) for r in lane["rationale"] + ) + + +def test_sample_board_renders_preview_lanes(run, root): + from adop_html import build_dashboard_payload + + payload = build_dashboard_payload(Path(root), sample_board_count=6) + assert payload["sample_rows_included"] > 0 + assert payload["preview_warning"] + assert all(lane["decision"] for lane in payload["lanes"]) + assert all(lane["rationale"] for lane in payload["lanes"]) + + +def test_render_html_multistate_embeds_all_lanes(run, root): + from adop_html import render_dashboard_html + + assert ( + run( + "watch", + "--artifact-root", + root, + "--candidate", + "vale", + "--interest-reason", + "r", + "--use-case", + "w-scene", + ) + == 0 + ) + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "ruff", + "--source", + "doc", + "--use-case", + "p-scene", + "--why-now", + "x", + ) + == 0 + ) + html = render_dashboard_html(Path(root)) + payload = _extract_payload(html) + scenes = {lane["scene"] for lane in payload["lanes"]} + assert {"w-scene", "p-scene"} <= scenes + for lane in payload["lanes"]: + assert lane["decision"] + assert lane["rationale"] + + +def test_large_sample_board_clones_seed_lanes(run, root): + from adop_html import build_dashboard_payload + + payload = build_dashboard_payload(Path(root), sample_board_count=40) + assert payload["metrics"]["managed_lanes"] == 40 + assert payload["sample_rows_included"] == 40 + # cloned sample lanes still carry renderable fields + assert all(lane["decision"] and lane["rationale"] for lane in payload["sample_lanes"]) + + +def test_historical_filter_opens_history_details(run, root): + from adop_html import render_dashboard_html + + assert ( + run( + "watch", + "--artifact-root", + root, + "--candidate", + "x", + "--interest-reason", + "r", + "--use-case", + "s", + ) + == 0 + ) + html = render_dashboard_html(Path(root)) + assert 'uiState.filter === "historical"' in html + assert "historyShell.open = true" in html + + +def test_dashboard_has_back_to_top(run, root): + from adop_html import render_dashboard_html + + assert ( + run( + "quick-intake", + "--artifact-root", + root, + "--candidate", + "ruff", + "--source", + "doc", + "--use-case", + "lint", + "--why-now", + "x", + ) + == 0 + ) + html = render_dashboard_html(Path(root)) + assert '