@warpgogol/forge 4.2.4 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +14 -6
- package/CHANGELOG.md +72 -0
- package/README.md +8 -8
- package/README.uk.md +5 -5
- package/dist/os/adr/handlers/list-create.d.ts.map +1 -1
- package/dist/os/adr/handlers/list-create.js +4 -1
- package/dist/os/adr/handlers/list-create.js.map +1 -1
- package/dist/os/adr/handlers/validate.d.ts.map +1 -1
- package/dist/os/adr/handlers/validate.js +6 -1
- package/dist/os/adr/handlers/validate.js.map +1 -1
- package/dist/os/adr/types.d.ts.map +1 -1
- package/dist/os/adr/types.js +7 -3
- package/dist/os/adr/types.js.map +1 -1
- package/dist/os/compass/compass.module.d.ts.map +1 -1
- package/dist/os/compass/compass.module.js +76 -10
- package/dist/os/compass/compass.module.js.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-audit-handler.js +7 -3
- package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-change-summary-handler.d.ts +1 -9
- package/dist/os/compass/handlers/compass-change-summary-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-change-summary-handler.js +44 -186
- package/dist/os/compass/handlers/compass-change-summary-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-inventory-handler.d.ts +2 -0
- package/dist/os/compass/handlers/compass-inventory-handler.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-inventory-handler.js +24 -12
- package/dist/os/compass/handlers/compass-inventory-handler.js.map +1 -1
- package/dist/os/compass/handlers/compass-inventory.d.ts +7 -5
- package/dist/os/compass/handlers/compass-inventory.d.ts.map +1 -1
- package/dist/os/compass/handlers/compass-inventory.js +84 -241
- package/dist/os/compass/handlers/compass-inventory.js.map +1 -1
- package/dist/os/compass/handlers/compass-migrate-handler.d.ts +4 -0
- package/dist/os/compass/handlers/compass-migrate-handler.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-migrate-handler.js +100 -0
- package/dist/os/compass/handlers/compass-migrate-handler.js.map +1 -0
- package/dist/os/compass/handlers/compass-migrate.d.ts +34 -0
- package/dist/os/compass/handlers/compass-migrate.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-migrate.js +273 -0
- package/dist/os/compass/handlers/compass-migrate.js.map +1 -0
- package/dist/os/compass/handlers/resolve-scan-root.d.ts.map +1 -1
- package/dist/os/compass/handlers/resolve-scan-root.js +15 -8
- package/dist/os/compass/handlers/resolve-scan-root.js.map +1 -1
- package/dist/os/compass/handlers/summary-record.d.ts +46 -0
- package/dist/os/compass/handlers/summary-record.d.ts.map +1 -0
- package/dist/os/compass/handlers/summary-record.js +233 -0
- package/dist/os/compass/handlers/summary-record.js.map +1 -0
- package/dist/os/compass/index.d.ts +4 -1
- package/dist/os/compass/index.d.ts.map +1 -1
- package/dist/os/compass/index.js +9 -1
- package/dist/os/compass/index.js.map +1 -1
- package/dist/os/compass/policy.d.ts +65 -0
- package/dist/os/compass/policy.d.ts.map +1 -0
- package/dist/os/compass/policy.js +241 -0
- package/dist/os/compass/policy.js.map +1 -0
- package/dist/os/core/core.module.d.ts.map +1 -1
- package/dist/os/core/core.module.js +11 -23
- package/dist/os/core/core.module.js.map +1 -1
- package/dist/os/core/handlers/forge-autonomy-validate.d.ts.map +1 -1
- package/dist/os/core/handlers/forge-autonomy-validate.js +7 -1
- package/dist/os/core/handlers/forge-autonomy-validate.js.map +1 -1
- package/dist/os/core/handlers/package-health.d.ts.map +1 -1
- package/dist/os/core/handlers/package-health.js +6 -1
- package/dist/os/core/handlers/package-health.js.map +1 -1
- package/dist/os/core/handlers/pinned-check.d.ts.map +1 -1
- package/dist/os/core/handlers/pinned-check.js +6 -1
- package/dist/os/core/handlers/pinned-check.js.map +1 -1
- package/dist/os/core/handlers/pinned-validate.d.ts.map +1 -1
- package/dist/os/core/handlers/pinned-validate.js +6 -1
- package/dist/os/core/handlers/pinned-validate.js.map +1 -1
- package/dist/os/core/handlers/validate.d.ts.map +1 -1
- package/dist/os/core/handlers/validate.js +6 -1
- package/dist/os/core/handlers/validate.js.map +1 -1
- package/dist/os/exploration/exploration.module.d.ts.map +1 -1
- package/dist/os/exploration/exploration.module.js +6 -4
- package/dist/os/exploration/exploration.module.js.map +1 -1
- package/dist/os/exploration/handlers/list.d.ts.map +1 -1
- package/dist/os/exploration/handlers/list.js +4 -1
- package/dist/os/exploration/handlers/list.js.map +1 -1
- package/dist/os/mission/handlers/archive.d.ts.map +1 -1
- package/dist/os/mission/handlers/archive.js +7 -3
- package/dist/os/mission/handlers/archive.js.map +1 -1
- package/dist/os/mission/index.d.ts.map +1 -1
- package/dist/os/mission/index.js +13 -0
- package/dist/os/mission/index.js.map +1 -1
- package/dist/os/notes/index.d.ts.map +1 -1
- package/dist/os/notes/index.js +7 -1
- package/dist/os/notes/index.js.map +1 -1
- package/dist/os/rfc/frontmatter-io.d.ts.map +1 -1
- package/dist/os/rfc/frontmatter-io.js +6 -1
- package/dist/os/rfc/frontmatter-io.js.map +1 -1
- package/dist/os/rfc/handlers/archive.d.ts.map +1 -1
- package/dist/os/rfc/handlers/archive.js +6 -1
- package/dist/os/rfc/handlers/archive.js.map +1 -1
- package/dist/os/rfc/handlers/implement-stamp.d.ts.map +1 -1
- package/dist/os/rfc/handlers/implement-stamp.js +7 -3
- package/dist/os/rfc/handlers/implement-stamp.js.map +1 -1
- package/dist/os/rfc/handlers/index-graph.d.ts.map +1 -1
- package/dist/os/rfc/handlers/index-graph.js +6 -1
- package/dist/os/rfc/handlers/index-graph.js.map +1 -1
- package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
- package/dist/os/rfc/handlers/lifecycle.js +6 -1
- package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
- package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
- package/dist/os/rfc/handlers/list-create.js +6 -1
- package/dist/os/rfc/handlers/list-create.js.map +1 -1
- package/dist/os/rfc/handlers/pipeline-status.d.ts.map +1 -1
- package/dist/os/rfc/handlers/pipeline-status.js +6 -1
- package/dist/os/rfc/handlers/pipeline-status.js.map +1 -1
- package/dist/os/rfc/handlers/supersede-propose.d.ts.map +1 -1
- package/dist/os/rfc/handlers/supersede-propose.js +6 -1
- package/dist/os/rfc/handlers/supersede-propose.js.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
- package/dist/os/rfc/handlers/validate-rules.js +7 -3
- package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
- package/dist/os/rfc/handlers/validate.d.ts.map +1 -1
- package/dist/os/rfc/handlers/validate.js +6 -1
- package/dist/os/rfc/handlers/validate.js.map +1 -1
- package/dist/os/rfc/index.d.ts.map +1 -1
- package/dist/os/rfc/index.js +6 -1
- package/dist/os/rfc/index.js.map +1 -1
- package/dist/os/rfc/rfc.module.d.ts.map +1 -1
- package/dist/os/rfc/rfc.module.js +6 -1
- package/dist/os/rfc/rfc.module.js.map +1 -1
- package/dist/os/rfc/types.d.ts +7 -4
- package/dist/os/rfc/types.d.ts.map +1 -1
- package/dist/os/rfc/types.js +7 -4
- package/dist/os/rfc/types.js.map +1 -1
- package/dist/os/session/handlers/save.d.ts.map +1 -1
- package/dist/os/session/handlers/save.js +6 -1
- package/dist/os/session/handlers/save.js.map +1 -1
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.d.ts.map +1 -1
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js +6 -1
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js.map +1 -1
- package/dist/os/werkstatt/handlers/workspace-deps.js +3 -3
- package/dist/os/werkstatt/handlers/workspace-deps.js.map +1 -1
- package/dist/os/workflow/index.d.ts.map +1 -1
- package/dist/os/workflow/index.js +6 -1
- package/dist/os/workflow/index.js.map +1 -1
- package/dist/src/config/forge-config.d.ts +62 -4
- package/dist/src/config/forge-config.d.ts.map +1 -1
- package/dist/src/config/forge-config.js +35 -19
- package/dist/src/config/forge-config.js.map +1 -1
- package/dist/src/forge-module.d.ts.map +1 -1
- package/dist/src/forge-module.js +6 -1
- package/dist/src/forge-module.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/migration-adapters/git-utils.d.ts.map +1 -1
- package/dist/src/migration-adapters/git-utils.js +6 -2
- package/dist/src/migration-adapters/git-utils.js.map +1 -1
- package/dist/src/migration-adapters/ignored-files.d.ts.map +1 -1
- package/dist/src/migration-adapters/ignored-files.js +6 -1
- package/dist/src/migration-adapters/ignored-files.js.map +1 -1
- package/dist/src/migration-adapters/index.d.ts.map +1 -1
- package/dist/src/migration-adapters/index.js +4 -1
- package/dist/src/migration-adapters/index.js.map +1 -1
- package/dist/src/migration-adapters/types.d.ts.map +1 -1
- package/dist/src/migration-adapters/types.js +6 -2
- package/dist/src/migration-adapters/types.js.map +1 -1
- package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/agents-generate.js +18 -14
- package/dist/src/onboarding/agents-generate.js.map +1 -1
- package/dist/src/onboarding/create.d.ts.map +1 -1
- package/dist/src/onboarding/create.js +7 -6
- package/dist/src/onboarding/create.js.map +1 -1
- package/dist/src/onboarding/doctor.d.ts.map +1 -1
- package/dist/src/onboarding/doctor.js +7 -13
- package/dist/src/onboarding/doctor.js.map +1 -1
- package/dist/src/onboarding/init.d.ts.map +1 -1
- package/dist/src/onboarding/init.js +7 -12
- package/dist/src/onboarding/init.js.map +1 -1
- package/dist/src/onboarding/invariant-engine.d.ts.map +1 -1
- package/dist/src/onboarding/invariant-engine.js +7 -2
- package/dist/src/onboarding/invariant-engine.js.map +1 -1
- package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-generate.js +7 -2
- package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
- package/dist/src/onboarding/nested-agents-templates.js +11 -3
- package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
- package/dist/src/onboarding/scaffold-project.d.ts.map +1 -1
- package/dist/src/onboarding/scaffold-project.js +6 -1
- package/dist/src/onboarding/scaffold-project.js.map +1 -1
- package/dist/src/onboarding/upgrade.d.ts.map +1 -1
- package/dist/src/onboarding/upgrade.js +7 -3
- package/dist/src/onboarding/upgrade.js.map +1 -1
- package/dist/src/profiles/profile-schema.d.ts +54 -4
- package/dist/src/profiles/profile-schema.d.ts.map +1 -1
- package/dist/src/profiles/profile-schema.js +34 -14
- package/dist/src/profiles/profile-schema.js.map +1 -1
- package/dist/src/profiles/stack-profile.d.ts +24 -0
- package/dist/src/profiles/stack-profile.d.ts.map +1 -1
- package/dist/src/profiles/stack-profile.js +7 -3
- package/dist/src/profiles/stack-profile.js.map +1 -1
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +7 -9
- package/dist/src/registry.js.map +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +6 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/fs-atomic.d.ts.map +1 -1
- package/dist/src/utils/fs-atomic.js +13 -1
- package/dist/src/utils/fs-atomic.js.map +1 -1
- package/dist/src/utils/fs-idempotent.d.ts.map +1 -1
- package/dist/src/utils/fs-idempotent.js +13 -0
- package/dist/src/utils/fs-idempotent.js.map +1 -1
- package/dist/src/utils/fs-trash-sync.d.ts.map +1 -1
- package/dist/src/utils/fs-trash-sync.js +13 -1
- package/dist/src/utils/fs-trash-sync.js.map +1 -1
- package/dist/src/utils/fs-trash.d.ts.map +1 -1
- package/dist/src/utils/fs-trash.js +13 -1
- package/dist/src/utils/fs-trash.js.map +1 -1
- package/dist/src/utils/fs.d.ts.map +1 -1
- package/dist/src/utils/fs.js +14 -3
- package/dist/src/utils/fs.js.map +1 -1
- package/dist/src/utils/hash.d.ts.map +1 -1
- package/dist/src/utils/hash.js +12 -1
- package/dist/src/utils/hash.js.map +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +13 -2
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/string-utils.d.ts.map +1 -1
- package/dist/src/utils/string-utils.js +12 -1
- package/dist/src/utils/string-utils.js.map +1 -1
- package/dist/src/validators/port-validate.js +1 -1
- package/dist/src/validators/port-validate.js.map +1 -1
- package/dist/src/validators/skill-validate.d.ts.map +1 -1
- package/dist/src/validators/skill-validate.js +7 -8
- package/dist/src/validators/skill-validate.js.map +1 -1
- package/docs/getting-started.md +1 -1
- package/docs/reference/forge-yaml.md +38 -9
- package/docs/reference/profiles.md +8 -8
- package/os/adr/handlers/list-create.ts +4 -1
- package/os/adr/handlers/validate.ts +6 -1
- package/os/adr/types.ts +7 -3
- package/os/compass/compass.module.ts +79 -13
- package/os/compass/handlers/compass-audit-handler.ts +22 -3
- package/os/compass/handlers/compass-change-summary-handler.ts +55 -227
- package/os/compass/handlers/compass-inventory-handler.ts +30 -12
- package/os/compass/handlers/compass-inventory.ts +103 -240
- package/os/compass/handlers/compass-migrate-handler.ts +122 -0
- package/os/compass/handlers/compass-migrate.ts +361 -0
- package/os/compass/handlers/resolve-scan-root-package.test.ts +70 -0
- package/os/compass/handlers/resolve-scan-root.ts +15 -8
- package/os/compass/handlers/summary-record.ts +313 -0
- package/os/compass/handlers/tests/compass-migrate.test.ts +291 -0
- package/os/compass/handlers/tests/compass-policy.test.ts +249 -0
- package/os/compass/handlers/tests/compass-v2-contract.test.ts +9 -9
- package/os/compass/handlers/tests/summary-record.test.ts +224 -0
- package/os/compass/index.ts +23 -5
- package/os/compass/policy.ts +350 -0
- package/os/core/core.module.ts +11 -23
- package/os/core/handlers/forge-autonomy-validate.ts +7 -1
- package/os/core/handlers/package-health.ts +6 -1
- package/os/core/handlers/pinned-check.ts +6 -1
- package/os/core/handlers/pinned-validate.ts +6 -1
- package/os/core/handlers/validate.ts +6 -1
- package/os/exploration/exploration.module.ts +72 -71
- package/os/exploration/handlers/list.ts +4 -1
- package/os/mission/handlers/archive.ts +7 -3
- package/os/mission/index.ts +14 -0
- package/os/notes/index.ts +7 -1
- package/os/rfc/frontmatter-io.ts +6 -1
- package/os/rfc/handlers/archive.ts +6 -1
- package/os/rfc/handlers/implement-stamp.ts +7 -3
- package/os/rfc/handlers/index-graph.ts +6 -1
- package/os/rfc/handlers/lifecycle.ts +6 -1
- package/os/rfc/handlers/list-create.ts +6 -1
- package/os/rfc/handlers/pipeline-status.ts +6 -1
- package/os/rfc/handlers/supersede-propose.ts +6 -1
- package/os/rfc/handlers/validate-rules.ts +7 -3
- package/os/rfc/handlers/validate.ts +6 -1
- package/os/rfc/index.ts +6 -1
- package/os/rfc/rfc.module.ts +6 -1
- package/os/rfc/types.ts +7 -4
- package/os/session/handlers/save.ts +6 -1
- package/os/werkstatt/handlers/werkstatt-lock-recover.ts +6 -1
- package/os/werkstatt/handlers/workspace-deps.ts +3 -3
- package/os/workflow/index.ts +6 -1
- package/package.json +1 -1
- package/profiles/{godot-csharp.yaml → godot-game.yaml} +2 -2
- package/profiles/{knowledge-typescript-turborepo.yaml → knowledge.yaml} +1 -1
- package/profiles/{phaser-turborepo.yaml → phaser-game.yaml} +2 -2
- package/profiles/{typescript-turborepo.yaml → typescript.yaml} +2 -2
- package/skills/fo/fo-add-tests/SKILL.md +14 -0
- package/skills/fo/fo-architecture/SKILL.md +14 -0
- package/skills/fo/fo-compass-annotate/SKILL.md +14 -0
- package/skills/fo/fo-design-summit/SKILL.md +14 -0
- package/skills/fo/fo-doc-audit/SKILL.md +14 -0
- package/skills/fo/fo-explore/SKILL.md +14 -0
- package/skills/fo/fo-extract-dna/SKILL.md +14 -0
- package/skills/fo/fo-fix/SKILL.md +14 -0
- package/skills/fo/fo-handoff/SKILL.md +14 -0
- package/skills/fo/fo-harvest/SKILL.md +14 -0
- package/skills/fo/fo-idea/SKILL.md +14 -0
- package/skills/fo/fo-idea-audit/SKILL.md +14 -0
- package/skills/fo/fo-idea-create-adr/SKILL.md +14 -0
- package/skills/fo/fo-idea-create-rfc/SKILL.md +14 -0
- package/skills/fo/fo-idea-enhance/SKILL.md +14 -0
- package/skills/fo/fo-idea-i-just-want-to-see-the-plan/SKILL.md +14 -0
- package/skills/fo/fo-idea-i-just-want-to-see-the-result/SKILL.md +14 -0
- package/skills/fo/fo-idea-implement/SKILL.md +14 -0
- package/skills/fo/fo-idea-plan/SKILL.md +14 -0
- package/skills/fo/fo-idea-status/SKILL.md +14 -0
- package/skills/fo/fo-knowledge-distill/SKILL.md +14 -0
- package/skills/fo/fo-memory-sync/SKILL.md +14 -0
- package/skills/fo/fo-qa/SKILL.md +14 -0
- package/skills/fo/fo-review/SKILL.md +14 -0
- package/skills/fo/fo-session-retro/SKILL.md +14 -0
- package/skills/fo/fo-session-save/SKILL.md +14 -0
- package/skills/fo/fo-spec-ingest/SKILL.md +14 -0
- package/skills/fo/fo-step-commit/SKILL.md +14 -0
- package/skills/fo/fo-triage/SKILL.md +14 -0
- package/skills/meta/forge-bootstrap/SKILL.md +14 -0
- package/skills/meta/port-to-forge/SKILL.md +14 -0
- package/skills/meta/skill-create/SKILL.md +14 -0
- package/skills/shared/grilling/SKILL.md +14 -0
- package/skills/shared/my-preferences/SKILL.md +14 -0
- package/skills/shared/windows-ai-tooling/SKILL.md +14 -0
- package/skills/shared/writing-great-skills/SKILL.md +14 -0
- package/src/config/forge-config.ts +40 -23
- package/src/forge-module.ts +6 -1
- package/src/index.ts +10 -2
- package/src/migration-adapters/git-utils.ts +6 -2
- package/src/migration-adapters/ignored-files.ts +6 -1
- package/src/migration-adapters/index.ts +4 -1
- package/src/migration-adapters/types.ts +6 -2
- package/src/onboarding/agents-generate.ts +23 -14
- package/src/onboarding/create.ts +7 -6
- package/src/onboarding/doctor.ts +7 -13
- package/src/onboarding/init.ts +7 -12
- package/src/onboarding/invariant-engine.ts +7 -2
- package/src/onboarding/nested-agents-generate.ts +7 -2
- package/src/onboarding/nested-agents-templates.ts +11 -3
- package/src/onboarding/scaffold-project.ts +6 -1
- package/src/onboarding/upgrade.ts +7 -3
- package/src/profiles/profile-schema.ts +42 -18
- package/src/profiles/stack-profile.ts +7 -3
- package/src/registry.ts +7 -9
- package/src/tests/agents-generate-domain.test.ts +3 -3
- package/src/tests/agents-generate.test.ts +2 -2
- package/src/tests/create.test.ts +4 -4
- package/src/tests/forge-autonomy-validate.test.ts +1 -1
- package/src/tests/package-files.test.ts +53 -1
- package/src/tests/profile-schema.test.ts +4 -4
- package/src/tests/scaffold-project.test.ts +3 -3
- package/src/tests/stack-profile.test.ts +12 -12
- package/src/types.ts +6 -1
- package/src/utils/fs-atomic.ts +13 -1
- package/src/utils/fs-idempotent.ts +13 -0
- package/src/utils/fs-trash-sync.ts +13 -1
- package/src/utils/fs-trash.ts +13 -1
- package/src/utils/fs.ts +14 -3
- package/src/utils/hash.ts +12 -1
- package/src/utils/index.ts +13 -2
- package/src/utils/string-utils.ts +12 -1
- package/src/validators/port-validate.ts +1 -1
- package/src/validators/skill-validate.ts +7 -8
- package/skills/shared/extension-e2e-mitm/SKILL.md +0 -53
- package/skills/shared/extension-e2e-mitm/fixture-proxy.ts +0 -113
- package/src/types/werkstatt-engine-shims.d.ts +0 -1416
- package/src/types/werkstatt-shared-shims.d.ts +0 -5686
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Unit tests for RFC-1096 Compass policy externalization — generic
|
|
4
|
+
defaults, profile overlay, bindings.compass overrides, merge semantics, and
|
|
5
|
+
the named-config-error contract.</purpose>
|
|
6
|
+
<non-goals>
|
|
7
|
+
<item>Do not test v2 rule diagnostics — covered by compass-v2-contract.test.ts.</item>
|
|
8
|
+
</non-goals>
|
|
9
|
+
</MODULE_CONTRACT>
|
|
10
|
+
<CHANGE_SUMMARY>
|
|
11
|
+
<item>RFC-1096: initial coverage for resolveCompassPolicy (AC-1..AC-5).</item>
|
|
12
|
+
</CHANGE_SUMMARY>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
16
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, readdirSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import {
|
|
21
|
+
resolveCompassPolicy,
|
|
22
|
+
CompassPolicyConfigError,
|
|
23
|
+
parseGovernanceIdParts,
|
|
24
|
+
} from "../../policy.ts";
|
|
25
|
+
import { runCompassInventory } from "../compass-inventory-handler.ts";
|
|
26
|
+
import type { ForgeRuntimeContext } from "../../../../src/types.ts";
|
|
27
|
+
|
|
28
|
+
// packages/forge — real forge root so `profile:` resolution finds shipped profiles.
|
|
29
|
+
const FORGE_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
30
|
+
// os/compass directory — scanned by the AC-4 literal check.
|
|
31
|
+
const COMPASS_DIR = fileURLToPath(new URL("../../", import.meta.url));
|
|
32
|
+
|
|
33
|
+
const FORGE_YAML = (bindings: string) => `
|
|
34
|
+
schema: forge/config@1
|
|
35
|
+
project:
|
|
36
|
+
name: test-ws
|
|
37
|
+
stack: [typescript]
|
|
38
|
+
packageManager: pnpm
|
|
39
|
+
paths:
|
|
40
|
+
rfcsDir: docs/rfcs
|
|
41
|
+
adrsDir: docs/adrs
|
|
42
|
+
plansDir: docs/plans
|
|
43
|
+
auditsDir: docs/audits
|
|
44
|
+
specsDir: docs/specs
|
|
45
|
+
skillsDir: .agents/skills
|
|
46
|
+
bindings:
|
|
47
|
+
schema: forge/bindings@1
|
|
48
|
+
commands: {}
|
|
49
|
+
paths: {}
|
|
50
|
+
${bindings}`;
|
|
51
|
+
|
|
52
|
+
const FORGE_YAML_WITH_PROFILE = `
|
|
53
|
+
schema: forge/config@1
|
|
54
|
+
profile: godot-game
|
|
55
|
+
project:
|
|
56
|
+
name: test-ws
|
|
57
|
+
stack: [godot]
|
|
58
|
+
packageManager: pnpm
|
|
59
|
+
paths:
|
|
60
|
+
rfcsDir: docs/rfcs
|
|
61
|
+
adrsDir: docs/adrs
|
|
62
|
+
plansDir: docs/plans
|
|
63
|
+
auditsDir: docs/audits
|
|
64
|
+
specsDir: docs/specs
|
|
65
|
+
skillsDir: .agents/skills
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
function makeContext(workspaceRoot: string): ForgeRuntimeContext {
|
|
69
|
+
return {
|
|
70
|
+
workspaceRoot,
|
|
71
|
+
forgeRoot: FORGE_ROOT,
|
|
72
|
+
dryRun: true,
|
|
73
|
+
logger: { info: () => {}, warn: () => {}, error: () => {} },
|
|
74
|
+
} as unknown as ForgeRuntimeContext;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectTsFiles(dir: string): string[] {
|
|
78
|
+
const out: string[] = [];
|
|
79
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
80
|
+
const abs = join(dir, entry.name);
|
|
81
|
+
if (entry.isDirectory()) out.push(...collectTsFiles(abs));
|
|
82
|
+
else if (entry.isFile() && entry.name.endsWith(".ts")) out.push(abs);
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe("resolveCompassPolicy (RFC-1096)", () => {
|
|
88
|
+
let root: string;
|
|
89
|
+
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
root = mkdtempSync(join(tmpdir(), "compass-policy-"));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
rmSync(root, { recursive: true, force: true });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("AC-1: bindings.compass.scanRoots replaces the generic scan roots", () => {
|
|
99
|
+
writeFileSync(join(root, "forge.yaml"), FORGE_YAML(" compass:\n scanRoots: [src]\n"));
|
|
100
|
+
const policy = resolveCompassPolicy(root, FORGE_ROOT);
|
|
101
|
+
expect(policy.scanRoots).toEqual(["src"]);
|
|
102
|
+
expect(policy.source.overriddenKeys).toContain("scanRoots");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("AC-1b: union keys merge and `!` subtracts", () => {
|
|
106
|
+
writeFileSync(
|
|
107
|
+
join(root, "forge.yaml"),
|
|
108
|
+
FORGE_YAML(
|
|
109
|
+
' compass:\n fileExtensions: [.ts, .svelte, "!.astro"]\n ignoredDirs: [spec, "!coverage"]\n',
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
const policy = resolveCompassPolicy(root, FORGE_ROOT);
|
|
113
|
+
expect(policy.fileExtensions.has(".svelte")).toBe(true);
|
|
114
|
+
expect(policy.fileExtensions.has(".ts")).toBe(true);
|
|
115
|
+
expect(policy.fileExtensions.has(".astro")).toBe(false);
|
|
116
|
+
expect(policy.ignoredDirs.has("spec")).toBe(true);
|
|
117
|
+
expect(policy.ignoredDirs.has("coverage")).toBe(false);
|
|
118
|
+
expect(policy.ignoredDirs.has("node_modules")).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("AC-1c: consumer layerRules replace the generic set wholesale", () => {
|
|
122
|
+
writeFileSync(
|
|
123
|
+
join(root, "forge.yaml"),
|
|
124
|
+
FORGE_YAML(
|
|
125
|
+
" compass:\n layerRules:\n - pattern: src/pages/**\n layer: route\n risk: high\n",
|
|
126
|
+
),
|
|
127
|
+
);
|
|
128
|
+
const policy = resolveCompassPolicy(root, FORGE_ROOT);
|
|
129
|
+
expect(policy.matchLayer("src/pages/index.astro")?.layer).toBe("route");
|
|
130
|
+
expect(policy.matchLayer("src/pages/index.astro")?.risk).toBe("high");
|
|
131
|
+
// Replace semantics: generic rules are gone when the consumer sets the key.
|
|
132
|
+
expect(policy.matchLayer("src/components/x.ts")).toBeUndefined();
|
|
133
|
+
expect(policy.layerRules).toHaveLength(1);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("AC-2: profile compass section applies when forge.yaml declares `profile`", () => {
|
|
137
|
+
writeFileSync(join(root, "forge.yaml"), FORGE_YAML_WITH_PROFILE);
|
|
138
|
+
const policy = resolveCompassPolicy(root, FORGE_ROOT);
|
|
139
|
+
expect(policy.source.profile).toBe("godot-game");
|
|
140
|
+
for (const ext of [".cs", ".tscn", ".tres", ".gd"]) {
|
|
141
|
+
expect(policy.fileExtensions.has(ext), `missing ${ext}`).toBe(true);
|
|
142
|
+
}
|
|
143
|
+
expect(policy.source.overriddenKeys).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("AC-3: non-compilable idPattern throws CompassPolicyConfigError naming the key", () => {
|
|
147
|
+
writeFileSync(join(root, "forge.yaml"), FORGE_YAML(' compass:\n idPattern: "([invalid"\n'));
|
|
148
|
+
expect(() => resolveCompassPolicy(root, FORGE_ROOT)).toThrow(CompassPolicyConfigError);
|
|
149
|
+
try {
|
|
150
|
+
resolveCompassPolicy(root, FORGE_ROOT);
|
|
151
|
+
expect.unreachable();
|
|
152
|
+
} catch (err) {
|
|
153
|
+
expect(err).toBeInstanceOf(CompassPolicyConfigError);
|
|
154
|
+
expect((err as CompassPolicyConfigError).key).toBe("idPattern");
|
|
155
|
+
expect((err as Error).message).toContain("idPattern");
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("AC-3b: idPattern that fails the NAMESPACE-NUMBER probe is rejected", () => {
|
|
160
|
+
writeFileSync(join(root, "forge.yaml"), FORGE_YAML(' compass:\n idPattern: "^\\\\d+$"\n'));
|
|
161
|
+
expect(() => resolveCompassPolicy(root, FORGE_ROOT)).toThrow(CompassPolicyConfigError);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("AC-4: no consumer- or stack-specific path literals under os/compass", () => {
|
|
165
|
+
// Constructed dynamically so this test file does not itself carry the literals.
|
|
166
|
+
// Path-literal forms only — historical `@warpgogol/<pkg>` specifiers in
|
|
167
|
+
// CHANGE_SUMMARY comments are package names, not path literals.
|
|
168
|
+
const bannedPathLiterals = [
|
|
169
|
+
"apps/" + "main",
|
|
170
|
+
"DEFAULT_" + "SCAN_" + "ROOTS",
|
|
171
|
+
"werk" + "statt",
|
|
172
|
+
"packages/" + "os/",
|
|
173
|
+
"site-" + "kernel/",
|
|
174
|
+
];
|
|
175
|
+
// AC-4 names `site-kernel` (bare) as banned inside compass-inventory.ts.
|
|
176
|
+
const bannedInInventory = [...bannedPathLiterals, "site-" + "kernel"];
|
|
177
|
+
const inventoryFile = join(COMPASS_DIR, "handlers", "compass-inventory.ts");
|
|
178
|
+
|
|
179
|
+
const offenders: string[] = [];
|
|
180
|
+
for (const file of collectTsFiles(COMPASS_DIR)) {
|
|
181
|
+
const source = readFileSync(file, "utf8");
|
|
182
|
+
const banned = file === inventoryFile ? bannedInInventory : bannedPathLiterals;
|
|
183
|
+
for (const literal of banned) {
|
|
184
|
+
if (source.includes(literal)) {
|
|
185
|
+
offenders.push(`${file}: ${literal}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
expect(offenders, `banned literals found:\n${offenders.join("\n")}`).toEqual([]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("AC-5: highRiskPaths from bindings mark matching entries riskClass=high", async () => {
|
|
193
|
+
writeFileSync(
|
|
194
|
+
join(root, "forge.yaml"),
|
|
195
|
+
FORGE_YAML(
|
|
196
|
+
' compass:\n scanRoots: [packages]\n highRiskPaths: ["packages/fixture-pkg/src/kernel/**"]\n',
|
|
197
|
+
),
|
|
198
|
+
);
|
|
199
|
+
const fixture = join(root, "packages", "fixture-pkg", "src", "kernel");
|
|
200
|
+
mkdirSync(fixture, { recursive: true });
|
|
201
|
+
const body = Array.from({ length: 25 }, (_, i) => `export const line${i} = ${i};`).join("\n");
|
|
202
|
+
writeFileSync(
|
|
203
|
+
join(fixture, "engine.ts"),
|
|
204
|
+
`/*\n<MODULE_CONTRACT>\n<purpose>Fixture kernel engine file for the compass policy high-risk test.</purpose>\n<non-goals>\n <item>No production logic.</item>\n</non-goals>\n</MODULE_CONTRACT>\n*/\n${body}\n`,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const result = await runCompassInventory({ flags: {} } as never, makeContext(root));
|
|
208
|
+
const entry = result.data?.entries.find((e) => e.path.endsWith("kernel/engine.ts"));
|
|
209
|
+
expect(entry, "fixture entry must be inventoried").toBeDefined();
|
|
210
|
+
expect(entry!.riskClass).toBe("high");
|
|
211
|
+
expect(result.data?.policySource.overriddenKeys).toContain("highRiskPaths");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("excludedPaths mark matching files excluded with the configured reason", async () => {
|
|
215
|
+
writeFileSync(
|
|
216
|
+
join(root, "forge.yaml"),
|
|
217
|
+
FORGE_YAML(
|
|
218
|
+
" compass:\n scanRoots: [packages]\n excludedPaths:\n - pattern: src/templates/**\n reason: template-source\n",
|
|
219
|
+
),
|
|
220
|
+
);
|
|
221
|
+
const dir = join(root, "packages", "fixture-pkg", "src", "templates");
|
|
222
|
+
mkdirSync(dir, { recursive: true });
|
|
223
|
+
writeFileSync(join(dir, "widget.ts"), "export const t = 1;\n");
|
|
224
|
+
|
|
225
|
+
const result = await runCompassInventory({ flags: {} } as never, makeContext(root));
|
|
226
|
+
const entry = result.data?.entries.find((e) => e.path.endsWith("templates/widget.ts"));
|
|
227
|
+
expect(entry).toBeDefined();
|
|
228
|
+
expect(entry!.authoringStatus).toBe("excluded");
|
|
229
|
+
expect(entry!.exclusionReason).toBe("template-source");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("missing forge.yaml yields the generic policy", () => {
|
|
233
|
+
const policy = resolveCompassPolicy(root, FORGE_ROOT);
|
|
234
|
+
expect(policy.source.profile).toBeNull();
|
|
235
|
+
expect(policy.source.overriddenKeys).toEqual([]);
|
|
236
|
+
expect(policy.fileExtensions.has(".ts")).toBe(true);
|
|
237
|
+
expect(policy.scanRoots).toEqual(["src", "apps", "packages", "services"]);
|
|
238
|
+
expect(policy.idPattern.test("RFC-1096")).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("parseGovernanceIdParts splits on the last dash", () => {
|
|
242
|
+
expect(parseGovernanceIdParts("RFC-1095")).toEqual({ namespace: "RFC", numeric: 1095 });
|
|
243
|
+
expect(parseGovernanceIdParts("COMPASS-CS-07")).toEqual({
|
|
244
|
+
namespace: "COMPASS-CS",
|
|
245
|
+
numeric: 7,
|
|
246
|
+
});
|
|
247
|
+
expect(parseGovernanceIdParts("no-number")).toBeNull();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
@@ -3,7 +3,6 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { runCompassValidation, runCompassInventory } from "../compass-inventory-handler.ts";
|
|
6
|
-
import { runCompassChangeSummaryValidate } from "../compass-change-summary-handler.ts";
|
|
7
6
|
import type {
|
|
8
7
|
ForgeCommandInput,
|
|
9
8
|
ForgeCommandResult,
|
|
@@ -34,8 +33,12 @@ const MC = (purpose: string) =>
|
|
|
34
33
|
const KD = (items: string[]) =>
|
|
35
34
|
`<KEY_DECISIONS>\n${items.map((i) => ` <item>${i}</item>`).join("\n")}\n</KEY_DECISIONS>`;
|
|
36
35
|
const KD_EMPTY = `<KEY_DECISIONS>\n</KEY_DECISIONS>`;
|
|
36
|
+
// Dynamic tags: a literal CHANGE_SUMMARY block in this file would be matched
|
|
37
|
+
// by compass.summary.record itself and rewritten at commit time.
|
|
38
|
+
const CS_OPEN = "<" + "CHANGE_SUMMARY>";
|
|
39
|
+
const CS_CLOSE = "</" + "CHANGE_SUMMARY>";
|
|
37
40
|
const CS = (items: string[], history?: string) =>
|
|
38
|
-
|
|
41
|
+
`${CS_OPEN}\n${items.map((i) => ` <item>${i}</item>`).join("\n")}\n${history ? ` <history>${history}</history>\n` : ""}${CS_CLOSE}`;
|
|
39
42
|
const wrap = (...blocks: string[]) => `/*\n${blocks.join("\n")}\n*/`;
|
|
40
43
|
|
|
41
44
|
const PURPOSE_WITH_TOKEN =
|
|
@@ -100,7 +103,8 @@ describe("compass v2 contract (RFC-1094)", () => {
|
|
|
100
103
|
const items = [1, 2, 3, 4, 5, 6].map((n) => `RFC-10${n}0: change number ${n}.`);
|
|
101
104
|
writeFixture(MEDIUM_FILE, wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS(items)));
|
|
102
105
|
|
|
103
|
-
|
|
106
|
+
// RFC-1095: CS rules are covered by compass.validate via shared evaluateV2Rules.
|
|
107
|
+
const result = await runCompassValidation(makeInput(), makeContext(tempDir));
|
|
104
108
|
const diag = dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-05");
|
|
105
109
|
expect(diag, "CS-05 must fire when CHANGE_SUMMARY exceeds 5 items").toBeDefined();
|
|
106
110
|
expect(diag!.severity).toBe("warning");
|
|
@@ -113,7 +117,7 @@ describe("compass v2 contract (RFC-1094)", () => {
|
|
|
113
117
|
wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS(["fixed a bug in the widget"])),
|
|
114
118
|
);
|
|
115
119
|
|
|
116
|
-
const result = await
|
|
120
|
+
const result = await runCompassValidation(makeInput(), makeContext(tempDir));
|
|
117
121
|
const diag = dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-06");
|
|
118
122
|
expect(diag, "CS-06 must fire for items lacking a governance ID").toBeDefined();
|
|
119
123
|
});
|
|
@@ -203,7 +207,7 @@ describe("compass v2 contract (RFC-1094)", () => {
|
|
|
203
207
|
wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS([CS_ITEM], "RFC-0350, RFC-0348")),
|
|
204
208
|
);
|
|
205
209
|
|
|
206
|
-
const result = await
|
|
210
|
+
const result = await runCompassValidation(makeInput(), makeContext(tempDir));
|
|
207
211
|
expect(
|
|
208
212
|
dataOf(result).diagnostics.find((d) => d.ruleId === "COMPASS-CS-07"),
|
|
209
213
|
"CS-07 must fire on descending per-namespace order",
|
|
@@ -214,13 +218,9 @@ describe("compass v2 contract (RFC-1094)", () => {
|
|
|
214
218
|
writeFixture(MEDIUM_FILE, wrap(MC(PURPOSE_WITH_TOKEN), KD([KD_ITEM]), CS([])));
|
|
215
219
|
|
|
216
220
|
const warnResult = await runCompassValidation(makeInput(), makeContext(tempDir));
|
|
217
|
-
const csResult = await runCompassChangeSummaryValidate(makeInput(), makeContext(tempDir));
|
|
218
221
|
expect(
|
|
219
222
|
dataOf(warnResult).diagnostics.filter((d) => d.ruleId.startsWith("COMPASS-CS-")),
|
|
220
223
|
).toHaveLength(0);
|
|
221
|
-
expect(
|
|
222
|
-
dataOf(csResult).diagnostics.filter((d) => d.ruleId.startsWith("COMPASS-CS-")),
|
|
223
|
-
).toHaveLength(0);
|
|
224
224
|
});
|
|
225
225
|
|
|
226
226
|
it("fully compliant v2 file produces zero v2 diagnostics", async () => {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import {
|
|
6
|
+
runCompassSummaryRecord,
|
|
7
|
+
parseChangeSummary,
|
|
8
|
+
mergeHistoryIds,
|
|
9
|
+
buildChangeSummaryBlock,
|
|
10
|
+
stripConventionalPrefix,
|
|
11
|
+
isValidGovernanceId,
|
|
12
|
+
CHANGE_SUMMARY_WINDOW,
|
|
13
|
+
} from "../summary-record.ts";
|
|
14
|
+
import { resolveCompassPolicy } from "../../policy.ts";
|
|
15
|
+
import type { ForgeRuntimeContext } from "../../../../src/types.ts";
|
|
16
|
+
|
|
17
|
+
// Generic policy — this path has no forge.yaml, so resolution falls back to
|
|
18
|
+
// GENERIC defaults.
|
|
19
|
+
const policy = resolveCompassPolicy(join(tmpdir(), "summary-record-no-forge-yaml"));
|
|
20
|
+
|
|
21
|
+
const logger = {
|
|
22
|
+
section() {},
|
|
23
|
+
info() {},
|
|
24
|
+
warn() {},
|
|
25
|
+
error() {},
|
|
26
|
+
success() {},
|
|
27
|
+
getEvents() {
|
|
28
|
+
return [];
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
function makeContext(workspaceRoot: string, dryRun = false): ForgeRuntimeContext {
|
|
33
|
+
return {
|
|
34
|
+
workspaceRoot,
|
|
35
|
+
logger: logger as never,
|
|
36
|
+
dryRun,
|
|
37
|
+
outputFormat: "pretty",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function makeInput(flags: Record<string, unknown>) {
|
|
42
|
+
return { argv: [], flags: flags as never };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Dynamic tags: a literal CHANGE_SUMMARY block in this file would be matched
|
|
46
|
+
// by compass.summary.record itself and rewritten at commit time.
|
|
47
|
+
const CS_OPEN = "<" + "CHANGE_SUMMARY>";
|
|
48
|
+
const CS_CLOSE = "</" + "CHANGE_SUMMARY>";
|
|
49
|
+
const CS_BLOCK_RE = new RegExp(`${CS_OPEN}[\\s\\S]*?${CS_CLOSE}`);
|
|
50
|
+
|
|
51
|
+
const TS_HEADER = (items: string[], history?: string[]) => `/*
|
|
52
|
+
<MODULE_CONTRACT>
|
|
53
|
+
<purpose>Test fixture file for summary record coverage.</purpose>
|
|
54
|
+
<non-goals><item>No production logic.</item></non-goals>
|
|
55
|
+
</MODULE_CONTRACT>
|
|
56
|
+
${CS_OPEN}
|
|
57
|
+
${items.map((i) => ` <item>${i}</item>`).join("\n")}
|
|
58
|
+
${history && history.length > 0 ? ` <history>${history.join(", ")}</history>\n` : ""}${CS_CLOSE}
|
|
59
|
+
*/
|
|
60
|
+
export const x = 1;
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
describe("compass.summary.record", () => {
|
|
64
|
+
let root: string;
|
|
65
|
+
|
|
66
|
+
beforeEach(async () => {
|
|
67
|
+
root = await mkdtemp(join(tmpdir(), "summary-record-"));
|
|
68
|
+
await mkdir(join(root, "src"), { recursive: true });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(async () => {
|
|
72
|
+
await rm(root, { recursive: true, force: true });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("appends an ID-prefixed item to CHANGE_SUMMARY (AC-1)", async () => {
|
|
76
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER(["RFC-0001: existing item."]));
|
|
77
|
+
const result = await runCompassSummaryRecord(
|
|
78
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "added record command" }),
|
|
79
|
+
makeContext(root),
|
|
80
|
+
);
|
|
81
|
+
expect(result.exitCode).toBe(0);
|
|
82
|
+
const source = await readFile(join(root, "src", "a.ts"), "utf8");
|
|
83
|
+
expect(source).toContain("<item>RFC-1095: added record command</item>");
|
|
84
|
+
expect(result.data?.recorded).toEqual(["src/a.ts"]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("skips duplicate items with identical ID+text (AC-2)", async () => {
|
|
88
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER(["RFC-1095: added record command"]));
|
|
89
|
+
const result = await runCompassSummaryRecord(
|
|
90
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "added record command" }),
|
|
91
|
+
makeContext(root),
|
|
92
|
+
);
|
|
93
|
+
expect(result.data?.skipped).toEqual([{ file: "src/a.ts", reason: "duplicate" }]);
|
|
94
|
+
expect(result.data?.recorded).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("collapses the oldest item into <history> past the 5-item window (AC-3)", async () => {
|
|
98
|
+
const items = Array.from(
|
|
99
|
+
{ length: CHANGE_SUMMARY_WINDOW },
|
|
100
|
+
(_, i) => `RFC-000${i}: item ${i}.`,
|
|
101
|
+
);
|
|
102
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER(items));
|
|
103
|
+
const result = await runCompassSummaryRecord(
|
|
104
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "overflow item" }),
|
|
105
|
+
makeContext(root),
|
|
106
|
+
);
|
|
107
|
+
const source = await readFile(join(root, "src", "a.ts"), "utf8");
|
|
108
|
+
const parsed = parseChangeSummary(source.match(CS_BLOCK_RE)![0]);
|
|
109
|
+
expect(parsed.items).toHaveLength(CHANGE_SUMMARY_WINDOW);
|
|
110
|
+
expect(parsed.items.at(-1)).toBe("RFC-1095: overflow item");
|
|
111
|
+
expect(parsed.historyIds).toContain("RFC-0000");
|
|
112
|
+
expect(result.data?.collapsed).toEqual(["src/a.ts"]);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("merges collapsed IDs into existing history deduped and per-namespace ascending (AC-4)", async () => {
|
|
116
|
+
const items = Array.from(
|
|
117
|
+
{ length: CHANGE_SUMMARY_WINDOW },
|
|
118
|
+
(_, i) => `RFC-000${i}: item ${i}.`,
|
|
119
|
+
);
|
|
120
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER(items, ["RFC-0000", "ADR-0002"]));
|
|
121
|
+
await runCompassSummaryRecord(
|
|
122
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "overflow" }),
|
|
123
|
+
makeContext(root),
|
|
124
|
+
);
|
|
125
|
+
const source = await readFile(join(root, "src", "a.ts"), "utf8");
|
|
126
|
+
const parsed = parseChangeSummary(source.match(CS_BLOCK_RE)![0]);
|
|
127
|
+
// RFC-0000 deduped; ADR-0002 sorts before RFC namespace.
|
|
128
|
+
expect(parsed.historyIds).toEqual(["ADR-0002", "RFC-0000"]);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it("skips files without a CHANGE_SUMMARY block (AC-5)", async () => {
|
|
132
|
+
await writeFile(join(root, "src", "plain.ts"), "export const y = 2;\n");
|
|
133
|
+
const result = await runCompassSummaryRecord(
|
|
134
|
+
makeInput({ id: "RFC-1095", files: ["src/plain.ts"], text: "x" }),
|
|
135
|
+
makeContext(root),
|
|
136
|
+
);
|
|
137
|
+
expect(result.data?.skipped).toEqual([{ file: "src/plain.ts", reason: "no-block" }]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("skips missing files as unparseable without failing (AC-5)", async () => {
|
|
141
|
+
const result = await runCompassSummaryRecord(
|
|
142
|
+
makeInput({ id: "RFC-1095", files: ["src/ghost.ts"], text: "x" }),
|
|
143
|
+
makeContext(root),
|
|
144
|
+
);
|
|
145
|
+
expect(result.exitCode).toBe(0);
|
|
146
|
+
expect(result.data?.skipped).toEqual([{ file: "src/ghost.ts", reason: "unparseable" }]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("rejects a missing or malformed --id", async () => {
|
|
150
|
+
const bad = await runCompassSummaryRecord(
|
|
151
|
+
makeInput({ id: "not-an-id", files: ["src/a.ts"] }),
|
|
152
|
+
makeContext(root),
|
|
153
|
+
);
|
|
154
|
+
expect(bad.exitCode).toBe(1);
|
|
155
|
+
const missing = await runCompassSummaryRecord(
|
|
156
|
+
makeInput({ files: ["src/a.ts"] }),
|
|
157
|
+
makeContext(root),
|
|
158
|
+
);
|
|
159
|
+
expect(missing.exitCode).toBe(1);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("defaults item text to the bare ID when --text is absent", async () => {
|
|
163
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER([]));
|
|
164
|
+
await runCompassSummaryRecord(
|
|
165
|
+
makeInput({ id: "ADR-0042", files: ["src/a.ts"] }),
|
|
166
|
+
makeContext(root),
|
|
167
|
+
);
|
|
168
|
+
const source = await readFile(join(root, "src", "a.ts"), "utf8");
|
|
169
|
+
expect(source).toContain("<item>ADR-0042</item>");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("does not write files in dry-run mode", async () => {
|
|
173
|
+
const original = TS_HEADER(["RFC-0001: existing."]);
|
|
174
|
+
await writeFile(join(root, "src", "a.ts"), original);
|
|
175
|
+
const result = await runCompassSummaryRecord(
|
|
176
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "dry run item" }),
|
|
177
|
+
makeContext(root, true),
|
|
178
|
+
);
|
|
179
|
+
expect(result.data?.recorded).toEqual(["src/a.ts"]);
|
|
180
|
+
expect(await readFile(join(root, "src", "a.ts"), "utf8")).toBe(original);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it("strips a duplicated leading ID from --text", async () => {
|
|
184
|
+
await writeFile(join(root, "src", "a.ts"), TS_HEADER([]));
|
|
185
|
+
await runCompassSummaryRecord(
|
|
186
|
+
makeInput({ id: "RFC-1095", files: ["src/a.ts"], text: "RFC-1095 — record command" }),
|
|
187
|
+
makeContext(root),
|
|
188
|
+
);
|
|
189
|
+
const source = await readFile(join(root, "src", "a.ts"), "utf8");
|
|
190
|
+
expect(source).toContain("<item>RFC-1095: record command</item>");
|
|
191
|
+
expect(source).not.toContain("RFC-1095: RFC-1095");
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe("summary-record helpers", () => {
|
|
196
|
+
it("stripConventionalPrefix removes conventional commit prefixes", () => {
|
|
197
|
+
expect(stripConventionalPrefix("implement: RFC-1095 — record")).toBe("RFC-1095 — record");
|
|
198
|
+
expect(stripConventionalPrefix("fix(scope)!: bug")).toBe("bug");
|
|
199
|
+
expect(stripConventionalPrefix("plain subject")).toBe("plain subject");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("isValidGovernanceId accepts RFC/ADR/ticket shapes only", () => {
|
|
203
|
+
expect(isValidGovernanceId("RFC-1095", policy)).toBe(true);
|
|
204
|
+
expect(isValidGovernanceId("ADR-0042", policy)).toBe(true);
|
|
205
|
+
expect(isValidGovernanceId("PROJ-123", policy)).toBe(true);
|
|
206
|
+
expect(isValidGovernanceId("rfc-1095", policy)).toBe(false);
|
|
207
|
+
expect(isValidGovernanceId("RFC1095", policy)).toBe(false);
|
|
208
|
+
expect(isValidGovernanceId("RFC-1095 extra", policy)).toBe(false);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("mergeHistoryIds dedupes and sorts per-namespace ascending", () => {
|
|
212
|
+
expect(mergeHistoryIds(["RFC-0005"], ["RFC-0002", "ADR-0001", "RFC-0005"], policy)).toEqual([
|
|
213
|
+
"ADR-0001",
|
|
214
|
+
"RFC-0002",
|
|
215
|
+
"RFC-0005",
|
|
216
|
+
]);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("buildChangeSummaryBlock omits <history> when empty", () => {
|
|
220
|
+
const block = buildChangeSummaryBlock(["RFC-1: a"], [], "x.ts");
|
|
221
|
+
expect(block).not.toContain("<history>");
|
|
222
|
+
expect(block).toContain("<item>RFC-1: a</item>");
|
|
223
|
+
});
|
|
224
|
+
});
|
package/os/compass/index.ts
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
</MODULE_CONTRACT>
|
|
8
8
|
<CHANGE_SUMMARY>
|
|
9
9
|
<item>RFC-0374: initial compass module barrel.</item>
|
|
10
|
+
<item>RFC-1095: compass.summary.record, trim repair rewrite, commit integration</item>
|
|
11
|
+
<item>RFC-1095: review findings: execFileSync git add, drop speculative barrel exports</item>
|
|
12
|
+
<item>RFC-1097: steps 1-4 — compass.migrate codemod
|
|
13
|
+
|
|
14
|
+
Add the v1 to v2 Compass header codemod: migrateFile pure transform (collapse, strip, seed, reorder, purpose-flag actions), migrateWorkspace walker, runCompassMigrate handler with dirty-tree refusal and --force/--files/--dry-run flags, module registration, and 15 unit tests.</item>
|
|
10
15
|
</CHANGE_SUMMARY>
|
|
11
16
|
*/
|
|
12
17
|
|
|
@@ -25,9 +30,22 @@ export {
|
|
|
25
30
|
runCompassAuditValidate,
|
|
26
31
|
isAuditDue,
|
|
27
32
|
} from "./handlers/compass-audit-handler.ts";
|
|
33
|
+
export { runCompassSummaryTrim } from "./handlers/compass-change-summary-handler.ts";
|
|
34
|
+
export { runCompassMigrate } from "./handlers/compass-migrate-handler.ts";
|
|
35
|
+
export {
|
|
36
|
+
migrateFile,
|
|
37
|
+
migrateWorkspace,
|
|
38
|
+
type MigrateAction,
|
|
39
|
+
type MigrateResult,
|
|
40
|
+
} from "./handlers/compass-migrate.ts";
|
|
28
41
|
export {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
runCompassSummaryRecord,
|
|
43
|
+
parseChangeSummary,
|
|
44
|
+
mergeHistoryIds,
|
|
45
|
+
buildChangeSummaryBlock,
|
|
46
|
+
stripConventionalPrefix,
|
|
47
|
+
isValidGovernanceId,
|
|
48
|
+
CHANGE_SUMMARY_WINDOW,
|
|
49
|
+
type SummaryRecordResult,
|
|
50
|
+
type SummaryRecordSkipReason,
|
|
51
|
+
} from "./handlers/summary-record.ts";
|