dorfl 0.0.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advance-ci-template.d.ts +73 -0
- package/dist/advance-ci-template.d.ts.map +1 -0
- package/dist/advance-ci-template.js +104 -0
- package/dist/advance-ci-template.js.map +1 -0
- package/dist/advance-classify.d.ts +132 -0
- package/dist/advance-classify.d.ts.map +1 -0
- package/dist/advance-classify.js +120 -0
- package/dist/advance-classify.js.map +1 -0
- package/dist/advance-drivers.d.ts +182 -0
- package/dist/advance-drivers.d.ts.map +1 -0
- package/dist/advance-drivers.js +231 -0
- package/dist/advance-drivers.js.map +1 -0
- package/dist/advance-isolated.d.ts +156 -0
- package/dist/advance-isolated.d.ts.map +1 -0
- package/dist/advance-isolated.js +256 -0
- package/dist/advance-isolated.js.map +1 -0
- package/dist/advance-lifecycle-template.d.ts +107 -0
- package/dist/advance-lifecycle-template.d.ts.map +1 -0
- package/dist/advance-lifecycle-template.js +668 -0
- package/dist/advance-lifecycle-template.js.map +1 -0
- package/dist/advance-loop-driver.d.ts +325 -0
- package/dist/advance-loop-driver.d.ts.map +1 -0
- package/dist/advance-loop-driver.js +437 -0
- package/dist/advance-loop-driver.js.map +1 -0
- package/dist/advance-treeless-publish.d.ts +108 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -0
- package/dist/advance-treeless-publish.js +71 -0
- package/dist/advance-treeless-publish.js.map +1 -0
- package/dist/advance.d.ts +340 -0
- package/dist/advance.d.ts.map +1 -0
- package/dist/advance.js +1122 -0
- package/dist/advance.js.map +1 -0
- package/dist/advancing-lock.d.ts +294 -0
- package/dist/advancing-lock.d.ts.map +1 -0
- package/dist/advancing-lock.js +594 -0
- package/dist/advancing-lock.js.map +1 -0
- package/dist/agent-launch.d.ts +79 -0
- package/dist/agent-launch.d.ts.map +1 -0
- package/dist/agent-launch.js +61 -0
- package/dist/agent-launch.js.map +1 -0
- package/dist/agent-stop.d.ts +149 -0
- package/dist/agent-stop.d.ts.map +1 -0
- package/dist/agent-stop.js +307 -0
- package/dist/agent-stop.js.map +1 -0
- package/dist/apply-decide.d.ts +127 -0
- package/dist/apply-decide.d.ts.map +1 -0
- package/dist/apply-decide.js +176 -0
- package/dist/apply-decide.js.map +1 -0
- package/dist/apply-merge-action.d.ts +206 -0
- package/dist/apply-merge-action.d.ts.map +1 -0
- package/dist/apply-merge-action.js +307 -0
- package/dist/apply-merge-action.js.map +1 -0
- package/dist/apply-persist.d.ts +174 -0
- package/dist/apply-persist.d.ts.map +1 -0
- package/dist/apply-persist.js +359 -0
- package/dist/apply-persist.js.map +1 -0
- package/dist/arbiter.d.ts +120 -0
- package/dist/arbiter.d.ts.map +1 -0
- package/dist/arbiter.js +255 -0
- package/dist/arbiter.js.map +1 -0
- package/dist/brand.d.ts +70 -0
- package/dist/brand.d.ts.map +1 -0
- package/dist/brand.js +84 -0
- package/dist/brand.js.map +1 -0
- package/dist/buildable-body.d.ts +132 -0
- package/dist/buildable-body.d.ts.map +1 -0
- package/dist/buildable-body.js +131 -0
- package/dist/buildable-body.js.map +1 -0
- package/dist/categorise.d.ts +66 -0
- package/dist/categorise.d.ts.map +1 -0
- package/dist/categorise.js +106 -0
- package/dist/categorise.js.map +1 -0
- package/dist/claim-cas.d.ts +117 -0
- package/dist/claim-cas.d.ts.map +1 -0
- package/dist/claim-cas.js +312 -0
- package/dist/claim-cas.js.map +1 -0
- package/dist/cli-spinner.d.ts +112 -0
- package/dist/cli-spinner.d.ts.map +1 -0
- package/dist/cli-spinner.js +157 -0
- package/dist/cli-spinner.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3100 -0
- package/dist/cli.js.map +1 -0
- package/dist/close-job-template.d.ts +70 -0
- package/dist/close-job-template.d.ts.map +1 -0
- package/dist/close-job-template.js +180 -0
- package/dist/close-job-template.js.map +1 -0
- package/dist/close-job.d.ts +95 -0
- package/dist/close-job.d.ts.map +1 -0
- package/dist/close-job.js +226 -0
- package/dist/close-job.js.map +1 -0
- package/dist/complete.d.ts +361 -0
- package/dist/complete.d.ts.map +1 -0
- package/dist/complete.js +885 -0
- package/dist/complete.js.map +1 -0
- package/dist/concurrency.d.ts +68 -0
- package/dist/concurrency.d.ts.map +1 -0
- package/dist/concurrency.js +112 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/config-override.d.ts +76 -0
- package/dist/config-override.d.ts.map +1 -0
- package/dist/config-override.js +50 -0
- package/dist/config-override.js.map +1 -0
- package/dist/config.d.ts +668 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +241 -0
- package/dist/config.js.map +1 -0
- package/dist/continue-branch.d.ts +249 -0
- package/dist/continue-branch.d.ts.map +1 -0
- package/dist/continue-branch.js +389 -0
- package/dist/continue-branch.js.map +1 -0
- package/dist/cwd-section.d.ts +186 -0
- package/dist/cwd-section.d.ts.map +1 -0
- package/dist/cwd-section.js +209 -0
- package/dist/cwd-section.js.map +1 -0
- package/dist/decision-engine.d.ts +170 -0
- package/dist/decision-engine.d.ts.map +1 -0
- package/dist/decision-engine.js +136 -0
- package/dist/decision-engine.js.map +1 -0
- package/dist/detect.d.ts +17 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +118 -0
- package/dist/detect.js.map +1 -0
- package/dist/do-autopick.d.ts +85 -0
- package/dist/do-autopick.d.ts.map +1 -0
- package/dist/do-autopick.js +112 -0
- package/dist/do-autopick.js.map +1 -0
- package/dist/do-config.d.ts +312 -0
- package/dist/do-config.d.ts.map +1 -0
- package/dist/do-config.js +358 -0
- package/dist/do-config.js.map +1 -0
- package/dist/do-remote-auto.d.ts +75 -0
- package/dist/do-remote-auto.d.ts.map +1 -0
- package/dist/do-remote-auto.js +111 -0
- package/dist/do-remote-auto.js.map +1 -0
- package/dist/do.d.ts +621 -0
- package/dist/do.d.ts.map +1 -0
- package/dist/do.js +1882 -0
- package/dist/do.js.map +1 -0
- package/dist/drop-source.d.ts +96 -0
- package/dist/drop-source.d.ts.map +1 -0
- package/dist/drop-source.js +91 -0
- package/dist/drop-source.js.map +1 -0
- package/dist/eligibility.d.ts +46 -0
- package/dist/eligibility.d.ts.map +1 -0
- package/dist/eligibility.js +34 -0
- package/dist/eligibility.js.map +1 -0
- package/dist/env-config.d.ts +51 -0
- package/dist/env-config.d.ts.map +1 -0
- package/dist/env-config.js +272 -0
- package/dist/env-config.js.map +1 -0
- package/dist/failure-cause.d.ts +70 -0
- package/dist/failure-cause.d.ts.map +1 -0
- package/dist/failure-cause.js +126 -0
- package/dist/failure-cause.js.map +1 -0
- package/dist/format.d.ts +43 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +256 -0
- package/dist/format.js.map +1 -0
- package/dist/frontmatter.d.ts +208 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +344 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/gate-readiness.d.ts +84 -0
- package/dist/gate-readiness.d.ts.map +1 -0
- package/dist/gate-readiness.js +103 -0
- package/dist/gate-readiness.js.map +1 -0
- package/dist/gc.d.ts +165 -0
- package/dist/gc.d.ts.map +1 -0
- package/dist/gc.js +313 -0
- package/dist/gc.js.map +1 -0
- package/dist/gh-failure.d.ts +42 -0
- package/dist/gh-failure.d.ts.map +1 -0
- package/dist/gh-failure.js +49 -0
- package/dist/gh-failure.js.map +1 -0
- package/dist/git.d.ts +75 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +130 -0
- package/dist/git.js.map +1 -0
- package/dist/github.d.ts +187 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +343 -0
- package/dist/github.js.map +1 -0
- package/dist/harness.d.ts +242 -0
- package/dist/harness.d.ts.map +1 -0
- package/dist/harness.js +157 -0
- package/dist/harness.js.map +1 -0
- package/dist/identity.d.ts +167 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +231 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +147 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/install-ci-branch-protection.d.ts +147 -0
- package/dist/install-ci-branch-protection.d.ts.map +1 -0
- package/dist/install-ci-branch-protection.js +166 -0
- package/dist/install-ci-branch-protection.js.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts +15 -0
- package/dist/install-ci-capabilities/advance-lifecycle.d.ts.map +1 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js +28 -0
- package/dist/install-ci-capabilities/advance-lifecycle.js.map +1 -0
- package/dist/install-ci-capabilities/close-job.d.ts +13 -0
- package/dist/install-ci-capabilities/close-job.d.ts.map +1 -0
- package/dist/install-ci-capabilities/close-job.js +26 -0
- package/dist/install-ci-capabilities/close-job.js.map +1 -0
- package/dist/install-ci-capabilities/example-noop.d.ts +16 -0
- package/dist/install-ci-capabilities/example-noop.d.ts.map +1 -0
- package/dist/install-ci-capabilities/example-noop.js +23 -0
- package/dist/install-ci-capabilities/example-noop.js.map +1 -0
- package/dist/install-ci-capabilities/intake.d.ts +15 -0
- package/dist/install-ci-capabilities/intake.d.ts.map +1 -0
- package/dist/install-ci-capabilities/intake.js +28 -0
- package/dist/install-ci-capabilities/intake.js.map +1 -0
- package/dist/install-ci-capabilities/verify.d.ts +14 -0
- package/dist/install-ci-capabilities/verify.d.ts.map +1 -0
- package/dist/install-ci-capabilities/verify.js +27 -0
- package/dist/install-ci-capabilities/verify.js.map +1 -0
- package/dist/install-ci-core.d.ts +446 -0
- package/dist/install-ci-core.d.ts.map +1 -0
- package/dist/install-ci-core.js +760 -0
- package/dist/install-ci-core.js.map +1 -0
- package/dist/install-ci-github.d.ts +167 -0
- package/dist/install-ci-github.d.ts.map +1 -0
- package/dist/install-ci-github.js +315 -0
- package/dist/install-ci-github.js.map +1 -0
- package/dist/install-ci.d.ts +105 -0
- package/dist/install-ci.d.ts.map +1 -0
- package/dist/install-ci.js +363 -0
- package/dist/install-ci.js.map +1 -0
- package/dist/intake-event.d.ts +88 -0
- package/dist/intake-event.d.ts.map +1 -0
- package/dist/intake-event.js +66 -0
- package/dist/intake-event.js.map +1 -0
- package/dist/intake-marker.d.ts +95 -0
- package/dist/intake-marker.d.ts.map +1 -0
- package/dist/intake-marker.js +127 -0
- package/dist/intake-marker.js.map +1 -0
- package/dist/intake-triage.d.ts +48 -0
- package/dist/intake-triage.d.ts.map +1 -0
- package/dist/intake-triage.js +95 -0
- package/dist/intake-triage.js.map +1 -0
- package/dist/intake-trigger-template.d.ts +185 -0
- package/dist/intake-trigger-template.d.ts.map +1 -0
- package/dist/intake-trigger-template.js +449 -0
- package/dist/intake-trigger-template.js.map +1 -0
- package/dist/intake.d.ts +569 -0
- package/dist/intake.d.ts.map +1 -0
- package/dist/intake.js +1628 -0
- package/dist/intake.js.map +1 -0
- package/dist/integration-core.d.ts +539 -0
- package/dist/integration-core.d.ts.map +1 -0
- package/dist/integration-core.js +2195 -0
- package/dist/integration-core.js.map +1 -0
- package/dist/integrator.d.ts +343 -0
- package/dist/integrator.d.ts.map +1 -0
- package/dist/integrator.js +400 -0
- package/dist/integrator.js.map +1 -0
- package/dist/isolation.d.ts +219 -0
- package/dist/isolation.d.ts.map +1 -0
- package/dist/isolation.js +261 -0
- package/dist/isolation.js.map +1 -0
- package/dist/issue-provider.d.ts +349 -0
- package/dist/issue-provider.d.ts.map +1 -0
- package/dist/issue-provider.js +360 -0
- package/dist/issue-provider.js.map +1 -0
- package/dist/item-lock.d.ts +626 -0
- package/dist/item-lock.d.ts.map +1 -0
- package/dist/item-lock.js +1381 -0
- package/dist/item-lock.js.map +1 -0
- package/dist/item-path.d.ts +49 -0
- package/dist/item-path.d.ts.map +1 -0
- package/dist/item-path.js +66 -0
- package/dist/item-path.js.map +1 -0
- package/dist/ledger-lint.d.ts +129 -0
- package/dist/ledger-lint.d.ts.map +1 -0
- package/dist/ledger-lint.js +249 -0
- package/dist/ledger-lint.js.map +1 -0
- package/dist/ledger-read.d.ts +357 -0
- package/dist/ledger-read.d.ts.map +1 -0
- package/dist/ledger-read.js +442 -0
- package/dist/ledger-read.js.map +1 -0
- package/dist/ledger-write.d.ts +330 -0
- package/dist/ledger-write.d.ts.map +1 -0
- package/dist/ledger-write.js +411 -0
- package/dist/ledger-write.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +30 -0
- package/dist/lifecycle-gather.d.ts.map +1 -0
- package/dist/lifecycle-gather.js +205 -0
- package/dist/lifecycle-gather.js.map +1 -0
- package/dist/lifecycle-pools.d.ts +180 -0
- package/dist/lifecycle-pools.d.ts.map +1 -0
- package/dist/lifecycle-pools.js +78 -0
- package/dist/lifecycle-pools.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +166 -0
- package/dist/merge-question-surfacer.d.ts.map +1 -0
- package/dist/merge-question-surfacer.js +297 -0
- package/dist/merge-question-surfacer.js.map +1 -0
- package/dist/mint-adr.d.ts +126 -0
- package/dist/mint-adr.d.ts.map +1 -0
- package/dist/mint-adr.js +257 -0
- package/dist/mint-adr.js.map +1 -0
- package/dist/mirror-pool-scan.d.ts +125 -0
- package/dist/mirror-pool-scan.d.ts.map +1 -0
- package/dist/mirror-pool-scan.js +104 -0
- package/dist/mirror-pool-scan.js.map +1 -0
- package/dist/needs-attention.d.ts +341 -0
- package/dist/needs-attention.d.ts.map +1 -0
- package/dist/needs-attention.js +900 -0
- package/dist/needs-attention.js.map +1 -0
- package/dist/orphan-sidecar.d.ts +79 -0
- package/dist/orphan-sidecar.d.ts.map +1 -0
- package/dist/orphan-sidecar.js +71 -0
- package/dist/orphan-sidecar.js.map +1 -0
- package/dist/output.d.ts +48 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +66 -0
- package/dist/output.js.map +1 -0
- package/dist/pi-harness.d.ts +179 -0
- package/dist/pi-harness.d.ts.map +1 -0
- package/dist/pi-harness.js +342 -0
- package/dist/pi-harness.js.map +1 -0
- package/dist/placement.d.ts +99 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +67 -0
- package/dist/placement.js.map +1 -0
- package/dist/prd-to-spec.d.ts +329 -0
- package/dist/prd-to-spec.d.ts.map +1 -0
- package/dist/prd-to-spec.js +706 -0
- package/dist/prd-to-spec.js.map +1 -0
- package/dist/prepare.d.ts +121 -0
- package/dist/prepare.d.ts.map +1 -0
- package/dist/prepare.js +140 -0
- package/dist/prepare.js.map +1 -0
- package/dist/prompt.d.ts +363 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +499 -0
- package/dist/prompt.js.map +1 -0
- package/dist/protocol/ADR-FORMAT.md +47 -0
- package/dist/protocol/CLAIM-PROTOCOL.md +217 -0
- package/dist/protocol/REVIEW-PROTOCOL.md +119 -0
- package/dist/protocol/SURFACE-PROTOCOL.md +121 -0
- package/dist/protocol/TASKING-PROTOCOL.md +122 -0
- package/dist/protocol/WORK-CONTRACT.md +276 -0
- package/dist/protocol/spec-template.md +71 -0
- package/dist/protocol/task-template.md +65 -0
- package/dist/readiness.d.ts +66 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +36 -0
- package/dist/readiness.js.map +1 -0
- package/dist/reap-branches.d.ts +102 -0
- package/dist/reap-branches.d.ts.map +1 -0
- package/dist/reap-branches.js +149 -0
- package/dist/reap-branches.js.map +1 -0
- package/dist/recover-isolated.d.ts +72 -0
- package/dist/recover-isolated.d.ts.map +1 -0
- package/dist/recover-isolated.js +188 -0
- package/dist/recover-isolated.js.map +1 -0
- package/dist/registry.d.ts +172 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +296 -0
- package/dist/registry.js.map +1 -0
- package/dist/repo-config.d.ts +201 -0
- package/dist/repo-config.d.ts.map +1 -0
- package/dist/repo-config.js +414 -0
- package/dist/repo-config.js.map +1 -0
- package/dist/repo-key.d.ts +20 -0
- package/dist/repo-key.d.ts.map +1 -0
- package/dist/repo-key.js +68 -0
- package/dist/repo-key.js.map +1 -0
- package/dist/repo-mirror.d.ts +177 -0
- package/dist/repo-mirror.d.ts.map +1 -0
- package/dist/repo-mirror.js +271 -0
- package/dist/repo-mirror.js.map +1 -0
- package/dist/retry-backoff.d.ts +90 -0
- package/dist/retry-backoff.d.ts.map +1 -0
- package/dist/retry-backoff.js +98 -0
- package/dist/retry-backoff.js.map +1 -0
- package/dist/review-gate.d.ts +173 -0
- package/dist/review-gate.d.ts.map +1 -0
- package/dist/review-gate.js +261 -0
- package/dist/review-gate.js.map +1 -0
- package/dist/review-verdict.d.ts +149 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +332 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/run.d.ts +221 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +963 -0
- package/dist/run.js.map +1 -0
- package/dist/scan.d.ts +308 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +374 -0
- package/dist/scan.js.map +1 -0
- package/dist/select-order.d.ts +75 -0
- package/dist/select-order.d.ts.map +1 -0
- package/dist/select-order.js +108 -0
- package/dist/select-order.js.map +1 -0
- package/dist/select-priority.d.ts +188 -0
- package/dist/select-priority.d.ts.map +1 -0
- package/dist/select-priority.js +80 -0
- package/dist/select-priority.js.map +1 -0
- package/dist/select.d.ts +25 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +43 -0
- package/dist/select.js.map +1 -0
- package/dist/session-path.d.ts +36 -0
- package/dist/session-path.d.ts.map +1 -0
- package/dist/session-path.js +129 -0
- package/dist/session-path.js.map +1 -0
- package/dist/sidecar-apply.d.ts +83 -0
- package/dist/sidecar-apply.d.ts.map +1 -0
- package/dist/sidecar-apply.js +111 -0
- package/dist/sidecar-apply.js.map +1 -0
- package/dist/sidecar.d.ts +245 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +481 -0
- package/dist/sidecar.js.map +1 -0
- package/dist/slug-namespace.d.ts +204 -0
- package/dist/slug-namespace.d.ts.map +1 -0
- package/dist/slug-namespace.js +229 -0
- package/dist/slug-namespace.js.map +1 -0
- package/dist/spec-complete.d.ts +44 -0
- package/dist/spec-complete.d.ts.map +1 -0
- package/dist/spec-complete.js +69 -0
- package/dist/spec-complete.js.map +1 -0
- package/dist/start.d.ts +97 -0
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +633 -0
- package/dist/start.js.map +1 -0
- package/dist/status.d.ts +199 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +228 -0
- package/dist/status.js.map +1 -0
- package/dist/surface-gate.d.ts +162 -0
- package/dist/surface-gate.d.ts.map +1 -0
- package/dist/surface-gate.js +206 -0
- package/dist/surface-gate.js.map +1 -0
- package/dist/surface-persist.d.ts +86 -0
- package/dist/surface-persist.d.ts.map +1 -0
- package/dist/surface-persist.js +129 -0
- package/dist/surface-persist.js.map +1 -0
- package/dist/tasker-review-loop.d.ts +249 -0
- package/dist/tasker-review-loop.d.ts.map +1 -0
- package/dist/tasker-review-loop.js +369 -0
- package/dist/tasker-review-loop.js.map +1 -0
- package/dist/tasking-eligibility.d.ts +74 -0
- package/dist/tasking-eligibility.d.ts.map +1 -0
- package/dist/tasking-eligibility.js +52 -0
- package/dist/tasking-eligibility.js.map +1 -0
- package/dist/tasking-lock.d.ts +111 -0
- package/dist/tasking-lock.d.ts.map +1 -0
- package/dist/tasking-lock.js +256 -0
- package/dist/tasking-lock.js.map +1 -0
- package/dist/tasking.d.ts +275 -0
- package/dist/tasking.d.ts.map +1 -0
- package/dist/tasking.js +951 -0
- package/dist/tasking.js.map +1 -0
- package/dist/triage-gate.d.ts +127 -0
- package/dist/triage-gate.d.ts.map +1 -0
- package/dist/triage-gate.js +139 -0
- package/dist/triage-gate.js.map +1 -0
- package/dist/triage-persist.d.ts +163 -0
- package/dist/triage-persist.d.ts.map +1 -0
- package/dist/triage-persist.js +387 -0
- package/dist/triage-persist.js.map +1 -0
- package/dist/verdict-json.d.ts +32 -0
- package/dist/verdict-json.d.ts.map +1 -0
- package/dist/verdict-json.js +74 -0
- package/dist/verdict-json.js.map +1 -0
- package/dist/verify-workflow-template.d.ts +60 -0
- package/dist/verify-workflow-template.d.ts.map +1 -0
- package/dist/verify-workflow-template.js +126 -0
- package/dist/verify-workflow-template.js.map +1 -0
- package/dist/verify.d.ts +60 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +62 -0
- package/dist/verify.js.map +1 -0
- package/dist/watch-session.d.ts +112 -0
- package/dist/watch-session.d.ts.map +1 -0
- package/dist/watch-session.js +347 -0
- package/dist/watch-session.js.map +1 -0
- package/dist/work-layout.d.ts +198 -0
- package/dist/work-layout.d.ts.map +1 -0
- package/dist/work-layout.js +217 -0
- package/dist/work-layout.js.map +1 -0
- package/dist/work-on.d.ts +154 -0
- package/dist/work-on.d.ts.map +1 -0
- package/dist/work-on.js +387 -0
- package/dist/work-on.js.map +1 -0
- package/dist/workspace.d.ts +224 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +325 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +46 -2
- package/src/advance-ci-template.ts +203 -0
- package/src/advance-classify.ts +197 -0
- package/src/advance-drivers.ts +414 -0
- package/src/advance-isolated.ts +432 -0
- package/src/advance-lifecycle-template.ts +791 -0
- package/src/advance-loop-driver.ts +745 -0
- package/src/advance-treeless-publish.ts +177 -0
- package/src/advance.ts +1564 -0
- package/src/advancing-lock.ts +988 -0
- package/src/agent-launch.ts +137 -0
- package/src/agent-stop.ts +361 -0
- package/src/apply-decide.ts +242 -0
- package/src/apply-merge-action.ts +502 -0
- package/src/apply-persist.ts +518 -0
- package/src/arbiter.ts +372 -0
- package/src/brand.ts +111 -0
- package/src/buildable-body.ts +196 -0
- package/src/categorise.ts +158 -0
- package/src/claim-cas.ts +513 -0
- package/src/cli-spinner.ts +225 -0
- package/src/cli.ts +4379 -0
- package/src/close-job-template.ts +236 -0
- package/src/close-job.ts +319 -0
- package/src/complete.ts +1379 -0
- package/src/concurrency.ts +151 -0
- package/src/config-override.ts +116 -0
- package/src/config.ts +883 -0
- package/src/continue-branch.ts +542 -0
- package/src/cwd-section.ts +392 -0
- package/src/decision-engine.ts +272 -0
- package/src/detect.ts +124 -0
- package/src/do-autopick.ts +223 -0
- package/src/do-config.ts +589 -0
- package/src/do-remote-auto.ts +197 -0
- package/src/do.ts +2623 -0
- package/src/drop-source.ts +194 -0
- package/src/eligibility.ts +79 -0
- package/src/env-config.ts +305 -0
- package/src/failure-cause.ts +142 -0
- package/src/format.ts +313 -0
- package/src/frontmatter.ts +477 -0
- package/src/gate-readiness.ts +147 -0
- package/src/gc.ts +510 -0
- package/src/gh-failure.ts +53 -0
- package/src/git.ts +186 -0
- package/src/github.ts +468 -0
- package/src/harness.ts +355 -0
- package/src/identity.ts +322 -0
- package/src/index.ts +785 -0
- package/src/install-ci-branch-protection.ts +255 -0
- package/src/install-ci-capabilities/advance-lifecycle.ts +34 -0
- package/src/install-ci-capabilities/close-job.ts +32 -0
- package/src/install-ci-capabilities/example-noop.ts +24 -0
- package/src/install-ci-capabilities/intake.ts +34 -0
- package/src/install-ci-capabilities/verify.ts +33 -0
- package/src/install-ci-core.ts +1088 -0
- package/src/install-ci-github.ts +376 -0
- package/src/install-ci.ts +552 -0
- package/src/intake-event.ts +102 -0
- package/src/intake-marker.ts +195 -0
- package/src/intake-triage.ts +138 -0
- package/src/intake-trigger-template.ts +591 -0
- package/src/intake.ts +2445 -0
- package/src/integration-core.ts +3065 -0
- package/src/integrator.ts +771 -0
- package/src/isolation.ts +484 -0
- package/src/issue-provider.ts +733 -0
- package/src/item-lock.ts +1858 -0
- package/src/item-path.ts +75 -0
- package/src/ledger-lint.ts +332 -0
- package/src/ledger-read.ts +924 -0
- package/src/ledger-write.ts +865 -0
- package/src/lifecycle-gather.ts +298 -0
- package/src/lifecycle-pools.ts +250 -0
- package/src/merge-question-surfacer.ts +496 -0
- package/src/mint-adr.ts +362 -0
- package/src/mirror-pool-scan.ts +240 -0
- package/src/needs-attention.ts +1506 -0
- package/src/orphan-sidecar.ts +150 -0
- package/src/output.ts +89 -0
- package/src/pi-harness.ts +403 -0
- package/src/placement.ts +131 -0
- package/src/prd-to-spec.ts +1059 -0
- package/src/prepare.ts +230 -0
- package/src/prompt.ts +763 -0
- package/src/readiness.ts +98 -0
- package/src/reap-branches.ts +278 -0
- package/src/recover-isolated.ts +276 -0
- package/src/registry.ts +475 -0
- package/src/repo-config.ts +550 -0
- package/src/repo-key.ts +74 -0
- package/src/repo-mirror.ts +367 -0
- package/src/retry-backoff.ts +130 -0
- package/src/review-gate.ts +389 -0
- package/src/review-verdict.ts +422 -0
- package/src/run.ts +1430 -0
- package/src/scan.ts +611 -0
- package/src/select-order.ts +143 -0
- package/src/select-priority.ts +266 -0
- package/src/select.ts +62 -0
- package/src/session-path.ts +153 -0
- package/src/sidecar-apply.ts +216 -0
- package/src/sidecar.ts +700 -0
- package/src/slug-namespace.ts +367 -0
- package/src/spec-complete.ts +118 -0
- package/src/start.ts +974 -0
- package/src/status.ts +441 -0
- package/src/surface-gate.ts +337 -0
- package/src/surface-persist.ts +241 -0
- package/src/tasker-review-loop.ts +671 -0
- package/src/tasking-eligibility.ts +114 -0
- package/src/tasking-lock.ts +416 -0
- package/src/tasking.ts +1437 -0
- package/src/triage-gate.ts +248 -0
- package/src/triage-persist.ts +570 -0
- package/src/verdict-json.ts +73 -0
- package/src/verify-workflow-template.ts +159 -0
- package/src/verify.ts +123 -0
- package/src/watch-session.ts +397 -0
- package/src/work-layout.ts +262 -0
- package/src/work-on.ts +660 -0
- package/src/workspace.ts +502 -0
package/src/sidecar.ts
ADDED
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
import {parseSlugArg, type SlugNamespace} from './slug-namespace.js';
|
|
2
|
+
import {workItemRel} from './work-layout.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The **question/answer SIDECAR contract** (prd `advance-loop`, task
|
|
6
|
+
* `advance-sidecar-contract`) — the one genuinely-new piece of the `advance`
|
|
7
|
+
* family. A strict, tooling-OWNED per-item file `work/questions/<type>-<slug>.md`
|
|
8
|
+
* carrying per-entry answered-state, so the tooling fully owns the Q&A artifact
|
|
9
|
+
* (no fragile round-trip parse of human-authored prose in the item body).
|
|
10
|
+
*
|
|
11
|
+
* This module is the FORMAT + the in-memory model + the pure read/write/append/
|
|
12
|
+
* derived-`allAnswered` operations. The ATOMIC apply (mutate the item body AND
|
|
13
|
+
* the sidecar in ONE commit) lives in {@link applyAtomic} (`sidecar-apply.ts`).
|
|
14
|
+
* It builds NOTHING above the contract — no tick, no verb, no lock, no rungs;
|
|
15
|
+
* later tasks of the family consume these.
|
|
16
|
+
*
|
|
17
|
+
* # On-disk text format (REFORMATTED — ADR `question-sidecar-human-readable-format`)
|
|
18
|
+
*
|
|
19
|
+
* The sidecar is the HUMAN-FACING surface of the answer loop: humans read the
|
|
20
|
+
* question and write the answer in this file, very often through the GitHub web
|
|
21
|
+
* UI. The original `key: |` YAML-block-scalar format rendered as run-together
|
|
22
|
+
* noise on GitHub (literal `|` pipes, collapsed indentation). The on-disk format
|
|
23
|
+
* is now **real Markdown with machine state in HTML comments**:
|
|
24
|
+
*
|
|
25
|
+
* - An IDENTITY HTML comment at the top carries `item`/`type`/`slug`/
|
|
26
|
+
* `allAnswered` (`<!-- dorfl-sidecar: item=... type=... slug=... allAnswered=... -->`).
|
|
27
|
+
* - Per entry: a `## Qn` heading (entry separator + answer boundary), a BOLD
|
|
28
|
+
* question line, a BLOCKQUOTE context, an ITALIC suggested-default
|
|
29
|
+
* (`_Suggested default: …_`), a per-entry HTML COMMENT carrying
|
|
30
|
+
* `id=…` (and an explicit `answered=…` OVERRIDE when it disagrees with the
|
|
31
|
+
* answer-derived predicate), a fixed answer marker
|
|
32
|
+
* `**Your answer** (write below this line):`, then the human's answer prose.
|
|
33
|
+
* (The `disposition=…` field is RETIRED — task
|
|
34
|
+
* `agentic-apply-retire-disposition-vocabulary`; an entry is binary, and a
|
|
35
|
+
* stale `disposition=` token in an old sidecar is parsed away.)
|
|
36
|
+
* - The answer is HEADING-DELIMITED: it spans from the answer marker up to
|
|
37
|
+
* the next entry `## ` heading (NOT a `---` rule), so a literal `---`
|
|
38
|
+
* inside an answer cannot break parsing.
|
|
39
|
+
*
|
|
40
|
+
* GitHub renders HTML comments as nothing, so the machine state is invisible to
|
|
41
|
+
* the human and unbreakable by their edit. The human just types prose under the
|
|
42
|
+
* answer marker with no format knowledge.
|
|
43
|
+
*
|
|
44
|
+
* Round-trip is **SEMANTIC** (model-equal, not byte-equal): a parse → serialise
|
|
45
|
+
* canonicalises whitespace; the MODEL (entries, ids, answers, answered-state) is
|
|
46
|
+
* preserved. The load-bearing rules are kept:
|
|
47
|
+
*
|
|
48
|
+
* - **Identity-keyed, NOT folder-keyed.** The path is derived PURELY from the
|
|
49
|
+
* item's NAMESPACED identity (`<type>-<slug>`, `:`→`-` for the filename),
|
|
50
|
+
* using {@link parseSlugArg} (the `slug-namespace.ts` resolver) as the single
|
|
51
|
+
* source of truth for the identity. There is NO back-pointer field in the
|
|
52
|
+
* item body — the only in-body signal is the existing `needsAnswers` flag —
|
|
53
|
+
* so the sidecar survives the item's `git mv`s between lifecycle folders
|
|
54
|
+
* with no lock-step move.
|
|
55
|
+
* - **The answered predicate (MAINTAINER-RESOLVED §1):** a non-empty
|
|
56
|
+
* `answer` ⇒ ANSWERED, with an explicit `answered=…` HTML-comment field as
|
|
57
|
+
* an OVERRIDE. The override is encoded in the per-entry HTML comment ONLY
|
|
58
|
+
* when it DISAGREES with the derived predicate (an empty answer with
|
|
59
|
+
* `answered=true`, or a non-empty answer with `answered=false`); otherwise
|
|
60
|
+
* the field is omitted so a tolerant "human only types under the answer
|
|
61
|
+
* marker" edit cannot be re-interpreted as a sticky override on the next
|
|
62
|
+
* parse.
|
|
63
|
+
* - **`allAnswered` is DERIVED** — recomputed from the entries on every
|
|
64
|
+
* serialise; the classifier MAY read it for a cheap scan but MUST NOT trust
|
|
65
|
+
* it over the entries.
|
|
66
|
+
* - **Entry ids are stable + monotonic** (`q1`, `q2`, …), NEVER reused.
|
|
67
|
+
* APPEND adds `qN+1` and never mutates an existing answered entry (the
|
|
68
|
+
* sidecar is the item's full Q&A history).
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The item-types a sidecar can key onto (the slug-namespace + obs).
|
|
73
|
+
*
|
|
74
|
+
* HARD CUTOVER (spec `prd-to-spec-vocabulary-cutover-and-migration-command`,
|
|
75
|
+
* contract step): the legacy `'prd'` type member is GONE — the parent-spec type
|
|
76
|
+
* is `'spec'` only. A `spec:<slug>` identity keys onto the `spec-<slug>`
|
|
77
|
+
* lock/sidecar entry. The on-disk `prd-<slug>.md` sidecar FILE (dorfl's
|
|
78
|
+
* not-yet-converted data) is still probed by {@link sidecarPathCandidates} as a
|
|
79
|
+
* legacy file-path fallback — that is a DATA alias the migration command removes,
|
|
80
|
+
* NOT this type member.
|
|
81
|
+
*/
|
|
82
|
+
export type SidecarType = 'spec' | 'task' | 'observation';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The optional, machine-only `kind` AXIS on a question entry — the dispatch
|
|
86
|
+
* signal the answered-question apply rung reads to choose deterministic-action
|
|
87
|
+
* (`merge`, `stuck`) vs agentic-content (`triage`, `spec`) routing WITHOUT
|
|
88
|
+
* sniffing the shape of another field. Absent ⇒ the existing binary content
|
|
89
|
+
* entry (every pre-`kind` sidecar parses + renders byte-identically).
|
|
90
|
+
*
|
|
91
|
+
* INTERIM PRIMITIVE — REMOVE when question sidecars move to KIND-BASED
|
|
92
|
+
* SUBFOLDERS (`work/questions/merge/`, …), where the folder ENCODES the kind
|
|
93
|
+
* and this per-entry field is redundant. See the observation
|
|
94
|
+
* `questions-folder-rename-and-kind-axis-prefix-vs-subfolder-2026-06-21` + idea
|
|
95
|
+
* `folder-taxonomy-and-prd-edit-handshake`. Built deliberately as a single
|
|
96
|
+
* typed field read in exactly ONE place (the apply dispatch, a later task) so
|
|
97
|
+
* the folder-cutover can DELETE it in one move.
|
|
98
|
+
*
|
|
99
|
+
* A mistyped/unknown `kind=` token parses to `undefined` (silent-on-malformed,
|
|
100
|
+
* mirroring the retired `disposition` precedent), never a throw, never a
|
|
101
|
+
* coerce.
|
|
102
|
+
*/
|
|
103
|
+
export type SidecarKind = 'merge' | 'stuck' | 'triage' | 'spec';
|
|
104
|
+
|
|
105
|
+
const SIDECAR_KINDS: ReadonlySet<SidecarKind> = new Set<SidecarKind>([
|
|
106
|
+
'merge',
|
|
107
|
+
'stuck',
|
|
108
|
+
'triage',
|
|
109
|
+
'spec',
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
/** One question entry in the sidecar (the per-entry source of truth). */
|
|
113
|
+
export interface SidecarEntry {
|
|
114
|
+
/** Stable, monotonic id (`q1`, `q2`, …); never reused once minted. */
|
|
115
|
+
id: string;
|
|
116
|
+
/** The question, verbatim. */
|
|
117
|
+
question: string;
|
|
118
|
+
/** Inline context so the human need not open the item. Empty when absent. */
|
|
119
|
+
context: string;
|
|
120
|
+
/** Optional suggested default (the surface-questions humility aid). */
|
|
121
|
+
default?: string;
|
|
122
|
+
/**
|
|
123
|
+
* The human's answer; empty/absent while unanswered. A NON-EMPTY answer ⇒
|
|
124
|
+
* answered (see {@link isEntryAnswered}).
|
|
125
|
+
*/
|
|
126
|
+
answer: string;
|
|
127
|
+
/**
|
|
128
|
+
* The explicit `answered` OVERRIDE, when the human/tooling wrote one. `true`
|
|
129
|
+
* forces answered; `false` overrides a non-empty answer back to unanswered.
|
|
130
|
+
* `undefined` ⇒ no override (the answered-ness derives from `answer`).
|
|
131
|
+
*/
|
|
132
|
+
answeredOverride?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Optional dispatch axis (see {@link SidecarKind}). Absent ⇒ the existing
|
|
135
|
+
* binary content entry. INTERIM — removable once question sidecars move to
|
|
136
|
+
* kind-based SUBFOLDERS.
|
|
137
|
+
*/
|
|
138
|
+
kind?: SidecarKind;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The parsed sidecar: identity frontmatter + ordered entries. */
|
|
142
|
+
export interface SidecarModel {
|
|
143
|
+
/** The NAMESPACED identity (`spec:autotask`, `task:foo`, `observation:bar`). */
|
|
144
|
+
item: string;
|
|
145
|
+
/** The item type (redundant with the filename; explicit for the parser). */
|
|
146
|
+
type: SidecarType;
|
|
147
|
+
/** The bare slug. */
|
|
148
|
+
slug: string;
|
|
149
|
+
/** The ordered question entries (the source of truth for answered-state). */
|
|
150
|
+
entries: SidecarEntry[];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Raised for a structurally-invalid sidecar the parser cannot trust. */
|
|
154
|
+
export class SidecarParseError extends Error {
|
|
155
|
+
constructor(message: string) {
|
|
156
|
+
super(message);
|
|
157
|
+
this.name = 'SidecarParseError';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Is this entry ANSWERED? The MAINTAINER-RESOLVED §1 predicate: a non-empty
|
|
163
|
+
* `answer` ⇒ answered, with the explicit `answered=…` override (carried in the
|
|
164
|
+
* per-entry HTML comment) winning when present. An explicit `answered=false`
|
|
165
|
+
* overrides a non-empty answer back to unanswered; an explicit `answered=true`
|
|
166
|
+
* forces answered even with an empty answer.
|
|
167
|
+
*/
|
|
168
|
+
export function isEntryAnswered(entry: SidecarEntry): boolean {
|
|
169
|
+
if (entry.answeredOverride !== undefined) {
|
|
170
|
+
return entry.answeredOverride;
|
|
171
|
+
}
|
|
172
|
+
return entry.answer.trim() !== '';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Every entry that is NOT yet answered (derived from the entries). */
|
|
176
|
+
export function pendingEntries(model: SidecarModel): SidecarEntry[] {
|
|
177
|
+
return model.entries.filter((entry) => !isEntryAnswered(entry));
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Is the WHOLE sidecar answered? DERIVED from the entries (never read off the
|
|
182
|
+
* identity comment mirror). An EMPTY sidecar (no entries) is NOT all-answered —
|
|
183
|
+
* a sidecar with no open questions should not exist (it would be deleted on
|
|
184
|
+
* full resolution), so `allAnswered` over zero entries is `false`, keeping the
|
|
185
|
+
* "pending ⇒ NO-OP" classifier honest.
|
|
186
|
+
*/
|
|
187
|
+
export function allAnswered(model: SidecarModel): boolean {
|
|
188
|
+
return model.entries.length > 0 && pendingEntries(model).length === 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const TYPE_TO_NAMESPACE: Record<SidecarType, string> = {
|
|
192
|
+
spec: 'spec',
|
|
193
|
+
task: 'task',
|
|
194
|
+
observation: 'observation',
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/** Map a namespaced-identity prefix onto the sidecar type. */
|
|
198
|
+
function typeForNamespace(
|
|
199
|
+
explicit: SlugNamespace | undefined,
|
|
200
|
+
rawPrefix: string,
|
|
201
|
+
): SidecarType {
|
|
202
|
+
// A `spec:` prefix / `explicit === 'spec'` maps to the `'spec'` type — NOT the
|
|
203
|
+
// `task` fall-through below — so `spec:<slug>` gets its own `spec-<slug>`
|
|
204
|
+
// lock/sidecar entry.
|
|
205
|
+
if (explicit === 'spec') {
|
|
206
|
+
return 'spec';
|
|
207
|
+
}
|
|
208
|
+
if (explicit === 'task') {
|
|
209
|
+
return 'task';
|
|
210
|
+
}
|
|
211
|
+
// `parseSlugArg` only knows `task:`/`prd:`; the sidecar adds the
|
|
212
|
+
// `observation:` namespace (`obs:` is the CLI alias the verb resolves; the
|
|
213
|
+
// sidecar stores the canonical `observation`). A bare slug (no prefix)
|
|
214
|
+
// defaults to the task namespace, matching the resolver's "bare = task".
|
|
215
|
+
if (rawPrefix === 'observation' || rawPrefix === 'obs') {
|
|
216
|
+
return 'observation';
|
|
217
|
+
}
|
|
218
|
+
return 'task';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The resolved identity of a sidecar: `{type, slug, item}`. */
|
|
222
|
+
export interface SidecarIdentity {
|
|
223
|
+
type: SidecarType;
|
|
224
|
+
slug: string;
|
|
225
|
+
/** The canonical namespaced identity (`<namespace>:<slug>`). */
|
|
226
|
+
item: string;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Resolve a namespaced-identity argument (`spec:autotask`, `task:foo`,
|
|
231
|
+
* `observation:bar`, `obs:bar`, or a bare `<slug>` = task) into its
|
|
232
|
+
* `{type, slug, item}`. PURE string work over {@link parseSlugArg} — the
|
|
233
|
+
* resolver is the single source of truth for the task/spec split; the sidecar
|
|
234
|
+
* extends it with the `observation` namespace.
|
|
235
|
+
*/
|
|
236
|
+
export function resolveSidecarIdentity(identity: string): SidecarIdentity {
|
|
237
|
+
// `observation:`/`obs:` are not slug-namespace prefixes — peel them first.
|
|
238
|
+
const colon = identity.indexOf(':');
|
|
239
|
+
if (colon !== -1) {
|
|
240
|
+
const prefix = identity.slice(0, colon);
|
|
241
|
+
if (prefix === 'observation' || prefix === 'obs') {
|
|
242
|
+
const slug = identity.slice(colon + 1);
|
|
243
|
+
return {type: 'observation', slug, item: `observation:${slug}`};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const parsed = parseSlugArg(identity);
|
|
247
|
+
const type = typeForNamespace(parsed.explicit, identity.slice(0, colon));
|
|
248
|
+
return {
|
|
249
|
+
type,
|
|
250
|
+
slug: parsed.slug,
|
|
251
|
+
item: `${TYPE_TO_NAMESPACE[type]}:${parsed.slug}`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Derive the sidecar PATH `work/questions/<type>-<slug>.md` from the item's
|
|
257
|
+
* namespaced identity. The identity is the single source of truth; `:`→`-` for
|
|
258
|
+
* the filename. Accepts the same forms as {@link resolveSidecarIdentity}.
|
|
259
|
+
*/
|
|
260
|
+
export function sidecarPathFor(identity: string): string {
|
|
261
|
+
const {type, slug} = resolveSidecarIdentity(identity);
|
|
262
|
+
return workItemRel('questions', `${type}-${slug}.md`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* The ordered candidate sidecar PATHS a READER should probe for `identity`: the
|
|
267
|
+
* canonical {@link sidecarPathFor} path FIRST, then any legacy-data fallback that
|
|
268
|
+
* may still be on disk before the migration command converts it.
|
|
269
|
+
*
|
|
270
|
+
* MIGRATE step (prd `prd-to-spec-vocabulary-cutover-and-migration-command`): the
|
|
271
|
+
* producer side now emits `spec:<slug>`, so {@link sidecarPathFor} resolves
|
|
272
|
+
* `work/questions/spec-<slug>.md`. But the ON-DISK sidecar is still the legacy
|
|
273
|
+
* `work/questions/prd-<slug>.md` until `dorfl prd-to-spec` renames the DATA. So a
|
|
274
|
+
* `spec`-typed identity ALSO probes the legacy `prd-<slug>.md` as a fallback, and
|
|
275
|
+
* a reader takes the FIRST candidate that exists. This is a FILE-PATH DATA alias
|
|
276
|
+
* the migration command removes (it converts `prd-<slug>.md → spec-<slug>.md` on
|
|
277
|
+
* disk); it is NOT the `SlugNamespace`/`SidecarType` `'prd'` type member. Every
|
|
278
|
+
* non-`spec` type has a single candidate (its canonical path), unchanged.
|
|
279
|
+
*/
|
|
280
|
+
export function sidecarPathCandidates(identity: string): string[] {
|
|
281
|
+
const {type, slug} = resolveSidecarIdentity(identity);
|
|
282
|
+
const canonical = workItemRel('questions', `${type}-${slug}.md`);
|
|
283
|
+
if (type === 'spec') {
|
|
284
|
+
return [canonical, workItemRel('questions', `prd-${slug}.md`)];
|
|
285
|
+
}
|
|
286
|
+
return [canonical];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// --- Format constants -----------------------------------------------------
|
|
290
|
+
|
|
291
|
+
/** The fixed answer marker the human types prose under. */
|
|
292
|
+
const ANSWER_MARKER = '**Your answer** (write below this line):';
|
|
293
|
+
|
|
294
|
+
// --- Parse ----------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
/** Pull the next monotonic id given the highest existing id number. */
|
|
297
|
+
function nextId(entries: SidecarEntry[]): string {
|
|
298
|
+
let max = 0;
|
|
299
|
+
for (const entry of entries) {
|
|
300
|
+
const m = /^q(\d+)$/.exec(entry.id);
|
|
301
|
+
if (m) {
|
|
302
|
+
const n = Number(m[1]);
|
|
303
|
+
if (n > max) {
|
|
304
|
+
max = n;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return `q${max + 1}`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Normalise CRLF → LF and strip a leading BOM. */
|
|
312
|
+
function normaliseText(text: string): string {
|
|
313
|
+
return text.replace(/\r\n/g, '\n').replace(/^\uFEFF/, '');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** Strip leading + trailing blank lines, leaving inner blanks intact. */
|
|
317
|
+
function trimBlankLines(value: string): string {
|
|
318
|
+
return value.replace(/^\n+/, '').replace(/\n+$/, '');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
interface IdentityFields {
|
|
322
|
+
item: string;
|
|
323
|
+
allAnswered?: boolean;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Parse the top identity HTML comment. */
|
|
327
|
+
function parseIdentityComment(line: string): IdentityFields | undefined {
|
|
328
|
+
const match = /^<!--\s*dorfl-sidecar:\s*(.*?)\s*-->\s*$/.exec(line);
|
|
329
|
+
if (!match) {
|
|
330
|
+
return undefined;
|
|
331
|
+
}
|
|
332
|
+
const fields: Record<string, string> = {};
|
|
333
|
+
for (const token of match[1].split(/\s+/)) {
|
|
334
|
+
if (token === '') {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
const eq = token.indexOf('=');
|
|
338
|
+
if (eq === -1) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
fields[token.slice(0, eq)] = token.slice(eq + 1);
|
|
342
|
+
}
|
|
343
|
+
const item = fields['item'];
|
|
344
|
+
if (item === undefined || item === '') {
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
const out: IdentityFields = {item};
|
|
348
|
+
if (fields['allAnswered'] !== undefined) {
|
|
349
|
+
out.allAnswered = fields['allAnswered'] === 'true';
|
|
350
|
+
}
|
|
351
|
+
return out;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
interface EntryFields {
|
|
355
|
+
id?: string;
|
|
356
|
+
answered?: boolean;
|
|
357
|
+
kind?: SidecarKind;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Parse a per-entry `<!-- qN fields: key=val … -->` HTML comment. */
|
|
361
|
+
function parseEntryComment(line: string): EntryFields | undefined {
|
|
362
|
+
const match = /^<!--\s*q\d+\s+fields:\s*(.*?)\s*-->\s*$/.exec(line);
|
|
363
|
+
if (!match) {
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
const out: EntryFields = {};
|
|
367
|
+
for (const token of match[1].split(/\s+/)) {
|
|
368
|
+
if (token === '') {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const eq = token.indexOf('=');
|
|
372
|
+
if (eq === -1) {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const key = token.slice(0, eq);
|
|
376
|
+
const value = token.slice(eq + 1);
|
|
377
|
+
if (key === 'id') {
|
|
378
|
+
out.id = value;
|
|
379
|
+
} else if (key === 'answered') {
|
|
380
|
+
if (value === 'true') {
|
|
381
|
+
out.answered = true;
|
|
382
|
+
} else if (value === 'false') {
|
|
383
|
+
out.answered = false;
|
|
384
|
+
}
|
|
385
|
+
} else if (key === 'kind') {
|
|
386
|
+
// Silent-on-malformed (retired `disposition` precedent): a mistyped or
|
|
387
|
+
// unknown kind reads as `undefined`, never a throw, never a coerce.
|
|
388
|
+
if (SIDECAR_KINDS.has(value as SidecarKind)) {
|
|
389
|
+
out.kind = value as SidecarKind;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Parse one entry's content lines (between two `## ` headings) into an entry. */
|
|
397
|
+
function parseEntrySection(lines: string[]): SidecarEntry {
|
|
398
|
+
let id = '';
|
|
399
|
+
let question = '';
|
|
400
|
+
const contextLines: string[] = [];
|
|
401
|
+
let inBlockquote = false;
|
|
402
|
+
let defaultVal: string | undefined;
|
|
403
|
+
let answeredOverride: boolean | undefined;
|
|
404
|
+
let kind: SidecarKind | undefined;
|
|
405
|
+
let answerStart = -1;
|
|
406
|
+
|
|
407
|
+
for (let i = 0; i < lines.length; i++) {
|
|
408
|
+
const line = lines[i];
|
|
409
|
+
if (line === ANSWER_MARKER) {
|
|
410
|
+
answerStart = i + 1;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
const entryFields = parseEntryComment(line);
|
|
414
|
+
if (entryFields) {
|
|
415
|
+
if (entryFields.id !== undefined) {
|
|
416
|
+
id = entryFields.id;
|
|
417
|
+
}
|
|
418
|
+
if (entryFields.answered !== undefined) {
|
|
419
|
+
answeredOverride = entryFields.answered;
|
|
420
|
+
}
|
|
421
|
+
if (entryFields.kind !== undefined) {
|
|
422
|
+
kind = entryFields.kind;
|
|
423
|
+
}
|
|
424
|
+
inBlockquote = false;
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
// Blockquote context: collect the FIRST contiguous run of `> …` lines.
|
|
428
|
+
// Once a non-quote line breaks the run, later `>` lines (e.g. the human
|
|
429
|
+
// quoting something in their answer's preamble) are ignored as context.
|
|
430
|
+
const blockquoteMatch = /^>\s?(.*)$/.exec(line);
|
|
431
|
+
if (blockquoteMatch && (contextLines.length === 0 || inBlockquote)) {
|
|
432
|
+
contextLines.push(blockquoteMatch[1]);
|
|
433
|
+
inBlockquote = true;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (inBlockquote && !blockquoteMatch && line.trim() !== '') {
|
|
437
|
+
inBlockquote = false;
|
|
438
|
+
}
|
|
439
|
+
// Italic "_Suggested default: …_" line.
|
|
440
|
+
const defaultMatch = /^_Suggested default:\s*(.+?)_\s*$/.exec(line);
|
|
441
|
+
if (defaultMatch && defaultVal === undefined) {
|
|
442
|
+
defaultVal = defaultMatch[1];
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
// Bold question line (first one wins; skip the answer marker line which
|
|
446
|
+
// has trailing `(write below this line):` and so does not match this regex).
|
|
447
|
+
const questionMatch = /^\*\*(.+?)\*\*\s*$/.exec(line);
|
|
448
|
+
if (questionMatch && question === '') {
|
|
449
|
+
question = questionMatch[1];
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (id === '') {
|
|
455
|
+
throw new SidecarParseError(
|
|
456
|
+
'sidecar entry is missing its id (HTML comment)',
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const context = trimBlankLines(contextLines.join('\n'));
|
|
461
|
+
let answer = '';
|
|
462
|
+
if (answerStart >= 0) {
|
|
463
|
+
answer = trimBlankLines(lines.slice(answerStart).join('\n'));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Re-interpret the override: store it ONLY when it DISAGREES with the
|
|
467
|
+
// answer-derived predicate (so a redundant `answered=false` over an empty
|
|
468
|
+
// answer cannot become a sticky override that freezes a later tolerant edit).
|
|
469
|
+
const derivedAnswered = answer.trim() !== '';
|
|
470
|
+
let finalOverride: boolean | undefined;
|
|
471
|
+
if (answeredOverride === true && !derivedAnswered) {
|
|
472
|
+
finalOverride = true;
|
|
473
|
+
} else if (answeredOverride === false && derivedAnswered) {
|
|
474
|
+
finalOverride = false;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const entry: SidecarEntry = {
|
|
478
|
+
id,
|
|
479
|
+
question,
|
|
480
|
+
context,
|
|
481
|
+
answer,
|
|
482
|
+
};
|
|
483
|
+
if (defaultVal !== undefined) {
|
|
484
|
+
entry.default = defaultVal;
|
|
485
|
+
}
|
|
486
|
+
if (finalOverride !== undefined) {
|
|
487
|
+
entry.answeredOverride = finalOverride;
|
|
488
|
+
}
|
|
489
|
+
if (kind !== undefined) {
|
|
490
|
+
entry.kind = kind;
|
|
491
|
+
}
|
|
492
|
+
return entry;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Parse a sidecar document into its typed model. The on-disk format is the
|
|
497
|
+
* human-readable Markdown + HTML-comments form (see the module doc); the
|
|
498
|
+
* identity comment lives at the top, each entry opens with a `## ` heading, and
|
|
499
|
+
* the answer is heading-delimited (everything between the answer marker and the
|
|
500
|
+
* next `## ` heading is the answer). TOLERANT of the human typing only under
|
|
501
|
+
* the answer marker: a non-empty answer ⇒ answered (no comment edit needed).
|
|
502
|
+
*/
|
|
503
|
+
export function parseSidecar(text: string): SidecarModel {
|
|
504
|
+
const lines = normaliseText(text).split('\n');
|
|
505
|
+
|
|
506
|
+
// Find the identity HTML comment (scan from the top, skipping blank lines).
|
|
507
|
+
let identity: IdentityFields | undefined;
|
|
508
|
+
let cursor = 0;
|
|
509
|
+
while (cursor < lines.length) {
|
|
510
|
+
const line = lines[cursor];
|
|
511
|
+
cursor++;
|
|
512
|
+
if (line.trim() === '') {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
identity = parseIdentityComment(line);
|
|
516
|
+
if (identity) {
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
throw new SidecarParseError(
|
|
520
|
+
'sidecar must open with an `<!-- dorfl-sidecar: … -->` identity comment',
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (!identity) {
|
|
524
|
+
throw new SidecarParseError('sidecar is missing its identity HTML comment');
|
|
525
|
+
}
|
|
526
|
+
const resolved = resolveSidecarIdentity(identity.item);
|
|
527
|
+
|
|
528
|
+
// Split the rest into per-entry sections on `## ` headings. Anything before
|
|
529
|
+
// the first heading is preamble we ignore (a stray human note, or the blank
|
|
530
|
+
// line after the identity comment).
|
|
531
|
+
const entries: SidecarEntry[] = [];
|
|
532
|
+
let sectionLines: string[] | undefined;
|
|
533
|
+
for (let i = cursor; i < lines.length; i++) {
|
|
534
|
+
const line = lines[i];
|
|
535
|
+
if (/^##\s+/.test(line)) {
|
|
536
|
+
if (sectionLines !== undefined) {
|
|
537
|
+
entries.push(parseEntrySection(sectionLines));
|
|
538
|
+
}
|
|
539
|
+
sectionLines = [];
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
if (sectionLines !== undefined) {
|
|
543
|
+
sectionLines.push(line);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
if (sectionLines !== undefined) {
|
|
547
|
+
entries.push(parseEntrySection(sectionLines));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return {
|
|
551
|
+
item: resolved.item,
|
|
552
|
+
type: resolved.type,
|
|
553
|
+
slug: resolved.slug,
|
|
554
|
+
entries,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// --- Serialise ------------------------------------------------------------
|
|
559
|
+
|
|
560
|
+
/** Collapse internal newlines so a value renders as a single Markdown line. */
|
|
561
|
+
function singleLine(value: string): string {
|
|
562
|
+
return value.replace(/\s*\n\s*/g, ' ').trim();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/** Render a multi-line value as a Markdown blockquote (each line `> …`). */
|
|
566
|
+
function blockquote(value: string): string[] {
|
|
567
|
+
const trimmed = trimBlankLines(value);
|
|
568
|
+
if (trimmed === '') {
|
|
569
|
+
return [];
|
|
570
|
+
}
|
|
571
|
+
return trimmed.split('\n').map((line) => (line === '' ? '>' : `> ${line}`));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Serialise a model to its CANONICAL human-readable Markdown text. The identity
|
|
576
|
+
* HTML comment at the top carries `item`/`type`/`slug` + the derived
|
|
577
|
+
* `allAnswered` mirror; each entry is a `## Qn` heading, a bold question line,
|
|
578
|
+
* a blockquote context (when non-empty), an italic suggested-default (when
|
|
579
|
+
* present), a per-entry HTML comment carrying `id` (and an explicit `answered`
|
|
580
|
+
* override when it disagrees with the derived predicate), the fixed answer
|
|
581
|
+
* marker, then the answer prose. Round-trip is SEMANTIC:
|
|
582
|
+
* `parseSidecar(serialiseSidecar(m))` recovers an equal MODEL; re-serialising
|
|
583
|
+
* canonicalises the text.
|
|
584
|
+
*/
|
|
585
|
+
export function serialiseSidecar(model: SidecarModel): string {
|
|
586
|
+
const out: string[] = [];
|
|
587
|
+
const identityParts = [
|
|
588
|
+
`item=${model.item}`,
|
|
589
|
+
`type=${model.type}`,
|
|
590
|
+
`slug=${model.slug}`,
|
|
591
|
+
`allAnswered=${allAnswered(model)}`,
|
|
592
|
+
];
|
|
593
|
+
out.push(`<!-- dorfl-sidecar: ${identityParts.join(' ')} -->`);
|
|
594
|
+
|
|
595
|
+
model.entries.forEach((entry) => {
|
|
596
|
+
out.push('');
|
|
597
|
+
const heading = entry.id.replace(/^q/, 'Q');
|
|
598
|
+
out.push(`## ${heading}`);
|
|
599
|
+
out.push('');
|
|
600
|
+
out.push(`**${singleLine(entry.question)}**`);
|
|
601
|
+
const ctx = blockquote(entry.context);
|
|
602
|
+
if (ctx.length > 0) {
|
|
603
|
+
out.push('');
|
|
604
|
+
out.push(...ctx);
|
|
605
|
+
}
|
|
606
|
+
if (entry.default !== undefined && entry.default.trim() !== '') {
|
|
607
|
+
out.push('');
|
|
608
|
+
out.push(`_Suggested default: ${singleLine(entry.default)}_`);
|
|
609
|
+
}
|
|
610
|
+
out.push('');
|
|
611
|
+
// Per-entry machine comment. The override is emitted ONLY when it
|
|
612
|
+
// DISAGREES with the answer-derived predicate (otherwise the
|
|
613
|
+
// just-serialised line would re-parse as a sticky override).
|
|
614
|
+
const fields: string[] = [`id=${entry.id}`];
|
|
615
|
+
const derivedAnswered = entry.answer.trim() !== '';
|
|
616
|
+
if (entry.answeredOverride === true && !derivedAnswered) {
|
|
617
|
+
fields.push('answered=true');
|
|
618
|
+
} else if (entry.answeredOverride === false && derivedAnswered) {
|
|
619
|
+
fields.push('answered=false');
|
|
620
|
+
}
|
|
621
|
+
if (entry.kind !== undefined) {
|
|
622
|
+
fields.push(`kind=${entry.kind}`);
|
|
623
|
+
}
|
|
624
|
+
out.push(`<!-- ${entry.id} fields: ${fields.join(' ')} -->`);
|
|
625
|
+
out.push('');
|
|
626
|
+
out.push(ANSWER_MARKER);
|
|
627
|
+
const answerText = trimBlankLines(entry.answer);
|
|
628
|
+
if (answerText !== '') {
|
|
629
|
+
out.push('');
|
|
630
|
+
out.push(answerText);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
out.push('');
|
|
634
|
+
return out.join('\n');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// --- Append ---------------------------------------------------------------
|
|
638
|
+
|
|
639
|
+
/** A new question to append (no id — the appender mints the next monotonic id). */
|
|
640
|
+
export interface NewQuestion {
|
|
641
|
+
question: string;
|
|
642
|
+
context?: string;
|
|
643
|
+
default?: string;
|
|
644
|
+
/**
|
|
645
|
+
* Optional dispatch {@link SidecarKind} the surfacer stamps so the apply
|
|
646
|
+
* rung can route a runner-ACTION question (`merge`, `stuck`) to the
|
|
647
|
+
* deterministic dispatch vs a CONTENT question (`triage`, `spec`) to the
|
|
648
|
+
* agentic `decide()` path. INTERIM — removable once kind-subfolders land.
|
|
649
|
+
*/
|
|
650
|
+
kind?: SidecarKind;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Append `newQuestions` to the model, minting `qN+1…` ids off the highest
|
|
655
|
+
* existing id. NEVER overwrites or mutates an existing entry (answered or not) —
|
|
656
|
+
* the sidecar is the item's full Q&A history. Appending flips a previously-
|
|
657
|
+
* `allAnswered` sidecar back to not-all-answered (the new entries are pending).
|
|
658
|
+
* Returns a NEW model (the input is not mutated).
|
|
659
|
+
*/
|
|
660
|
+
export function appendQuestions(
|
|
661
|
+
model: SidecarModel,
|
|
662
|
+
newQuestions: NewQuestion[],
|
|
663
|
+
): SidecarModel {
|
|
664
|
+
const entries = model.entries.slice();
|
|
665
|
+
for (const q of newQuestions) {
|
|
666
|
+
const entry: SidecarEntry = {
|
|
667
|
+
id: nextId(entries),
|
|
668
|
+
question: q.question,
|
|
669
|
+
context: q.context ?? '',
|
|
670
|
+
answer: '',
|
|
671
|
+
};
|
|
672
|
+
if (q.default !== undefined) {
|
|
673
|
+
entry.default = q.default;
|
|
674
|
+
}
|
|
675
|
+
if (q.kind !== undefined) {
|
|
676
|
+
entry.kind = q.kind;
|
|
677
|
+
}
|
|
678
|
+
entries.push(entry);
|
|
679
|
+
}
|
|
680
|
+
return {...model, entries};
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Build a fresh sidecar model for `identity` carrying `newQuestions` (ids minted
|
|
685
|
+
* `q1…`). The first-pass constructor the surface-question rung uses before there
|
|
686
|
+
* is any sidecar to append to.
|
|
687
|
+
*/
|
|
688
|
+
export function newSidecar(
|
|
689
|
+
identity: string,
|
|
690
|
+
newQuestions: NewQuestion[],
|
|
691
|
+
): SidecarModel {
|
|
692
|
+
const resolved = resolveSidecarIdentity(identity);
|
|
693
|
+
const base: SidecarModel = {
|
|
694
|
+
item: resolved.item,
|
|
695
|
+
type: resolved.type,
|
|
696
|
+
slug: resolved.slug,
|
|
697
|
+
entries: [],
|
|
698
|
+
};
|
|
699
|
+
return appendQuestions(base, newQuestions);
|
|
700
|
+
}
|