@warpgogol/forge 0.3.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 +149 -0
- package/README.md +164 -0
- package/bin/cli.js +16 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +265 -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 +112 -0
- package/dist/os/adr/adr.module.js.map +1 -0
- package/dist/os/adr/frontmatter-io.d.ts +11 -0
- package/dist/os/adr/frontmatter-io.d.ts.map +1 -0
- package/dist/os/adr/frontmatter-io.js +64 -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 +162 -0
- package/dist/os/adr/handlers/archive.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 +165 -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 +219 -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 +59 -0
- package/dist/os/adr/types.d.ts.map +1 -0
- package/dist/os/adr/types.js +54 -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 +44 -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 +73 -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 +167 -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 +162 -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 +238 -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 +205 -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 +213 -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 +418 -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 +41 -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 +318 -0
- package/dist/os/core/core.module.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/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 +209 -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 +44 -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 +18 -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 +324 -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 +33 -0
- package/dist/os/naming/naming.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 +71 -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 +166 -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 +44 -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/rfc/acceptance.d.ts +15 -0
- package/dist/os/rfc/acceptance.d.ts.map +1 -0
- package/dist/os/rfc/acceptance.js +290 -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 +14 -0
- package/dist/os/rfc/frontmatter-io.d.ts.map +1 -0
- package/dist/os/rfc/frontmatter-io.js +98 -0
- package/dist/os/rfc/frontmatter-io.js.map +1 -0
- package/dist/os/rfc/handlers/archive.d.ts +24 -0
- package/dist/os/rfc/handlers/archive.d.ts.map +1 -0
- package/dist/os/rfc/handlers/archive.js +161 -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 +195 -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 +334 -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 +189 -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 +150 -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 +235 -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 +99 -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 +200 -0
- package/dist/os/rfc/handlers/supersede-propose.js.map +1 -0
- package/dist/os/rfc/handlers/validate-rules.d.ts +29 -0
- package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -0
- package/dist/os/rfc/handlers/validate-rules.js +461 -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 +101 -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 +355 -0
- package/dist/os/rfc/rfc.module.js.map +1 -0
- package/dist/os/rfc/types.d.ts +448 -0
- package/dist/os/rfc/types.d.ts.map +1 -0
- package/dist/os/rfc/types.js +95 -0
- package/dist/os/rfc/types.js.map +1 -0
- package/dist/os/rfc/verification-evidence.d.ts +8 -0
- package/dist/os/rfc/verification-evidence.d.ts.map +1 -0
- package/dist/os/rfc/verification-evidence.js +182 -0
- package/dist/os/rfc/verification-evidence.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 +138 -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 +209 -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/save.d.ts +6 -0
- package/dist/os/session/handlers/save.d.ts.map +1 -0
- package/dist/os/session/handlers/save.js +285 -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 +188 -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 +20 -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 +113 -0
- package/dist/os/session/session.module.js.map +1 -0
- package/dist/os/session/types.d.ts +101 -0
- package/dist/os/session/types.d.ts.map +1 -0
- package/dist/os/session/types.js +52 -0
- package/dist/os/session/types.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 +132 -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 +222 -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 +435 -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 +67 -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 +127 -0
- package/dist/os/werkstatt/handlers/lock.js.map +1 -0
- package/dist/os/werkstatt/handlers/schema.d.ts +32 -0
- package/dist/os/werkstatt/handlers/schema.d.ts.map +1 -0
- package/dist/os/werkstatt/handlers/schema.js +41 -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 +142 -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 +43 -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/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 +58 -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 +254 -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 +48 -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 +52 -0
- package/dist/src/cli-output.js.map +1 -0
- package/dist/src/config/forge-config.d.ts +170 -0
- package/dist/src/config/forge-config.d.ts.map +1 -0
- package/dist/src/config/forge-config.js +259 -0
- package/dist/src/config/forge-config.js.map +1 -0
- package/dist/src/forge-module.d.ts +16 -0
- package/dist/src/forge-module.d.ts.map +1 -0
- package/dist/src/forge-module.js +15 -0
- package/dist/src/forge-module.js.map +1 -0
- package/dist/src/index.d.ts +30 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +48 -0
- package/dist/src/index.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 +46 -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 +58 -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 +11 -0
- package/dist/src/onboarding/agents-generate.d.ts.map +1 -0
- package/dist/src/onboarding/agents-generate.js +415 -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 +232 -0
- package/dist/src/onboarding/create.js.map +1 -0
- package/dist/src/onboarding/doctor.d.ts +30 -0
- package/dist/src/onboarding/doctor.d.ts.map +1 -0
- package/dist/src/onboarding/doctor.js +380 -0
- package/dist/src/onboarding/doctor.js.map +1 -0
- package/dist/src/onboarding/extended-behavioral-layer.d.ts +10 -0
- package/dist/src/onboarding/extended-behavioral-layer.d.ts.map +1 -0
- package/dist/src/onboarding/extended-behavioral-layer.js +82 -0
- package/dist/src/onboarding/extended-behavioral-layer.js.map +1 -0
- package/dist/src/onboarding/init.d.ts +25 -0
- package/dist/src/onboarding/init.d.ts.map +1 -0
- package/dist/src/onboarding/init.js +260 -0
- package/dist/src/onboarding/init.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 +167 -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 +120 -0
- package/dist/src/onboarding/scaffold.js.map +1 -0
- package/dist/src/onboarding/upgrade.d.ts +17 -0
- package/dist/src/onboarding/upgrade.d.ts.map +1 -0
- package/dist/src/onboarding/upgrade.js +293 -0
- package/dist/src/onboarding/upgrade.js.map +1 -0
- package/dist/src/profiles/stack-profile.d.ts +51 -0
- package/dist/src/profiles/stack-profile.d.ts.map +1 -0
- package/dist/src/profiles/stack-profile.js +119 -0
- package/dist/src/profiles/stack-profile.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 +135 -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 +153 -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 +30 -0
- package/dist/src/utils/fs-idempotent.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 +21 -0
- package/dist/src/utils/generated-marker.d.ts.map +1 -0
- package/dist/src/utils/generated-marker.js +128 -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 +7 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +19 -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/port-validate.d.ts +12 -0
- package/dist/src/validators/port-validate.d.ts.map +1 -0
- package/dist/src/validators/port-validate.js +73 -0
- package/dist/src/validators/port-validate.js.map +1 -0
- package/dist/src/validators/skill-validate.d.ts +14 -0
- package/dist/src/validators/skill-validate.d.ts.map +1 -0
- package/dist/src/validators/skill-validate.js +487 -0
- package/dist/src/validators/skill-validate.js.map +1 -0
- package/os/rfc/rfc-0000-template.md +222 -0
- package/package.json +262 -0
- package/profiles/astro-typescript-turborepo.yaml +144 -0
- package/profiles/forge-shell.yaml +77 -0
- package/profiles/phaser-turborepo.yaml +144 -0
- package/skills/_shared/fo-pipeline-conventions.md +119 -0
- package/skills/_shared/fo-session-summary.md +95 -0
- package/skills/fo/fo-add-tests/SKILL.md +166 -0
- package/skills/fo/fo-add-tests/mocking.md +60 -0
- package/skills/fo/fo-add-tests/pbt-guide.md +193 -0
- package/skills/fo/fo-add-tests/tests-reference.md +92 -0
- package/skills/fo/fo-architecture/SKILL.md +83 -0
- package/skills/fo/fo-architecture/architecture-vocabulary.md +104 -0
- package/skills/fo/fo-architecture/deepening.md +37 -0
- package/skills/fo/fo-architecture/design-it-twice.md +44 -0
- package/skills/fo/fo-architecture/html-report.md +104 -0
- package/skills/fo/fo-compass-annotate/SKILL.md +128 -0
- package/skills/fo/fo-compass-annotate/reference/comment-styles.md +25 -0
- package/skills/fo/fo-compass-annotate/reference/learned-principles.md +12 -0
- package/skills/fo/fo-compass-annotate/reference/risk-patterns.md +23 -0
- package/skills/fo/fo-compass-annotate/templates/audit-prompt.md +36 -0
- package/skills/fo/fo-compass-annotate/templates/header-format.md +57 -0
- package/skills/fo/fo-compass-annotate/templates/header-prompt.md +35 -0
- package/skills/fo/fo-doc-audit/SKILL.md +264 -0
- package/skills/fo/fo-extract-dna/SKILL.md +248 -0
- package/skills/fo/fo-fix/SKILL.md +152 -0
- package/skills/fo/fo-fix/bug-diagnosis.md +128 -0
- package/skills/fo/fo-handoff/SKILL.md +58 -0
- package/skills/fo/fo-harvest/SKILL.md +80 -0
- package/skills/fo/fo-idea/SKILL.md +242 -0
- package/skills/fo/fo-idea-audit/SKILL.md +248 -0
- package/skills/fo/fo-idea-create-adr/SKILL.md +130 -0
- package/skills/fo/fo-idea-create-rfc/SKILL.md +130 -0
- package/skills/fo/fo-idea-enhance/SKILL.md +218 -0
- package/skills/fo/fo-idea-i-just-want-to-see-the-plan/SKILL.md +36 -0
- package/skills/fo/fo-idea-i-just-want-to-see-the-result/SKILL.md +167 -0
- package/skills/fo/fo-idea-implement/SKILL.md +463 -0
- package/skills/fo/fo-idea-plan/SKILL.md +230 -0
- package/skills/fo/fo-idea-status/SKILL.md +103 -0
- package/skills/fo/fo-memory-sync/SKILL.md +204 -0
- package/skills/fo/fo-memory-sync/fix-patterns.md +24 -0
- package/skills/fo/fo-memory-sync/learned-principles.md +6 -0
- package/skills/fo/fo-memory-sync/qa-log.md +5 -0
- package/skills/fo/fo-qa/SKILL.md +142 -0
- package/skills/fo/fo-review/SKILL.md +273 -0
- package/skills/fo/fo-session-retro/SKILL.md +276 -0
- package/skills/fo/fo-session-save/SKILL.md +71 -0
- package/skills/fo/fo-session-save/fix-patterns.md +33 -0
- package/skills/fo/fo-session-save/learned-principles.md +19 -0
- package/skills/fo/fo-session-save/qa-log.md +11 -0
- package/skills/fo/fo-spec-ingest/SKILL.md +127 -0
- package/skills/fo/fo-triage/SKILL.md +97 -0
- package/skills/meta/forge-bootstrap/SKILL.md +335 -0
- package/skills/meta/forge-bootstrap/forge-about.md +35 -0
- package/skills/meta/forge-bootstrap/milestone-gallery/.gitkeep +0 -0
- package/skills/meta/forge-bootstrap/operator-profile-template.md +53 -0
- package/skills/meta/forge-bootstrap/project-narrative-template.md +31 -0
- package/skills/meta/port-to-forge/SKILL.md +65 -0
- package/skills/meta/skill-create/SKILL.md +73 -0
- package/skills/shared/grilling/SKILL.md +28 -0
- package/skills/shared/grilling/learned-principles.md +12 -0
- package/skills/shared/grilling/qa-log.md +10 -0
- package/skills/shared/my-preferences/SKILL.md +77 -0
- package/skills/shared/windows-ai-tooling/SKILL.md +200 -0
- package/skills/shared/writing-great-skills/SKILL.md +132 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Property-Based Testing Guide
|
|
2
|
+
|
|
3
|
+
Reference for writing property-based tests (PBT) using `fast-check` + `vitest`. Check the project's invariants file for the canonical PBT rule.
|
|
4
|
+
|
|
5
|
+
## Decision tree
|
|
6
|
+
|
|
7
|
+
Every function MUST be classified per this tree. The decision is made **per-function**, not per-file.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Is the function pure (no I/O, no side effects, no mutation of inputs)?
|
|
11
|
+
├── No → Use example-based tests or integration tests. Do NOT use PBT.
|
|
12
|
+
└── Yes → Does the function have at least one verifiable algebraic property?
|
|
13
|
+
├── No → Use example-based tests. Do NOT use PBT.
|
|
14
|
+
└── Yes → Is the input domain large enough that examples are insufficient?
|
|
15
|
+
├── No (small enum, <10 values) → Use example-based tests with exhaustive coverage. Do NOT use PBT.
|
|
16
|
+
└── Yes → Write a property-based test in a *.pbt.test.ts file using fast-check.
|
|
17
|
+
├── Check if a *.pbt.test.ts already exists for this function.
|
|
18
|
+
│ ├── Yes → Add the new property to the existing file.
|
|
19
|
+
│ └── No → Create a new *.pbt.test.ts file alongside the existing tests.
|
|
20
|
+
└── Choose the property pattern from the catalog below.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## When PBT is required
|
|
24
|
+
|
|
25
|
+
ALL of the following must be true:
|
|
26
|
+
|
|
27
|
+
1. **Pure function** — output determined solely by input. No I/O, no `Date.now()`, no `Math.random()`, no side effects, no mutation of input arguments.
|
|
28
|
+
2. **Verifiable property** — at least one algebraic property holds and is non-trivial.
|
|
29
|
+
3. **Non-trivial input space** — large enough that hand-picked examples are unlikely to cover all edge cases. Functions with a small enum input (<10 values) do not need PBT.
|
|
30
|
+
|
|
31
|
+
## When PBT is NOT required
|
|
32
|
+
|
|
33
|
+
ANY of the following:
|
|
34
|
+
|
|
35
|
+
1. **Impure function** — I/O, `Date.now()`, `Math.random()`, external state mutation.
|
|
36
|
+
2. **UI / Astro component** — verified by visual inspection, Playwright, or snapshot tests.
|
|
37
|
+
3. **Build pipeline / OS command** — reads files, runs processes, orchestrates pipelines. Integration-tested.
|
|
38
|
+
4. **Content validation rules** — deterministic rule checks (e.g. "does this YAML key exist?").
|
|
39
|
+
5. **Small input domain** — closed enum with <10 values. Exhaustive example-based coverage is sufficient.
|
|
40
|
+
6. **Thin delegation** — one-line pass-through to another function that is already property-tested.
|
|
41
|
+
7. **No verifiable property** — behavior cannot be expressed as a universal quantifier over input.
|
|
42
|
+
|
|
43
|
+
## Property patterns catalog
|
|
44
|
+
|
|
45
|
+
Each pattern shows the fast-check idiom. Use the pattern names exactly — do not invent custom property names.
|
|
46
|
+
|
|
47
|
+
### Idempotency
|
|
48
|
+
|
|
49
|
+
`f(f(x)) == f(x)` for all valid `x`.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { test, expect } from "vitest";
|
|
53
|
+
import fc from "fast-check";
|
|
54
|
+
import { normalizeText } from "../text-normalize.ts";
|
|
55
|
+
|
|
56
|
+
test("normalizeText is idempotent", () => {
|
|
57
|
+
fc.assert(
|
|
58
|
+
fc.property(fc.string({ maxLength: 500 }), (input) => {
|
|
59
|
+
const once = normalizeText(input, ALL_ON);
|
|
60
|
+
expect(normalizeText(once, ALL_ON)).toBe(once);
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Round-trip
|
|
67
|
+
|
|
68
|
+
`g(f(x)) == x` for all valid `x`.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { test, expect } from "vitest";
|
|
72
|
+
import fc from "fast-check";
|
|
73
|
+
import { parseSemver, formatSemver } from "../semver.ts";
|
|
74
|
+
|
|
75
|
+
test("parseSemver and formatSemver are round-trip", () => {
|
|
76
|
+
fc.assert(
|
|
77
|
+
fc.property(
|
|
78
|
+
fc.record({
|
|
79
|
+
major: fc.nat({ max: 999 }),
|
|
80
|
+
minor: fc.nat({ max: 999 }),
|
|
81
|
+
patch: fc.nat({ max: 999 }),
|
|
82
|
+
}),
|
|
83
|
+
({ major, minor, patch }) => {
|
|
84
|
+
const formatted = formatSemver({ major, minor, patch });
|
|
85
|
+
const reparsed = parseSemver(formatted);
|
|
86
|
+
expect(reparsed).toEqual({ major, minor, patch });
|
|
87
|
+
},
|
|
88
|
+
),
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Immutability
|
|
94
|
+
|
|
95
|
+
`f(x)` does not mutate `x`; `x` is deep-equal before and after.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { test, expect } from "vitest";
|
|
99
|
+
import fc from "fast-check";
|
|
100
|
+
import { substituteRefsDeep } from "../substitute-deep.ts";
|
|
101
|
+
|
|
102
|
+
test("substituteRefsDeep does not mutate input", async () => {
|
|
103
|
+
fc.assert(
|
|
104
|
+
fc.asyncProperty(
|
|
105
|
+
fc.object({ maxLength: 5, maxDepth: 3 }),
|
|
106
|
+
fc.func(fc.string()),
|
|
107
|
+
async (input, resolve) => {
|
|
108
|
+
const snapshot = JSON.parse(JSON.stringify(input));
|
|
109
|
+
await substituteRefsDeep(input, resolve);
|
|
110
|
+
expect(input).toEqual(snapshot);
|
|
111
|
+
},
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Transitivity
|
|
118
|
+
|
|
119
|
+
`a < b and b < c implies a < c`.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
test("compareSemver is transitive: a < b and b < c implies a < c", () => {
|
|
123
|
+
fc.assert(
|
|
124
|
+
fc.property(
|
|
125
|
+
semverStringArbitrary,
|
|
126
|
+
semverStringArbitrary,
|
|
127
|
+
semverStringArbitrary,
|
|
128
|
+
(a, b, c) => {
|
|
129
|
+
if (compareSemver(a, b) < 0 && compareSemver(b, c) < 0) {
|
|
130
|
+
expect(compareSemver(a, c)).toBeLessThan(0);
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Antisymmetry
|
|
139
|
+
|
|
140
|
+
`f(a, b) == -f(b, a)`.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
test("compareSemver is antisymmetric: compare(a, b) == -compare(b, a)", () => {
|
|
144
|
+
fc.assert(
|
|
145
|
+
fc.property(semverStringArbitrary, semverStringArbitrary, (a, b) => {
|
|
146
|
+
expect(compareSemver(a, b)).toBe(-compareSemver(b, a));
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Other patterns
|
|
153
|
+
|
|
154
|
+
- **Commutativity:** `f(a, b) == f(b, a)` for all valid `a`, `b`.
|
|
155
|
+
- **Associativity:** `f(f(a, b), c) == f(a, f(b, c))` for all valid `a`, `b`, `c`.
|
|
156
|
+
- **Monotonicity:** `a <= b => f(a) <= f(b)` for all valid `a`, `b`.
|
|
157
|
+
- **Reflexivity:** `f(x, x) == 0` (or identity element).
|
|
158
|
+
- **Distributivity:** `f(a, g(b, c)) == g(f(a, b), f(a, c))` for all valid inputs.
|
|
159
|
+
- **Invariance:** `p(f(x)) == p(x)` for some invariant `p` and all valid `x`.
|
|
160
|
+
|
|
161
|
+
## Agent rules (MUST / MUST NOT)
|
|
162
|
+
|
|
163
|
+
- **MUST** use `vitest` as the test runner for all packages with real test scripts.
|
|
164
|
+
- **MUST** add `fast-check` as a dev dependency before writing PBT in a package.
|
|
165
|
+
- **MUST** place PBT in `*.pbt.test.ts` files (not in regular `*.test.ts` files).
|
|
166
|
+
- **MUST** use `fc.assert(fc.property(...))` or `fc.assert(fc.asyncProperty(...))` as the PBT idiom.
|
|
167
|
+
- **MUST** set a `numRuns` value when the default 100 is insufficient: `fc.assert(fc.property(...), { numRuns: 1000 })`.
|
|
168
|
+
- **MUST NOT** use PBT for impure functions, I/O-bound code, UI components, or build pipelines.
|
|
169
|
+
- **MUST NOT** replace existing example-based tests with PBT. PBT complements example-based tests.
|
|
170
|
+
- **MUST NOT** use `fast-check` in production code or runtime dependencies. It is dev-only.
|
|
171
|
+
- **MUST NOT** invent custom property names. Use the names from the catalog above.
|
|
172
|
+
- **MUST** write a comment above each property stating which algebraic property it verifies and why it holds.
|
|
173
|
+
- **MUST** ensure PBT tests are deterministic. If the property involves randomness, the random source must be the fast-check generator, not `Math.random()` or `Date.now()`.
|
|
174
|
+
- **MAY** use `fc.pre()` to constrain generated inputs to the function's valid domain.
|
|
175
|
+
- **MAY** create custom arbitraries with `fc.record()`, `fc.array()`, `fc.tuple()`, etc.
|
|
176
|
+
|
|
177
|
+
## Functions that qualify for PBT (non-exhaustive)
|
|
178
|
+
|
|
179
|
+
| Function | Package | Property | Pattern |
|
|
180
|
+
| --- | --- | --- | --- |
|
|
181
|
+
| `normalizeText` | `@warpgogol/share` | `normalize(normalize(x)) == normalize(x)` | Idempotency |
|
|
182
|
+
| `normalizeHtml` | `@warpgogol/share` | `normalize(normalize(x)) == normalize(x)` | Idempotency |
|
|
183
|
+
| `normalizeMarkdown` | `@warpgogol/share` | `normalize(normalize(x)) == normalize(x)` | Idempotency |
|
|
184
|
+
| `normalizeJson` | `@warpgogol/share` | `normalize(normalize(x)) == normalize(x)` | Idempotency |
|
|
185
|
+
| `compareSemver` | `@warpgogol/site-kernel-handoff` | `a < b and b < c implies a < c` | Transitivity |
|
|
186
|
+
| `compareSemver` | `@warpgogol/site-kernel-handoff` | `compare(a, b) == -compare(b, a)` | Antisymmetry |
|
|
187
|
+
| `parseSemver` / `formatSemver` | `@warpgogol/site-kernel-handoff` | `parse(format(parsed)) == parsed` | Round-trip |
|
|
188
|
+
| `substituteRefsDeep` | `@warpgogol/share` | Input is not mutated after call | Immutability |
|
|
189
|
+
| `markdownTwinRelPath` | `@warpgogol/share` | `twinUrlPath(twinRelPath(x))` is consistent for valid paths | Round-trip |
|
|
190
|
+
| `deepMergeEntryData` | `@warpgogol/share` | Merge is associative for nested objects (not arrays) | Associativity |
|
|
191
|
+
| `citySlug` | `@warpgogol/geo` | Output only contains lowercase alphanumeric and hyphens | Invariance |
|
|
192
|
+
| `redactUrl` | `@warpgogol/observability` | Output never contains a query string | Invariance |
|
|
193
|
+
| `validateAgainstCapabilitySchema` | `@warpgogol/agent-gate` | Non-object input always fails validation | Invariance |
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Test Reference — Good vs Bad
|
|
2
|
+
|
|
3
|
+
Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists — and survives refactors because it doesn't care about internal structure.
|
|
4
|
+
|
|
5
|
+
## Good: Tests observable behavior
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// GOOD: Tests observable behavior
|
|
9
|
+
test("user can checkout with valid cart", async () => {
|
|
10
|
+
const cart = createCart();
|
|
11
|
+
cart.add(product);
|
|
12
|
+
const result = await checkout(cart, paymentMethod);
|
|
13
|
+
expect(result.status).toBe("confirmed");
|
|
14
|
+
});
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Bad: Tests implementation details
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// BAD: Tests implementation details
|
|
21
|
+
test("checkout calls paymentService.process", async () => {
|
|
22
|
+
const mockPayment = vi.mock(paymentService);
|
|
23
|
+
await checkout(cart, payment);
|
|
24
|
+
expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This test breaks if you rename `paymentService.process`, switch to a different payment API, or inline the call — even if checkout still works perfectly. It tests _how_, not _what_.
|
|
29
|
+
|
|
30
|
+
## Bad: Tautological test
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// BAD: Expected value is recomputed the way the code computes it
|
|
34
|
+
test("calculateTotal sums line items", () => {
|
|
35
|
+
const items = [{ price: 10 }, { price: 5 }];
|
|
36
|
+
const expected = items.reduce((sum, i) => sum + i.price, 0);
|
|
37
|
+
expect(calculateTotal(items)).toBe(expected);
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If `calculateTotal` has a bug (e.g. off-by-one in the reduce), the test still passes — the expected value has the same bug. Use an independent known literal:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
// GOOD: Expected value is an independent, known literal
|
|
45
|
+
test("calculateTotal sums line items", () => {
|
|
46
|
+
expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Bad: Horizontal slicing
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// BAD: Tests only one layer, missing the integration
|
|
54
|
+
test("validateInput returns true for valid input", () => {
|
|
55
|
+
expect(validateInput("valid")).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("processData returns result for valid input", () => {
|
|
59
|
+
// Mocks validateInput, so the real validation is never tested
|
|
60
|
+
vi.mock("../validate").mockReturnValue(true);
|
|
61
|
+
const result = processData("invalid-but-mocked-valid");
|
|
62
|
+
expect(result).toBeDefined();
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The first test checks validation in isolation. The second mocks it out. If `validateInput` changes its rules, the second test still passes — but the integration is broken. Prefer a vertical slice that exercises the real validation through the public API.
|
|
67
|
+
|
|
68
|
+
## Good: Integration-style test through public API
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// GOOD: Exercises the full path through the public interface
|
|
72
|
+
test("processData rejects invalid input through the public API", () => {
|
|
73
|
+
const result = processData("invalid input here");
|
|
74
|
+
expect(result.ok).toBe(false);
|
|
75
|
+
expect(result.error).toMatch(/invalid/);
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
This test survives internal refactors — `validateInput` can be renamed, split, or inlined — as long as `processData` still rejects invalid input, the test passes.
|
|
80
|
+
|
|
81
|
+
## Test naming
|
|
82
|
+
|
|
83
|
+
Name tests as specifications, not as test cases:
|
|
84
|
+
|
|
85
|
+
- Good: `"user can checkout with valid cart"`
|
|
86
|
+
- Good: `"processData rejects invalid input"`
|
|
87
|
+
- Good: `"normalizeText is idempotent"`
|
|
88
|
+
- Bad: `"test1"`
|
|
89
|
+
- Bad: `"checkout test"`
|
|
90
|
+
- Bad: `"should work"`
|
|
91
|
+
|
|
92
|
+
The name is the specification. If you can't name what behavior you're testing, you don't know what you're testing.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fo-architecture
|
|
3
|
+
description: Scan a codebase for deepening opportunities, present as HTML report, then grill through the chosen one. Uses deep-module vocabulary.
|
|
4
|
+
invocation: user
|
|
5
|
+
category: fo
|
|
6
|
+
concerns: document-only
|
|
7
|
+
dependsOn: ['my-preferences', 'grilling']
|
|
8
|
+
languagePolicy: ref(PREFERENCES.md)
|
|
9
|
+
triggers: ["analyze codebase architecture", "find deepening opportunities", "review code structure quality"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# fo-architecture
|
|
13
|
+
|
|
14
|
+
Before starting, read `PREFERENCES.md` at the repository root. If the file is missing or `aiLanguage` is unset, ask the operator once and create the file using the `my-preferences` skill semantics.
|
|
15
|
+
|
|
16
|
+
Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
|
|
17
|
+
|
|
18
|
+
This skill uses the deep-module vocabulary from `architecture-vocabulary.md` (**module**, **interface**, **depth**, **seam**, **adapter**, **leverage**, **locality**). Use these terms exactly in every suggestion — don't drift into "component," "service," "API," or "boundary."
|
|
19
|
+
|
|
20
|
+
## Process
|
|
21
|
+
|
|
22
|
+
### 1. Explore
|
|
23
|
+
|
|
24
|
+
The scope of the review must be determined **explicitly** before any other action. Search for the package or repository root to review in this order:
|
|
25
|
+
|
|
26
|
+
1. **Prompt** — the operator's invocation arguments (a package path, a folder, or the repository root).
|
|
27
|
+
2. **Session context** — files open in the IDE or prior conversation context that clearly indicates which package to review.
|
|
28
|
+
3. **Stop** — if no scope is found, ask: "Which package or folder should I review? Provide a path like `packages/growth` or specify the repository root for a full review."
|
|
29
|
+
|
|
30
|
+
Read the project's domain glossary (`CONTEXT.md` or `UBIQUITOUS_LANGUAGE.md`) and any ADRs in the area you're touching first.
|
|
31
|
+
|
|
32
|
+
Then explore the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
|
|
33
|
+
|
|
34
|
+
- Where does understanding one concept require bouncing between many small modules?
|
|
35
|
+
- Where are modules **shallow** — interface nearly as complex as the implementation?
|
|
36
|
+
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
|
|
37
|
+
- Where do tightly-coupled modules leak across their seams?
|
|
38
|
+
- Which parts of the codebase are untested, or hard to test through their current interface?
|
|
39
|
+
|
|
40
|
+
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
|
|
41
|
+
|
|
42
|
+
### 2. Present candidates as an HTML report
|
|
43
|
+
|
|
44
|
+
Write a self-contained HTML file into `docs/reviews/architecture/`. Derive the subfolder name from the relative path of the reviewed module folder, converted to kebab-case and lowercased (for example, `packages/agent-gate` becomes `packages-agent-gate`). Name the file `arch-[YYYY-MM-DD]-[HH]-<short-desc>.html`.
|
|
45
|
+
|
|
46
|
+
If the review target is the repository root, use `root` as both the subfolder and the `<short-desc>`.
|
|
47
|
+
|
|
48
|
+
See `html-report.md` for the full HTML scaffold, diagram patterns, and styling guidance.
|
|
49
|
+
|
|
50
|
+
For each candidate, render a card with:
|
|
51
|
+
|
|
52
|
+
- **Files** — which files/modules are involved
|
|
53
|
+
- **Problem** — why the current architecture is causing friction
|
|
54
|
+
- **Solution** — plain English description of what would change
|
|
55
|
+
- **Benefits** — explained in terms of locality and leverage, and how tests would improve
|
|
56
|
+
- **Before / After diagram** — side-by-side, custom-drawn, illustrating the shallowness and the deepening
|
|
57
|
+
- **Recommendation strength** — one of `Strong`, `Worth exploring`, `Speculative`, rendered as a badge
|
|
58
|
+
|
|
59
|
+
End the report with a **Top recommendation** section: which candidate you'd tackle first and why.
|
|
60
|
+
|
|
61
|
+
Use domain glossary vocabulary for the domain, and the deep-module vocabulary for the architecture. If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler."
|
|
62
|
+
|
|
63
|
+
**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly in the card.
|
|
64
|
+
|
|
65
|
+
Do NOT propose interfaces yet. After the file is written, ask the operator: "Which of these would you like to explore?"
|
|
66
|
+
|
|
67
|
+
### 3. Grilling loop
|
|
68
|
+
|
|
69
|
+
Once the operator picks a candidate, run the `grilling` skill to walk the design tree — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
|
|
70
|
+
|
|
71
|
+
Side effects happen inline as decisions crystallize:
|
|
72
|
+
|
|
73
|
+
- **Naming a deepened module after a concept not in the domain glossary?** Add the term. Create the file lazily if it doesn't exist.
|
|
74
|
+
- **Sharpening a fuzzy term during the conversation?** Update the glossary right there.
|
|
75
|
+
- **Operator rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: "Want me to record this as an ADR so future architecture reviews don't re-suggest it?" Only offer when the reason would actually be needed by a future explorer.
|
|
76
|
+
- **Want to explore alternative interfaces for the deepened module?** Use the design-it-twice parallel sub-agent pattern from `design-it-twice.md`.
|
|
77
|
+
|
|
78
|
+
## Constraints
|
|
79
|
+
|
|
80
|
+
- **Document-only.** This skill produces HTML reports and domain glossary updates — it does not modify source code.
|
|
81
|
+
- **Use the deep-module vocabulary exactly.** Don't substitute "component," "service," "API," or "boundary." See `architecture-vocabulary.md`.
|
|
82
|
+
- **Commit only your own files.** Stage only the HTML report and glossary updates. See `_shared/fo-pipeline-conventions.md` §Commit discipline.
|
|
83
|
+
- **Session summary.** End every session with the closing block defined in `_shared/fo-session-summary.md`.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Architecture Vocabulary — Deep Modules
|
|
2
|
+
|
|
3
|
+
Design **deep modules**: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. Use this language and these principles wherever code is being designed or restructured. The aim is leverage for callers, locality for maintainers, and testability for everyone.
|
|
4
|
+
|
|
5
|
+
## Glossary
|
|
6
|
+
|
|
7
|
+
Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
|
|
8
|
+
|
|
9
|
+
**Module** — anything with an interface and an implementation. Deliberately scale-agnostic: a function, class, package, or tier-spanning slice. _Avoid_: unit, component, service.
|
|
10
|
+
|
|
11
|
+
**Interface** — everything a caller must know to use the module correctly: the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. _Avoid_: API, signature (too narrow — they refer only to the type-level surface).
|
|
12
|
+
|
|
13
|
+
**Implementation** — what's inside a module, its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
|
|
14
|
+
|
|
15
|
+
**Depth** — leverage at the interface: the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface, **shallow** when the interface is nearly as complex as the implementation.
|
|
16
|
+
|
|
17
|
+
**Seam** _(Michael Feathers)_ — a place where you can alter behaviour without editing in that place; the _location_ at which a module's interface lives. Where to put the seam is its own design decision, distinct from what goes behind it. _Avoid_: boundary (overloaded with DDD's bounded context).
|
|
18
|
+
|
|
19
|
+
**Adapter** — a concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside).
|
|
20
|
+
|
|
21
|
+
**Leverage** — what callers get from depth: more capability per unit of interface they learn. One implementation pays back across N call sites and M tests.
|
|
22
|
+
|
|
23
|
+
**Locality** — what maintainers get from depth: change, bugs, knowledge, and verification concentrate in one place rather than spreading across callers. Fix once, fixed everywhere.
|
|
24
|
+
|
|
25
|
+
## Deep vs shallow
|
|
26
|
+
|
|
27
|
+
**Deep module** = small interface + lots of implementation:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
┌─────────────────────┐
|
|
31
|
+
│ Small Interface │ ← Few methods, simple params
|
|
32
|
+
├─────────────────────┤
|
|
33
|
+
│ │
|
|
34
|
+
│ Deep Implementation│ ← Complex logic hidden
|
|
35
|
+
│ │
|
|
36
|
+
└─────────────────────┘
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Shallow module** = large interface + little implementation (avoid):
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌─────────────────────────────────┐
|
|
43
|
+
│ Large Interface │ ← Many methods, complex params
|
|
44
|
+
├─────────────────────────────────┤
|
|
45
|
+
│ Thin Implementation │ ← Just passes through
|
|
46
|
+
└─────────────────────────────────┘
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
When designing an interface, ask:
|
|
50
|
+
|
|
51
|
+
- Can I reduce the number of methods?
|
|
52
|
+
- Can I simplify the parameters?
|
|
53
|
+
- Can I hide more complexity inside?
|
|
54
|
+
|
|
55
|
+
## Principles
|
|
56
|
+
|
|
57
|
+
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
|
|
58
|
+
- **The deletion test.** Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
|
|
59
|
+
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape.
|
|
60
|
+
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
|
|
61
|
+
|
|
62
|
+
## Designing for testability
|
|
63
|
+
|
|
64
|
+
Good interfaces make testing natural:
|
|
65
|
+
|
|
66
|
+
1. **Accept dependencies, don't create them.**
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Testable
|
|
70
|
+
function processOrder(order, paymentGateway) {}
|
|
71
|
+
|
|
72
|
+
// Hard to test
|
|
73
|
+
function processOrder(order) {
|
|
74
|
+
const gateway = new StripeGateway();
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
2. **Return results, don't produce side effects.**
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// Testable
|
|
82
|
+
function calculateDiscount(cart): Discount {}
|
|
83
|
+
|
|
84
|
+
// Hard to test
|
|
85
|
+
function applyDiscount(cart): void {
|
|
86
|
+
cart.total -= discount;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
3. **Small surface area.** Fewer methods = fewer tests needed. Fewer params = simpler test setup.
|
|
91
|
+
|
|
92
|
+
## Relationships
|
|
93
|
+
|
|
94
|
+
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
|
|
95
|
+
- **Depth** is a property of a **Module**, measured against its **Interface**.
|
|
96
|
+
- A **Seam** is where a **Module**'s **Interface** lives.
|
|
97
|
+
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
|
|
98
|
+
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
|
|
99
|
+
|
|
100
|
+
## Rejected framings
|
|
101
|
+
|
|
102
|
+
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
|
|
103
|
+
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
|
|
104
|
+
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Deepening
|
|
2
|
+
|
|
3
|
+
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in `architecture-vocabulary.md` — **module**, **interface**, **seam**, **adapter**.
|
|
4
|
+
|
|
5
|
+
## Dependency categories
|
|
6
|
+
|
|
7
|
+
When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
|
|
8
|
+
|
|
9
|
+
### 1. In-process
|
|
10
|
+
|
|
11
|
+
Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.
|
|
12
|
+
|
|
13
|
+
### 2. Local-substitutable
|
|
14
|
+
|
|
15
|
+
Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
|
|
16
|
+
|
|
17
|
+
### 3. Remote but owned (Ports & Adapters)
|
|
18
|
+
|
|
19
|
+
Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
|
|
20
|
+
|
|
21
|
+
Recommendation shape: _"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."_
|
|
22
|
+
|
|
23
|
+
### 4. True external (Mock)
|
|
24
|
+
|
|
25
|
+
Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
|
|
26
|
+
|
|
27
|
+
## Seam discipline
|
|
28
|
+
|
|
29
|
+
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
|
|
30
|
+
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
|
|
31
|
+
|
|
32
|
+
## Testing strategy: replace, don't layer
|
|
33
|
+
|
|
34
|
+
- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
|
|
35
|
+
- Write new tests at the deepened module's interface. The **interface is the test surface**.
|
|
36
|
+
- Tests assert on observable outcomes through the interface, not internal state.
|
|
37
|
+
- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Design It Twice
|
|
2
|
+
|
|
3
|
+
When the operator wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
|
|
4
|
+
|
|
5
|
+
Uses the vocabulary in `architecture-vocabulary.md` — **module**, **interface**, **seam**, **adapter**, **leverage**.
|
|
6
|
+
|
|
7
|
+
## Process
|
|
8
|
+
|
|
9
|
+
### 1. Frame the problem space
|
|
10
|
+
|
|
11
|
+
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
|
|
12
|
+
|
|
13
|
+
- The constraints any new interface would need to satisfy
|
|
14
|
+
- The dependencies it would rely on, and which category they fall into (see `deepening.md`)
|
|
15
|
+
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
|
|
16
|
+
|
|
17
|
+
Show this to the operator, then immediately proceed to Step 2. The operator reads and thinks while the sub-agents work in parallel.
|
|
18
|
+
|
|
19
|
+
### 2. Spawn sub-agents
|
|
20
|
+
|
|
21
|
+
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
|
|
22
|
+
|
|
23
|
+
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from `deepening.md`, what sits behind the seam). Give each agent a different design constraint:
|
|
24
|
+
|
|
25
|
+
- Agent 1: "Minimize the interface — aim for 1–3 entry points max. Maximise leverage per entry point."
|
|
26
|
+
- Agent 2: "Maximise flexibility — support many use cases and extension."
|
|
27
|
+
- Agent 3: "Optimise for the most common caller — make the default case trivial."
|
|
28
|
+
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
|
|
29
|
+
|
|
30
|
+
Include both `architecture-vocabulary.md` vocabulary and domain glossary vocabulary in the brief so each sub-agent names things consistently.
|
|
31
|
+
|
|
32
|
+
Each sub-agent outputs:
|
|
33
|
+
|
|
34
|
+
1. Interface (types, methods, params — plus invariants, ordering, error modes)
|
|
35
|
+
2. Usage example showing how callers use it
|
|
36
|
+
3. What the implementation hides behind the seam
|
|
37
|
+
4. Dependency strategy and adapters (see `deepening.md`)
|
|
38
|
+
5. Trade-offs — where leverage is high, where it's thin
|
|
39
|
+
|
|
40
|
+
### 3. Present and compare
|
|
41
|
+
|
|
42
|
+
Present designs sequentially so the operator can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**.
|
|
43
|
+
|
|
44
|
+
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the operator wants a strong read, not a menu.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# HTML Report Format
|
|
2
|
+
|
|
3
|
+
The architectural review is rendered as a single self-contained HTML file in `docs/reviews/architecture/<kebab-case-module-path>/arch-[YYYY-MM-DD-HH].html` under the repository root. Tailwind and Mermaid both come from CDNs. Mermaid handles graph-shaped diagrams reliably; hand-built divs and inline SVG handle the more editorial visuals (mass diagrams, cross-sections). Mix the two — don't lean on Mermaid for everything, it'll start to look generic.
|
|
4
|
+
|
|
5
|
+
## Scaffold
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<!doctype html>
|
|
9
|
+
<html lang="en">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charset="utf-8" />
|
|
12
|
+
<title>Architecture review — {{repo name}}</title>
|
|
13
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
14
|
+
<script type="module">
|
|
15
|
+
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
16
|
+
mermaid.initialize({ startOnLoad: true, theme: "neutral", securityLevel: "loose" });
|
|
17
|
+
</script>
|
|
18
|
+
<style>
|
|
19
|
+
.seam { stroke-dasharray: 4 4; }
|
|
20
|
+
.leak { stroke: #dc2626; }
|
|
21
|
+
.deep { background: linear-gradient(135deg, #0f172a, #1e293b); }
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body class="bg-stone-50 text-slate-900 font-sans">
|
|
25
|
+
<main class="max-w-5xl mx-auto px-6 py-12 space-y-12">
|
|
26
|
+
<header>...</header>
|
|
27
|
+
<section id="candidates" class="space-y-10">...</section>
|
|
28
|
+
<section id="top-recommendation">...</section>
|
|
29
|
+
</main>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Header
|
|
35
|
+
|
|
36
|
+
Repo name, date, and a compact legend: solid box = module, dashed line = seam, red arrow = leakage, thick dark box = deep module. No introduction paragraph — straight into the candidates.
|
|
37
|
+
|
|
38
|
+
## Candidate card
|
|
39
|
+
|
|
40
|
+
Each candidate is one `<article>`:
|
|
41
|
+
|
|
42
|
+
- **Title** — short, names the deepening (e.g. "Collapse the Order intake pipeline").
|
|
43
|
+
- **Badge row** — recommendation strength (`Strong` = emerald, `Worth exploring` = amber, `Speculative` = slate), plus a tag for the dependency category (`in-process`, `local-substitutable`, `ports & adapters`, `mock`).
|
|
44
|
+
- **Files** — monospaced list, `font-mono text-sm`.
|
|
45
|
+
- **Before / After diagram** — the centrepiece. Two columns, side by side.
|
|
46
|
+
- **Problem** — one sentence. What hurts.
|
|
47
|
+
- **Solution** — one sentence. What changes.
|
|
48
|
+
- **Wins** — bullets, ≤6 words each.
|
|
49
|
+
- **ADR callout** (if applicable) — one line in an amber-tinted box.
|
|
50
|
+
|
|
51
|
+
No paragraphs of explanation. If the diagram needs a paragraph to be understood, redraw the diagram.
|
|
52
|
+
|
|
53
|
+
## Diagram patterns
|
|
54
|
+
|
|
55
|
+
### Mermaid graph (dependencies / call flow)
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<div class="rounded-lg border border-slate-200 bg-white p-4">
|
|
59
|
+
<pre class="mermaid">
|
|
60
|
+
flowchart LR
|
|
61
|
+
A[OrderHandler] --> B[OrderValidator]
|
|
62
|
+
B --> C[OrderRepo]
|
|
63
|
+
C -.leak.-> D[PricingClient]
|
|
64
|
+
classDef leak stroke:#dc2626,stroke-width:2px;
|
|
65
|
+
class C,D leak
|
|
66
|
+
</pre>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Hand-built boxes-and-arrows
|
|
71
|
+
|
|
72
|
+
Modules as `<div>`s with borders and labels. Arrows as inline SVG. Use when Mermaid's layout fights you.
|
|
73
|
+
|
|
74
|
+
### Cross-section (layered shallowness)
|
|
75
|
+
|
|
76
|
+
Stack horizontal bands to show layers. Before: 6 thin layers. After: 1 thick band.
|
|
77
|
+
|
|
78
|
+
### Mass diagram (interface vs implementation)
|
|
79
|
+
|
|
80
|
+
Two rectangles per module. Before: interface nearly as tall as implementation (shallow). After: interface short, implementation tall (deep).
|
|
81
|
+
|
|
82
|
+
### Call-graph collapse
|
|
83
|
+
|
|
84
|
+
Before: tree of calls as nested boxes. After: same tree collapsed into one box, internal calls faded.
|
|
85
|
+
|
|
86
|
+
## Style guidance
|
|
87
|
+
|
|
88
|
+
- Lean editorial, not corporate-dashboard. Generous whitespace.
|
|
89
|
+
- Colour sparingly: one accent plus red for leakage and amber for warnings.
|
|
90
|
+
- Keep diagrams ~320px tall for side-by-side viewing.
|
|
91
|
+
- Use `text-xs uppercase tracking-wider` for module labels.
|
|
92
|
+
- Only scripts: Tailwind CDN and Mermaid ESM import.
|
|
93
|
+
|
|
94
|
+
## Top recommendation section
|
|
95
|
+
|
|
96
|
+
One larger card. Candidate name, one sentence on why, anchor link to its card.
|
|
97
|
+
|
|
98
|
+
## Tone
|
|
99
|
+
|
|
100
|
+
Use exactly: module, interface, implementation, depth, deep, shallow, seam, adapter, leverage, locality.
|
|
101
|
+
|
|
102
|
+
Never substitute: component, service, unit (for module) · API, signature (for interface) · boundary (for seam) · layer, wrapper (for module).
|
|
103
|
+
|
|
104
|
+
Wins bullets name the gain in glossary terms: "locality: bugs concentrate in one module", "leverage: one interface, N call sites". Don't write "easier to maintain" or "cleaner code".
|