@warpgogol/forge 4.1.5 → 4.2.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 +2 -1
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTING.md +36 -0
- package/README.md +129 -535
- package/README.uk.md +145 -554
- package/SECURITY.md +17 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +296 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/os/adr/adr.module.d.ts +3 -0
- package/dist/os/adr/adr.module.d.ts.map +1 -0
- package/dist/os/adr/adr.module.js +153 -0
- package/dist/os/adr/adr.module.js.map +1 -0
- package/dist/os/adr/frontmatter-io.d.ts +12 -0
- package/dist/os/adr/frontmatter-io.d.ts.map +1 -0
- package/dist/os/adr/frontmatter-io.js +67 -0
- package/dist/os/adr/frontmatter-io.js.map +1 -0
- package/dist/os/adr/handlers/archive.d.ts +24 -0
- package/dist/os/adr/handlers/archive.d.ts.map +1 -0
- package/dist/os/adr/handlers/archive.js +194 -0
- package/dist/os/adr/handlers/archive.js.map +1 -0
- package/dist/os/adr/handlers/implement-stamp.d.ts +4 -0
- package/dist/os/adr/handlers/implement-stamp.d.ts.map +1 -0
- package/dist/os/adr/handlers/implement-stamp.js +264 -0
- package/dist/os/adr/handlers/implement-stamp.js.map +1 -0
- package/dist/os/adr/handlers/list-create.d.ts +5 -0
- package/dist/os/adr/handlers/list-create.d.ts.map +1 -0
- package/dist/os/adr/handlers/list-create.js +201 -0
- package/dist/os/adr/handlers/list-create.js.map +1 -0
- package/dist/os/adr/handlers/validate.d.ts +4 -0
- package/dist/os/adr/handlers/validate.d.ts.map +1 -0
- package/dist/os/adr/handlers/validate.js +304 -0
- package/dist/os/adr/handlers/validate.js.map +1 -0
- package/dist/os/adr/index.d.ts +8 -0
- package/dist/os/adr/index.d.ts.map +1 -0
- package/dist/os/adr/index.js +18 -0
- package/dist/os/adr/index.js.map +1 -0
- package/dist/os/adr/types.d.ts +81 -0
- package/dist/os/adr/types.d.ts.map +1 -0
- package/dist/os/adr/types.js +62 -0
- package/dist/os/adr/types.js.map +1 -0
- package/dist/os/audit/audit.module.d.ts +3 -0
- package/dist/os/audit/audit.module.d.ts.map +1 -0
- package/dist/os/audit/audit.module.js +50 -0
- package/dist/os/audit/audit.module.js.map +1 -0
- package/dist/os/audit/frontmatter-io.d.ts +12 -0
- package/dist/os/audit/frontmatter-io.d.ts.map +1 -0
- package/dist/os/audit/frontmatter-io.js +71 -0
- package/dist/os/audit/frontmatter-io.js.map +1 -0
- package/dist/os/audit/handlers/archive.d.ts +4 -0
- package/dist/os/audit/handlers/archive.d.ts.map +1 -0
- package/dist/os/audit/handlers/archive.js +207 -0
- package/dist/os/audit/handlers/archive.js.map +1 -0
- package/dist/os/audit/index.d.ts +6 -0
- package/dist/os/audit/index.d.ts.map +1 -0
- package/dist/os/audit/index.js +16 -0
- package/dist/os/audit/index.js.map +1 -0
- package/dist/os/audit/types.d.ts +12 -0
- package/dist/os/audit/types.d.ts.map +1 -0
- package/dist/os/audit/types.js +17 -0
- package/dist/os/audit/types.js.map +1 -0
- package/dist/os/compass/compass.module.d.ts +3 -0
- package/dist/os/compass/compass.module.d.ts.map +1 -0
- package/dist/os/compass/compass.module.js +191 -0
- package/dist/os/compass/compass.module.js.map +1 -0
- package/dist/os/compass/handlers/compass-audit-handler.d.ts +36 -0
- package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-audit-handler.js +260 -0
- package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -0
- package/dist/os/compass/handlers/compass-change-summary-handler.d.ts +19 -0
- package/dist/os/compass/handlers/compass-change-summary-handler.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-change-summary-handler.js +222 -0
- package/dist/os/compass/handlers/compass-change-summary-handler.js.map +1 -0
- package/dist/os/compass/handlers/compass-inventory-handler.d.ts +28 -0
- package/dist/os/compass/handlers/compass-inventory-handler.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-inventory-handler.js +387 -0
- package/dist/os/compass/handlers/compass-inventory-handler.js.map +1 -0
- package/dist/os/compass/handlers/compass-inventory.d.ts +30 -0
- package/dist/os/compass/handlers/compass-inventory.d.ts.map +1 -0
- package/dist/os/compass/handlers/compass-inventory.js +472 -0
- package/dist/os/compass/handlers/compass-inventory.js.map +1 -0
- package/dist/os/compass/handlers/git-revision.d.ts +8 -0
- package/dist/os/compass/handlers/git-revision.d.ts.map +1 -0
- package/dist/os/compass/handlers/git-revision.js +99 -0
- package/dist/os/compass/handlers/git-revision.js.map +1 -0
- package/dist/os/compass/handlers/resolve-scan-root.d.ts +3 -0
- package/dist/os/compass/handlers/resolve-scan-root.d.ts.map +1 -0
- package/dist/os/compass/handlers/resolve-scan-root.js +57 -0
- package/dist/os/compass/handlers/resolve-scan-root.js.map +1 -0
- package/dist/os/compass/index.d.ts +8 -0
- package/dist/os/compass/index.d.ts.map +1 -0
- package/dist/os/compass/index.js +19 -0
- package/dist/os/compass/index.js.map +1 -0
- package/dist/os/core/core.module.d.ts +3 -0
- package/dist/os/core/core.module.d.ts.map +1 -0
- package/dist/os/core/core.module.js +855 -0
- package/dist/os/core/core.module.js.map +1 -0
- package/dist/os/core/handlers/assets-check.d.ts +19 -0
- package/dist/os/core/handlers/assets-check.d.ts.map +1 -0
- package/dist/os/core/handlers/assets-check.js +89 -0
- package/dist/os/core/handlers/assets-check.js.map +1 -0
- package/dist/os/core/handlers/assets-helpers.d.ts +28 -0
- package/dist/os/core/handlers/assets-helpers.d.ts.map +1 -0
- package/dist/os/core/handlers/assets-helpers.js +128 -0
- package/dist/os/core/handlers/assets-helpers.js.map +1 -0
- package/dist/os/core/handlers/assets-list.d.ts +9 -0
- package/dist/os/core/handlers/assets-list.d.ts.map +1 -0
- package/dist/os/core/handlers/assets-list.js +72 -0
- package/dist/os/core/handlers/assets-list.js.map +1 -0
- package/dist/os/core/handlers/build.d.ts +16 -0
- package/dist/os/core/handlers/build.d.ts.map +1 -0
- package/dist/os/core/handlers/build.js +123 -0
- package/dist/os/core/handlers/build.js.map +1 -0
- package/dist/os/core/handlers/determinism-check.d.ts +20 -0
- package/dist/os/core/handlers/determinism-check.d.ts.map +1 -0
- package/dist/os/core/handlers/determinism-check.js +289 -0
- package/dist/os/core/handlers/determinism-check.js.map +1 -0
- package/dist/os/core/handlers/dev.d.ts +10 -0
- package/dist/os/core/handlers/dev.d.ts.map +1 -0
- package/dist/os/core/handlers/dev.js +102 -0
- package/dist/os/core/handlers/dev.js.map +1 -0
- package/dist/os/core/handlers/forge-autonomy-validate.d.ts +18 -0
- package/dist/os/core/handlers/forge-autonomy-validate.d.ts.map +1 -0
- package/dist/os/core/handlers/forge-autonomy-validate.js +115 -0
- package/dist/os/core/handlers/forge-autonomy-validate.js.map +1 -0
- package/dist/os/core/handlers/knowledge-compact.d.ts +4 -0
- package/dist/os/core/handlers/knowledge-compact.d.ts.map +1 -0
- package/dist/os/core/handlers/knowledge-compact.js +204 -0
- package/dist/os/core/handlers/knowledge-compact.js.map +1 -0
- package/dist/os/core/handlers/package-health.d.ts +17 -0
- package/dist/os/core/handlers/package-health.d.ts.map +1 -0
- package/dist/os/core/handlers/package-health.js +217 -0
- package/dist/os/core/handlers/package-health.js.map +1 -0
- package/dist/os/core/handlers/pinned-check.d.ts +49 -0
- package/dist/os/core/handlers/pinned-check.d.ts.map +1 -0
- package/dist/os/core/handlers/pinned-check.js +159 -0
- package/dist/os/core/handlers/pinned-check.js.map +1 -0
- package/dist/os/core/handlers/pinned-init.d.ts +19 -0
- package/dist/os/core/handlers/pinned-init.d.ts.map +1 -0
- package/dist/os/core/handlers/pinned-init.js +283 -0
- package/dist/os/core/handlers/pinned-init.js.map +1 -0
- package/dist/os/core/handlers/pinned-types.d.ts +29 -0
- package/dist/os/core/handlers/pinned-types.d.ts.map +1 -0
- package/dist/os/core/handlers/pinned-types.js +16 -0
- package/dist/os/core/handlers/pinned-types.js.map +1 -0
- package/dist/os/core/handlers/pinned-validate.d.ts +4 -0
- package/dist/os/core/handlers/pinned-validate.d.ts.map +1 -0
- package/dist/os/core/handlers/pinned-validate.js +248 -0
- package/dist/os/core/handlers/pinned-validate.js.map +1 -0
- package/dist/os/core/handlers/profile-resolve.d.ts +13 -0
- package/dist/os/core/handlers/profile-resolve.d.ts.map +1 -0
- package/dist/os/core/handlers/profile-resolve.js +65 -0
- package/dist/os/core/handlers/profile-resolve.js.map +1 -0
- package/dist/os/core/handlers/public-surface.d.ts +13 -0
- package/dist/os/core/handlers/public-surface.d.ts.map +1 -0
- package/dist/os/core/handlers/public-surface.js +148 -0
- package/dist/os/core/handlers/public-surface.js.map +1 -0
- package/dist/os/core/handlers/release-prepare.d.ts +25 -0
- package/dist/os/core/handlers/release-prepare.d.ts.map +1 -0
- package/dist/os/core/handlers/release-prepare.js +228 -0
- package/dist/os/core/handlers/release-prepare.js.map +1 -0
- package/dist/os/core/handlers/release-publish.d.ts +13 -0
- package/dist/os/core/handlers/release-publish.d.ts.map +1 -0
- package/dist/os/core/handlers/release-publish.js +173 -0
- package/dist/os/core/handlers/release-publish.js.map +1 -0
- package/dist/os/core/handlers/validate.d.ts +39 -0
- package/dist/os/core/handlers/validate.d.ts.map +1 -0
- package/dist/os/core/handlers/validate.js +241 -0
- package/dist/os/core/handlers/validate.js.map +1 -0
- package/dist/os/core/index.d.ts +2 -0
- package/dist/os/core/index.d.ts.map +1 -0
- package/dist/os/core/index.js +13 -0
- package/dist/os/core/index.js.map +1 -0
- package/dist/os/exploration/exploration.module.d.ts +3 -0
- package/dist/os/exploration/exploration.module.d.ts.map +1 -0
- package/dist/os/exploration/exploration.module.js +88 -0
- package/dist/os/exploration/exploration.module.js.map +1 -0
- package/dist/os/exploration/frontmatter-io.d.ts +13 -0
- package/dist/os/exploration/frontmatter-io.d.ts.map +1 -0
- package/dist/os/exploration/frontmatter-io.js +61 -0
- package/dist/os/exploration/frontmatter-io.js.map +1 -0
- package/dist/os/exploration/handlers/archive.d.ts +4 -0
- package/dist/os/exploration/handlers/archive.d.ts.map +1 -0
- package/dist/os/exploration/handlers/archive.js +141 -0
- package/dist/os/exploration/handlers/archive.js.map +1 -0
- package/dist/os/exploration/handlers/list.d.ts +4 -0
- package/dist/os/exploration/handlers/list.d.ts.map +1 -0
- package/dist/os/exploration/handlers/list.js +64 -0
- package/dist/os/exploration/handlers/list.js.map +1 -0
- package/dist/os/exploration/handlers/show.d.ts +4 -0
- package/dist/os/exploration/handlers/show.d.ts.map +1 -0
- package/dist/os/exploration/handlers/show.js +97 -0
- package/dist/os/exploration/handlers/show.js.map +1 -0
- package/dist/os/exploration/index.d.ts +7 -0
- package/dist/os/exploration/index.d.ts.map +1 -0
- package/dist/os/exploration/index.js +17 -0
- package/dist/os/exploration/index.js.map +1 -0
- package/dist/os/exploration/types.d.ts +38 -0
- package/dist/os/exploration/types.d.ts.map +1 -0
- package/dist/os/exploration/types.js +23 -0
- package/dist/os/exploration/types.js.map +1 -0
- package/dist/os/mission/handlers/archive.d.ts +4 -0
- package/dist/os/mission/handlers/archive.d.ts.map +1 -0
- package/dist/os/mission/handlers/archive.js +454 -0
- package/dist/os/mission/handlers/archive.js.map +1 -0
- package/dist/os/mission/index.d.ts +2 -0
- package/dist/os/mission/index.d.ts.map +1 -0
- package/dist/os/mission/index.js +2 -0
- package/dist/os/mission/index.js.map +1 -0
- package/dist/os/mission/mission.module.d.ts +3 -0
- package/dist/os/mission/mission.module.d.ts.map +1 -0
- package/dist/os/mission/mission.module.js +55 -0
- package/dist/os/mission/mission.module.js.map +1 -0
- package/dist/os/mission/types.d.ts +23 -0
- package/dist/os/mission/types.d.ts.map +1 -0
- package/dist/os/mission/types.js +19 -0
- package/dist/os/mission/types.js.map +1 -0
- package/dist/os/naming/index.d.ts +3 -0
- package/dist/os/naming/index.d.ts.map +1 -0
- package/dist/os/naming/index.js +14 -0
- package/dist/os/naming/index.js.map +1 -0
- package/dist/os/naming/naming-convention.d.ts +25 -0
- package/dist/os/naming/naming-convention.d.ts.map +1 -0
- package/dist/os/naming/naming-convention.js +334 -0
- package/dist/os/naming/naming-convention.js.map +1 -0
- package/dist/os/naming/naming.module.d.ts +3 -0
- package/dist/os/naming/naming.module.d.ts.map +1 -0
- package/dist/os/naming/naming.module.js +41 -0
- package/dist/os/naming/naming.module.js.map +1 -0
- package/dist/os/notes/index.d.ts +2 -0
- package/dist/os/notes/index.d.ts.map +1 -0
- package/dist/os/notes/index.js +10 -0
- package/dist/os/notes/index.js.map +1 -0
- package/dist/os/notes/notes.module.d.ts +3 -0
- package/dist/os/notes/notes.module.d.ts.map +1 -0
- package/dist/os/notes/notes.module.js +93 -0
- package/dist/os/notes/notes.module.js.map +1 -0
- package/dist/os/plan/frontmatter-io.d.ts +12 -0
- package/dist/os/plan/frontmatter-io.d.ts.map +1 -0
- package/dist/os/plan/frontmatter-io.js +69 -0
- package/dist/os/plan/frontmatter-io.js.map +1 -0
- package/dist/os/plan/handlers/archive.d.ts +4 -0
- package/dist/os/plan/handlers/archive.d.ts.map +1 -0
- package/dist/os/plan/handlers/archive.js +206 -0
- package/dist/os/plan/handlers/archive.js.map +1 -0
- package/dist/os/plan/index.d.ts +6 -0
- package/dist/os/plan/index.d.ts.map +1 -0
- package/dist/os/plan/index.js +16 -0
- package/dist/os/plan/index.js.map +1 -0
- package/dist/os/plan/plan.module.d.ts +3 -0
- package/dist/os/plan/plan.module.d.ts.map +1 -0
- package/dist/os/plan/plan.module.js +50 -0
- package/dist/os/plan/plan.module.js.map +1 -0
- package/dist/os/plan/types.d.ts +12 -0
- package/dist/os/plan/types.d.ts.map +1 -0
- package/dist/os/plan/types.js +17 -0
- package/dist/os/plan/types.js.map +1 -0
- package/dist/os/plugin/plugin.module.d.ts +25 -0
- package/dist/os/plugin/plugin.module.d.ts.map +1 -0
- package/dist/os/plugin/plugin.module.js +212 -0
- package/dist/os/plugin/plugin.module.js.map +1 -0
- package/dist/os/program/discovery.d.ts +61 -0
- package/dist/os/program/discovery.d.ts.map +1 -0
- package/dist/os/program/discovery.js +236 -0
- package/dist/os/program/discovery.js.map +1 -0
- package/dist/os/program/handlers/complete.d.ts +12 -0
- package/dist/os/program/handlers/complete.d.ts.map +1 -0
- package/dist/os/program/handlers/complete.js +362 -0
- package/dist/os/program/handlers/complete.js.map +1 -0
- package/dist/os/program/handlers/lease.d.ts +13 -0
- package/dist/os/program/handlers/lease.d.ts.map +1 -0
- package/dist/os/program/handlers/lease.js +328 -0
- package/dist/os/program/handlers/lease.js.map +1 -0
- package/dist/os/program/handlers/seal.d.ts +12 -0
- package/dist/os/program/handlers/seal.d.ts.map +1 -0
- package/dist/os/program/handlers/seal.js +277 -0
- package/dist/os/program/handlers/seal.js.map +1 -0
- package/dist/os/program/handlers/validate.d.ts +14 -0
- package/dist/os/program/handlers/validate.d.ts.map +1 -0
- package/dist/os/program/handlers/validate.js +170 -0
- package/dist/os/program/handlers/validate.js.map +1 -0
- package/dist/os/program/lease.d.ts +39 -0
- package/dist/os/program/lease.d.ts.map +1 -0
- package/dist/os/program/lease.js +129 -0
- package/dist/os/program/lease.js.map +1 -0
- package/dist/os/program/program.module.d.ts +3 -0
- package/dist/os/program/program.module.d.ts.map +1 -0
- package/dist/os/program/program.module.js +209 -0
- package/dist/os/program/program.module.js.map +1 -0
- package/dist/os/program/schemas.d.ts +225 -0
- package/dist/os/program/schemas.d.ts.map +1 -0
- package/dist/os/program/schemas.js +174 -0
- package/dist/os/program/schemas.js.map +1 -0
- package/dist/os/program/state.d.ts +38 -0
- package/dist/os/program/state.d.ts.map +1 -0
- package/dist/os/program/state.js +189 -0
- package/dist/os/program/state.js.map +1 -0
- package/dist/os/rfc/acceptance.d.ts +15 -0
- package/dist/os/rfc/acceptance.d.ts.map +1 -0
- package/dist/os/rfc/acceptance.js +438 -0
- package/dist/os/rfc/acceptance.js.map +1 -0
- package/dist/os/rfc/decision-log.d.ts +6 -0
- package/dist/os/rfc/decision-log.d.ts.map +1 -0
- package/dist/os/rfc/decision-log.js +269 -0
- package/dist/os/rfc/decision-log.js.map +1 -0
- package/dist/os/rfc/dna-trace.d.ts +27 -0
- package/dist/os/rfc/dna-trace.d.ts.map +1 -0
- package/dist/os/rfc/dna-trace.js +214 -0
- package/dist/os/rfc/dna-trace.js.map +1 -0
- package/dist/os/rfc/frontmatter-io.d.ts +18 -0
- package/dist/os/rfc/frontmatter-io.d.ts.map +1 -0
- package/dist/os/rfc/frontmatter-io.js +110 -0
- package/dist/os/rfc/frontmatter-io.js.map +1 -0
- package/dist/os/rfc/handlers/archive.d.ts +25 -0
- package/dist/os/rfc/handlers/archive.d.ts.map +1 -0
- package/dist/os/rfc/handlers/archive.js +247 -0
- package/dist/os/rfc/handlers/archive.js.map +1 -0
- package/dist/os/rfc/handlers/check.d.ts +4 -0
- package/dist/os/rfc/handlers/check.d.ts.map +1 -0
- package/dist/os/rfc/handlers/check.js +203 -0
- package/dist/os/rfc/handlers/check.js.map +1 -0
- package/dist/os/rfc/handlers/implement-stamp.d.ts +4 -0
- package/dist/os/rfc/handlers/implement-stamp.d.ts.map +1 -0
- package/dist/os/rfc/handlers/implement-stamp.js +452 -0
- package/dist/os/rfc/handlers/implement-stamp.js.map +1 -0
- package/dist/os/rfc/handlers/index-graph.d.ts +13 -0
- package/dist/os/rfc/handlers/index-graph.d.ts.map +1 -0
- package/dist/os/rfc/handlers/index-graph.js +192 -0
- package/dist/os/rfc/handlers/index-graph.js.map +1 -0
- package/dist/os/rfc/handlers/lifecycle.d.ts +13 -0
- package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -0
- package/dist/os/rfc/handlers/lifecycle.js +155 -0
- package/dist/os/rfc/handlers/lifecycle.js.map +1 -0
- package/dist/os/rfc/handlers/list-create.d.ts +6 -0
- package/dist/os/rfc/handlers/list-create.d.ts.map +1 -0
- package/dist/os/rfc/handlers/list-create.js +260 -0
- package/dist/os/rfc/handlers/list-create.js.map +1 -0
- package/dist/os/rfc/handlers/pipeline-status.d.ts +26 -0
- package/dist/os/rfc/handlers/pipeline-status.d.ts.map +1 -0
- package/dist/os/rfc/handlers/pipeline-status.js +124 -0
- package/dist/os/rfc/handlers/pipeline-status.js.map +1 -0
- package/dist/os/rfc/handlers/shared.d.ts +22 -0
- package/dist/os/rfc/handlers/shared.d.ts.map +1 -0
- package/dist/os/rfc/handlers/shared.js +105 -0
- package/dist/os/rfc/handlers/shared.js.map +1 -0
- package/dist/os/rfc/handlers/supersede-propose.d.ts +4 -0
- package/dist/os/rfc/handlers/supersede-propose.d.ts.map +1 -0
- package/dist/os/rfc/handlers/supersede-propose.js +203 -0
- package/dist/os/rfc/handlers/supersede-propose.js.map +1 -0
- package/dist/os/rfc/handlers/validate-rules.d.ts +80 -0
- package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -0
- package/dist/os/rfc/handlers/validate-rules.js +907 -0
- package/dist/os/rfc/handlers/validate-rules.js.map +1 -0
- package/dist/os/rfc/handlers/validate.d.ts +4 -0
- package/dist/os/rfc/handlers/validate.d.ts.map +1 -0
- package/dist/os/rfc/handlers/validate.js +134 -0
- package/dist/os/rfc/handlers/validate.js.map +1 -0
- package/dist/os/rfc/handlers.d.ts +7 -0
- package/dist/os/rfc/handlers.d.ts.map +1 -0
- package/dist/os/rfc/handlers.js +19 -0
- package/dist/os/rfc/handlers.js.map +1 -0
- package/dist/os/rfc/index.d.ts +18 -0
- package/dist/os/rfc/index.d.ts.map +1 -0
- package/dist/os/rfc/index.js +24 -0
- package/dist/os/rfc/index.js.map +1 -0
- package/dist/os/rfc/rfc.module.d.ts +3 -0
- package/dist/os/rfc/rfc.module.d.ts.map +1 -0
- package/dist/os/rfc/rfc.module.js +390 -0
- package/dist/os/rfc/rfc.module.js.map +1 -0
- package/dist/os/rfc/types.d.ts +559 -0
- package/dist/os/rfc/types.d.ts.map +1 -0
- package/dist/os/rfc/types.js +118 -0
- package/dist/os/rfc/types.js.map +1 -0
- package/dist/os/rfc/verification-evidence.d.ts +15 -0
- package/dist/os/rfc/verification-evidence.d.ts.map +1 -0
- package/dist/os/rfc/verification-evidence.js +185 -0
- package/dist/os/rfc/verification-evidence.js.map +1 -0
- package/dist/os/rfc/verification-refresh.d.ts +4 -0
- package/dist/os/rfc/verification-refresh.d.ts.map +1 -0
- package/dist/os/rfc/verification-refresh.js +160 -0
- package/dist/os/rfc/verification-refresh.js.map +1 -0
- package/dist/os/session/atif-parser.d.ts +29 -0
- package/dist/os/session/atif-parser.d.ts.map +1 -0
- package/dist/os/session/atif-parser.js +140 -0
- package/dist/os/session/atif-parser.js.map +1 -0
- package/dist/os/session/frontmatter-io.d.ts +31 -0
- package/dist/os/session/frontmatter-io.d.ts.map +1 -0
- package/dist/os/session/frontmatter-io.js +146 -0
- package/dist/os/session/frontmatter-io.js.map +1 -0
- package/dist/os/session/handlers/archive.d.ts +4 -0
- package/dist/os/session/handlers/archive.d.ts.map +1 -0
- package/dist/os/session/handlers/archive.js +241 -0
- package/dist/os/session/handlers/archive.js.map +1 -0
- package/dist/os/session/handlers/list.d.ts +4 -0
- package/dist/os/session/handlers/list.d.ts.map +1 -0
- package/dist/os/session/handlers/list.js +147 -0
- package/dist/os/session/handlers/list.js.map +1 -0
- package/dist/os/session/handlers/metrics-aggregate.d.ts +4 -0
- package/dist/os/session/handlers/metrics-aggregate.d.ts.map +1 -0
- package/dist/os/session/handlers/metrics-aggregate.js +183 -0
- package/dist/os/session/handlers/metrics-aggregate.js.map +1 -0
- package/dist/os/session/handlers/metrics-rfc.d.ts +4 -0
- package/dist/os/session/handlers/metrics-rfc.d.ts.map +1 -0
- package/dist/os/session/handlers/metrics-rfc.js +272 -0
- package/dist/os/session/handlers/metrics-rfc.js.map +1 -0
- package/dist/os/session/handlers/metrics-session.d.ts +9 -0
- package/dist/os/session/handlers/metrics-session.d.ts.map +1 -0
- package/dist/os/session/handlers/metrics-session.js +167 -0
- package/dist/os/session/handlers/metrics-session.js.map +1 -0
- package/dist/os/session/handlers/save.d.ts +6 -0
- package/dist/os/session/handlers/save.d.ts.map +1 -0
- package/dist/os/session/handlers/save.js +319 -0
- package/dist/os/session/handlers/save.js.map +1 -0
- package/dist/os/session/handlers/validate.d.ts +4 -0
- package/dist/os/session/handlers/validate.d.ts.map +1 -0
- package/dist/os/session/handlers/validate.js +224 -0
- package/dist/os/session/handlers/validate.js.map +1 -0
- package/dist/os/session/index.d.ts +9 -0
- package/dist/os/session/index.d.ts.map +1 -0
- package/dist/os/session/index.js +21 -0
- package/dist/os/session/index.js.map +1 -0
- package/dist/os/session/session.module.d.ts +3 -0
- package/dist/os/session/session.module.d.ts.map +1 -0
- package/dist/os/session/session.module.js +147 -0
- package/dist/os/session/session.module.js.map +1 -0
- package/dist/os/session/types.d.ts +214 -0
- package/dist/os/session/types.d.ts.map +1 -0
- package/dist/os/session/types.js +61 -0
- package/dist/os/session/types.js.map +1 -0
- package/dist/os/spec/live-spec-list.d.ts +4 -0
- package/dist/os/spec/live-spec-list.d.ts.map +1 -0
- package/dist/os/spec/live-spec-list.js +72 -0
- package/dist/os/spec/live-spec-list.js.map +1 -0
- package/dist/os/spec/live-spec-merge.d.ts +4 -0
- package/dist/os/spec/live-spec-merge.d.ts.map +1 -0
- package/dist/os/spec/live-spec-merge.js +316 -0
- package/dist/os/spec/live-spec-merge.js.map +1 -0
- package/dist/os/spec/live-spec-show.d.ts +4 -0
- package/dist/os/spec/live-spec-show.d.ts.map +1 -0
- package/dist/os/spec/live-spec-show.js +105 -0
- package/dist/os/spec/live-spec-show.js.map +1 -0
- package/dist/os/spec/live-spec-types.d.ts +71 -0
- package/dist/os/spec/live-spec-types.d.ts.map +1 -0
- package/dist/os/spec/live-spec-types.js +14 -0
- package/dist/os/spec/live-spec-types.js.map +1 -0
- package/dist/os/spec/live-spec-validate.d.ts +4 -0
- package/dist/os/spec/live-spec-validate.d.ts.map +1 -0
- package/dist/os/spec/live-spec-validate.js +140 -0
- package/dist/os/spec/live-spec-validate.js.map +1 -0
- package/dist/os/spec/spec-materialize.d.ts +21 -0
- package/dist/os/spec/spec-materialize.d.ts.map +1 -0
- package/dist/os/spec/spec-materialize.js +342 -0
- package/dist/os/spec/spec-materialize.js.map +1 -0
- package/dist/os/spec/spec-schema.d.ts +126 -0
- package/dist/os/spec/spec-schema.d.ts.map +1 -0
- package/dist/os/spec/spec-schema.js +118 -0
- package/dist/os/spec/spec-schema.js.map +1 -0
- package/dist/os/spec/spec-status.d.ts +33 -0
- package/dist/os/spec/spec-status.d.ts.map +1 -0
- package/dist/os/spec/spec-status.js +228 -0
- package/dist/os/spec/spec-status.js.map +1 -0
- package/dist/os/spec/spec-validate.d.ts +4 -0
- package/dist/os/spec/spec-validate.d.ts.map +1 -0
- package/dist/os/spec/spec-validate.js +438 -0
- package/dist/os/spec/spec-validate.js.map +1 -0
- package/dist/os/spec/spec.module.d.ts +3 -0
- package/dist/os/spec/spec.module.d.ts.map +1 -0
- package/dist/os/spec/spec.module.js +129 -0
- package/dist/os/spec/spec.module.js.map +1 -0
- package/dist/os/werkstatt/handlers/lock.d.ts +10 -0
- package/dist/os/werkstatt/handlers/lock.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/lock.js +153 -0
- package/dist/os/werkstatt/handlers/lock.js.map +1 -0
- package/dist/os/werkstatt/handlers/schema.d.ts +33 -0
- package/dist/os/werkstatt/handlers/schema.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/schema.js +42 -0
- package/dist/os/werkstatt/handlers/schema.js.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.d.ts +15 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js +152 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-status.d.ts +18 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-status.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-status.js +51 -0
- package/dist/os/werkstatt/handlers/werkstatt-lock-status.js.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.d.ts +11 -0
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js +79 -0
- package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js.map +1 -0
- package/dist/os/werkstatt/handlers/workspace-deps.d.ts +12 -0
- package/dist/os/werkstatt/handlers/workspace-deps.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/workspace-deps.js +42 -0
- package/dist/os/werkstatt/handlers/workspace-deps.js.map +1 -0
- package/dist/os/werkstatt/index.d.ts +7 -0
- package/dist/os/werkstatt/index.d.ts.map +1 -0
- package/dist/os/werkstatt/index.js +18 -0
- package/dist/os/werkstatt/index.js.map +1 -0
- package/dist/os/werkstatt/werkstatt.module.d.ts +3 -0
- package/dist/os/werkstatt/werkstatt.module.d.ts.map +1 -0
- package/dist/os/werkstatt/werkstatt.module.js +63 -0
- package/dist/os/werkstatt/werkstatt.module.js.map +1 -0
- package/dist/os/workflow/handlers.d.ts +12 -0
- package/dist/os/workflow/handlers.d.ts.map +1 -0
- package/dist/os/workflow/handlers.js +262 -0
- package/dist/os/workflow/handlers.js.map +1 -0
- package/dist/os/workflow/index.d.ts +4 -0
- package/dist/os/workflow/index.d.ts.map +1 -0
- package/dist/os/workflow/index.js +18 -0
- package/dist/os/workflow/index.js.map +1 -0
- package/dist/os/workflow/types.d.ts +69 -0
- package/dist/os/workflow/types.d.ts.map +1 -0
- package/dist/os/workflow/types.js +23 -0
- package/dist/os/workflow/types.js.map +1 -0
- package/dist/os/workflow/workflow.module.d.ts +3 -0
- package/dist/os/workflow/workflow.module.d.ts.map +1 -0
- package/dist/os/workflow/workflow.module.js +56 -0
- package/dist/os/workflow/workflow.module.js.map +1 -0
- package/dist/src/cli-output.d.ts +5 -0
- package/dist/src/cli-output.d.ts.map +1 -0
- package/dist/src/cli-output.js +53 -0
- package/dist/src/cli-output.js.map +1 -0
- package/dist/src/compass/contract-registry.d.ts +7 -0
- package/dist/src/compass/contract-registry.d.ts.map +1 -0
- package/dist/src/compass/contract-registry.js +112 -0
- package/dist/src/compass/contract-registry.js.map +1 -0
- package/dist/src/compass/types.d.ts +28 -0
- package/dist/src/compass/types.d.ts.map +1 -0
- package/dist/src/compass/types.js +14 -0
- package/dist/src/compass/types.js.map +1 -0
- package/dist/src/config/forge-config.d.ts +242 -0
- package/dist/src/config/forge-config.d.ts.map +1 -0
- package/dist/src/config/forge-config.js +363 -0
- package/dist/src/config/forge-config.js.map +1 -0
- package/dist/src/forge-module.d.ts +29 -0
- package/dist/src/forge-module.d.ts.map +1 -0
- package/dist/src/forge-module.js +16 -0
- package/dist/src/forge-module.js.map +1 -0
- package/dist/src/index.d.ts +39 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +59 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/knowledge/budgets.d.ts +38 -0
- package/dist/src/knowledge/budgets.d.ts.map +1 -0
- package/dist/src/knowledge/budgets.js +157 -0
- package/dist/src/knowledge/budgets.js.map +1 -0
- package/dist/src/knowledge/compact.d.ts +47 -0
- package/dist/src/knowledge/compact.d.ts.map +1 -0
- package/dist/src/knowledge/compact.js +320 -0
- package/dist/src/knowledge/compact.js.map +1 -0
- package/dist/src/knowledge/index.d.ts +7 -0
- package/dist/src/knowledge/index.d.ts.map +1 -0
- package/dist/src/knowledge/index.js +19 -0
- package/dist/src/knowledge/index.js.map +1 -0
- package/dist/src/knowledge/parse.d.ts +3 -0
- package/dist/src/knowledge/parse.d.ts.map +1 -0
- package/dist/src/knowledge/parse.js +205 -0
- package/dist/src/knowledge/parse.js.map +1 -0
- package/dist/src/knowledge/promote.d.ts +37 -0
- package/dist/src/knowledge/promote.d.ts.map +1 -0
- package/dist/src/knowledge/promote.js +120 -0
- package/dist/src/knowledge/promote.js.map +1 -0
- package/dist/src/knowledge/schema.d.ts +61 -0
- package/dist/src/knowledge/schema.d.ts.map +1 -0
- package/dist/src/knowledge/schema.js +64 -0
- package/dist/src/knowledge/schema.js.map +1 -0
- package/dist/src/knowledge/serialize.d.ts +3 -0
- package/dist/src/knowledge/serialize.d.ts.map +1 -0
- package/dist/src/knowledge/serialize.js +67 -0
- package/dist/src/knowledge/serialize.js.map +1 -0
- package/dist/src/migration-adapters/git-utils.d.ts +3 -0
- package/dist/src/migration-adapters/git-utils.d.ts.map +1 -0
- package/dist/src/migration-adapters/git-utils.js +65 -0
- package/dist/src/migration-adapters/git-utils.js.map +1 -0
- package/dist/src/migration-adapters/ignored-files.d.ts +12 -0
- package/dist/src/migration-adapters/ignored-files.d.ts.map +1 -0
- package/dist/src/migration-adapters/ignored-files.js +190 -0
- package/dist/src/migration-adapters/ignored-files.js.map +1 -0
- package/dist/src/migration-adapters/index.d.ts +8 -0
- package/dist/src/migration-adapters/index.d.ts.map +1 -0
- package/dist/src/migration-adapters/index.js +17 -0
- package/dist/src/migration-adapters/index.js.map +1 -0
- package/dist/src/migration-adapters/node-typescript-pnpm/index.d.ts +3 -0
- package/dist/src/migration-adapters/node-typescript-pnpm/index.d.ts.map +1 -0
- package/dist/src/migration-adapters/node-typescript-pnpm/index.js +141 -0
- package/dist/src/migration-adapters/node-typescript-pnpm/index.js.map +1 -0
- package/dist/src/migration-adapters/phaser-pnpm/index.d.ts +3 -0
- package/dist/src/migration-adapters/phaser-pnpm/index.d.ts.map +1 -0
- package/dist/src/migration-adapters/phaser-pnpm/index.js +142 -0
- package/dist/src/migration-adapters/phaser-pnpm/index.js.map +1 -0
- package/dist/src/migration-adapters/registry.d.ts +6 -0
- package/dist/src/migration-adapters/registry.d.ts.map +1 -0
- package/dist/src/migration-adapters/registry.js +55 -0
- package/dist/src/migration-adapters/registry.js.map +1 -0
- package/dist/src/migration-adapters/types.d.ts +35 -0
- package/dist/src/migration-adapters/types.d.ts.map +1 -0
- package/dist/src/migration-adapters/types.js +31 -0
- package/dist/src/migration-adapters/types.js.map +1 -0
- package/dist/src/onboarding/agents-generate.d.ts +31 -0
- package/dist/src/onboarding/agents-generate.d.ts.map +1 -0
- package/dist/src/onboarding/agents-generate.js +373 -0
- package/dist/src/onboarding/agents-generate.js.map +1 -0
- package/dist/src/onboarding/create.d.ts +12 -0
- package/dist/src/onboarding/create.d.ts.map +1 -0
- package/dist/src/onboarding/create.js +408 -0
- package/dist/src/onboarding/create.js.map +1 -0
- package/dist/src/onboarding/doctor.d.ts +45 -0
- package/dist/src/onboarding/doctor.d.ts.map +1 -0
- package/dist/src/onboarding/doctor.js +1222 -0
- package/dist/src/onboarding/doctor.js.map +1 -0
- package/dist/src/onboarding/init.d.ts +36 -0
- package/dist/src/onboarding/init.d.ts.map +1 -0
- package/dist/src/onboarding/init.js +412 -0
- package/dist/src/onboarding/init.js.map +1 -0
- package/dist/src/onboarding/invariant-engine.d.ts +17 -0
- package/dist/src/onboarding/invariant-engine.d.ts.map +1 -0
- package/dist/src/onboarding/invariant-engine.js +416 -0
- package/dist/src/onboarding/invariant-engine.js.map +1 -0
- package/dist/src/onboarding/memory-scaffold.d.ts +18 -0
- package/dist/src/onboarding/memory-scaffold.d.ts.map +1 -0
- package/dist/src/onboarding/memory-scaffold.js +151 -0
- package/dist/src/onboarding/memory-scaffold.js.map +1 -0
- package/dist/src/onboarding/nested-agents-generate.d.ts +18 -0
- package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -0
- package/dist/src/onboarding/nested-agents-generate.js +69 -0
- package/dist/src/onboarding/nested-agents-generate.js.map +1 -0
- package/dist/src/onboarding/nested-agents-templates.d.ts +26 -0
- package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -0
- package/dist/src/onboarding/nested-agents-templates.js +179 -0
- package/dist/src/onboarding/nested-agents-templates.js.map +1 -0
- package/dist/src/onboarding/profile-validate.d.ts +13 -0
- package/dist/src/onboarding/profile-validate.d.ts.map +1 -0
- package/dist/src/onboarding/profile-validate.js +109 -0
- package/dist/src/onboarding/profile-validate.js.map +1 -0
- package/dist/src/onboarding/scaffold-project.d.ts +12 -0
- package/dist/src/onboarding/scaffold-project.d.ts.map +1 -0
- package/dist/src/onboarding/scaffold-project.js +264 -0
- package/dist/src/onboarding/scaffold-project.js.map +1 -0
- package/dist/src/onboarding/scaffold.d.ts +15 -0
- package/dist/src/onboarding/scaffold.d.ts.map +1 -0
- package/dist/src/onboarding/scaffold.js +123 -0
- package/dist/src/onboarding/scaffold.js.map +1 -0
- package/dist/src/onboarding/upgrade.d.ts +27 -0
- package/dist/src/onboarding/upgrade.d.ts.map +1 -0
- package/dist/src/onboarding/upgrade.js +453 -0
- package/dist/src/onboarding/upgrade.js.map +1 -0
- package/dist/src/onboarding/workspace-discovery.d.ts +11 -0
- package/dist/src/onboarding/workspace-discovery.d.ts.map +1 -0
- package/dist/src/onboarding/workspace-discovery.js +138 -0
- package/dist/src/onboarding/workspace-discovery.js.map +1 -0
- package/dist/src/plugin/forge-plugin-manifest.d.ts +38 -0
- package/dist/src/plugin/forge-plugin-manifest.d.ts.map +1 -0
- package/dist/src/plugin/forge-plugin-manifest.js +52 -0
- package/dist/src/plugin/forge-plugin-manifest.js.map +1 -0
- package/dist/src/profiles/profile-schema.d.ts +361 -0
- package/dist/src/profiles/profile-schema.d.ts.map +1 -0
- package/dist/src/profiles/profile-schema.js +208 -0
- package/dist/src/profiles/profile-schema.js.map +1 -0
- package/dist/src/profiles/stack-profile.d.ts +173 -0
- package/dist/src/profiles/stack-profile.d.ts.map +1 -0
- package/dist/src/profiles/stack-profile.js +148 -0
- package/dist/src/profiles/stack-profile.js.map +1 -0
- package/dist/src/profiles/terminology-utils.d.ts +8 -0
- package/dist/src/profiles/terminology-utils.d.ts.map +1 -0
- package/dist/src/profiles/terminology-utils.js +31 -0
- package/dist/src/profiles/terminology-utils.js.map +1 -0
- package/dist/src/registry.d.ts +38 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +174 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/skill-schema.d.ts +30 -0
- package/dist/src/skill-schema.d.ts.map +1 -0
- package/dist/src/skill-schema.js +34 -0
- package/dist/src/skill-schema.js.map +1 -0
- package/dist/src/types.d.ts +158 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +17 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/fs-atomic.d.ts +7 -0
- package/dist/src/utils/fs-atomic.d.ts.map +1 -0
- package/dist/src/utils/fs-atomic.js +81 -0
- package/dist/src/utils/fs-atomic.js.map +1 -0
- package/dist/src/utils/fs-idempotent.d.ts +2 -0
- package/dist/src/utils/fs-idempotent.d.ts.map +1 -0
- package/dist/src/utils/fs-idempotent.js +39 -0
- package/dist/src/utils/fs-idempotent.js.map +1 -0
- package/dist/src/utils/fs-trash-sync.d.ts +2 -0
- package/dist/src/utils/fs-trash-sync.d.ts.map +1 -0
- package/dist/src/utils/fs-trash-sync.js +34 -0
- package/dist/src/utils/fs-trash-sync.js.map +1 -0
- package/dist/src/utils/fs-trash.d.ts +2 -0
- package/dist/src/utils/fs-trash.d.ts.map +1 -0
- package/dist/src/utils/fs-trash.js +24 -0
- package/dist/src/utils/fs-trash.js.map +1 -0
- package/dist/src/utils/fs.d.ts +8 -0
- package/dist/src/utils/fs.d.ts.map +1 -0
- package/dist/src/utils/fs.js +60 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/src/utils/generated-marker.d.ts +28 -0
- package/dist/src/utils/generated-marker.d.ts.map +1 -0
- package/dist/src/utils/generated-marker.js +138 -0
- package/dist/src/utils/generated-marker.js.map +1 -0
- package/dist/src/utils/hash.d.ts +2 -0
- package/dist/src/utils/hash.d.ts.map +1 -0
- package/dist/src/utils/hash.js +18 -0
- package/dist/src/utils/hash.js.map +1 -0
- package/dist/src/utils/index.d.ts +9 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +21 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/string-utils.d.ts +2 -0
- package/dist/src/utils/string-utils.d.ts.map +1 -0
- package/dist/src/utils/string-utils.js +19 -0
- package/dist/src/utils/string-utils.js.map +1 -0
- package/dist/src/validators/note-frontmatter-validate.d.ts +20 -0
- package/dist/src/validators/note-frontmatter-validate.d.ts.map +1 -0
- package/dist/src/validators/note-frontmatter-validate.js +117 -0
- package/dist/src/validators/note-frontmatter-validate.js.map +1 -0
- package/dist/src/validators/note-link-validate.d.ts +21 -0
- package/dist/src/validators/note-link-validate.d.ts.map +1 -0
- package/dist/src/validators/note-link-validate.js +121 -0
- package/dist/src/validators/note-link-validate.js.map +1 -0
- package/dist/src/validators/note-orphan-detect.d.ts +19 -0
- package/dist/src/validators/note-orphan-detect.d.ts.map +1 -0
- package/dist/src/validators/note-orphan-detect.js +123 -0
- package/dist/src/validators/note-orphan-detect.js.map +1 -0
- package/dist/src/validators/port-validate.d.ts +12 -0
- package/dist/src/validators/port-validate.d.ts.map +1 -0
- package/dist/src/validators/port-validate.js +78 -0
- package/dist/src/validators/port-validate.js.map +1 -0
- package/dist/src/validators/skill-validate.d.ts +29 -0
- package/dist/src/validators/skill-validate.d.ts.map +1 -0
- package/dist/src/validators/skill-validate.js +689 -0
- package/dist/src/validators/skill-validate.js.map +1 -0
- package/docs/concepts/governance-model.md +33 -0
- package/docs/concepts/project-contract.md +61 -0
- package/docs/concepts/why-forge.md +46 -0
- package/docs/getting-started.md +44 -0
- package/docs/guides/existing-project.md +38 -0
- package/docs/guides/rfc-adr.md +61 -0
- package/docs/guides/skills.md +60 -0
- package/docs/guides/upgrading.md +35 -0
- package/docs/maintainers/publishing.md +34 -0
- package/docs/reference/cli.md +140 -0
- package/docs/reference/forge-yaml.md +114 -0
- package/docs/reference/profiles.md +37 -0
- package/docs/reference/programmatic-api.md +79 -0
- package/os/core/core.module.ts +15 -0
- package/os/core/handlers/public-surface.ts +189 -0
- package/os/rfc/handlers/validate-rules.ts +0 -1
- package/os/rfc/verification-refresh.test.ts +15 -14
- package/os/rfc/verification-refresh.ts +1 -7
- package/os/session/handlers/metrics-session.ts +10 -9
- package/package.json +15 -7
- package/skills/fo/fo-session-retro/SKILL.md +2 -1
- package/src/tests/metrics-rfc-1053.test.ts +14 -4
- package/src/tests/public-surface.test.ts +247 -0
|
@@ -0,0 +1,907 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>Per-RFC validation rules (V-01..V-34, RFC-DIR-01) extracted from the validate handler for modularity.</purpose>
|
|
4
|
+
<non-goals>
|
|
5
|
+
<item>Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file.</item>
|
|
6
|
+
</non-goals>
|
|
7
|
+
</MODULE_CONTRACT>
|
|
8
|
+
<CHANGE_SUMMARY>
|
|
9
|
+
<item>RFC-0303 Phase 3: extracted from validate.ts as part of the handler split.</item>
|
|
10
|
+
<item>RFC-0722: add RFC-DIR-01 directory structure warning rule for unsanctioned subdirectories.</item>
|
|
11
|
+
<item>RFC-0755: add V-RFC-33 frontmatter YAML parseability check (checkFrontmatterYamlParse helper).</item>
|
|
12
|
+
<item>RFC-0795: add V-33 dependsOn referential integrity/self-dependency/rejected-dependency and V-34 batch slug format rules.</item>
|
|
13
|
+
<item>RFC-0997: add V-35 probe→criterion referential integrity, V-36 criterion identifier discipline, V-37 evidence mechanism validity, and computeProbeCoverage for non-blocking coverage reports.</item>
|
|
14
|
+
<item>RFC-1006: add V-38 document readiness completeness, V-39 non-atomic criterion, V-40 unbounded quantity, V-41 weasel verb, V-42 criterion versioning annotation format. Add evaluateDocumentReadiness and extend evaluateAcceptanceCriteria with supersession tracking and reject-checklist scans.</item>
|
|
15
|
+
</CHANGE_SUMMARY>
|
|
16
|
+
*/
|
|
17
|
+
import path from "node:path";
|
|
18
|
+
import { readFile, stat } from "node:fs/promises";
|
|
19
|
+
import { execFile } from "node:child_process";
|
|
20
|
+
import { parse as yamlParse } from "yaml";
|
|
21
|
+
import { validateAcceptanceShape } from "../acceptance.js";
|
|
22
|
+
import { RFC_DIR, RFC_ID_PATTERN, RFC_STATUSES, RFC_KINDS, RFC_SCOPES, RFC_FULL_REQUIRED_SECTIONS, RFC_METADATA_CUTOFF, RFC_VERSION_BUMP_CUTOFF, RFC_PROBE_BINDING_CUTOFF, RFC_CRITERIA_CONTENT_CUTOFF, } from "../types.js";
|
|
23
|
+
import { DNA_DOCS, AP_DOCS } from "./shared.js";
|
|
24
|
+
const DNA_DOC = DNA_DOCS[0];
|
|
25
|
+
const AP_DOC = AP_DOCS[0];
|
|
26
|
+
// RFC-0335: reviewer identity validation constants
|
|
27
|
+
const REVIEWER_IDENTITY_PATTERN = /^(human|agent):[a-z0-9][a-z0-9-]*$/;
|
|
28
|
+
const DECIDED_STATUSES = [
|
|
29
|
+
"accepted",
|
|
30
|
+
"implemented",
|
|
31
|
+
"rejected",
|
|
32
|
+
"superseded",
|
|
33
|
+
];
|
|
34
|
+
// ─── RFC-1006: reject checklist constants ──────────────────────────────────
|
|
35
|
+
const UNBOUNDED_QUANTITY_TRIGGERS = [
|
|
36
|
+
"fast",
|
|
37
|
+
"quick",
|
|
38
|
+
"scalable",
|
|
39
|
+
"reasonable load",
|
|
40
|
+
"most requests",
|
|
41
|
+
"efficiently",
|
|
42
|
+
"high performance",
|
|
43
|
+
"low latency",
|
|
44
|
+
"responsively",
|
|
45
|
+
"smoothly",
|
|
46
|
+
];
|
|
47
|
+
const WEASEL_VERBS = [
|
|
48
|
+
"handle gracefully",
|
|
49
|
+
"behave correctly",
|
|
50
|
+
"as appropriate",
|
|
51
|
+
"robust",
|
|
52
|
+
"user-friendly",
|
|
53
|
+
"works correctly",
|
|
54
|
+
"is reliable",
|
|
55
|
+
"gracefully handle",
|
|
56
|
+
"correctly handle",
|
|
57
|
+
"properly handle",
|
|
58
|
+
];
|
|
59
|
+
// Detects "SHALL <verb-phrase> and <verb-phrase>" — non-atomic criterion.
|
|
60
|
+
// Conservative: only fires when "and" appears after SHALL/SHOULD/MUST and
|
|
61
|
+
// is followed by a verb-like word (a-z, not a noun-phrase connector).
|
|
62
|
+
const NON_ATOMIC_PATTERN = /\b(?:SHALL|SHOULD|MUST)\s+\S[^()]*?\band\s+(?:return|report|exit|emit|write|read|create|delete|update|persist|validate|reject|accept|log|send|store|load|fetch|process|register|enqueue|schedule|trigger|notify|redirect|render|display|show|hide|enable|disable|start|stop|restart|check|verify|ensure|prevent|allow|block|deny|grant|revoke|parse|serialize|deserialize|encode|decode|compress|decompress|encrypt|decrypt|hash|sign|verify|transform|convert|map|filter|sort|group|aggregate|count|sum|average|min|max|find|search|resolve|lookup)\b/i;
|
|
63
|
+
const SUPERSEDED_ANNOTATION_PATTERN = /^>\s*Superseded\s+(AC-\d+)\s+\((\d{4}-\d{2}-\d{2})\):\s*(.+)$/;
|
|
64
|
+
/**
|
|
65
|
+
* Strip fenced code blocks (``` ... ```) from a markdown body.
|
|
66
|
+
* Prevents section extractors from matching headings inside code block examples.
|
|
67
|
+
* Pure function — no I/O, no side effects.
|
|
68
|
+
*/
|
|
69
|
+
export function stripFencedCodeBlocks(body) {
|
|
70
|
+
return body.replace(/```[\s\S]*?```/g, "");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Extract the raw document-readiness section text from an RFC body.
|
|
74
|
+
* Pure function — no I/O, no side effects.
|
|
75
|
+
*/
|
|
76
|
+
export function extractDocumentReadinessSection(body) {
|
|
77
|
+
const stripped = stripFencedCodeBlocks(body);
|
|
78
|
+
const match = stripped.match(/## Document readiness\s*\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
79
|
+
return match ? match[1] : null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Evaluate document-readiness checkboxes (RFC-1006).
|
|
83
|
+
* Pure function — no I/O, no side effects.
|
|
84
|
+
*/
|
|
85
|
+
export function evaluateDocumentReadiness(body) {
|
|
86
|
+
const section = extractDocumentReadinessSection(body);
|
|
87
|
+
if (!section) {
|
|
88
|
+
return {
|
|
89
|
+
totalChecked: 0,
|
|
90
|
+
totalUnchecked: 0,
|
|
91
|
+
uncheckedLines: [],
|
|
92
|
+
criterionIds: [],
|
|
93
|
+
linesWithoutId: [],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const uncheckedLines = section
|
|
97
|
+
.split("\n")
|
|
98
|
+
.filter((line) => /^- \[ \]/.test(line))
|
|
99
|
+
.map((line) => line.trim());
|
|
100
|
+
const totalUnchecked = uncheckedLines.length;
|
|
101
|
+
const checkedLines = section.split("\n").filter((line) => /^- \[x\]/.test(line));
|
|
102
|
+
const checklistLines = section.split("\n").filter((line) => /^- \[[ x]\]/.test(line));
|
|
103
|
+
const criterionIds = [];
|
|
104
|
+
const linesWithoutId = [];
|
|
105
|
+
for (const line of checklistLines) {
|
|
106
|
+
const match = line.match(/^\s*- \[[ x]\]\s*DR-(\d+):/);
|
|
107
|
+
if (match) {
|
|
108
|
+
criterionIds.push(`DR-${match[1]}`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
linesWithoutId.push(line.trim());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
totalChecked: checkedLines.length,
|
|
116
|
+
totalUnchecked,
|
|
117
|
+
uncheckedLines,
|
|
118
|
+
criterionIds,
|
|
119
|
+
linesWithoutId,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Extract the raw acceptance-criteria section text from an RFC body.
|
|
124
|
+
* Returns `undefined` when the section is absent.
|
|
125
|
+
*/
|
|
126
|
+
export function extractAcceptanceCriteriaSection(body) {
|
|
127
|
+
const stripped = stripFencedCodeBlocks(body);
|
|
128
|
+
const match = stripped.match(/## Acceptance criteria\s*\n([\s\S]*?)(?=\n## |\n*$)/);
|
|
129
|
+
return match?.[1];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Evaluate acceptance criteria checkboxes and inline evidence annotations.
|
|
133
|
+
* Pure function — no I/O, no side effects. Used by both V-26/V-27 validation
|
|
134
|
+
* and the `rfc.implement.stamp` precondition checks (RFC-0476).
|
|
135
|
+
*/
|
|
136
|
+
export function evaluateAcceptanceCriteria(body) {
|
|
137
|
+
const section = extractAcceptanceCriteriaSection(body);
|
|
138
|
+
if (!section) {
|
|
139
|
+
return {
|
|
140
|
+
totalChecked: 0,
|
|
141
|
+
totalUnchecked: 0,
|
|
142
|
+
uncheckedLines: [],
|
|
143
|
+
checkedWithoutEvidence: [],
|
|
144
|
+
criterionIds: [],
|
|
145
|
+
duplicateCriterionIds: [],
|
|
146
|
+
linesWithoutId: [],
|
|
147
|
+
supersededIds: [],
|
|
148
|
+
nonAtomicViolations: [],
|
|
149
|
+
unboundedQuantityViolations: [],
|
|
150
|
+
weaselVerbViolations: [],
|
|
151
|
+
malformedSupersessionAnnotations: [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const uncheckedLines = section
|
|
155
|
+
.split("\n")
|
|
156
|
+
.filter((line) => /^- \[ \]/.test(line))
|
|
157
|
+
.map((line) => line.trim());
|
|
158
|
+
const checkedLines = section.split("\n").filter((line) => /^- \[x\]/.test(line));
|
|
159
|
+
const checkedWithoutEvidence = [];
|
|
160
|
+
for (const line of checkedLines) {
|
|
161
|
+
if (!/\(evidence:\s*.+\)/.test(line)) {
|
|
162
|
+
checkedWithoutEvidence.push(line.trim());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const checklistLines = section.split("\n").filter((line) => /^- \[[ x]\]/.test(line));
|
|
166
|
+
const criterionIds = [];
|
|
167
|
+
const seenIds = new Set();
|
|
168
|
+
const duplicateCriterionIds = [];
|
|
169
|
+
const linesWithoutId = [];
|
|
170
|
+
for (const line of checklistLines) {
|
|
171
|
+
const match = line.match(/^\s*- \[[ x]\]\s*AC-(\d+):/);
|
|
172
|
+
if (match) {
|
|
173
|
+
const id = `AC-${match[1]}`;
|
|
174
|
+
if (seenIds.has(id)) {
|
|
175
|
+
duplicateCriterionIds.push(id);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
seenIds.add(id);
|
|
179
|
+
criterionIds.push(id);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
linesWithoutId.push(line.trim());
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// RFC-1006: detect supersession annotations and exclude superseded criteria
|
|
187
|
+
const sectionLines = section.split("\n");
|
|
188
|
+
const supersededIds = [];
|
|
189
|
+
const supersededSet = new Set();
|
|
190
|
+
const malformedSupersessionAnnotations = [];
|
|
191
|
+
for (let i = 0; i < sectionLines.length; i++) {
|
|
192
|
+
const line = sectionLines[i];
|
|
193
|
+
const annMatch = line.match(SUPERSEDED_ANNOTATION_PATTERN);
|
|
194
|
+
if (annMatch) {
|
|
195
|
+
const acId = annMatch[1];
|
|
196
|
+
const dateStr = annMatch[2];
|
|
197
|
+
supersededIds.push(acId);
|
|
198
|
+
supersededSet.add(acId);
|
|
199
|
+
// Validate date format (already captured by regex, but check range)
|
|
200
|
+
const date = new Date(dateStr);
|
|
201
|
+
if (isNaN(date.getTime())) {
|
|
202
|
+
malformedSupersessionAnnotations.push(line.trim());
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (/^>\s*Superseded\s+AC-/.test(line.trim()) &&
|
|
206
|
+
!SUPERSEDED_ANNOTATION_PATTERN.test(line.trim())) {
|
|
207
|
+
// Line starts with supersession annotation pattern but doesn't match full format
|
|
208
|
+
malformedSupersessionAnnotations.push(line.trim());
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Remove superseded criteria from unchecked count
|
|
212
|
+
const effectiveUncheckedLines = uncheckedLines.filter((line) => {
|
|
213
|
+
const match = line.match(/^\s*- \[ \]\s*AC-(\d+):/);
|
|
214
|
+
if (match) {
|
|
215
|
+
return !supersededSet.has(`AC-${match[1]}`);
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
});
|
|
219
|
+
// RFC-1006: reject checklist scans on criterion text
|
|
220
|
+
const nonAtomicViolations = [];
|
|
221
|
+
const unboundedQuantityViolations = [];
|
|
222
|
+
const weaselVerbViolations = [];
|
|
223
|
+
for (const line of checklistLines) {
|
|
224
|
+
const acMatch = line.match(/^\s*- \[[ x]\]\s*(AC-\d+):/);
|
|
225
|
+
if (!acMatch)
|
|
226
|
+
continue;
|
|
227
|
+
const acId = acMatch[1];
|
|
228
|
+
if (supersededSet.has(acId))
|
|
229
|
+
continue;
|
|
230
|
+
const text = line.toLowerCase();
|
|
231
|
+
// V-39: non-atomic criterion
|
|
232
|
+
if (NON_ATOMIC_PATTERN.test(line)) {
|
|
233
|
+
nonAtomicViolations.push({ line: line.trim(), acId });
|
|
234
|
+
}
|
|
235
|
+
// V-40: unbounded quantity
|
|
236
|
+
for (const trigger of UNBOUNDED_QUANTITY_TRIGGERS) {
|
|
237
|
+
if (text.includes(trigger)) {
|
|
238
|
+
unboundedQuantityViolations.push({ line: line.trim(), acId, trigger });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// V-41: weasel verbs
|
|
242
|
+
for (const trigger of WEASEL_VERBS) {
|
|
243
|
+
if (text.includes(trigger)) {
|
|
244
|
+
weaselVerbViolations.push({ line: line.trim(), acId, trigger });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
totalChecked: checkedLines.length,
|
|
250
|
+
totalUnchecked: effectiveUncheckedLines.length,
|
|
251
|
+
uncheckedLines: effectiveUncheckedLines,
|
|
252
|
+
checkedWithoutEvidence,
|
|
253
|
+
criterionIds,
|
|
254
|
+
duplicateCriterionIds,
|
|
255
|
+
linesWithoutId,
|
|
256
|
+
supersededIds,
|
|
257
|
+
nonAtomicViolations,
|
|
258
|
+
unboundedQuantityViolations,
|
|
259
|
+
weaselVerbViolations,
|
|
260
|
+
malformedSupersessionAnnotations,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// ─── RFC-0997: probe-criterion binding and coverage ────────────────────────
|
|
264
|
+
/**
|
|
265
|
+
* Compute a non-blocking probe coverage report for an RFC.
|
|
266
|
+
* Pure function — no I/O, no side effects.
|
|
267
|
+
*/
|
|
268
|
+
export function computeProbeCoverage(criterionIds, acceptance) {
|
|
269
|
+
const probes = Array.isArray(acceptance) ? acceptance : [];
|
|
270
|
+
const probeBackedCriteria = new Set();
|
|
271
|
+
const unboundProbes = [];
|
|
272
|
+
for (const probe of probes) {
|
|
273
|
+
const criterion = probe.criterion;
|
|
274
|
+
if (typeof criterion === "string" && /^AC-\d+$/.test(criterion)) {
|
|
275
|
+
if (criterionIds.includes(criterion)) {
|
|
276
|
+
probeBackedCriteria.add(criterion);
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
unboundProbes.push(criterion);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
unboundProbes.push(String(criterion ?? "(missing)"));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const uncoveredCriteria = criterionIds.filter((id) => !probeBackedCriteria.has(id));
|
|
287
|
+
const totalCriteria = criterionIds.length;
|
|
288
|
+
const probeBackedCount = probeBackedCriteria.size;
|
|
289
|
+
return {
|
|
290
|
+
totalCriteria,
|
|
291
|
+
probeBackedCriteria: probeBackedCount,
|
|
292
|
+
coverageRatio: totalCriteria > 0 ? probeBackedCount / totalCriteria : 0,
|
|
293
|
+
unboundProbes,
|
|
294
|
+
uncoveredCriteria,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
// ─── RFC-0625: V-32 implementation commit drift detection ──────────────────
|
|
298
|
+
function execGitLog(workspaceRoot, args) {
|
|
299
|
+
return new Promise((resolve) => {
|
|
300
|
+
execFile("git", args, { cwd: workspaceRoot, timeout: 10000 }, (err, stdout) => {
|
|
301
|
+
if (err) {
|
|
302
|
+
resolve("");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
resolve(stdout.trim());
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
async function checkImplementationCommitDrift(workspaceRoot, rfcId, createdAt, currentStatus) {
|
|
310
|
+
if (currentStatus === "implemented") {
|
|
311
|
+
return { found: false, commitCount: 0 };
|
|
312
|
+
}
|
|
313
|
+
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
314
|
+
if (!datePattern.test(createdAt)) {
|
|
315
|
+
return { found: false, commitCount: 0 };
|
|
316
|
+
}
|
|
317
|
+
const log = await execGitLog(workspaceRoot, ["log", `--since=${createdAt}`, "--oneline"]);
|
|
318
|
+
if (!log) {
|
|
319
|
+
return { found: false, commitCount: 0 };
|
|
320
|
+
}
|
|
321
|
+
const pattern = new RegExp(`implement:\\s+${rfcId}\\b`, "i");
|
|
322
|
+
const matchingLines = log.split("\n").filter((line) => pattern.test(line.trim()));
|
|
323
|
+
return { found: matchingLines.length > 0, commitCount: matchingLines.length };
|
|
324
|
+
}
|
|
325
|
+
// ─── RFC-0709: V-NC-01 NEEDS CLARIFICATION marker detection ────────────────
|
|
326
|
+
const NC_MARKER_CUTOFF = "2026-08-06";
|
|
327
|
+
const NC_MARKER_PATTERN = /^>\s*NEEDS CLARIFICATION:\s*(.+)$/;
|
|
328
|
+
/**
|
|
329
|
+
* Collect NEEDS CLARIFICATION markers from an RFC body.
|
|
330
|
+
* Skips lines inside fenced code blocks. Pure function — no I/O.
|
|
331
|
+
*/
|
|
332
|
+
export function collectMarkers(body, status, createdAt) {
|
|
333
|
+
if (createdAt < NC_MARKER_CUTOFF)
|
|
334
|
+
return [];
|
|
335
|
+
const severity = status === "draft" ? "warn" : "error";
|
|
336
|
+
const markers = [];
|
|
337
|
+
const lines = body.split("\n");
|
|
338
|
+
let inCodeBlock = false;
|
|
339
|
+
for (let i = 0; i < lines.length; i++) {
|
|
340
|
+
const line = lines[i];
|
|
341
|
+
if (/^```/.test(line.trim())) {
|
|
342
|
+
inCodeBlock = !inCodeBlock;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (inCodeBlock)
|
|
346
|
+
continue;
|
|
347
|
+
const match = line.match(NC_MARKER_PATTERN);
|
|
348
|
+
if (match) {
|
|
349
|
+
markers.push({
|
|
350
|
+
line: i + 1,
|
|
351
|
+
text: match[1].trim(),
|
|
352
|
+
severity,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return markers;
|
|
357
|
+
}
|
|
358
|
+
export async function validateSingleRfc(fileName, parsed, allParsed, seenIds, validDnaIds, validApIds, knownKeys, workspaceRoot, addViolation, seenFilenameNumbers) {
|
|
359
|
+
const fm = parsed.frontmatter;
|
|
360
|
+
const body = parsed.body;
|
|
361
|
+
const relFile = path.join(RFC_DIR, fileName);
|
|
362
|
+
const rfcId = String(fm["id"] ?? "UNKNOWN");
|
|
363
|
+
const isArchived = fileName.startsWith("archive/");
|
|
364
|
+
// V-01: id format
|
|
365
|
+
if (!RFC_ID_PATTERN.test(rfcId)) {
|
|
366
|
+
addViolation(rfcId, relFile, "V-01", `id "${rfcId}" does not match format RFC-XXXX`);
|
|
367
|
+
}
|
|
368
|
+
// RFC-DIR-01: directory structure convention (RFC-0722)
|
|
369
|
+
// Warn when RFC files are found in subdirectories other than archive/ and verification/
|
|
370
|
+
const slashIdx = fileName.indexOf("/");
|
|
371
|
+
if (slashIdx > 0) {
|
|
372
|
+
const subDir = fileName.slice(0, slashIdx);
|
|
373
|
+
if (subDir !== "archive" && subDir !== "verification") {
|
|
374
|
+
addViolation(rfcId, relFile, "RFC-DIR-01", `${relFile} is in an unsanctioned subdirectory. Only archive/ and verification/ are allowed. Move the file to docs/rfcs/ root or write an ADR to formalize the subdirectory.`, "warning");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// V-02: id uniqueness
|
|
378
|
+
const prevFile = seenIds.get(rfcId);
|
|
379
|
+
if (prevFile) {
|
|
380
|
+
addViolation(rfcId, relFile, "V-02", `Duplicate id "${rfcId}" — also in ${prevFile}. ` +
|
|
381
|
+
`This happens when an RFC is created by scanning only the top-level docs/rfcs/ directory, missing archived RFCs under docs/rfcs/archive/. ` +
|
|
382
|
+
`Fix: renumber this RFC by deleting it and re-running rfc.create (or spec.materialize) which scans the full tree recursively.`);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
seenIds.set(rfcId, relFile);
|
|
386
|
+
}
|
|
387
|
+
// V-03: valid status
|
|
388
|
+
const status = String(fm["status"] ?? "");
|
|
389
|
+
if (!RFC_STATUSES.includes(status)) {
|
|
390
|
+
addViolation(rfcId, relFile, "V-03", `Invalid status "${status}". Must be one of: ${RFC_STATUSES.join(", ")}`);
|
|
391
|
+
}
|
|
392
|
+
// V-04: valid kind
|
|
393
|
+
const kind = String(fm["kind"] ?? "");
|
|
394
|
+
if (!RFC_KINDS.includes(kind)) {
|
|
395
|
+
addViolation(rfcId, relFile, "V-04", `Invalid kind "${kind}". Must be one of: ${RFC_KINDS.join(", ")}`);
|
|
396
|
+
}
|
|
397
|
+
// V-05: valid scope
|
|
398
|
+
const scope = String(fm["scope"] ?? "");
|
|
399
|
+
if (!RFC_SCOPES.includes(scope)) {
|
|
400
|
+
addViolation(rfcId, relFile, "V-05", `Invalid scope "${scope}". Must be one of: ${RFC_SCOPES.join(", ")}`);
|
|
401
|
+
}
|
|
402
|
+
// V-06: owners non-empty
|
|
403
|
+
const owners = fm["owners"];
|
|
404
|
+
if (!Array.isArray(owners) || owners.length === 0) {
|
|
405
|
+
addViolation(rfcId, relFile, "V-06", "owners must be a non-empty array");
|
|
406
|
+
}
|
|
407
|
+
// V-07: valid dates
|
|
408
|
+
const createdAt = String(fm["createdAt"] ?? "");
|
|
409
|
+
const updatedAt = String(fm["updatedAt"] ?? "");
|
|
410
|
+
const datePattern = /^\d{4}-\d{2}-\d{2}$/;
|
|
411
|
+
if (!datePattern.test(createdAt)) {
|
|
412
|
+
addViolation(rfcId, relFile, "V-07", `createdAt "${createdAt}" is not a valid ISO 8601 date (YYYY-MM-DD)`);
|
|
413
|
+
}
|
|
414
|
+
if (!datePattern.test(updatedAt)) {
|
|
415
|
+
addViolation(rfcId, relFile, "V-07", `updatedAt "${updatedAt}" is not a valid ISO 8601 date (YYYY-MM-DD)`);
|
|
416
|
+
}
|
|
417
|
+
const enhancedAt = fm["enhancedAt"];
|
|
418
|
+
if (enhancedAt && !datePattern.test(enhancedAt)) {
|
|
419
|
+
addViolation(rfcId, relFile, "V-07", `enhancedAt "${enhancedAt}" is not a valid ISO 8601 date (YYYY-MM-DD)`);
|
|
420
|
+
}
|
|
421
|
+
// V-08: implementedAt >= createdAt
|
|
422
|
+
const implementedAt = fm["implementedAt"];
|
|
423
|
+
if (implementedAt && datePattern.test(implementedAt) && datePattern.test(createdAt)) {
|
|
424
|
+
if (implementedAt < createdAt) {
|
|
425
|
+
addViolation(rfcId, relFile, "V-08", `implementedAt (${implementedAt}) precedes createdAt (${createdAt})`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
// V-08b: enhancedAt >= createdAt
|
|
429
|
+
if (enhancedAt && datePattern.test(enhancedAt) && datePattern.test(createdAt)) {
|
|
430
|
+
if (enhancedAt < createdAt) {
|
|
431
|
+
addViolation(rfcId, relFile, "V-08", `enhancedAt (${enhancedAt}) precedes createdAt (${createdAt})`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
// V-09: closedAt >= createdAt
|
|
435
|
+
const closedAt = fm["closedAt"];
|
|
436
|
+
if (closedAt && datePattern.test(closedAt) && datePattern.test(createdAt)) {
|
|
437
|
+
if (closedAt < createdAt) {
|
|
438
|
+
addViolation(rfcId, relFile, "V-09", `closedAt (${closedAt}) precedes createdAt (${createdAt})`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// V-10: supersedes point to existing RFCs (+ V-12 forward back-link check)
|
|
442
|
+
const supersedes = fm["supersedes"];
|
|
443
|
+
if (Array.isArray(supersedes)) {
|
|
444
|
+
for (const ref of supersedes) {
|
|
445
|
+
const refStr = String(ref);
|
|
446
|
+
if (!refStr)
|
|
447
|
+
continue;
|
|
448
|
+
if (!allParsed.has(refStr)) {
|
|
449
|
+
addViolation(rfcId, relFile, "V-10", `supersedes "${refStr}" does not match any existing RFC`);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
const otherBy = String(allParsed.get(refStr).parsed.frontmatter["supersededBy"] ?? "");
|
|
453
|
+
if (otherBy !== rfcId) {
|
|
454
|
+
addViolation(rfcId, relFile, "V-12", `${rfcId}.supersedes includes ${refStr}, but ${refStr}.supersededBy is "${otherBy || "(empty)"}" (expected ${rfcId})`, "warning");
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
// V-11: supersededBy points to existing RFC
|
|
460
|
+
// Skip for archived RFCs — superseding RFCs may have been themselves archived/removed
|
|
461
|
+
const supersededBy = fm["supersededBy"];
|
|
462
|
+
if (supersededBy && !allParsed.has(supersededBy) && !isArchived) {
|
|
463
|
+
addViolation(rfcId, relFile, "V-11", `supersededBy "${supersededBy}" does not match any existing RFC`);
|
|
464
|
+
}
|
|
465
|
+
// V-12 (reverse): supersededBy target must list this RFC in its supersedes
|
|
466
|
+
if (supersededBy && allParsed.has(supersededBy)) {
|
|
467
|
+
const otherFm = allParsed.get(supersededBy).parsed.frontmatter;
|
|
468
|
+
const otherSupersedes = otherFm["supersedes"];
|
|
469
|
+
if (!Array.isArray(otherSupersedes) || !otherSupersedes.map(String).includes(rfcId)) {
|
|
470
|
+
addViolation(rfcId, relFile, "V-12", `${rfcId} is supersededBy ${supersededBy}, but ${supersededBy}.supersedes does not include ${rfcId}`, "warning");
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// V-17: strict supersession lifecycle
|
|
474
|
+
if (supersededBy && status !== "superseded") {
|
|
475
|
+
addViolation(rfcId, relFile, "V-17", `supersededBy is set (${supersededBy}) but status is "${status}" (expected "superseded")`, "warning");
|
|
476
|
+
}
|
|
477
|
+
// V-13: required H2 sections (all RFCs use the full template)
|
|
478
|
+
const requiredSections = RFC_FULL_REQUIRED_SECTIONS;
|
|
479
|
+
const headings = [...body.matchAll(/^##\s+(.+)$/gm)].map((m) => m[1].trim());
|
|
480
|
+
for (const section of requiredSections) {
|
|
481
|
+
if (!headings.includes(section)) {
|
|
482
|
+
const severity = status === "draft" ? "warning" : "error";
|
|
483
|
+
// Skip error-severity V-13 for archived RFCs — template requirements evolved after archival
|
|
484
|
+
if (severity === "error" && isArchived)
|
|
485
|
+
continue;
|
|
486
|
+
addViolation(rfcId, relFile, "V-13", `Missing required section "## ${section}"`, severity);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// V-14: acceptance criteria must have at least 3 checklist items
|
|
490
|
+
const acceptanceSection = extractAcceptanceCriteriaSection(body);
|
|
491
|
+
if (acceptanceSection) {
|
|
492
|
+
const checklistItems = acceptanceSection.match(/^- \[[ x]\]/gm);
|
|
493
|
+
const count = checklistItems?.length ?? 0;
|
|
494
|
+
if (count < 3) {
|
|
495
|
+
addViolation(rfcId, relFile, "V-14", `Acceptance criteria has ${count} checklist items; minimum is 3`, status === "draft" ? "warning" : "error");
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
// V-26: implemented RFCs must have all acceptance criteria checked (RFC-0463)
|
|
499
|
+
// V-27: every checked criterion must carry inline (evidence: ...) annotation (RFC-0463)
|
|
500
|
+
// RFC-0476: both use the shared evaluateAcceptanceCriteria function.
|
|
501
|
+
const criteriaEval = evaluateAcceptanceCriteria(body);
|
|
502
|
+
if (acceptanceSection) {
|
|
503
|
+
if (status === "implemented" && criteriaEval.totalUnchecked > 0 && !isArchived) {
|
|
504
|
+
addViolation(rfcId, relFile, "V-26", `status is "implemented" but ${criteriaEval.totalUnchecked} acceptance criteria are unchecked. ` +
|
|
505
|
+
`Complete the work or split deferred criteria into a follow-up RFC via supersede.`);
|
|
506
|
+
}
|
|
507
|
+
for (const line of criteriaEval.checkedWithoutEvidence) {
|
|
508
|
+
addViolation(rfcId, relFile, "V-27", `checked acceptance criterion lacks inline (evidence: ...) annotation: "${line}"`);
|
|
509
|
+
}
|
|
510
|
+
// V-35/V-36/V-37: probe-criterion binding rules (RFC-0997, post-cutoff only)
|
|
511
|
+
const isPostCutoff = createdAt >= RFC_PROBE_BINDING_CUTOFF && !isArchived;
|
|
512
|
+
if (isPostCutoff) {
|
|
513
|
+
const acceptance = fm["acceptance"];
|
|
514
|
+
const probes = Array.isArray(acceptance) ? acceptance : [];
|
|
515
|
+
// V-35: every probe SHALL declare criterion referencing an existing AC-N id
|
|
516
|
+
for (let i = 0; i < probes.length; i++) {
|
|
517
|
+
const probe = probes[i];
|
|
518
|
+
const criterion = probe.criterion;
|
|
519
|
+
if (criterion === undefined || criterion === null) {
|
|
520
|
+
addViolation(rfcId, relFile, "V-35", `acceptance probe ${i} ("${probe.probe}") lacks required "criterion" field (post-cutoff RFCs must bind probes to AC-N criteria per RFC-0997)`);
|
|
521
|
+
}
|
|
522
|
+
else if (typeof criterion !== "string" || !/^AC-\d+$/.test(criterion)) {
|
|
523
|
+
addViolation(rfcId, relFile, "V-35", `acceptance probe ${i} ("${probe.probe}") has criterion "${String(criterion)}" which does not match format "AC-N"`);
|
|
524
|
+
}
|
|
525
|
+
else if (!criteriaEval.criterionIds.includes(criterion)) {
|
|
526
|
+
addViolation(rfcId, relFile, "V-35", `acceptance probe ${i} ("${probe.probe}") declares criterion "${criterion}" which does not exist in acceptance criteria`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
// V-36: every top-level checklist line SHALL start with a unique AC-N: identifier
|
|
530
|
+
for (const line of criteriaEval.linesWithoutId) {
|
|
531
|
+
addViolation(rfcId, relFile, "V-36", `acceptance criterion checklist line lacks "AC-N:" identifier: "${line}"`);
|
|
532
|
+
}
|
|
533
|
+
for (const dupId of criteriaEval.duplicateCriterionIds) {
|
|
534
|
+
addViolation(rfcId, relFile, "V-36", `duplicate acceptance criterion identifier "${dupId}" — each AC-N must be unique`);
|
|
535
|
+
}
|
|
536
|
+
// V-37: checked criteria evidence SHALL resolve
|
|
537
|
+
if (acceptanceSection) {
|
|
538
|
+
const checkedLines = acceptanceSection
|
|
539
|
+
.split("\n")
|
|
540
|
+
.filter((line) => /^- \[x\]/.test(line));
|
|
541
|
+
for (const line of checkedLines) {
|
|
542
|
+
const evidenceMatch = line.match(/\(evidence:\s*(.+?)\)/);
|
|
543
|
+
if (!evidenceMatch)
|
|
544
|
+
continue;
|
|
545
|
+
const evidence = evidenceMatch[1].trim();
|
|
546
|
+
if (evidence.startsWith("probe:")) {
|
|
547
|
+
const refId = evidence.slice("probe:".length).trim();
|
|
548
|
+
const hasProbe = probes.some((p) => p.criterion === refId);
|
|
549
|
+
if (!criteriaEval.criterionIds.includes(refId)) {
|
|
550
|
+
addViolation(rfcId, relFile, "V-37", `evidence "probe:${refId}" references a criterion that does not exist in acceptance criteria`);
|
|
551
|
+
}
|
|
552
|
+
else if (!hasProbe) {
|
|
553
|
+
addViolation(rfcId, relFile, "V-37", `evidence "probe:${refId}" references a criterion with no bound probe`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
else if (evidence.startsWith("test:")) {
|
|
557
|
+
const testPath = evidence.slice("test:".length).trim();
|
|
558
|
+
try {
|
|
559
|
+
await stat(path.join(workspaceRoot, testPath));
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
addViolation(rfcId, relFile, "V-37", `evidence "test:${testPath}" references a file that does not exist`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
else if (/^[^:]+:\d+$/.test(evidence)) {
|
|
566
|
+
const lastColon = evidence.lastIndexOf(":");
|
|
567
|
+
const filePath = evidence.slice(0, lastColon).trim();
|
|
568
|
+
try {
|
|
569
|
+
await stat(path.join(workspaceRoot, filePath));
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
addViolation(rfcId, relFile, "V-37", `evidence "${evidence}" references a file that does not exist`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
// V-38..V-42: criteria content rules (RFC-1006, post-cutoff only)
|
|
580
|
+
const isCriteriaPostCutoff = createdAt >= RFC_CRITERIA_CONTENT_CUTOFF && !isArchived;
|
|
581
|
+
if (isCriteriaPostCutoff) {
|
|
582
|
+
const severity = status === "draft" ? "warning" : "error";
|
|
583
|
+
// V-38: document readiness completeness for accepted+ RFCs
|
|
584
|
+
const drEval = evaluateDocumentReadiness(body);
|
|
585
|
+
if (drEval.totalUnchecked > 0 && (status === "accepted" || status === "implemented")) {
|
|
586
|
+
addViolation(rfcId, relFile, "V-38", `status is "${status}" but ${drEval.totalUnchecked} document readiness criteria are unchecked in "## Document readiness" section.`, severity);
|
|
587
|
+
}
|
|
588
|
+
// V-39: non-atomic criterion
|
|
589
|
+
for (const v of criteriaEval.nonAtomicViolations) {
|
|
590
|
+
addViolation(rfcId, relFile, "V-39", `acceptance criterion ${v.acId} is non-atomic (contains "and" joining two behaviors): "${v.line}". Split into separate criteria.`, severity);
|
|
591
|
+
}
|
|
592
|
+
// V-40: unbounded quantity
|
|
593
|
+
for (const v of criteriaEval.unboundedQuantityViolations) {
|
|
594
|
+
addViolation(rfcId, relFile, "V-40", `acceptance criterion ${v.acId} contains unbounded quantity "${v.trigger}": state a specific number or reference the decision that will set it.`, severity);
|
|
595
|
+
}
|
|
596
|
+
// V-41: weasel verb
|
|
597
|
+
for (const v of criteriaEval.weaselVerbViolations) {
|
|
598
|
+
addViolation(rfcId, relFile, "V-41", `acceptance criterion ${v.acId} contains weasel verb "${v.trigger}": replace with a specific observable behavior.`, severity);
|
|
599
|
+
}
|
|
600
|
+
// V-42: malformed supersession annotation
|
|
601
|
+
for (const line of criteriaEval.malformedSupersessionAnnotations) {
|
|
602
|
+
addViolation(rfcId, relFile, "V-42", `malformed criterion supersession annotation (expected "> Superseded AC-N (YYYY-MM-DD): <reason>"): "${line}"`, "error");
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
// V-15: title consistency
|
|
606
|
+
const titleStr = String(fm["title"] ?? "");
|
|
607
|
+
const headingMatch = body.match(/^#\s+(?:RFC-\d{4}:\s*)?(.+)$/m);
|
|
608
|
+
if (headingMatch && titleStr) {
|
|
609
|
+
const headingTitle = headingMatch[1].trim();
|
|
610
|
+
if (headingTitle !== titleStr) {
|
|
611
|
+
addViolation(rfcId, relFile, "V-15", `Frontmatter title "${titleStr}" does not match body heading "${headingTitle}"`, "warning");
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
// V-16: status <-> date coupling
|
|
615
|
+
const implementedAtStr = String(fm["implementedAt"] ?? "");
|
|
616
|
+
const closedAtStr = String(fm["closedAt"] ?? "");
|
|
617
|
+
const isTerminalStatus = status === "superseded" || status === "rejected";
|
|
618
|
+
if (status === "implemented" && !implementedAtStr) {
|
|
619
|
+
addViolation(rfcId, relFile, "V-16", `status is "implemented" but implementedAt is empty`, "error");
|
|
620
|
+
}
|
|
621
|
+
if ((status === "accepted" || status === "draft") && implementedAtStr) {
|
|
622
|
+
addViolation(rfcId, relFile, "V-16", `status is "${status}" but implementedAt is set (${implementedAtStr})`, "error");
|
|
623
|
+
}
|
|
624
|
+
if (isTerminalStatus && !closedAtStr) {
|
|
625
|
+
addViolation(rfcId, relFile, "V-16", `status is "${status}" but closedAt is empty`, "warning");
|
|
626
|
+
}
|
|
627
|
+
if (!isTerminalStatus && closedAtStr) {
|
|
628
|
+
addViolation(rfcId, relFile, "V-16", `closedAt is set (${closedAtStr}) but status "${status}" is not terminal (superseded/rejected)`, "warning");
|
|
629
|
+
}
|
|
630
|
+
// V-18: referential integrity of related[]
|
|
631
|
+
const related = fm["related"];
|
|
632
|
+
if (Array.isArray(related)) {
|
|
633
|
+
for (const ref of related) {
|
|
634
|
+
const refStr = String(ref).trim();
|
|
635
|
+
if (!refStr)
|
|
636
|
+
continue;
|
|
637
|
+
if (/^RFC-\d{4}$/.test(refStr)) {
|
|
638
|
+
if (!allParsed.has(refStr)) {
|
|
639
|
+
addViolation(rfcId, relFile, "V-18", `related "${refStr}" does not match any existing RFC`, "warning");
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
else if (/^DNA-\d+$/i.test(refStr) && validDnaIds.size > 0) {
|
|
643
|
+
if (!validDnaIds.has(parseInt(refStr.slice(4), 10))) {
|
|
644
|
+
addViolation(rfcId, relFile, "V-18", `related "${refStr}" is not defined in ${DNA_DOC}`, "warning");
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
else if (/^AP-\d+$/i.test(refStr) && validApIds.size > 0) {
|
|
648
|
+
if (!validApIds.has(parseInt(refStr.slice(3), 10))) {
|
|
649
|
+
addViolation(rfcId, relFile, "V-18", `related "${refStr}" is not defined in ${AP_DOC}`, "warning");
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// V-19: amends/amendedBy referential + bidirectional integrity
|
|
655
|
+
const amends = fm["amends"];
|
|
656
|
+
if (Array.isArray(amends)) {
|
|
657
|
+
for (const ref of amends) {
|
|
658
|
+
const refStr = String(ref);
|
|
659
|
+
if (!refStr)
|
|
660
|
+
continue;
|
|
661
|
+
if (!allParsed.has(refStr)) {
|
|
662
|
+
addViolation(rfcId, relFile, "V-19", `amends "${refStr}" does not match any existing RFC`);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
const otherAmendedBy = allParsed.get(refStr).parsed.frontmatter["amendedBy"];
|
|
666
|
+
const arr = Array.isArray(otherAmendedBy) ? otherAmendedBy.map(String) : [];
|
|
667
|
+
if (!arr.includes(rfcId)) {
|
|
668
|
+
addViolation(rfcId, relFile, "V-19", `${rfcId}.amends includes ${refStr}, but ${refStr}.amendedBy does not include ${rfcId}`, "warning");
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const amendedBy = fm["amendedBy"];
|
|
674
|
+
if (Array.isArray(amendedBy)) {
|
|
675
|
+
for (const ref of amendedBy) {
|
|
676
|
+
const refStr = String(ref);
|
|
677
|
+
if (!refStr)
|
|
678
|
+
continue;
|
|
679
|
+
if (!allParsed.has(refStr)) {
|
|
680
|
+
addViolation(rfcId, relFile, "V-19", `amendedBy "${refStr}" does not match any existing RFC`);
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
const otherAmends = allParsed.get(refStr).parsed.frontmatter["amends"];
|
|
684
|
+
const arr = Array.isArray(otherAmends) ? otherAmends.map(String) : [];
|
|
685
|
+
if (!arr.includes(rfcId)) {
|
|
686
|
+
addViolation(rfcId, relFile, "V-19", `${rfcId}.amendedBy includes ${refStr}, but ${refStr}.amends does not include ${rfcId}`, "warning");
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// V-21: filename must be lowercase kebab-case
|
|
692
|
+
const lowerRfcId = rfcId.toLowerCase();
|
|
693
|
+
const fileBasename = fileName.split("/").pop().split("\\").pop();
|
|
694
|
+
if (!fileBasename.startsWith(lowerRfcId)) {
|
|
695
|
+
addViolation(rfcId, relFile, "V-21", `Filename "${fileBasename}" must start with lowercase "${lowerRfcId}"`);
|
|
696
|
+
}
|
|
697
|
+
else if (!/^[a-z0-9-]+\.md$/.test(fileBasename)) {
|
|
698
|
+
addViolation(rfcId, relFile, "V-21", `Filename "${fileName}" is not lowercase kebab-case (only a-z, 0-9, hyphens allowed)`);
|
|
699
|
+
}
|
|
700
|
+
// V-20: no unknown frontmatter keys
|
|
701
|
+
for (const key of Object.keys(fm)) {
|
|
702
|
+
if (!knownKeys.has(key)) {
|
|
703
|
+
addViolation(rfcId, relFile, "V-20", `unknown frontmatter key "${key}" (not in the RFC schema)`, "warning");
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// V-22: well-formed acceptance probe shape
|
|
707
|
+
for (const issue of validateAcceptanceShape(fm["acceptance"])) {
|
|
708
|
+
addViolation(rfcId, relFile, "V-22", issue.message, "warning");
|
|
709
|
+
}
|
|
710
|
+
// V-23: verification evidence for post-cutoff, probe-bearing, implemented RFCs
|
|
711
|
+
const createdAtStr = String(fm["createdAt"] ?? "");
|
|
712
|
+
const hasProbes = Array.isArray(fm["acceptance"]) && fm["acceptance"].length > 0;
|
|
713
|
+
if (createdAtStr >= RFC_METADATA_CUTOFF && hasProbes && status === "implemented") {
|
|
714
|
+
const slug = rfcId.toLowerCase();
|
|
715
|
+
const evidenceRelPath = path.join(RFC_DIR, "verification", `${slug}.generated.yaml`);
|
|
716
|
+
const evidenceAbsPath = path.join(workspaceRoot, evidenceRelPath);
|
|
717
|
+
let evidenceOk = false;
|
|
718
|
+
let evidenceOverall = "";
|
|
719
|
+
try {
|
|
720
|
+
const evidenceContent = await readFile(evidenceAbsPath, "utf-8");
|
|
721
|
+
const evidence = yamlParse(evidenceContent);
|
|
722
|
+
evidenceOverall = String(evidence.overall ?? "");
|
|
723
|
+
evidenceOk = evidenceOverall === "pass";
|
|
724
|
+
}
|
|
725
|
+
catch {
|
|
726
|
+
// file missing or unparseable
|
|
727
|
+
}
|
|
728
|
+
if (!evidenceOk) {
|
|
729
|
+
addViolation(rfcId, relFile, "V-23", `status is "implemented" with acceptance probes and createdAt >= ${RFC_METADATA_CUTOFF}, ` +
|
|
730
|
+
`but evidence file ${evidenceRelPath} is missing, unparseable, or overall is not "pass" ` +
|
|
731
|
+
`(got "${evidenceOverall || "missing"}"). Run: site-kernel run rfc.verification.emit --id ${rfcId}`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
// V-25: reviewer identity on newly decided RFCs (RFC-0335)
|
|
735
|
+
{
|
|
736
|
+
const reviewers = fm["reviewers"];
|
|
737
|
+
const reviewerArr = Array.isArray(reviewers) ? reviewers.map(String) : [];
|
|
738
|
+
const isDecided = DECIDED_STATUSES.includes(status);
|
|
739
|
+
const isPostCutoff = createdAtStr >= RFC_METADATA_CUTOFF;
|
|
740
|
+
if (isPostCutoff && isDecided && reviewerArr.length === 0) {
|
|
741
|
+
addViolation(rfcId, relFile, "V-25", `${status} RFC created ${createdAtStr} has empty reviewers — the deciding human must record their identity (RFC-0335).`);
|
|
742
|
+
}
|
|
743
|
+
for (const entry of reviewerArr) {
|
|
744
|
+
if (!REVIEWER_IDENTITY_PATTERN.test(entry)) {
|
|
745
|
+
addViolation(rfcId, relFile, "V-25", `reviewer "${entry}" does not match the identity format ^(human|agent):[a-z0-9][a-z0-9-]*$ (RFC-0335).`);
|
|
746
|
+
}
|
|
747
|
+
else if (entry.startsWith("agent:")) {
|
|
748
|
+
addViolation(rfcId, relFile, "V-25", `agent reviewer identities are reserved until an RFC grants AI reviewer authority over RFC governance (see RFC-0279 for the pattern) (RFC-0335).`);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
// V-24: satisfies DNA-trace frontmatter (RFC-0331)
|
|
753
|
+
{
|
|
754
|
+
const satisfies = fm["satisfies"];
|
|
755
|
+
const satisfiesArr = Array.isArray(satisfies) ? satisfies.map(String) : [];
|
|
756
|
+
const satisfiesPresent = satisfiesArr.length > 0;
|
|
757
|
+
const kind = String(fm["kind"] ?? "");
|
|
758
|
+
const requiresSatisfies = createdAtStr >= RFC_METADATA_CUTOFF && (kind === "architecture" || kind === "contract");
|
|
759
|
+
// Format: all RFCs, any age — each entry must match ^DNA-\d+$
|
|
760
|
+
if (satisfiesPresent) {
|
|
761
|
+
for (const entry of satisfiesArr) {
|
|
762
|
+
if (!/^DNA-\d+$/.test(entry)) {
|
|
763
|
+
addViolation(rfcId, relFile, "V-24", `satisfies entry "${entry}" does not match the format ^DNA-\\d+$ (RFC-0331).`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
// Presence: post-cutoff architecture/contract RFCs must have non-empty satisfies
|
|
768
|
+
if (requiresSatisfies && !satisfiesPresent) {
|
|
769
|
+
addViolation(rfcId, relFile, "V-24", `${kind} RFC created ${createdAtStr} (>= ${RFC_METADATA_CUTOFF}) must declare at least one DNA invariant in satisfies (RFC-0331).`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
// V-28: RFC-id monotonicity (RFC-0478) — no RFC may have an id lower
|
|
773
|
+
// than the maximum id among RFCs with a strictly earlier createdAt.
|
|
774
|
+
// Same-day RFCs (equal createdAt) are unconstrained relative to each other.
|
|
775
|
+
// Only applies to RFCs created strictly after RFC_VERSION_BUMP_CUTOFF;
|
|
776
|
+
// pre-cutoff and same-day RFCs predate the rule and are exempt.
|
|
777
|
+
// All RFCs (including pre-cutoff) participate in the comparison set.
|
|
778
|
+
{
|
|
779
|
+
const currentIdNum = parseInt(rfcId.replace(/^RFC-/, ""), 10);
|
|
780
|
+
const currentCreatedAt = String(fm["createdAt"] ?? "");
|
|
781
|
+
if (currentCreatedAt > RFC_VERSION_BUMP_CUTOFF) {
|
|
782
|
+
let maxEarlierId = 0;
|
|
783
|
+
for (const [otherId, other] of allParsed) {
|
|
784
|
+
if (otherId === rfcId)
|
|
785
|
+
continue;
|
|
786
|
+
const otherCreatedAt = String(other.parsed.frontmatter["createdAt"] ?? "");
|
|
787
|
+
if (otherCreatedAt < currentCreatedAt) {
|
|
788
|
+
const otherIdNum = parseInt(otherId.replace(/^RFC-/, ""), 10);
|
|
789
|
+
if (otherIdNum > maxEarlierId) {
|
|
790
|
+
maxEarlierId = otherIdNum;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (currentIdNum < maxEarlierId) {
|
|
795
|
+
addViolation(rfcId, relFile, "V-28", `RFC-id ${rfcId} (createdAt ${currentCreatedAt}) is lower than RFC-${maxEarlierId} which has a strictly earlier createdAt. RFC-ids must be monotonically non-decreasing with respect to createdAt (RFC-0478).`);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
// V-29: versionBump required for post-cutoff accepted/implemented RFCs (RFC-0478, ADR-0029)
|
|
800
|
+
{
|
|
801
|
+
const versionBump = fm["versionBump"];
|
|
802
|
+
const isPostCutoff = createdAtStr >= RFC_VERSION_BUMP_CUTOFF;
|
|
803
|
+
const requiresVersionBump = status === "implemented" || status === "accepted";
|
|
804
|
+
if (isPostCutoff && requiresVersionBump && versionBump === undefined) {
|
|
805
|
+
addViolation(rfcId, relFile, "V-29", `status is "${status}" and createdAt >= ${RFC_VERSION_BUMP_CUTOFF}, but versionBump is absent. Post-cutoff accepted/implemented RFCs must declare versionBump (RFC-0478).`);
|
|
806
|
+
}
|
|
807
|
+
if (versionBump === "none") {
|
|
808
|
+
const commands = fm["commands"];
|
|
809
|
+
const added = Array.isArray(commands?.added) ? commands.added : [];
|
|
810
|
+
const changed = Array.isArray(commands?.changed) ? commands.changed : [];
|
|
811
|
+
if (added.length > 0 || changed.length > 0) {
|
|
812
|
+
addViolation(rfcId, relFile, "V-29", `versionBump is "none" but commands.added or commands.changed is non-empty. Commands imply code changes; use "patch" or "minor" instead (RFC-0478).`, "warning");
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// V-30: breaksC field consistency (RFC-0480)
|
|
817
|
+
// If breaksC: true — @warpgogol/ontology must be in packagesImpacted.
|
|
818
|
+
// If breaksC absent/false but @warpgol/ontology in packagesImpacted — warning.
|
|
819
|
+
// Skip for archived RFCs — package names changed during ecosystem consolidation (RFC-0776):
|
|
820
|
+
// @warpgogol/ontology → @warpgogol/werkstatt-shared/ontology
|
|
821
|
+
{
|
|
822
|
+
const breaksC = fm["breaksC"];
|
|
823
|
+
const packagesImpacted = Array.isArray(fm["packagesImpacted"])
|
|
824
|
+
? fm["packagesImpacted"].map(String)
|
|
825
|
+
: [];
|
|
826
|
+
const hasOntology = packagesImpacted.some((p) => p.includes("@warpgogol/werkstatt-shared/ontology"));
|
|
827
|
+
if (breaksC === true && !hasOntology && !isArchived) {
|
|
828
|
+
addViolation(rfcId, relFile, "V-30", `breaksC is true but @warpgogol/ontology is not in packagesImpacted. RFCs that break Layer C must update the declarative C-contract in packages/ontology/src/external-surfaces/ (RFC-0480).`);
|
|
829
|
+
}
|
|
830
|
+
if (breaksC !== true && hasOntology) {
|
|
831
|
+
addViolation(rfcId, relFile, "V-30", `@warpgogol/ontology is in packagesImpacted but breaksC is not true. If this RFC modifies packages/ontology/src/external-surfaces/, declare breaksC: true (RFC-0480).`, "warning");
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
// V-31: filename-number uniqueness and filename/id consistency (RFC-0491)
|
|
835
|
+
if (seenFilenameNumbers) {
|
|
836
|
+
const fileBasename = fileName.split("/").pop().split("\\").pop();
|
|
837
|
+
const fileNumMatch = fileBasename.match(/^rfc-(\d{4})/);
|
|
838
|
+
if (fileNumMatch) {
|
|
839
|
+
const fileNum = parseInt(fileNumMatch[1], 10);
|
|
840
|
+
const idNum = parseInt(rfcId.replace(/^RFC-/, ""), 10);
|
|
841
|
+
// Sub-check A: filename number must match frontmatter id number
|
|
842
|
+
if (!Number.isNaN(idNum) && fileNum !== idNum) {
|
|
843
|
+
addViolation(rfcId, relFile, "V-31", `Filename number ${String(fileNum).padStart(4, "0")} does not match frontmatter id ${rfcId}`);
|
|
844
|
+
}
|
|
845
|
+
// Sub-check B: filename number must be unique across the full tree
|
|
846
|
+
const prevNumFile = seenFilenameNumbers.get(fileNum);
|
|
847
|
+
if (prevNumFile) {
|
|
848
|
+
addViolation(rfcId, relFile, "V-31", `Duplicate filename number ${String(fileNum).padStart(4, "0")} — also in ${prevNumFile}`);
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
seenFilenameNumbers.set(fileNum, relFile);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
// V-32: implementation commit drift detection (RFC-0625)
|
|
856
|
+
// Warns when implement: RFC-XXXX commits exist in git history since createdAt
|
|
857
|
+
// but the RFC status is not yet "implemented".
|
|
858
|
+
{
|
|
859
|
+
const driftResult = await checkImplementationCommitDrift(workspaceRoot, rfcId, createdAt, status);
|
|
860
|
+
if (driftResult.found) {
|
|
861
|
+
addViolation(rfcId, relFile, "V-32", `${rfcId} has ${driftResult.commitCount} implement: commit(s) in git history since ${createdAt} but status is still "${status}". Run rfc.implement.stamp to transition to implemented.`, "warning");
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
// V-33: dependsOn referential integrity, self-dependency, rejected-dependency deadlock (RFC-0795)
|
|
865
|
+
const dependsOn = fm["dependsOn"];
|
|
866
|
+
if (Array.isArray(dependsOn)) {
|
|
867
|
+
for (const depRef of dependsOn) {
|
|
868
|
+
const depStr = String(depRef);
|
|
869
|
+
if (!depStr)
|
|
870
|
+
continue;
|
|
871
|
+
if (depStr === rfcId) {
|
|
872
|
+
addViolation(rfcId, relFile, "V-33", `dependsOn includes itself — an RFC cannot depend on itself`, "warning");
|
|
873
|
+
}
|
|
874
|
+
else if (!allParsed.has(depStr)) {
|
|
875
|
+
addViolation(rfcId, relFile, "V-33", `dependsOn "${depStr}" does not match any existing RFC`, "warning");
|
|
876
|
+
}
|
|
877
|
+
else {
|
|
878
|
+
const depStatus = String(allParsed.get(depStr).parsed.frontmatter["status"] ?? "");
|
|
879
|
+
if (depStatus === "rejected") {
|
|
880
|
+
addViolation(rfcId, relFile, "V-33", `dependsOn "${depStr}" has status "rejected" — this dependency will never be satisfied. Remove the entry or supersede the rejected RFC.`, "warning");
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// V-34: batch slug format (RFC-0795)
|
|
886
|
+
const batchSlug = fm["batch"];
|
|
887
|
+
if (batchSlug !== undefined && batchSlug !== null) {
|
|
888
|
+
const batchStr = String(batchSlug);
|
|
889
|
+
if (batchStr && !/^[a-z0-9]+(-[a-z0-9]+)*$/.test(batchStr)) {
|
|
890
|
+
addViolation(rfcId, relFile, "V-34", `batch slug "${batchStr}" does not match kebab-case pattern /^[a-z0-9]+(-[a-z0-9]+)*$/`, "warning");
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
// V-NC-01: NEEDS CLARIFICATION marker detection (RFC-0709)
|
|
894
|
+
const ncMarkers = collectMarkers(body, status, createdAt);
|
|
895
|
+
for (const marker of ncMarkers) {
|
|
896
|
+
addViolation(rfcId, relFile, "V-NC-01", `Unresolved NEEDS CLARIFICATION marker at line ${marker.line}: "${marker.text}"`, marker.severity === "error" ? "error" : "warning");
|
|
897
|
+
}
|
|
898
|
+
return ncMarkers;
|
|
899
|
+
}
|
|
900
|
+
// ─── V-RFC-33: frontmatter YAML parseability (RFC-0755) ────────────────────
|
|
901
|
+
export function checkFrontmatterYamlParse(fileName, result, addViolation) {
|
|
902
|
+
if (result && "error" in result) {
|
|
903
|
+
const relFile = path.join(RFC_DIR, fileName);
|
|
904
|
+
addViolation("UNKNOWN", relFile, "V-RFC-33", `RFC frontmatter YAML parse error in ${fileName}: ${result.error}`);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
//# sourceMappingURL=validate-rules.js.map
|