From edf5b3572fc924af88cd0e0dfaa1a49dc6a7ec98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Sun, 30 Aug 2026 19:03:07 +0800 Subject: [PATCH] refactor(toolkit): retire integrity-check surface --- .../integrity-check-template.md | 341 ----------- .../work-bundle-integrity-check.md | 536 ------------------ references/wb-create-rule-validation.yaml | 3 - rules/index.yaml | 35 -- .../rule-integrity-check-agent-authority.md | 35 -- .../rule-integrity-check-loading-boundary.md | 33 -- .../rule-integrity-check-report-lifecycle.md | 35 -- .../rule-integrity-check-scope.md | 34 -- .../rule-integrity-check-script-boundary.md | 37 -- rules/work-bundle/wb-create-rule.md | 4 +- scripts/integrity-check/common/report_io.py | 143 ----- scripts/integrity-check/common/status.py | 60 -- scripts/integrity_check_report.py | 441 -------------- scripts/work-bundle/README.md | 3 +- scripts/work-bundle/dispatcher.py | 6 +- scripts/work-bundle/integrity.py | 20 - scripts/work-bundle/rules.py | 3 +- scripts/work-bundle/skill_registry.py | 5 + skills/wb-create-rule/SKILL.md | 5 +- skills/wb-integrity-check/SKILL.md | 47 -- tests/test_integrity_check_report.py | 124 ---- tests/test_retired_integrity_check.py | 51 ++ 22 files changed, 62 insertions(+), 1939 deletions(-) delete mode 100644 references/integrity-check/integrity-check-template.md delete mode 100644 references/integrity-check/work-bundle-integrity-check.md delete mode 100644 rules/integrity-check/rule-integrity-check-agent-authority.md delete mode 100644 rules/integrity-check/rule-integrity-check-loading-boundary.md delete mode 100644 rules/integrity-check/rule-integrity-check-report-lifecycle.md delete mode 100644 rules/integrity-check/rule-integrity-check-scope.md delete mode 100644 rules/integrity-check/rule-integrity-check-script-boundary.md delete mode 100644 scripts/integrity-check/common/report_io.py delete mode 100644 scripts/integrity-check/common/status.py delete mode 100644 scripts/integrity_check_report.py delete mode 100644 scripts/work-bundle/integrity.py delete mode 100644 skills/wb-integrity-check/SKILL.md delete mode 100644 tests/test_integrity_check_report.py create mode 100644 tests/test_retired_integrity_check.py diff --git a/references/integrity-check/integrity-check-template.md b/references/integrity-check/integrity-check-template.md deleted file mode 100644 index 7f049f1..0000000 --- a/references/integrity-check/integrity-check-template.md +++ /dev/null @@ -1,341 +0,0 @@ ---- -report_id: wbi- -checker_skill: work-bundle-integrity-check -report_status: draft # draft | active | partially_fixed | closed | superseded -checked_at: -updated_at: -actor: agent | user | tool -user_bundle_root: ~/.work-bundle -source_bundle_root: -project_registry: ~/.work-bundle/registry/projects.yaml -scope: - - ~/.work-bundle/ - - / -out_of_scope: - - /.work-bundle/ unless explicitly requested ---- - -# Work-Bundle Integrity Check: - -## Executive Summary - -```yaml -total_files_scanned: -orphan_files: -broken_references: -weak_rules: -incomplete_skills: -missing_scripts: -script_responsibility_issues: -compression_loading_issues: -project_registry_issues: -installed_vs_source_drift_issues: -critical_issues: -high_issues: -medium_issues: -low_issues: -``` - -Summary: - -```text - -``` - -## Status History - -| Timestamp | Actor | From | To | Reason | Evidence | -|---|---|---|---|---|---| - -## Issue Status Summary - -```yaml -open: -fixed: -dismissed: -converted: -superseded: -``` - -## Critical Issues - -| Issue ID | Issue Status | Severity | Type | Affected File / Area | Risk | Recommended Fix | -|---|---|---|---|---|---|---| - -## Orphan File Findings - -| Issue ID | File | Root | Issue Status | Classification | Useful | Correct Owner | Recommended Action | -|---|---|---|---|---|---:|---|---| - -### WBI-: - -```yaml -issue_id: WBI- -issue_status: open # open | fixed | dismissed | converted | superseded -severity: critical | high | medium | low -root: user_bundle | source_bundle -file: -classification: useful_unreferenced | obsolete | duplicate | generated_artifact | historical_source | unknown -useful: yes | no | unknown -correct_owner: user-level-bootstrap | source-app-bootstrap | user-level-registry | source-registry | work-bundle-rules | work-bundle-skills | source-documentation | source-templates | knowledge-base | orchestration | none | unknown -recommended_action: link_from_entry | move | archive | remove | keep_ignored | compact_then_link | ask_user -repairable: true | false -repair_mode: edit_reference | move_file | archive_file | delete_file | rewrite_rule | rewrite_skill | ask_user -requires_user_decision: true | false -``` - -Reason: - -```text - -``` - -Recommended fix: - -```text - -``` - -Verification evidence after fix: - -```text - -``` - -## Broken References - -| Issue ID | Issue Status | Referring File | Missing Target | Severity | Recommended Fix | -|---|---|---|---|---|---| - -## Rule Branch Consistency - -| Issue ID | Issue Status | Rule File | Rule Status | Problems | Severity | Recommended Fix | -|---|---|---|---|---|---|---| - -Check dimensions: - -```text -- enable condition -- scope -- restriction strength -- must / should / may language -- forbidden behavior -- precedence -- validation requirement -- source knowledge / rationale -- registry reachability -- conflict with broader rule -``` - -## Skill Branch Consistency - -| Issue ID | Issue Status | Skill | Skill Status | Problems | Severity | Recommended Fix | -|---|---|---|---|---|---|---| - -Check dimensions: - -```text -- activation condition -- do-not-use condition -- input expectations -- output locations -- workflow/reference links -- script references -- output contract -- forbidden behavior -- overlap with sibling skills -- registry reachability -``` - -## Script Reference Consistency - -| Issue ID | Issue Status | Script | Referenced By | Script Status | Severity | Recommended Fix | -|---|---|---|---|---|---|---| - -## Script Responsibility Boundaries - -| Issue ID | Issue Status | Script | Responsibility Issue | Severity | Recommended Action | Recommended Fix | -|---|---|---|---|---|---|---| - -Responsibility contract: - -```text -Scripts may create reports, preserve WBI issue IDs, append agent-authored issues, update issue status, append status history, generate sidecar status summaries, archive reports by explicit request, and validate report-file structure. - -Scripts must not own policy, validate integrity as authority, classify orphans, generate recommendations, compare installed/source drift as authority, close issues without evidence, execute project code, or crawl old Project Registry project roots by default. -``` - -Script boundary rule source: - -```text -rules/integrity-check/index.yaml -``` - -## Work-Bundle Compression and Conditional Loading - -| Issue ID | Issue Status | File / Rule | Compression Issue | Runtime Loading Risk | Recommended Action | Recommended Fix | -|---|---|---|---|---|---|---| - -Check required targets: - -```text -agent-bootstrap -project-domain-profile.yaml -repository-binding.md -roadmap files -``` - -Startup loading validation: - -```yaml -startup_loads_minimal_bootstrap_rules: true | false | unknown -startup_loads_project_registry_metadata: true | false | unknown -startup_loads_latest_working_on_selection: true | false | unknown -startup_loads_skill_rule_registry_metadata: true | false | unknown -startup_loads_active_global_safety_boundaries: true | false | unknown -startup_loads_next_load_routing_rules: true | false | unknown -startup_loads_full_roadmap_files: true | false | unknown -startup_loads_full_project_domain_profile: true | false | unknown -startup_loads_full_repository_binding: true | false | unknown -startup_loads_all_project_notes: true | false | unknown -startup_loads_all_skill_references: true | false | unknown -startup_loads_all_rules: true | false | unknown -``` - -Compression principles checked: - -```text -- rule isolation -- conditional loading -- skill-scope-aware rules -- lazy loading -- metadata-first registries -- compact runtime references -- separation of runtime instruction vs reference detail vs historical design -``` - -## Registry Consistency - -| Issue ID | Issue Status | Registry | Registry Status | Problems | Severity | Recommended Fix | -|---|---|---|---|---|---|---| - -Registry loading check: - -```yaml -metadata_first: true | false | unknown -heavy_files_embedded_eagerly: true | false | unknown -heavy_files_referenced_lazily: true | false | unknown -startup_safe: true | false | unknown -``` - -## Project Registry Consistency - -Project Registry path: - -```text -~/.work-bundle/registry/projects.yaml -``` - -| Issue ID | Issue Status | Issue Type | Severity | Project | Recommended Fix | -|---|---|---|---|---|---| - -Latest-working-on validation: - -```yaml -latest_working_on_source: -latest_working_on_project: -registry_contains_project: true | false -project_status: active | inactive | archived | deprecated | current | unknown -safe_for_initiative_browsing: true | false -problem: -``` - -## Installed-vs-Source Drift - -| Issue ID | Issue Status | File | Drift Type | Severity | Recommended Fix | -|---|---|---|---|---|---| - -## Authority / Precedence Conflicts - -| Issue ID | Issue Status | Higher Authority | Lower Authority | Conflict | Severity | Recommended Fix | -|---|---|---|---|---|---|---| - -## Knowledge Base Updates Required After Fix - -| Issue ID | Fixed By | Knowledge / Rule / Skill / Registry Updates Required | Verification Evidence | -|---|---|---|---| - -Use this section to prevent stale instruction drift. If a fix changes durable behavior, update the corresponding source of truth, not only the broken file. - -Examples: - -```text -- If a workflow problem is fixed, update the workflow instruction or skill reference. -- If a rule weakness is fixed, update the rule file and registry metadata. -- If a skill explanation is fixed, update SKILL.md and related references. -- If Project Registry behavior is fixed, update the Project Registry contract and latest-working-on rule. -- If source/install structure changes, update project-structure documentation and install/bootstrap rules. -- If a compression/loading problem is fixed, update bootstrap instructions, registry metadata, skill references, workflow instructions, or source documentation so future agents know what loads by default and what loads on demand. -- If a script responsibility problem is fixed, update the script and `rules/integrity-check/*.yaml` when the responsibility contract changes. -``` - -## Recommended Fix Plan - -Apply fixes in this order: - -```text -1. Fix broken bootstrap or runtime authority references. -2. Fix Project Registry issues. -3. Fix script responsibility violations that can mutate files, execute code, close issues, crawl project roots, or own validation authority. -4. Fix context-loading/compression problems that cause token waste. -5. Fix broken references. -6. Resolve installed-vs-source drift. -7. Register useful orphan skills/rules/scripts/templates. -8. Strengthen weak rules. -9. Clarify incomplete skills. -10. Archive or remove obsolete files. -11. Normalize registry entries. -12. Re-run targeted validation. -13. Update report and issue statuses. -``` - -## Proposed Patch Summary - -| Target File | Change Type | Related Issue IDs | Patch Summary | -|---|---|---|---| - -## Open Questions - -| Question ID | Related Issue IDs | Question | Blocking | Recommended Decision Owner | -|---|---|---|---:|---| - -## Closure Checklist - -A report can be marked `closed` only when: - -```text -All issues are fixed, dismissed, converted, or superseded. -Every fixed issue has verification evidence. -Every fix that changes durable behavior has updated the corresponding knowledge/rule/skill/registry source of truth. -No fixed issue leaves stale instruction, project-structure, workflow, or registry documentation behind. -No fixed compression/loading issue leaves stale bootstrap, registry metadata, skill reference, workflow, or source documentation behind. -No fixed script responsibility issue leaves stale script behavior, script documentation, or `rules/integrity-check/*.yaml` contract behind. -No helper script can mutate files, execute project code, crawl old project roots, close issues, classify orphans, or generate integrity recommendations without explicit agent/user authority. -Startup/bootstrap no longer eagerly loads roadmap files, project-domain-profile.yaml, repository-binding.md, all project notes, all skill references, or all rules unless explicitly required. -``` - -## Final Validation - -```yaml -validation_status: not_run | passed | failed | partial -validated_at: -validated_by: agent | user | tool -remaining_critical_issues: -remaining_high_issues: -remaining_script_responsibility_issues: -remaining_compression_loading_issues: -helper_script_boundaries_safe: true | false | unknown -startup_context_load_safe: true | false | unknown -report_can_close: true | false -reason_if_not_closable: -``` \ No newline at end of file diff --git a/references/integrity-check/work-bundle-integrity-check.md b/references/integrity-check/work-bundle-integrity-check.md deleted file mode 100644 index a1b2433..0000000 --- a/references/integrity-check/work-bundle-integrity-check.md +++ /dev/null @@ -1,536 +0,0 @@ -# work-bundle-integrity-check - -Use this skill to audit the `work-bundle` system itself, not individual project knowledge bases by default. - -Runtime enforcement rules are extracted under `rules/integrity-check/` and indexed by `rules/integrity-check/index.yaml`. - -Primary goals: - -```text -- detect orphan files, broken references, stale registries, incomplete skills, weak rules, invalid scripts -- validate Project Registry and latest-working-on safety -- detect installed-vs-source drift between `~/.work-bundle/` and `/` -- detect excessive context loading and recommend work-bundle compression -- validate integrity-check helper scripts stay within report/status/file-management responsibilities and do not become policy validators -- write lifecycle-tracked integrity reports back into the work-bundle knowledge base -``` - -## Scope - -Default roots: - -```text -~/.work-bundle/ -/ -``` - -Required runtime authority: - -```text -~/.work-bundle/registry/projects.yaml -``` - -Out of scope by default: - -```text -/.work-bundle/ -``` - -Do not validate individual project-scoped `.work-bundle/` directories unless the user explicitly asks. Project bundles may be sampled only to verify that global work-bundle rules describe a valid project-level installation contract. - -## Output - -Write reports to: - -```text -~/.work-bundle/knowledge/integrity-checks/-work-bundle-integrity-check.md -``` - -Use `integrity-check-template.md` as the report template. - -Do not overwrite previous reports. - -## Authority Order - -Treat these as authority layers, highest first: - -```text -1. User-level bootstrap/runtime loader in `~/.work-bundle/` -2. Project Registry: `~/.work-bundle/registry/projects.yaml` -3. Source bootstrap rules: `/app-bootstrap/` -4. User-level registries: `~/.work-bundle/registries/` -5. Source registries: `/registries/` -6. Global work-bundle rules -7. Work-bundle skill registry -8. Individual skill `SKILL.md` files -9. Skill reference files -10. Skill scripts -11. Knowledge notes and generated reports -``` - -A file is reachable only if referenced directly or indirectly from an accepted authority entry point for its root. - -Runtime files under `~/.work-bundle/` must be reachable from runtime loader, Project Registry, active registry, active rule, active skill, or latest-working-on contract. - -Source files under `/` must have a clear source owner: registry, manifest, docs index, template index, skill, rule, or script reference. - -## Lifecycle - -Report status: - -```text -draft | active | partially_fixed | closed | superseded -``` - -Issue status: - -```text -open | fixed | dismissed | converted | superseded -``` - -A report may be closed only when every issue is: - -```text -fixed | dismissed | converted | superseded -``` - -Every transition must record timestamp, actor, previous status, new status, reason, and evidence. - -## Core Checks - -For every discovered file, answer: - -```text -Is it reachable? -Is it useful? -Which authority should own/reference it? -Should it be removed, archived, ignored, compacted, split, or lazy-loaded? -Does it force unnecessary context loading? -What exact fix is recommended? -What source-of-truth files must be updated after the fix? -Does any helper script exceed its allowed responsibility? -Does any script attempt to own policy, validation judgment, orphan classification, compression judgment, or repair authority? -``` - -## Workflow - -### 1. Inventory - -Build an inventory for both default roots when available. - -Record: - -```yaml -root: user_bundle | source_bundle -path: -type: rule | skill | script | registry | knowledge | orchestration | documentation | template | unknown -install_state: installed_only | source_only | mirrored | generated | unknown -runtime_role: active_runtime | source_definition | packaging_asset | documentation | generated_report | archive | unknown -``` - -Classify by path and content. Do not rely only on extension. - -### 2. Reference Graph - -Detect references from: - -```text -markdown links -plain paths -script paths -skill references -registry entries -Project Registry entries -front matter references -``` - -Normalize paths. Report broken references separately from orphan files. - -### 3. Orphan Files - -A file is orphaned when it is not reachable from any accepted authority entry point for its root. - -Classify: - -```yaml -orphan_status: useful_unreferenced | obsolete | duplicate | generated_artifact | historical_source | unknown -recommended_action: link_from_entry | move | archive | remove | keep_ignored | compact_then_link | ask_user -``` - -Do not treat unreferenced files as removable by default. - -### 4. Ownership - -Use one primary owner per useful orphan: - -```text -user-level bootstrap/runtime loader -source app-bootstrap rule -user-level registry -source registry -global work-bundle rule -work-bundle skill -source documentation -source template -knowledge base -orchestration artifact -``` - -Fix advice must state: - -```text -Add reference from to because . -``` - -### 5. Rule Branches - -For every rule, verify: - -```text -clear enable/load condition -clear scope -must/should/may restriction strength -forbidden behavior -precedence rule -validation requirement -source rationale when applicable -registry/bootstrap reachability -no conflict with higher authority -no duplicate rule without purpose -``` - -Rules that only apply to one skill must be skill-scoped, not globally loaded. - -### 6. Skill Branches - -For every skill, verify: - -```text -concise `SKILL.md` or equivalent entry -activation condition -do-not-use condition -input expectations -output locations -workflow/reference links -script references -output contract -forbidden behavior -no hidden/unregistered dependency -no responsibility overlap with sibling skills -``` - -### 7. Script References - -For every referenced script, verify: - -```text -script exists -path resolves from referring file -purpose is documented -invocation role is obvious -script is not orphaned if executable -``` - -Do not execute scripts unless explicitly requested. - -### 8. Script Responsibility Boundaries - -Validate helper scripts against `rules/integrity-check/index.yaml`. - -Core rule: - -```text -Scripts support report creation, issue bookkeeping, status updates, sidecar summaries, report archiving, and report-file structure checks only. -``` - -Scripts must not perform or replace the integrity check. - -Allowed script responsibilities: - -```text -create a new report from `integrity-check-template.md` -allocate or preserve stable WBI issue IDs -append agent-authored issues to report sections -update issue status after agent/user verification -append status history -normalize report metadata/front matter -generate machine-readable sidecar status summaries -archive generated reports when explicitly requested -validate report-file structure only -``` - -Forbidden script responsibilities: - -```text -own policy or interpretation -validate rule strength as authority -validate skill completeness as authority -validate registries as authority -validate Project Registry correctness as authority -build a reference graph and treat it as final judgment -classify orphan files as useful/removable by itself -generate compression/loading recommendations without agent-authored findings -compare installed/source drift as authority -generate recommendations without agent-authored findings -close issues without agent/user-provided evidence -execute project scripts or arbitrary shell commands -crawl old Project Registry project roots by default -``` - -Classify script responsibility issues: - -```yaml -script_responsibility_issue: owns_policy | validates_integrity_as_authority | classifies_orphans | generates_recommendations | auto_repairs_without_request | closes_without_evidence | executes_project_code | crawls_project_roots | mutates_without_explicit_mode | missing_report_status_support | missing_issue_status_support -recommended_action: restrict_to_report_helper | remove_validator_authority | require_agent_authored_findings | add_explicit_repair_mode | add_evidence_requirement | remove_execution_path | add_scope_guard | update_script_design_reference -severity: critical | high | medium | low -``` - -Severity guidance: - -```text -critical: - Script can mutate files, execute code, close issues, or crawl project roots without explicit request. - -high: - Script owns policy, validates integrity as authority, classifies orphans, or generates recommendations independently. - -medium: - Script lacks report lifecycle/status support, evidence requirement, or explicit repair-mode boundary. - -low: - Script has naming, metadata, or documentation mismatch with `rules/integrity-check/index.yaml`. -``` - -Fix advice must update both the script and `rules/integrity-check/*.yaml` when the responsibility contract changes. - -### 9. Project Registry - -Validate directly: - -```text -~/.work-bundle/registry/projects.yaml -``` - -Missing, unreadable, malformed, or bootstrap-unreachable Project Registry is `critical`. - -Verify: - -```text -valid YAML -stable project id/slug/name -unique active project identifiers -resolvable project root for active projects -clear status: active | inactive | archived | deprecated | current -latest-working-on references an existing registry entry -archived/deprecated project is not latest-working-on unless explicitly allowed -project root respects workspace boundaries unless marked external -project-level `.work-bundle/` path matches project root when recorded -old projects are not browsed proactively unless selected or explicitly requested -``` - -Issue types: - -```text -missing_registry | invalid_yaml | missing_project_id | duplicate_project_id | missing_project_root | broken_project_root | invalid_status | stale_latest_working_on | archived_selected | workspace_boundary_violation | project_bundle_path_mismatch | initiative_scope_violation -``` - -### 10. Registry Consistency - -Check all registries under both roots. - -Verify: - -```text -entries point to existing files -registered skills/rules/scripts exist -disabled/deprecated entries are marked -paths are normalized -archived files are not active -registry metadata is compact enough for startup use -heavy files are referenced lazily, not embedded eagerly -``` - -### 11. Installed-vs-Source Drift - -When both roots exist, compare installed runtime files with source definitions. - -Classify drift: - -```text -missing_install | stale_install | runtime_only | source_only_unregistered | shadowed_definition | deprecated_active | path_mismatch -``` - -Report drift separately from ordinary orphan files. - -### 12. Work-Bundle Compression - -Detect token-waste caused by excessive eager loading. - -Core rule: - -```text -Agents must not be forced to understand the whole work-bundle or the whole latest-working-on project at startup. -``` - -Startup may load only: - -```text -minimal bootstrap rules -Project Registry metadata -latest-working-on selection -skill/rule registry metadata -active global safety boundaries -next-load routing rules -``` - -Startup must not load: - -```text -full roadmap files -full project-domain-profile.yaml -full repository-binding.md -all project notes -all skill references -all workflow documents -all rules regardless of applicability -historical plans, handoffs, or archived designs -``` - -Check: - -```text -rule isolation -conditional loading -skill-scope-aware rules -lazy loading -metadata-first registries -compact runtime references -separation of runtime instruction vs reference detail vs historical design -``` - -Special restrictions: - -```text -agent-bootstrap: - Load only bootstrap boundary, registry metadata, latest-working-on metadata, and next-step routing rules. - -project-domain-profile.yaml: - Load only after project selection and only when the task needs domain assumptions, terminology, architecture constraints, or domain-specific rules. - -repository-binding.md: - Load only for repository/file-path mapping, coding-agent handoff, code search, or artifact-to-repo binding. - -roadmap files: - Load only for roadmap planning, backlog review, implementation ordering, or roadmap consistency checks. -``` - -Classify compression findings: - -```yaml -compression_issue: eager_global_load | missing_enable_condition | global_rule_should_be_skill_scoped | heavy_file_loaded_at_startup | roadmap_loaded_too_early | profile_loaded_too_early | repository_binding_loaded_too_early | metadata_missing | split_recommended | compact_reference_needed -recommended_action: isolate_rule | add_enable_condition | move_to_skill_scope | add_lazy_load_boundary | replace_with_metadata_stub | split_file | create_compact_reference | archive_verbose_source -runtime_loading_risk: none | low | medium | high -``` - -### 13. Write-Back and Closure - -The report must be written as durable work-bundle knowledge. - -Do not mutate source rules, skills, registries, scripts, or compact files unless explicitly asked. - -When an issue is fixed, update both: - -```text -1. issue/report status and verification evidence -2. affected source of truth: instruction, project-structure doc, workflow file, rule, skill, registry, Project Registry contract, bootstrap rule, or loading rule -``` - -If a compression/loading issue is fixed, update the affected bootstrap instruction, registry metadata, skill reference, workflow instruction, or source documentation so future agents know what loads by default and what loads on demand. - -## Severity - -```text -critical: - broken authority path, missing bootstrap rule, missing/malformed Project Registry, stale latest-working-on, missing required skill file, unsafe authority contradiction, or helper script that can mutate files/execute code/close issues/crawl project roots without explicit request - -high: - useful orphan rule/skill/script, broken script reference, weak global restriction, registry inconsistency, eager loading that forces large unnecessary context, or helper script that owns policy/validation/orphan/compression judgment - -medium: - incomplete skill explanation, missing rationale, duplicate rule, unclear ownership, missing load condition, rule that should be skill-scoped but is not in a high-cost path, or helper script missing report lifecycle/status/evidence boundaries - -low: - formatting issue, naming inconsistency, minor stale reference, generated artifact in acceptable but suboptimal location -``` - -## Fix Advice Rules - -Advice must be concrete and local. - -Good: - -```text -Move `project-domain-profile.yaml` out of startup load. Add registry metadata with `load_when: project_selected && task_requires_domain_context`, and update `agent-bootstrap` to load only metadata during startup. -``` - -Bad: - -```text -Make bootstrap smaller. -``` - -Good: - -```text -Remove orphan-classification authority from `scripts/integrity_check_report.py`; allow it only to append agent-authored orphan findings and preserve WBI IDs. Update `rules/integrity-check/*.yaml` to keep script responsibility limited to report/status bookkeeping. -``` - -Bad: - -```text -Make the script safer. -``` - -Good: - -```text -Add `rules/workspace-boundary.md` to `registries/rule-registry.md` under global rules because it constrains all agent file access. -``` - -Bad: - -```text -Improve organization. -``` - -## Constraints - -```text -Do not treat all unreferenced files as removable. -Do not promote generated artifacts into authority paths unless they define accepted rules, skills, bootstrap behavior, or runtime compact instructions. -Do not rewrite files unless explicitly asked. -Do not execute scripts unless explicitly asked. -Do not treat `/.work-bundle/` as the primary validation target. -Do not validate individual project knowledge bases unless explicitly asked. -Do not inspect old project directories from the Project Registry unless explicitly asked. -For initiative checks, inspect only system roots, Project Registry metadata, and latest-working-on selection. -``` - -## Completion Criteria - -```text -Inventory built for `~/.work-bundle/` and `/` when available. -Reference graph built for both roots. -Orphans classified with usefulness and owner recommendations. -Rules checked for restriction strength, scope, load conditions, and discoverability. -Skills checked for explanation quality, output contracts, and script correctness. -Scripts checked for reference correctness. -Helper scripts checked against `rules/integrity-check/index.yaml` for responsibility boundaries, explicit repair mode, evidence requirements, and no policy/validation authority. -Registries checked for active/stale/broken entries and metadata-first loading. -Project Registry validated for schema, project paths, statuses, latest-working-on, and initiative scope safety. -Installed-vs-source drift classified. -Compression/loading boundaries checked, including rule isolation, conditional loading, skill-scope-aware rules, and lazy-load restrictions for agent-bootstrap, project-domain-profile.yaml, repository-binding.md, and roadmap files. -Integrity report written to `~/.work-bundle/knowledge/integrity-checks/` using `integrity-check-template.md`. -If updating after fixes, every fixed issue links to verification evidence and source-of-truth updates. -Final response summarizes only critical/high findings and gives the report path. -``` \ No newline at end of file diff --git a/references/wb-create-rule-validation.yaml b/references/wb-create-rule-validation.yaml index f3ffb89..d0e7696 100644 --- a/references/wb-create-rule-validation.yaml +++ b/references/wb-create-rule-validation.yaml @@ -27,19 +27,16 @@ allowed_scopes: - work-bundle - keep-summarizing - orchestration - - integrity-check allowed_scope_patterns: - wb-* - ks-* - orch-* - - rule-integrity-check-* id_prefix_scope_map: wb-: work-bundle ks-: keep-summarizing orch-: orchestration - rule-integrity-check-: integrity-check path_rules: scoped: rules//*.md diff --git a/rules/index.yaml b/rules/index.yaml index 9626dda..5e05285 100644 --- a/rules/index.yaml +++ b/rules/index.yaml @@ -142,41 +142,6 @@ rules: enforcement: must load: conditional requires: [] - - id: rule-integrity-check-agent-authority - path: integrity-check/rule-integrity-check-agent-authority.md - applies_when: - - wb-integrity-check runs - enforcement: must - load: conditional - requires: [] - - id: rule-integrity-check-loading-boundary - path: integrity-check/rule-integrity-check-loading-boundary.md - applies_when: - - integrity-check evaluates startup and compression boundaries - enforcement: must - load: conditional - requires: [] - - id: rule-integrity-check-report-lifecycle - path: integrity-check/rule-integrity-check-report-lifecycle.md - applies_when: - - integrity report is created or updated - enforcement: must - load: conditional - requires: [] - - id: rule-integrity-check-scope - path: integrity-check/rule-integrity-check-scope.md - applies_when: - - wb-integrity-check runs - enforcement: must - load: conditional - requires: [] - - id: rule-integrity-check-script-boundary - path: integrity-check/rule-integrity-check-script-boundary.md - applies_when: - - integrity-check helper CLI commands run - enforcement: must - load: conditional - requires: [] - id: rule-work-bundle-lifecycle-authority path: lifecycle-authority.md applies_when: diff --git a/rules/integrity-check/rule-integrity-check-agent-authority.md b/rules/integrity-check/rule-integrity-check-agent-authority.md deleted file mode 100644 index 59c81be..0000000 --- a/rules/integrity-check/rule-integrity-check-agent-authority.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -id: rule-integrity-check-agent-authority -applies_when: - - wb-integrity-check runs -enforcement: must -load: conditional -requires: [] ---- - -# Integrity Check Agent Authority - -## Purpose - -- Define the enforceable contract for `rule-integrity-check-agent-authority`. - -## Must - -- agent owns integrity judgment, severity assignment, ownership classification, and fix recommendations -- script outputs may include risk signals and evidence only -- final accept/reject/escalation decisions are human-owned - -## Must Not - -- script-level policy authority -- script-level integrity correctness authority -- script auto-close of issues without agent or user evidence - -## Validation - -- report recommendations include agent-authored rationale -- script output marks final decision owner as human - -## On Violation - -- Stop the operation, report the violated rule, and make the minimal correction before continuing. diff --git a/rules/integrity-check/rule-integrity-check-loading-boundary.md b/rules/integrity-check/rule-integrity-check-loading-boundary.md deleted file mode 100644 index 707dac4..0000000 --- a/rules/integrity-check/rule-integrity-check-loading-boundary.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -id: rule-integrity-check-loading-boundary -applies_when: - - integrity-check evaluates startup and compression boundaries -enforcement: must -load: conditional -requires: [] ---- - -# Integrity Check Loading Boundary - -## Purpose - -- Define the enforceable contract for `rule-integrity-check-loading-boundary`. - -## Must - -- detect and report eager loading that violates startup-minimal context rules -- verify loading boundaries for project metadata, agent entry, role profiles, and roadmap files -- classify loading issues with explicit risk level and concrete remediation action - -## Must Not - -- forcing startup to load full roadmap, full project metadata, all role profiles, all notes, all rules, or all skill references - -## Validation - -- report includes compression/loading section and startup-loading matrix -- findings include runtime_loading_risk and recommended_action fields - -## On Violation - -- Stop the operation, report the violated rule, and make the minimal correction before continuing. diff --git a/rules/integrity-check/rule-integrity-check-report-lifecycle.md b/rules/integrity-check/rule-integrity-check-report-lifecycle.md deleted file mode 100644 index 4cf031d..0000000 --- a/rules/integrity-check/rule-integrity-check-report-lifecycle.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -id: rule-integrity-check-report-lifecycle -applies_when: - - integrity report is created or updated -enforcement: must -load: conditional -requires: [] ---- - -# Integrity Check Report Lifecycle - -## Purpose - -- Define the enforceable contract for `rule-integrity-check-report-lifecycle`. - -## Must - -- report status uses only: draft, active, partially_fixed, closed, superseded -- issue status uses only: open, fixed, dismissed, converted, superseded -- report may close only when all issues are non-open -- each issue transition records timestamp, actor, from, to, reason, and evidence - -## Must Not - -- overwrite previous generated report by default -- close report with open issues - -## Validation - -- report structure contains metadata, status history, issue summary, and closure checklist -- status sidecar and issue sidecar remain synchronized with report status values - -## On Violation - -- Stop the operation, report the violated rule, and make the minimal correction before continuing. diff --git a/rules/integrity-check/rule-integrity-check-scope.md b/rules/integrity-check/rule-integrity-check-scope.md deleted file mode 100644 index 512a36e..0000000 --- a/rules/integrity-check/rule-integrity-check-scope.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -id: rule-integrity-check-scope -applies_when: - - wb-integrity-check runs -enforcement: must -load: conditional -requires: [] ---- - -# Integrity Check Scope - -## Purpose - -- Define the enforceable contract for `rule-integrity-check-scope`. - -## Must - -- validate only system roots by default: ~/.work-bundle and -- treat project-local .work-bundle directories as out-of-scope unless explicitly requested -- validate the project registry resolved from `$work_bundle_config_root/bootstrap.yaml` -> `project_registry` as critical authority - -## Must Not - -- default crawl of project-root .work-bundle -- proactive scan of old project roots from project registry - -## Validation - -- integrity report scope section records default roots and explicit out-of-scope roots -- critical issue is produced when project registry is missing or malformed - -## On Violation - -- Stop the operation, report the violated rule, and make the minimal correction before continuing. diff --git a/rules/integrity-check/rule-integrity-check-script-boundary.md b/rules/integrity-check/rule-integrity-check-script-boundary.md deleted file mode 100644 index 881e8ff..0000000 --- a/rules/integrity-check/rule-integrity-check-script-boundary.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -id: rule-integrity-check-script-boundary -applies_when: - - integrity-check helper CLI commands run -enforcement: must -load: conditional -requires: [] ---- - -# Integrity Check Script Boundary - -## Purpose - -- Define the enforceable contract for `rule-integrity-check-script-boundary`. - -## Must - -- helper CLI supports only report lifecycle operations: new, add-issue, update-status, summarize-status, archive-report, validate-report -- validate-report performs structure checks only -- all status updates require reason and evidence for fixed outcomes - -## Must Not - -- executing project scripts or arbitrary shell commands -- orphan classification authority -- policy interpretation authority -- recommendation generation without agent-authored findings -- finding-correctness validation mode in validate-report - -## Validation - -- CLI help output contains only approved subcommands -- validate-report rejects finding-correctness mode - -## On Violation - -- Stop the operation, report the violated rule, and make the minimal correction before continuing. diff --git a/rules/work-bundle/wb-create-rule.md b/rules/work-bundle/wb-create-rule.md index 9b3807d..51660e3 100644 --- a/rules/work-bundle/wb-create-rule.md +++ b/rules/work-bundle/wb-create-rule.md @@ -19,8 +19,8 @@ Summarize the enforceable contract for creating, migrating, and validating work- ## Must - Resolve rule-store scope before rule work: `toolkit` → `$work_bundle_root/rules/`, `global` → `$work_bundle_config_root/rules/`, `project` → `$workspace_root/.work-bundle/rules/`, or explicit root compatibility mode. -- Treat rule-store scope separately from rule area directories (`work-bundle/`, `keep-summarizing/`, `orchestration/`, `integrity-check/`). -- Store scoped rules under `//.md` using the prefix map (`wb-` → `work-bundle/`, `ks-` → `keep-summarizing/`, `orch-` → `orchestration/`, `rule-integrity-check-` → `integrity-check/`). +- Treat rule-store scope separately from rule area directories (`work-bundle/`, `keep-summarizing/`, `orchestration/`). +- Store scoped rules under `//.md` using the prefix map (`wb-` → `work-bundle/`, `ks-` → `keep-summarizing/`, `orch-` → `orchestration/`). - Store cross-cutting rules at `/.md` directly under the selected rules root; do not use a `global/` area directory. - Use canonical front matter: `id`, `applies_when`, `enforcement`, `load`, `requires`. - Include body sections: Purpose, Must, Must Not, Validation, On Violation. diff --git a/scripts/integrity-check/common/report_io.py b/scripts/integrity-check/common/report_io.py deleted file mode 100644 index 5c31ae4..0000000 --- a/scripts/integrity-check/common/report_io.py +++ /dev/null @@ -1,143 +0,0 @@ -from __future__ import annotations - -import json -import re -from datetime import datetime, timezone -from pathlib import Path - -REQUIRED_HEADINGS = [ - "## Status History", - "## Issue Status Summary", -] - -REQUIRED_FRONTMATTER_KEYS = ( - "report_id", - "checker_skill", - "report_status", - "checked_at", - "updated_at", - "actor", -) - -DEFAULT_REPORT_STATUSES = {"draft", "active", "partially_fixed", "closed", "superseded"} - - -def now_iso() -> str: - return datetime.now(timezone.utc).replace(microsecond=0).isoformat() - - -def slugify(text: str) -> str: - value = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-") - return value or "work-bundle-integrity-check" - - -def issue_sidecar_path(report_path: Path) -> Path: - return report_path.with_name(f"{report_path.name}.issues.json") - - -def status_sidecar_path(report_path: Path) -> Path: - return report_path.with_name(f"{report_path.stem}.status.json") - - -def read_text(path: Path) -> str: - return path.read_text(encoding="utf-8") - - -def write_text(path: Path, content: str) -> None: - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(content, encoding="utf-8") - - -def load_issue_sidecar(report_path: Path) -> dict: - path = issue_sidecar_path(report_path) - if not path.exists(): - return {"issues": [], "created_at": now_iso(), "updated_at": now_iso()} - return json.loads(read_text(path)) - - -def save_issue_sidecar(report_path: Path, data: dict) -> None: - data["updated_at"] = now_iso() - write_text(issue_sidecar_path(report_path), json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True) + "\n") - - -def next_issue_id(issues: list[dict]) -> str: - max_num = 0 - for item in issues: - issue_id = str(item.get("issue_id", "")) - if issue_id.startswith("WBI-"): - try: - max_num = max(max_num, int(issue_id.split("-", 1)[1])) - except ValueError: - continue - return f"WBI-{max_num + 1:03d}" - - -def fill_template(template_text: str, *, title: str, report_id: str, checked_at: str, actor: str) -> str: - text = template_text.replace("", title) - replacements = { - "": report_id.removeprefix("wbi-"), - "": checked_at, - "": "0", - "": "unknown", - } - for key, value in replacements.items(): - text = text.replace(key, value) - text = text.replace("actor: agent | user | tool", f"actor: {actor}") - text = text.replace( - "Summary:\n\n```text\n\n```", - "Summary:\n\n```text\nReport scaffold created. Findings and recommendations remain agent-authored.\n```", - ) - return text - - -def has_frontmatter(report_text: str) -> bool: - return report_text.lstrip().startswith("---\n") - - -def ensure_frontmatter(report_text: str) -> list[str]: - failures: list[str] = [] - if not has_frontmatter(report_text): - return ["missing_frontmatter"] - match = re.match(r"^---\n(.*?)\n---", report_text.lstrip(), re.DOTALL) - if not match: - failures.append("invalid_frontmatter_delimiters") - return failures - block = match.group(1) - for key in REQUIRED_FRONTMATTER_KEYS: - if not re.search(rf"^{re.escape(key)}:\s*\S", block, re.MULTILINE): - failures.append(f"missing_frontmatter_key:{key}") - return failures - - -def ensure_required_headings(report_text: str) -> list[str]: - missing = [heading for heading in REQUIRED_HEADINGS if heading not in report_text] - return missing - - -def ensure_report_structure(report_text: str) -> list[str]: - return ensure_frontmatter(report_text) + ensure_required_headings(report_text) - - -def parse_report_status(report_text: str) -> str | None: - match = re.search(r"^report_status:\s*([a-z_]+)", report_text, re.MULTILINE) - return match.group(1) if match else None - - -def insert_under_section(report_text: str, section: str, block: str) -> str: - marker = f"## {section}\n" - if marker not in report_text: - return report_text.rstrip() + f"\n\n{marker}\n{block}\n" - pivot = report_text.index(marker) + len(marker) - tail = report_text[pivot:] - next_header = tail.find("\n## ") - if next_header == -1: - insert_at = len(report_text) - else: - insert_at = pivot + next_header - prefix = report_text[:insert_at].rstrip() - suffix = report_text[insert_at:].lstrip("\n") - merged = f"{prefix}\n\n{block}\n" - if suffix: - merged += f"\n{suffix}" - return merged - diff --git a/scripts/integrity-check/common/status.py b/scripts/integrity-check/common/status.py deleted file mode 100644 index c89d0f3..0000000 --- a/scripts/integrity-check/common/status.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -ISSUE_STATUSES = {"open", "fixed", "dismissed", "converted", "superseded"} -REPORT_STATUSES = {"draft", "active", "partially_fixed", "closed", "superseded"} -SEVERITIES = {"critical", "high", "medium", "low"} -ISSUE_TYPES = { - "orphan", - "broken_reference", - "weak_rule", - "incomplete_skill", - "missing_script", - "registry_error", - "project_registry_error", - "installed_source_drift", - "compression_loading", - "authority_conflict", - "other", -} - - -def summarize_issues(issues: list[dict], report_status: str | None) -> dict: - counts = {name: 0 for name in ISSUE_STATUSES} - critical_open = 0 - high_open = 0 - for issue in issues: - status = str(issue.get("issue_status", "")).strip().lower() - severity = str(issue.get("severity", "")).strip().lower() - if status in counts: - counts[status] += 1 - if status == "open" and severity == "critical": - critical_open += 1 - if status == "open" and severity == "high": - high_open += 1 - closable = counts["open"] == 0 - blocking = critical_open + high_open - return { - "report_status": report_status or "unknown", - "open_count": counts["open"], - "fixed_count": counts["fixed"], - "dismissed_count": counts["dismissed"], - "converted_count": counts["converted"], - "superseded_count": counts["superseded"], - "critical_open_count": critical_open, - "high_open_count": high_open, - "blocking_count": blocking, - "closable": closable and (report_status != "closed" or counts["open"] == 0), - "boundary_risks": [ - { - "issue_id": issue.get("issue_id"), - "severity": issue.get("severity"), - "issue_status": issue.get("issue_status"), - "reason": "open high-severity issue requires human decision", - } - for issue in issues - if issue.get("issue_status") == "open" - and str(issue.get("severity", "")).lower() in {"critical", "high"} - ], - "decision_authority": "human", - } - diff --git a/scripts/integrity_check_report.py b/scripts/integrity_check_report.py deleted file mode 100644 index e8b5061..0000000 --- a/scripts/integrity_check_report.py +++ /dev/null @@ -1,441 +0,0 @@ -from __future__ import annotations - -import argparse -import json -import shutil -import sys -from datetime import datetime, timezone -from pathlib import Path - -COMMON_PATH = Path(__file__).with_name("integrity-check") / "common" -if str(COMMON_PATH) not in sys.path: - sys.path.insert(0, str(COMMON_PATH)) - -from report_io import ( # type: ignore - DEFAULT_REPORT_STATUSES, - ensure_report_structure, - fill_template, - insert_under_section, - issue_sidecar_path, - load_issue_sidecar, - next_issue_id, - now_iso, - parse_report_status, - read_text, - save_issue_sidecar, - slugify, - status_sidecar_path, - write_text, -) -from status import ISSUE_STATUSES, ISSUE_TYPES, REPORT_STATUSES, SEVERITIES, summarize_issues # type: ignore - -DEFAULT_TEMPLATE = Path("references/integrity-check/integrity-check-template.md") -DEFAULT_OUTPUT_ROOT = Path(".work-bundle/orchestration/reviews/integrity-checks") -SECTION_MAP = { - "orphan": "Orphan File Findings", - "broken_reference": "Broken References", - "weak_rule": "Rule Branch Consistency", - "incomplete_skill": "Skill Branch Consistency", - "missing_script": "Script Reference Consistency", - "registry_error": "Registry Consistency", - "project_registry_error": "Project Registry Consistency", - "installed_source_drift": "Installed-vs-Source Drift", - "compression_loading": "Work-Bundle Compression and Conditional Loading", - "authority_conflict": "Authority / Precedence Conflicts", - "other": "Critical Issues", -} - - -def _json_out(payload: dict, *, stream=sys.stdout) -> None: - stream.write(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n") - - -def _error(message: str, *, code: int = 2) -> int: - _json_out( - { - "status": "boundary_violation" if code == 2 else "failed", - "message": message, - "final_decision_owner": "human", - }, - stream=sys.stderr, - ) - return code - - -def _report_id() -> str: - return "wbi-" + datetime.now(timezone.utc).strftime("%Y%m%d-%H%M") - - -def _assert_exists(path: Path, label: str) -> None: - if not path.exists(): - raise FileNotFoundError(f"{label} does not exist: {path}") - - -def _create_report(args: argparse.Namespace) -> int: - template = Path(args.template) - _assert_exists(template, "template") - report_id = _report_id() - checked_at = now_iso() - title = args.title.strip() or "work-bundle-integrity-check" - output_root = Path(args.output_root) - output_root.mkdir(parents=True, exist_ok=True) - output_name = f"{report_id.removeprefix('wbi-')}-{slugify(title)}.md" - report_path = output_root / output_name - if report_path.exists(): - return _error(f"refusing to overwrite existing report: {report_path}", code=1) - - rendered = fill_template( - read_text(template), - title=title, - report_id=report_id, - checked_at=checked_at, - actor=args.actor, - ) - write_text(report_path, rendered) - save_issue_sidecar( - report_path, - { - "report_id": report_id, - "report_path": str(report_path), - "template_path": str(template), - "source_instruction": "references/integrity-check/work-bundle-integrity-check.md", - "report_status": "draft", - "final_decision_owner": "human", - "issues": [], - "created_at": checked_at, - "updated_at": checked_at, - }, - ) - _json_out( - { - "status": "passed", - "command": "new", - "report_path": str(report_path), - "issue_sidecar": str(issue_sidecar_path(report_path)), - "final_decision_owner": "human", - } - ) - return 0 - - -def _add_issue(args: argparse.Namespace) -> int: - report = Path(args.report) - _assert_exists(report, "report") - if args.severity not in SEVERITIES: - return _error(f"invalid severity: {args.severity}", code=1) - if args.issue_type not in ISSUE_TYPES: - return _error(f"invalid issue type: {args.issue_type}", code=1) - if not args.agent_authored: - return _error("add-issue requires explicit agent-authored findings") - - data = load_issue_sidecar(report) - issues = list(data.get("issues", [])) - issue_id = args.issue_id or next_issue_id(issues) - if any(item.get("issue_id") == issue_id for item in issues): - return _error(f"duplicate issue id: {issue_id}", code=1) - - issue = { - "issue_id": issue_id, - "issue_status": "open", - "severity": args.severity, - "type": args.issue_type, - "file": args.file or None, - "root": args.root or None, - "summary": args.summary.strip(), - "recommended_fix": args.recommended_fix.strip(), - "evidence": args.evidence, - "source_of_truth_updates": args.source_update, - "requires_human_decision": bool(args.requires_human_decision), - "status_history": [ - { - "at": now_iso(), - "actor": args.actor, - "from": None, - "to": "open", - "reason": "issue recorded", - "evidence": args.evidence[0], - } - ], - } - issues.append(issue) - data["issues"] = issues - data["report_status"] = "active" if issues else "draft" - save_issue_sidecar(report, data) - - section = SECTION_MAP.get(args.issue_type, "Critical Issues") - issue_block = ( - f"### {issue_id}: {issue['summary']}\n\n" - "```yaml\n" - f"issue_id: {issue_id}\n" - "issue_status: open\n" - f"severity: {issue['severity']}\n" - f"type: {issue['type']}\n" - f"file: {issue['file'] or 'null'}\n" - f"root: {issue['root'] or 'null'}\n" - f"summary: {issue['summary']}\n" - f"recommended_fix: {issue['recommended_fix']}\n" - f"requires_human_decision: {str(issue['requires_human_decision']).lower()}\n" - "evidence:\n" - + "".join(f" - {item}\n" for item in issue["evidence"]) - + "status_history:\n" - + f" - at: {issue['status_history'][0]['at']}\n" - + f" actor: {args.actor}\n" - + " from: null\n" - + " to: open\n" - + " reason: issue recorded\n" - + f" evidence: {issue['evidence'][0]}\n" - + "```\n" - ) - updated_report = insert_under_section(read_text(report), section, issue_block) - write_text(report, updated_report) - _json_out( - { - "status": "passed", - "command": "add-issue", - "issue_id": issue_id, - "report_path": str(report), - "final_decision_owner": "human", - } - ) - return 0 - - -def _update_status(args: argparse.Namespace) -> int: - report = Path(args.report) - _assert_exists(report, "report") - if args.status not in ISSUE_STATUSES - {"open"}: - return _error(f"invalid target status: {args.status}", code=1) - if not args.reason.strip(): - return _error("status change requires --reason", code=1) - if args.status == "fixed" and not args.evidence: - return _error("fixed status requires --evidence", code=1) - - data = load_issue_sidecar(report) - issues = list(data.get("issues", [])) - target = next((item for item in issues if item.get("issue_id") == args.issue_id), None) - if target is None: - return _error(f"unknown issue id: {args.issue_id}", code=1) - old_status = target.get("issue_status") - target["issue_status"] = args.status - target.setdefault("status_history", []).append( - { - "at": now_iso(), - "actor": args.actor, - "from": old_status, - "to": args.status, - "reason": args.reason.strip(), - "evidence": "; ".join(args.evidence) if args.evidence else "not-provided", - } - ) - if args.source_update: - target["source_of_truth_updates"] = args.source_update - save_issue_sidecar(report, data) - - trail = ( - f"| {now_iso()} | {args.actor} | {old_status} | {args.status} | " - f"{args.reason.strip()} | {'; '.join(args.evidence) if args.evidence else 'n/a'} |\n" - ) - report_text = read_text(report) - report_text = insert_under_section(report_text, "Status History", trail.rstrip()) - write_text(report, report_text) - _json_out( - { - "status": "passed", - "command": "update-status", - "issue_id": args.issue_id, - "from": old_status, - "to": args.status, - "final_decision_owner": "human", - } - ) - return 0 - - -def _summarize_status(args: argparse.Namespace) -> int: - report = Path(args.report) - _assert_exists(report, "report") - data = load_issue_sidecar(report) - report_status = parse_report_status(read_text(report)) or data.get("report_status") - summary = summarize_issues(list(data.get("issues", [])), report_status) - summary["report_path"] = str(report) - summary["last_updated_at"] = now_iso() - summary["final_decision_owner"] = "human" - output = Path(args.output) if args.output else status_sidecar_path(report) - write_text(output, json.dumps(summary, ensure_ascii=False, indent=2, sort_keys=True) + "\n") - _json_out({"status": "passed", "command": "summarize-status", "output": str(output), **summary}) - return 0 - - -def _archive_report(args: argparse.Namespace) -> int: - report = Path(args.report) - _assert_exists(report, "report") - sidecar = issue_sidecar_path(report) - summary_path = status_sidecar_path(report) - issues = load_issue_sidecar(report).get("issues", []) - has_open = any(item.get("issue_status") == "open" for item in issues) - if has_open and not args.allow_open: - return _error("refusing to archive report with open issues without --allow-open", code=1) - - archive_root = Path(args.archive_root) - archive_root.mkdir(parents=True, exist_ok=True) - archive_report = archive_root / report.name - if args.move: - shutil.move(str(report), str(archive_report)) - else: - shutil.copy2(report, archive_report) - - copied_sidecars: list[str] = [] - for path in (sidecar, summary_path): - if path.exists(): - target = archive_root / path.name - if args.move: - shutil.move(str(path), str(target)) - else: - shutil.copy2(path, target) - copied_sidecars.append(str(target)) - _json_out( - { - "status": "passed", - "command": "archive-report", - "archived_report": str(archive_report), - "moved": bool(args.move), - "sidecars": copied_sidecars, - "final_decision_owner": "human", - } - ) - return 0 - - -def _validate_report(args: argparse.Namespace) -> int: - if args.check_finding_correctness: - return _error( - "validate-report only checks structure; finding correctness remains agent/human authority" - ) - report = Path(args.report) - _assert_exists(report, "report") - data = load_issue_sidecar(report) - issues = list(data.get("issues", [])) - report_text = read_text(report) - - failures: list[str] = [] - warnings: list[str] = [] - structure_failures = ensure_report_structure(report_text) - for item in structure_failures: - if item.startswith("##"): - failures.append(f"missing_heading:{item}") - else: - failures.append(f"report_structure:{item}") - - report_status = parse_report_status(report_text) - if report_status and report_status not in DEFAULT_REPORT_STATUSES: - failures.append(f"invalid_report_status:{report_status}") - if report_status == "closed" and any(item.get("issue_status") == "open" for item in issues): - failures.append("closed_report_has_open_issues") - - seen: set[str] = set() - for issue in issues: - issue_id = str(issue.get("issue_id", "")).strip() - if not issue_id: - failures.append("missing_issue_id") - continue - if issue_id in seen: - failures.append(f"duplicate_issue_id:{issue_id}") - seen.add(issue_id) - status = issue.get("issue_status") - if status not in ISSUE_STATUSES: - failures.append(f"invalid_issue_status:{issue_id}:{status}") - if issue.get("severity") not in SEVERITIES: - failures.append(f"invalid_severity:{issue_id}:{issue.get('severity')}") - if issue.get("type") not in ISSUE_TYPES: - failures.append(f"invalid_type:{issue_id}:{issue.get('type')}") - if status == "fixed" and not issue.get("status_history"): - warnings.append(f"fixed_issue_without_status_history:{issue_id}") - - summary = summarize_issues(issues, report_status) - status = "passed" if not failures else "failed" - _json_out( - { - "status": status, - "command": "validate-report", - "report_path": str(report), - "failures": failures, - "warnings": warnings, - "summary": summary, - "final_decision_owner": "human", - } - ) - return 0 if not failures else 1 - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - prog="integrity_check_report.py", - description="Deterministic helper CLI for integrity report lifecycle operations.", - ) - sub = parser.add_subparsers(dest="subcommand", required=True) - - new = sub.add_parser("new", help="Create report from local template.") - new.add_argument("--template", default=str(DEFAULT_TEMPLATE)) - new.add_argument("--output-root", default=str(DEFAULT_OUTPUT_ROOT)) - new.add_argument("--title", default="work-bundle-integrity-check") - new.add_argument("--actor", default="agent", choices=["agent", "user", "tool"]) - new.set_defaults(handler=_create_report) - - add = sub.add_parser("add-issue", help="Append agent-authored issue to report.") - add.add_argument("--report", required=True) - add.add_argument("--issue-id") - add.add_argument("--severity", required=True, choices=sorted(SEVERITIES)) - add.add_argument("--type", dest="issue_type", required=True, choices=sorted(ISSUE_TYPES)) - add.add_argument("--file") - add.add_argument("--root", choices=["user_bundle", "source_bundle", "project"]) - add.add_argument("--summary", required=True) - add.add_argument("--recommended-fix", required=True) - add.add_argument("--evidence", action="append", required=True) - add.add_argument("--source-update", action="append", default=[]) - add.add_argument("--actor", default="agent", choices=["agent", "user", "tool"]) - add.add_argument("--requires-human-decision", action="store_true") - add.add_argument("--agent-authored", action="store_true", default=True) - add.set_defaults(handler=_add_issue) - - update = sub.add_parser("update-status", help="Update issue status with evidence.") - update.add_argument("--report", required=True) - update.add_argument("--issue-id", required=True) - update.add_argument( - "--status", - required=True, - choices=sorted(ISSUE_STATUSES - {"open"}), - ) - update.add_argument("--reason", required=True) - update.add_argument("--evidence", action="append", default=[]) - update.add_argument("--source-update", action="append", default=[]) - update.add_argument("--actor", default="agent", choices=["agent", "user", "tool"]) - update.set_defaults(handler=_update_status) - - summary = sub.add_parser("summarize-status", help="Generate machine-readable status sidecar.") - summary.add_argument("--report", required=True) - summary.add_argument("--output") - summary.set_defaults(handler=_summarize_status) - - archive = sub.add_parser("archive-report", help="Archive report and sidecar files.") - archive.add_argument("--report", required=True) - archive.add_argument("--archive-root", required=True) - archive.add_argument("--allow-open", action="store_true") - archive.add_argument("--move", action="store_true") - archive.set_defaults(handler=_archive_report) - - validate = sub.add_parser("validate-report", help="Validate report structure only.") - validate.add_argument("--report", required=True) - validate.add_argument("--check-finding-correctness", action="store_true") - validate.set_defaults(handler=_validate_report) - return parser - - -def main(argv: list[str] | None = None) -> int: - parser = build_parser() - parsed = parser.parse_args(argv) - return parsed.handler(parsed) - - -if __name__ == "__main__": - raise SystemExit(main()) - diff --git a/scripts/work-bundle/README.md b/scripts/work-bundle/README.md index d379fc1..537a9dc 100644 --- a/scripts/work-bundle/README.md +++ b/scripts/work-bundle/README.md @@ -2,7 +2,7 @@ Implementation modules in this directory are the manual maintenance surface for work-bundle helpers. -The top-level `../wb.py` entrypoint remains for compatibility with existing agent instructions. Implementation is split by skill area (`rules.py`, `project.py`, `member.py`, `doctor.py`, `metadata_profile.py`, `skill_registry.py`, `integrity.py`), with `dispatcher.py` only wiring commands. +The top-level `../wb.py` entrypoint remains for compatibility with existing agent instructions. Implementation is split by skill area (`rules.py`, `project.py`, `member.py`, `doctor.py`, `metadata_profile.py`, `skill_registry.py`), with `dispatcher.py` only wiring commands. Command examples: @@ -38,7 +38,6 @@ python3 scripts/wb.py violation-create-evidence --status active --short-descript python3 scripts/wb.py violation-build-index python3 scripts/wb.py violation-write-index python3 scripts/wb.py violation-archive-evidence --action completed -python3 scripts/wb.py integrity-check-report new --template references/integrity-check/integrity-check-template.md --output-root /tmp/reports --title check ``` Prefer `--scope` for `create-rules` and `validate-rules`: `toolkit` resolves to `$work_bundle_root/rules/`, `global` resolves to `$work_bundle_config_root/rules/`, and `project` resolves to `/.work-bundle/rules/`. The project-root form remains a single-repository compatibility alias. diff --git a/scripts/work-bundle/dispatcher.py b/scripts/work-bundle/dispatcher.py index 3a25b7d..135d9fb 100644 --- a/scripts/work-bundle/dispatcher.py +++ b/scripts/work-bundle/dispatcher.py @@ -5,14 +5,13 @@ from core import CLI_HELP_EPILOG, LEGACY_COMMAND_MIGRATIONS, out from doctor import cmd_doctor -from integrity import cmd_integrity_report, cmd_merge_skill_hints from instruction_audit import cmd_instruction_audit from legacy import cmd_legacy_command_removed from metadata_profile import cmd_domain_profile from bootstrap_config import cmd_migrate_work_bundle_config from project import cmd_cleanup_member, cmd_doctor_project, cmd_init_project, cmd_migrate_project, cmd_migrate_to_multi_repository, cmd_project, cmd_provision_member, cmd_register_project_command, cmd_session_start, cmd_set_prefer_subagent, cmd_show_project, cmd_validate_project from rules import cmd_create_rules, cmd_validate_rules -from skill_registry import cmd_registry +from skill_registry import cmd_merge_skill_hints, cmd_registry from violations import ( cmd_violation_archive_evidence, cmd_violation_build_index, @@ -56,7 +55,6 @@ def main() -> int: 'validate-runtime-artifacts': 'doctor', 'validate-repository-health': 'repository-health', 'validate-workflow-branches': 'workflow-branches', - 'integrity-report': 'integrity-check-report', } command = aliases.get(command, command) if command == 'migrate-work-bundle-config': @@ -151,8 +149,6 @@ def main() -> int: return cmd_registry(parsed.args) if command == 'merge-skill-hints': return cmd_merge_skill_hints(parsed.args) - if command == 'integrity-check-report': - return cmd_integrity_report(parsed.args) parser.error(f'unknown command: {parsed.command}') return 2 diff --git a/scripts/work-bundle/integrity.py b/scripts/work-bundle/integrity.py deleted file mode 100644 index ff68da1..0000000 --- a/scripts/work-bundle/integrity.py +++ /dev/null @@ -1,20 +0,0 @@ -from core import * - -def cmd_merge_skill_hints(args: list[str]) -> int: - out({'status': 'passed', 'suggested_skills': []}) - return 0 - - -def cmd_integrity_report(args: list[str]) -> int: - script = Path(__file__).resolve().parents[1] / 'integrity_check_report.py' - if not script.exists(): - out({'status': 'issues-found', 'failures': ['missing_integrity_report_cli'], 'expected': str(script)}) - return 1 - spec = importlib.util.spec_from_file_location('integrity_check_report', script) - if spec is None or spec.loader is None: - out({'status': 'issues-found', 'failures': ['load_integrity_report_cli_failed'], 'expected': str(script)}) - return 1 - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return int(module.main(args)) - diff --git a/scripts/work-bundle/rules.py b/scripts/work-bundle/rules.py index 12d7fa9..144415b 100644 --- a/scripts/work-bundle/rules.py +++ b/scripts/work-bundle/rules.py @@ -29,12 +29,11 @@ _DEFAULT_REQUIRED_FRONT_MATTER = ["id", "applies_when", "enforcement", "load", "requires"] _DEFAULT_REQUIRED_BODY_SECTIONS = ["Purpose", "Must", "Must Not", "Validation", "On Violation"] _DEFAULT_PROHIBITED_RULE_FIELDS = {"scope", "type", "blocks", "severity", "status", "source_authority"} -_DEFAULT_ALLOWED_SCOPES = ["work-bundle", "keep-summarizing", "orchestration", "integrity-check"] +_DEFAULT_ALLOWED_SCOPES = ["work-bundle", "keep-summarizing", "orchestration"] _DEFAULT_ID_PREFIX_SCOPE_MAP = { "wb-": "work-bundle", "ks-": "keep-summarizing", "orch-": "orchestration", - "rule-integrity-check-": "integrity-check", } _DEFAULT_FORBIDDEN_PATH_PREFIXES = ["global"] _RULE_STORE_SCOPES = {"toolkit", "global", "project"} diff --git a/scripts/work-bundle/skill_registry.py b/scripts/work-bundle/skill_registry.py index 5244884..919b511 100644 --- a/scripts/work-bundle/skill_registry.py +++ b/scripts/work-bundle/skill_registry.py @@ -1,5 +1,10 @@ from core import * + +def cmd_merge_skill_hints(args: list[str]) -> int: + out({'status': 'passed', 'suggested_skills': []}) + return 0 + def norm(text: str) -> str: return re.sub(r'[^a-z0-9]+', '-', text.lower()).strip('-') or 'unknown-skill' diff --git a/skills/wb-create-rule/SKILL.md b/skills/wb-create-rule/SKILL.md index 7f49284..8c66f07 100644 --- a/skills/wb-create-rule/SKILL.md +++ b/skills/wb-create-rule/SKILL.md @@ -34,7 +34,7 @@ Rule-store scope chooses which rules root is created, synced, or validated: | `project` | `$workspace_root/.work-bundle/rules/` | Workspace project-scope rules shared by its members. | | `explicit` | user-supplied `` | Backward-compatible direct root mode. | -Rule-store scope is not the same as a rule area directory. Area directories inside any rules root remain `work-bundle/`, `keep-summarizing/`, `orchestration/`, and `integrity-check/`. +Rule-store scope is not the same as a rule area directory. Area directories inside any rules root remain `work-bundle/`, `keep-summarizing/`, and `orchestration/`. **Toolkit write boundary:** agents must not create, edit, delete, migrate, or index `$work_bundle_root/rules/**` unless `$workspace_root == $work_bundle_root`. If the active workspace root is different from the toolkit root, stop with a boundary blocker instead of mutating toolkit rules. @@ -50,8 +50,6 @@ Rule-store scope is not the same as a rule area directory. Area directories insi ks-*.md orchestration/ orch-*.md - integrity-check/ - rule-integrity-check-*.md ``` | Rule kind | Location | When to use | @@ -68,7 +66,6 @@ There is **no** `global/` area directory inside a rules root. Global rules use r | `wb-` | `rules/work-bundle/` | | `ks-` | `rules/keep-summarizing/` | | `orch-` | `rules/orchestration/` | -| `rule-integrity-check-` | `rules/integrity-check/` | | no area prefix / cross-cutting | `/` | Allowed scope directory names are enumerated in `references/wb-create-rule-validation.yaml` under `allowed_scopes`. Mechanical path placement checks use `id_prefix_scope_map` and `path_rules` from that manifest. diff --git a/skills/wb-integrity-check/SKILL.md b/skills/wb-integrity-check/SKILL.md deleted file mode 100644 index 6de7513..0000000 --- a/skills/wb-integrity-check/SKILL.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: wb-integrity-check -description: "Agent-owned work-bundle integrity validation helper. Uses one bounded CLI for report lifecycle operations only." ---- - -# wb-integrity-check - -This skill preserves agent-owned integrity judgment. The CLI is a deterministic helper for report artifacts and status bookkeeping only. - -## Authority Sources - -- `rules/integrity-check/index.yaml` -- `references/integrity-check/integrity-check-template.md` - -Before running integrity-check workflow, load `rules/integrity-check/index.yaml` and apply every `severity: must` rule as mandatory execution constraints. - -## Command Surface - -Direct CLI: - -- `python3 scripts/integrity_check_report.py new --template references/integrity-check/integrity-check-template.md --output-root --title ` -- `python3 scripts/integrity_check_report.py add-issue --report <report.md> --severity <level> --type <kind> --summary <text> --recommended-fix <text> --evidence <text> [--evidence <text>]` -- `python3 scripts/integrity_check_report.py update-status --report <report.md> --issue-id WBI-<num> --status fixed|dismissed|converted|superseded --reason <text> [--evidence <text>]` -- `python3 scripts/integrity_check_report.py summarize-status --report <report.md> [--output <status.json>]` -- `python3 scripts/integrity_check_report.py archive-report --report <report.md> --archive-root <dir> [--allow-open] [--move]` -- `python3 scripts/integrity_check_report.py validate-report --report <report.md>` - -Unified dispatcher route: - -- `python3 scripts/wb.py integrity-check-report <subcommand> ...` - -## Boundary Contract - -- Integrity findings, severity, ownership, and recommendation authority remain agent/user owned. -- Helper CLI may report boundary risks as evidence signals. -- Final accept/reject/escalation decision is always human-owned. -- Runtime enforcement authority comes from `rules/integrity-check/*.yaml` and must be treated as `must` scope policy. -- CLI does not inspect bundle policy correctness, does not classify integrity correctness as authority, and does not execute arbitrary project code. -- `validate-report` checks structure only and rejects finding-correctness validation mode. - -## Expected Outputs - -- Report markdown generated from template. -- Issue sidecar: `<report>.issues.json` -- Status sidecar: `<report-stem>.status.json` -- JSON command output with `final_decision_owner: human`. - diff --git a/tests/test_integrity_check_report.py b/tests/test_integrity_check_report.py deleted file mode 100644 index ddadc00..0000000 --- a/tests/test_integrity_check_report.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[1] -SCRIPT = REPO_ROOT / "scripts" / "integrity_check_report.py" - - -def run_cli(*args: str) -> subprocess.CompletedProcess[str]: - return subprocess.run( - [sys.executable, str(SCRIPT), *args], - cwd=REPO_ROOT, - capture_output=True, - text=True, - check=False, - ) - - -def create_report(tmp_path: Path) -> Path: - output_root = tmp_path / "reports" - result = run_cli( - "new", - "--template", - str(REPO_ROOT / "references" / "integrity-check" / "integrity-check-template.md"), - "--output-root", - str(output_root), - "--title", - "fixture-report", - ) - assert result.returncode == 0, result.stderr - payload = json.loads(result.stdout) - return Path(payload["report_path"]) - - -def test_lifecycle_flow_and_validate_report(tmp_path: Path) -> None: - report = create_report(tmp_path) - add = run_cli( - "add-issue", - "--report", - str(report), - "--severity", - "high", - "--type", - "compression_loading", - "--summary", - "Startup load is too broad", - "--recommended-fix", - "Gate heavy docs behind conditional load", - "--evidence", - "agent-observation:startup-eager-load", - "--requires-human-decision", - ) - assert add.returncode == 0, add.stderr - - update = run_cli( - "update-status", - "--report", - str(report), - "--issue-id", - "WBI-001", - "--status", - "fixed", - "--reason", - "Boundary-safe guard implemented", - "--evidence", - "pytest:test_integrity_check_report", - ) - assert update.returncode == 0, update.stderr - - summarize = run_cli("summarize-status", "--report", str(report)) - assert summarize.returncode == 0, summarize.stderr - summary_payload = json.loads(summarize.stdout) - assert summary_payload["decision_authority"] == "human" - assert summary_payload["fixed_count"] == 1 - assert summary_payload["open_count"] == 0 - - validate = run_cli("validate-report", "--report", str(report)) - assert validate.returncode == 0, validate.stderr - validate_payload = json.loads(validate.stdout) - assert validate_payload["status"] == "passed" - - -def test_boundary_guard_rejects_policy_authority_flag(tmp_path: Path) -> None: - report = create_report(tmp_path) - result = run_cli("validate-report", "--report", str(report), "--check-finding-correctness") - assert result.returncode == 2 - assert "structure" in result.stderr - - -def test_update_status_requires_evidence_for_fixed(tmp_path: Path) -> None: - report = create_report(tmp_path) - add = run_cli( - "add-issue", - "--report", - str(report), - "--severity", - "medium", - "--type", - "other", - "--summary", - "Need source update", - "--recommended-fix", - "Add source update", - "--evidence", - "agent-observation", - ) - assert add.returncode == 0, add.stderr - update = run_cli( - "update-status", - "--report", - str(report), - "--issue-id", - "WBI-001", - "--status", - "fixed", - "--reason", - "fixed", - ) - assert update.returncode == 1 - assert "requires --evidence" in update.stderr - diff --git a/tests/test_retired_integrity_check.py b/tests/test_retired_integrity_check.py new file mode 100644 index 0000000..98a290f --- /dev/null +++ b/tests/test_retired_integrity_check.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def run_wb(command: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(REPO_ROOT / "scripts/wb.py"), command], + cwd=REPO_ROOT, + check=False, + capture_output=True, + text=True, + ) + + +def test_integrity_check_runtime_surface_is_retired() -> None: + retired_paths = [ + REPO_ROOT / "skills/wb-integrity-check", + REPO_ROOT / "rules/integrity-check", + REPO_ROOT / "references/integrity-check", + REPO_ROOT / "scripts/integrity-check", + REPO_ROOT / "scripts/integrity_check_report.py", + REPO_ROOT / "scripts/work-bundle/integrity.py", + ] + + for path in retired_paths: + assert not path.exists(), f"retired integrity-check path still exists: {path}" + + for command in ("integrity-check-report", "integrity-report"): + result = run_wb(command) + assert result.returncode == 2 + assert f"unknown command: {command}" in result.stderr + + +def test_integrity_check_is_absent_from_rule_authoring_contracts() -> None: + contract_paths = [ + REPO_ROOT / "rules/index.yaml", + REPO_ROOT / "references/wb-create-rule-validation.yaml", + REPO_ROOT / "skills/wb-create-rule/SKILL.md", + REPO_ROOT / "rules/work-bundle/wb-create-rule.md", + REPO_ROOT / "scripts/work-bundle/rules.py", + REPO_ROOT / "scripts/work-bundle/README.md", + ] + + for path in contract_paths: + assert "integrity-check" not in path.read_text(encoding="utf-8"), path