codetrellis 0.0.0-stage → 0.3.0
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/LICENSE +201 -0
- package/README.md +27 -2
- package/bin/codetrellis.mjs +4 -0
- package/package.json +41 -4
- package/reader/material-reader.mjs +130346 -0
- package/resources/tree-sitter/README.md +150 -0
- package/resources/tree-sitter/tree-sitter-c-sharp.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-go.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-java.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-kotlin.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-php.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-python.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-ruby.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-rust.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-swift.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/resources/tree-sitter/tree-sitter.wasm +0 -0
- package/src/backend/agent/claude-code-watcher.js +270 -0
- package/src/backend/index.js +8 -0
- package/src/backend/lib/sha256-file.js +39 -0
- package/src/backend/lib/zip-entries.js +98 -0
- package/src/backend/lifecycle.js +102 -0
- package/src/backend/mcp/binding-headers.js +54 -0
- package/src/backend/mcp/client-identity.js +44 -0
- package/src/backend/mcp/connector/command.js +110 -0
- package/src/backend/mcp/connector/core.js +278 -0
- package/src/backend/mcp/connector/files.js +121 -0
- package/src/backend/mcp/connector/hook.js +284 -0
- package/src/backend/mcp/connector/main.js +126 -0
- package/src/backend/mcp/connector/sse-upstream.js +152 -0
- package/src/backend/mcp/helpers.js +43 -0
- package/src/backend/mcp/prompt-builders.js +104 -0
- package/src/backend/mcp/resources.js +197 -0
- package/src/backend/mcp/server.js +813 -0
- package/src/backend/mcp/skill-guide.js +1528 -0
- package/src/backend/mcp/tools/architecture-tools.js +273 -0
- package/src/backend/mcp/tools/audio-tools.js +117 -0
- package/src/backend/mcp/tools/awareness-tools.js +623 -0
- package/src/backend/mcp/tools/budget-tools.js +185 -0
- package/src/backend/mcp/tools/channel-tools.js +205 -0
- package/src/backend/mcp/tools/contribution-tools.js +181 -0
- package/src/backend/mcp/tools/drift-tools.js +252 -0
- package/src/backend/mcp/tools/git-tools.js +316 -0
- package/src/backend/mcp/tools/governance-tools.js +175 -0
- package/src/backend/mcp/tools/graph-tools.js +181 -0
- package/src/backend/mcp/tools/intake-tools.js +244 -0
- package/src/backend/mcp/tools/mobile-tools.js +118 -0
- package/src/backend/mcp/tools/peer-tools.js +330 -0
- package/src/backend/mcp/tools/plan-item-tools.js +1601 -0
- package/src/backend/mcp/tools/plan-tools.js +680 -0
- package/src/backend/mcp/tools/presence-tools.js +185 -0
- package/src/backend/mcp/tools/project-config-tools.js +150 -0
- package/src/backend/mcp/tools/review-tools.js +172 -0
- package/src/backend/mcp/tools/session-tools.js +554 -0
- package/src/backend/mcp/tools/system-docs-tools.js +168 -0
- package/src/backend/mcp/tools/terminal-tools.js +186 -0
- package/src/backend/mcp/tools/test-tools.js +117 -0
- package/src/backend/mcp/tools/ui-tools.js +356 -0
- package/src/backend/mcp/types.js +15 -0
- package/src/backend/middleware/local-auth.js +116 -0
- package/src/backend/server.js +5951 -0
- package/src/backend/services/agent-event-log.js +350 -0
- package/src/backend/services/architecture-rule.js +584 -0
- package/src/backend/services/architecture-rules.js +275 -0
- package/src/backend/services/artefact-content-service.js +181 -0
- package/src/backend/services/artefact-service.js +305 -0
- package/src/backend/services/artefact-watcher.js +167 -0
- package/src/backend/services/ast-parser.js +327 -0
- package/src/backend/services/audio-buffer-service.js +152 -0
- package/src/backend/services/awareness-notices.js +139 -0
- package/src/backend/services/awareness-replies.js +194 -0
- package/src/backend/services/awareness-service.js +344 -0
- package/src/backend/services/awareness-signals.js +267 -0
- package/src/backend/services/baseline-store.js +83 -0
- package/src/backend/services/branch-workstreams.js +341 -0
- package/src/backend/services/breakpoint-service.js +584 -0
- package/src/backend/services/brief-service.js +299 -0
- package/src/backend/services/budget-service.js +528 -0
- package/src/backend/services/callsites/base.js +15 -0
- package/src/backend/services/callsites/csharp.js +148 -0
- package/src/backend/services/callsites/go.js +163 -0
- package/src/backend/services/callsites/index.js +53 -0
- package/src/backend/services/callsites/kotlin.js +123 -0
- package/src/backend/services/callsites/process-env.js +125 -0
- package/src/backend/services/callsites/python.js +91 -0
- package/src/backend/services/callsites/ruby.js +164 -0
- package/src/backend/services/callsites/shared.js +198 -0
- package/src/backend/services/callsites/swift.js +115 -0
- package/src/backend/services/callsites/typescript.js +87 -0
- package/src/backend/services/capability-token.js +108 -0
- package/src/backend/services/change-check.js +156 -0
- package/src/backend/services/channel-dispatcher-service.js +235 -0
- package/src/backend/services/channel-event-file-service.js +125 -0
- package/src/backend/services/channel-event-service.js +297 -0
- package/src/backend/services/check-runs.js +177 -0
- package/src/backend/services/checkout-identity.js +73 -0
- package/src/backend/services/claude-code-parallel.js +221 -0
- package/src/backend/services/claude-desktop-config.js +186 -0
- package/src/backend/services/cli-install.js +249 -0
- package/src/backend/services/clock.js +57 -0
- package/src/backend/services/cloud-files.js +191 -0
- package/src/backend/services/coalesce.js +50 -0
- package/src/backend/services/code-breakpoints.js +291 -0
- package/src/backend/services/comment-service.js +168 -0
- package/src/backend/services/commit-attribution.js +134 -0
- package/src/backend/services/commit-edges.js +99 -0
- package/src/backend/services/confined-fs.js +241 -0
- package/src/backend/services/conformity-gate.js +132 -0
- package/src/backend/services/contribution-service.js +409 -0
- package/src/backend/services/coverage-service.js +173 -0
- package/src/backend/services/criteria-service.js +721 -0
- package/src/backend/services/criterion-checks.js +387 -0
- package/src/backend/services/criterion-loop-service.js +411 -0
- package/src/backend/services/cross-system-service.js +231 -0
- package/src/backend/services/database.js +916 -0
- package/src/backend/services/db-schema.js +1358 -0
- package/src/backend/services/deviation-service.js +344 -0
- package/src/backend/services/diff-engine.js +168 -0
- package/src/backend/services/evidence.js +363 -0
- package/src/backend/services/external-intake-service.js +269 -0
- package/src/backend/services/external-pointer-service.js +195 -0
- package/src/backend/services/external-refs-service.js +214 -0
- package/src/backend/services/file-history.js +158 -0
- package/src/backend/services/file-watcher.js +193 -0
- package/src/backend/services/folder-requests.js +118 -0
- package/src/backend/services/freeze-service.js +189 -0
- package/src/backend/services/gemini-cli-hook.js +172 -0
- package/src/backend/services/git-activity-service.js +325 -0
- package/src/backend/services/git-blobs.js +70 -0
- package/src/backend/services/git-branches.js +357 -0
- package/src/backend/services/git-checkout.js +153 -0
- package/src/backend/services/git-commit-service.js +91 -0
- package/src/backend/services/git-env.js +121 -0
- package/src/backend/services/git-identity.js +84 -0
- package/src/backend/services/git-refs.js +386 -0
- package/src/backend/services/git-safety.js +58 -0
- package/src/backend/services/grant-guard.js +67 -0
- package/src/backend/services/html-view-policy.js +135 -0
- package/src/backend/services/human-decision.js +55 -0
- package/src/backend/services/importers.js +159 -0
- package/src/backend/services/intent-service.js +105 -0
- package/src/backend/services/ipc-dispatcher.js +164 -0
- package/src/backend/services/item-git-state.js +208 -0
- package/src/backend/services/line-changes.js +232 -0
- package/src/backend/services/line-history.js +174 -0
- package/src/backend/services/local-api-changes.js +40 -0
- package/src/backend/services/logger.js +217 -0
- package/src/backend/services/material-footprints.js +203 -0
- package/src/backend/services/material-place.js +82 -0
- package/src/backend/services/material-reader/docx-markdown.js +198 -0
- package/src/backend/services/material-reader/fixtures.test-helper.js +169 -0
- package/src/backend/services/material-reader/quote.js +44 -0
- package/src/backend/services/material-reader/reader-host.js +202 -0
- package/src/backend/services/material-signals.js +141 -0
- package/src/backend/services/mcp-capabilities.js +390 -0
- package/src/backend/services/mdns-service.js +304 -0
- package/src/backend/services/mobile-api-server.js +281 -0
- package/src/backend/services/mobile-approvals.js +306 -0
- package/src/backend/services/mobile-awareness.js +180 -0
- package/src/backend/services/mobile-breakpoints.js +115 -0
- package/src/backend/services/mobile-budget.js +98 -0
- package/src/backend/services/mobile-freeze.js +106 -0
- package/src/backend/services/mobile-proposals.js +100 -0
- package/src/backend/services/mobile-rpc-service.js +1289 -0
- package/src/backend/services/mobile-workstreams.js +160 -0
- package/src/backend/services/monorepo-detector.js +131 -0
- package/src/backend/services/other-work.js +45 -0
- package/src/backend/services/pack-seal.js +129 -0
- package/src/backend/services/paired-device-service.js +192 -0
- package/src/backend/services/pairing-server.js +323 -0
- package/src/backend/services/pairing-service.js +200 -0
- package/src/backend/services/pantry-resolution-service.js +166 -0
- package/src/backend/services/parsers/base.js +124 -0
- package/src/backend/services/parsers/csharp.js +224 -0
- package/src/backend/services/parsers/go.js +180 -0
- package/src/backend/services/parsers/index.js +101 -0
- package/src/backend/services/parsers/java.js +103 -0
- package/src/backend/services/parsers/kotlin.js +252 -0
- package/src/backend/services/parsers/php.js +113 -0
- package/src/backend/services/parsers/python.js +189 -0
- package/src/backend/services/parsers/ruby.js +212 -0
- package/src/backend/services/parsers/rust.js +97 -0
- package/src/backend/services/parsers/swift.js +221 -0
- package/src/backend/services/parsers/typescript.js +235 -0
- package/src/backend/services/pattern-scan.js +65 -0
- package/src/backend/services/patterns.js +281 -0
- package/src/backend/services/peer-audit-service.js +123 -0
- package/src/backend/services/peer-auth.js +153 -0
- package/src/backend/services/peer-capabilities.js +209 -0
- package/src/backend/services/peer-connection-service.js +450 -0
- package/src/backend/services/peer-grants.js +33 -0
- package/src/backend/services/persistence.js +116 -0
- package/src/backend/services/personal-sync-service.js +232 -0
- package/src/backend/services/pipeline-approvals.js +72 -0
- package/src/backend/services/pipeline.js +232 -0
- package/src/backend/services/plan-arrivals.js +68 -0
- package/src/backend/services/plan-changes-service.js +273 -0
- package/src/backend/services/plan-conflict-service.js +288 -0
- package/src/backend/services/plan-dependencies.js +91 -0
- package/src/backend/services/plan-doc-guard.js +90 -0
- package/src/backend/services/plan-documents-service.js +275 -0
- package/src/backend/services/plan-event-service.js +154 -0
- package/src/backend/services/plan-file-refs.js +106 -0
- package/src/backend/services/plan-file-service.js +1564 -0
- package/src/backend/services/plan-history-service.js +156 -0
- package/src/backend/services/plan-import-service.js +261 -0
- package/src/backend/services/plan-item-service.js +1223 -0
- package/src/backend/services/plan-migrate-service.js +395 -0
- package/src/backend/services/plan-overlay-service.js +173 -0
- package/src/backend/services/plan-phases-service.js +182 -0
- package/src/backend/services/plan-progress-service.js +134 -0
- package/src/backend/services/plan-review-service.js +262 -0
- package/src/backend/services/plan-service.js +682 -0
- package/src/backend/services/plan-status.js +113 -0
- package/src/backend/services/plan-template-publish-service.js +217 -0
- package/src/backend/services/plan-templates-service.js +242 -0
- package/src/backend/services/plan-templates.js +1164 -0
- package/src/backend/services/planned-overlap-actions.js +200 -0
- package/src/backend/services/plans-home.js +224 -0
- package/src/backend/services/play-forward.js +256 -0
- package/src/backend/services/playback-service.js +117 -0
- package/src/backend/services/power-service.js +167 -0
- package/src/backend/services/power-signals.js +81 -0
- package/src/backend/services/pr-draft-service.js +142 -0
- package/src/backend/services/presence-service.js +122 -0
- package/src/backend/services/pricing.js +60 -0
- package/src/backend/services/project-config-service.js +474 -0
- package/src/backend/services/project-scanner.js +184 -0
- package/src/backend/services/projection-service.js +69 -0
- package/src/backend/services/push-notification-service.js +288 -0
- package/src/backend/services/recent-projects-service.js +204 -0
- package/src/backend/services/record-chain.js +162 -0
- package/src/backend/services/recurrence-rule.js +63 -0
- package/src/backend/services/recurring-agent.js +89 -0
- package/src/backend/services/recurring-scheduler.js +77 -0
- package/src/backend/services/recurring-service.js +276 -0
- package/src/backend/services/reference-service.js +169 -0
- package/src/backend/services/release-public-key.js +30 -0
- package/src/backend/services/release-signature.js +64 -0
- package/src/backend/services/remote-audio-service.js +224 -0
- package/src/backend/services/remote-interaction-service.js +275 -0
- package/src/backend/services/remote-terminal-service.js +324 -0
- package/src/backend/services/rendition/child/main.js +76 -0
- package/src/backend/services/rendition/engine-host.js +249 -0
- package/src/backend/services/rendition/engine-lock.js +39 -0
- package/src/backend/services/rendition/engine-lock.json +14 -0
- package/src/backend/services/rendition/engine-manifest.js +97 -0
- package/src/backend/services/rendition/network-proof.js +73 -0
- package/src/backend/services/rendition/rendition-service.js +175 -0
- package/src/backend/services/replay-frames.js +387 -0
- package/src/backend/services/replay-state.js +201 -0
- package/src/backend/services/resolvers/base.js +108 -0
- package/src/backend/services/resolvers/csharp.js +109 -0
- package/src/backend/services/resolvers/go.js +105 -0
- package/src/backend/services/resolvers/index.js +60 -0
- package/src/backend/services/resolvers/java.js +63 -0
- package/src/backend/services/resolvers/kotlin.js +109 -0
- package/src/backend/services/resolvers/php.js +89 -0
- package/src/backend/services/resolvers/python.js +154 -0
- package/src/backend/services/resolvers/ruby.js +103 -0
- package/src/backend/services/resolvers/rust.js +77 -0
- package/src/backend/services/resolvers/swift.js +98 -0
- package/src/backend/services/resolvers/typescript.js +71 -0
- package/src/backend/services/retention.js +54 -0
- package/src/backend/services/review-architecture.js +236 -0
- package/src/backend/services/review-attest.js +68 -0
- package/src/backend/services/review-bundle.js +226 -0
- package/src/backend/services/review-graduation.js +93 -0
- package/src/backend/services/review-host/bitbucket.js +79 -0
- package/src/backend/services/review-host/detect.js +80 -0
- package/src/backend/services/review-host/github.js +97 -0
- package/src/backend/services/review-host/gitlab.js +77 -0
- package/src/backend/services/review-host/host-state.js +103 -0
- package/src/backend/services/review-host/http.js +58 -0
- package/src/backend/services/review-host/switch.js +120 -0
- package/src/backend/services/review-marks.js +72 -0
- package/src/backend/services/review-notes.js +169 -0
- package/src/backend/services/review-other-work.js +90 -0
- package/src/backend/services/review-queue-service.js +147 -0
- package/src/backend/services/review-risk.js +127 -0
- package/src/backend/services/review-task.js +121 -0
- package/src/backend/services/rule-approvals.js +275 -0
- package/src/backend/services/rule-baseline.js +134 -0
- package/src/backend/services/rule-changes.js +162 -0
- package/src/backend/services/rule-preview.js +47 -0
- package/src/backend/services/rule-proposals.js +104 -0
- package/src/backend/services/rule-scope.js +76 -0
- package/src/backend/services/rulebook.js +286 -0
- package/src/backend/services/rules-at.js +56 -0
- package/src/backend/services/rules-overview.js +88 -0
- package/src/backend/services/schema-reconciler.js +250 -0
- package/src/backend/services/secret-store.js +119 -0
- package/src/backend/services/section-workstreams.js +95 -0
- package/src/backend/services/self-write-tracker.js +75 -0
- package/src/backend/services/sensor-bridge-service.js +285 -0
- package/src/backend/services/session-service.js +192 -0
- package/src/backend/services/settings-service.js +324 -0
- package/src/backend/services/signal-breakpoints.js +172 -0
- package/src/backend/services/signed-approval-record.js +86 -0
- package/src/backend/services/signed-approvals.js +244 -0
- package/src/backend/services/signoff-pack.js +328 -0
- package/src/backend/services/signoff-rows.js +114 -0
- package/src/backend/services/skill-arrival-service.js +138 -0
- package/src/backend/services/skill-model.js +193 -0
- package/src/backend/services/skill-use-service.js +140 -0
- package/src/backend/services/skills-service.js +150 -0
- package/src/backend/services/snapshot-compare-service.js +397 -0
- package/src/backend/services/source-control.js +189 -0
- package/src/backend/services/spec-links-service.js +123 -0
- package/src/backend/services/spec-proposal-withdraw.js +50 -0
- package/src/backend/services/spec-proposals-service.js +470 -0
- package/src/backend/services/sql/embedded.js +95 -0
- package/src/backend/services/sql/index.js +63 -0
- package/src/backend/services/sql/refs.js +357 -0
- package/src/backend/services/sql/schema.js +312 -0
- package/src/backend/services/sql/tokenizer.js +195 -0
- package/src/backend/services/stack-overlaps.js +117 -0
- package/src/backend/services/stack-service.js +225 -0
- package/src/backend/services/state-sync-service.js +384 -0
- package/src/backend/services/stuck-sensor-service.js +200 -0
- package/src/backend/services/system-discovery.js +481 -0
- package/src/backend/services/system-docs-service.js +701 -0
- package/src/backend/services/task-attachments-service.js +340 -0
- package/src/backend/services/task-grounding.js +60 -0
- package/src/backend/services/task-records/check-run-record.js +146 -0
- package/src/backend/services/task-records/check-runs.js +115 -0
- package/src/backend/services/task-records/heads.js +88 -0
- package/src/backend/services/task-records/material-reads.js +225 -0
- package/src/backend/services/task-records/read-record.js +100 -0
- package/src/backend/services/task-records/record.js +173 -0
- package/src/backend/services/task-records/run-record.js +136 -0
- package/src/backend/services/task-records/shared-state.js +548 -0
- package/src/backend/services/task-records/signing.js +140 -0
- package/src/backend/services/task-records/split-signals.js +52 -0
- package/src/backend/services/task-records/test-runs.js +164 -0
- package/src/backend/services/task-records/trust.js +243 -0
- package/src/backend/services/task-rules.js +81 -0
- package/src/backend/services/task-workstreams.js +90 -0
- package/src/backend/services/terminal-history-service.js +266 -0
- package/src/backend/services/terminal-service.js +267 -0
- package/src/backend/services/test-clock.js +53 -0
- package/src/backend/services/tests/grounding.js +266 -0
- package/src/backend/services/tests/junit.js +104 -0
- package/src/backend/services/tests/teammate-runs.js +134 -0
- package/src/backend/services/tests/test-results.js +234 -0
- package/src/backend/services/tree-watcher.js +204 -0
- package/src/backend/services/trellis-service.js +204 -0
- package/src/backend/services/trusted-roots.js +186 -0
- package/src/backend/services/update-download-service.js +342 -0
- package/src/backend/services/update-service.js +266 -0
- package/src/backend/services/watch-ignore.js +76 -0
- package/src/backend/services/webhook-egress.js +206 -0
- package/src/backend/services/webrtc-service.js +413 -0
- package/src/backend/services/work-changes.js +59 -0
- package/src/backend/services/workstream-binding.js +82 -0
- package/src/backend/services/workstream-commits.js +116 -0
- package/src/backend/services/workstream-imports.js +177 -0
- package/src/backend/services/workstream-service.js +335 -0
- package/src/backend/services/workstream-symbols.js +223 -0
- package/src/backend/services/workstream-watch-service.js +391 -0
- package/src/backend/services/worktree-service.js +235 -0
- package/src/cli/agent.js +100 -0
- package/src/cli/args.js +209 -0
- package/src/cli/conformity.js +106 -0
- package/src/cli/desktop.js +91 -0
- package/src/cli/main.js +411 -0
- package/src/cli/pipeline.js +109 -0
- package/src/cli/plan-verbs.js +259 -0
- package/src/cli/review-adapters.js +220 -0
- package/src/cli/review-output.js +93 -0
- package/src/cli/review-post.js +89 -0
- package/src/cli/review-sink.js +183 -0
- package/src/cli/review-skill.js +44 -0
- package/src/cli/review-verify.js +104 -0
- package/src/cli/review.js +386 -0
- package/src/cli/sarif.js +148 -0
- package/src/cli/verbs.js +360 -0
- package/src/shared/build-info.js +39 -0
- package/src/shared/lib/agent-review.js +199 -0
- package/src/shared/lib/agent-turns.js +140 -0
- package/src/shared/lib/awareness-digest.js +120 -0
- package/src/shared/lib/branch-name.js +45 -0
- package/src/shared/lib/breakpoint-words.js +118 -0
- package/src/shared/lib/budget-words.js +56 -0
- package/src/shared/lib/burst.js +69 -0
- package/src/shared/lib/call-entry.js +105 -0
- package/src/shared/lib/check-compare.js +42 -0
- package/src/shared/lib/check-words.js +216 -0
- package/src/shared/lib/csv.js +62 -0
- package/src/shared/lib/folder-entry.js +87 -0
- package/src/shared/lib/freeze-words.js +60 -0
- package/src/shared/lib/fuzzy.js +84 -0
- package/src/shared/lib/git-state-words.js +80 -0
- package/src/shared/lib/grep-entry.js +86 -0
- package/src/shared/lib/grounding-line.js +61 -0
- package/src/shared/lib/import-line.js +71 -0
- package/src/shared/lib/item-status.js +249 -0
- package/src/shared/lib/line-changes.js +53 -0
- package/src/shared/lib/locator.js +71 -0
- package/src/shared/lib/matcher.js +120 -0
- package/src/shared/lib/merge-order.js +65 -0
- package/src/shared/lib/open-findings.js +68 -0
- package/src/shared/lib/other-work.js +108 -0
- package/src/shared/lib/package-entry.js +142 -0
- package/src/shared/lib/plan-vocab.js +40 -0
- package/src/shared/lib/pptx-xml.js +86 -0
- package/src/shared/lib/proposal-words.js +61 -0
- package/src/shared/lib/recurrence.js +183 -0
- package/src/shared/lib/references.js +72 -0
- package/src/shared/lib/rule-pattern.js +56 -0
- package/src/shared/lib/sdp-fingerprint.js +115 -0
- package/src/shared/lib/sdp-minimal.js +123 -0
- package/src/shared/lib/secret-paths.js +34 -0
- package/src/shared/lib/signal-words.js +172 -0
- package/src/shared/lib/signoff.js +56 -0
- package/src/shared/lib/skills-note.js +60 -0
- package/src/shared/lib/spec-sections.js +78 -0
- package/src/shared/lib/symbol-entry.js +60 -0
- package/src/shared/lib/tool-phrasing.js +338 -0
- package/src/shared/lib/turn-gap.js +27 -0
- package/src/shared/lib/workstream-words.js +38 -0
- package/src/shared/lib/xlsx-xml.js +144 -0
- package/src/shared/lib/zip-reader.js +99 -0
- package/src/shared/types/agent.js +27 -0
- package/src/shared/types/architecture-rules.js +30 -0
- package/src/shared/types/ast.js +15 -0
- package/src/shared/types/breakpoint.js +33 -0
- package/src/shared/types/channel.js +41 -0
- package/src/shared/types/criteria.js +15 -0
- package/src/shared/types/graph.js +15 -0
- package/src/shared/types/index.js +53 -0
- package/src/shared/types/peer.js +36 -0
- package/src/shared/types/pipeline.js +15 -0
- package/src/shared/types/plan.js +15 -0
- package/src/shared/types/play-forward.js +15 -0
- package/src/shared/types/power.js +15 -0
- package/src/shared/types/presence.js +15 -0
- package/src/shared/types/project-config.js +35 -0
- package/src/shared/types/project.js +15 -0
- package/src/shared/types/record.js +15 -0
- package/src/shared/types/recurring.js +15 -0
- package/src/shared/types/review.js +15 -0
- package/src/shared/types/settings.js +114 -0
- package/src/shared/types/stack.js +15 -0
- package/src/shared/types/system-doc.js +15 -0
- package/src/shared/types/system.js +15 -0
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var criteria_service_exports = {};
|
|
29
|
+
__export(criteria_service_exports, {
|
|
30
|
+
CRITERION_KINDS: () => CRITERION_KINDS,
|
|
31
|
+
CRITERION_POLICIES: () => CRITERION_POLICIES,
|
|
32
|
+
CriterionError: () => CriterionError,
|
|
33
|
+
GATE_CRITERION_TEXT: () => GATE_CRITERION_TEXT,
|
|
34
|
+
addCriterionAsAgent: () => addCriterionAsAgent,
|
|
35
|
+
addCriterionAsHuman: () => addCriterionAsHuman,
|
|
36
|
+
addVerifiedSignoff: () => addVerifiedSignoff,
|
|
37
|
+
criteriaForExport: () => criteriaForExport,
|
|
38
|
+
criteriaForTemplate: () => criteriaForTemplate,
|
|
39
|
+
decideCriterion: () => decideCriterion,
|
|
40
|
+
defaultPolicy: () => defaultPolicy,
|
|
41
|
+
deleteCriterion: () => deleteCriterion,
|
|
42
|
+
deriveState: () => deriveState,
|
|
43
|
+
ensureCriteria: () => ensureCriteria,
|
|
44
|
+
getCriterion: () => getCriterion,
|
|
45
|
+
importCriteria: () => importCriteria,
|
|
46
|
+
listAwaitingPerson: () => listAwaitingPerson,
|
|
47
|
+
listCriteria: () => listCriteria,
|
|
48
|
+
listSignoffs: () => listSignoffs,
|
|
49
|
+
parseAcceptanceSection: () => parseAcceptanceSection,
|
|
50
|
+
seedTemplateCriteria: () => seedTemplateCriteria,
|
|
51
|
+
submitCriterion: () => submitCriterion,
|
|
52
|
+
unmetHumanCriteria: () => unmetHumanCriteria,
|
|
53
|
+
updateCriterion: () => updateCriterion
|
|
54
|
+
});
|
|
55
|
+
module.exports = __toCommonJS(criteria_service_exports);
|
|
56
|
+
var import_agent_event_log = require("./agent-event-log");
|
|
57
|
+
var import_node_crypto = require("node:crypto");
|
|
58
|
+
var import_database = require("./database");
|
|
59
|
+
var import_plan_service = require("./plan-service");
|
|
60
|
+
var import_trusted_roots = require("./trusted-roots");
|
|
61
|
+
var import_signed_approvals = require("./signed-approvals");
|
|
62
|
+
var import_persistence = require("./persistence");
|
|
63
|
+
var import_human_decision = require("./human-decision");
|
|
64
|
+
var import_artefact_service = require("./artefact-service");
|
|
65
|
+
var _lazy___plan_file_service = __toESM(require("./plan-file-service"));
|
|
66
|
+
const CRITERION_KINDS = ["manual", "artefact", "citation", "code", "test"];
|
|
67
|
+
const CRITERION_POLICIES = ["agent", "propose", "human"];
|
|
68
|
+
const GATE_CRITERION_TEXT = "Reviewed and approved";
|
|
69
|
+
const STRICTNESS = { agent: 0, propose: 1, human: 2 };
|
|
70
|
+
class CriterionError extends Error {
|
|
71
|
+
constructor(message, status = 400) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.status = status;
|
|
74
|
+
}
|
|
75
|
+
status;
|
|
76
|
+
}
|
|
77
|
+
let lastTick = 0;
|
|
78
|
+
function tick() {
|
|
79
|
+
lastTick = Math.max(Date.now(), lastTick + 1);
|
|
80
|
+
return lastTick;
|
|
81
|
+
}
|
|
82
|
+
function defaultPolicy(kind) {
|
|
83
|
+
if (kind === "manual") return "human";
|
|
84
|
+
if (kind === "code") return "agent";
|
|
85
|
+
return "propose";
|
|
86
|
+
}
|
|
87
|
+
function normalisePolicy(kind, policy) {
|
|
88
|
+
return kind === "manual" ? "human" : policy;
|
|
89
|
+
}
|
|
90
|
+
function isKind(v) {
|
|
91
|
+
return typeof v === "string" && CRITERION_KINDS.includes(v);
|
|
92
|
+
}
|
|
93
|
+
function isPolicy(v) {
|
|
94
|
+
return typeof v === "string" && CRITERION_POLICIES.includes(v);
|
|
95
|
+
}
|
|
96
|
+
const CRITERION_COLS = "uid, item_uid, sort_order, text, kind, policy, source, author, author_type, created_at, updated_at, text_updated_at";
|
|
97
|
+
function rows(sql, params = []) {
|
|
98
|
+
return (0, import_database.getDb)().exec(sql, params)[0]?.values ?? [];
|
|
99
|
+
}
|
|
100
|
+
function toEvidence(r) {
|
|
101
|
+
let locator = null;
|
|
102
|
+
if (typeof r[4] === "string") {
|
|
103
|
+
try {
|
|
104
|
+
locator = JSON.parse(r[4]);
|
|
105
|
+
} catch {
|
|
106
|
+
locator = r[4];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
uid: r[0],
|
|
111
|
+
submissionUid: r[2],
|
|
112
|
+
attachmentUid: r[3] ?? null,
|
|
113
|
+
locator,
|
|
114
|
+
note: r[5] ?? null,
|
|
115
|
+
sha256AtSubmit: r[6] ?? null,
|
|
116
|
+
submittedBy: r[7],
|
|
117
|
+
submittedByType: r[8],
|
|
118
|
+
submittedAt: r[9]
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function toSignoff(r) {
|
|
122
|
+
let evidenceHashes = {};
|
|
123
|
+
if (typeof r[8] === "string") {
|
|
124
|
+
try {
|
|
125
|
+
evidenceHashes = JSON.parse(r[8]) ?? {};
|
|
126
|
+
} catch {
|
|
127
|
+
evidenceHashes = {};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
uid: r[0],
|
|
132
|
+
criterionUid: r[1],
|
|
133
|
+
decision: r[2],
|
|
134
|
+
actor: r[3],
|
|
135
|
+
actorType: r[4],
|
|
136
|
+
channel: r[5],
|
|
137
|
+
note: r[6] ?? null,
|
|
138
|
+
createdAt: r[7],
|
|
139
|
+
evidenceHashes,
|
|
140
|
+
anchor: typeof r[9] === "string" ? { attachmentUid: r[9], locator: parseJson(r[10]) } : null,
|
|
141
|
+
device: r[11] ?? null
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function parseJson(v) {
|
|
145
|
+
if (typeof v !== "string") return null;
|
|
146
|
+
try {
|
|
147
|
+
return JSON.parse(v);
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function latestSubmissionOf(criterionUid) {
|
|
153
|
+
const last = rows(
|
|
154
|
+
`SELECT submission_uid FROM criterion_evidence WHERE criterion_uid = ?
|
|
155
|
+
ORDER BY submitted_at DESC LIMIT 1`,
|
|
156
|
+
[criterionUid]
|
|
157
|
+
)[0];
|
|
158
|
+
if (!last) return [];
|
|
159
|
+
return rows(
|
|
160
|
+
`SELECT uid, criterion_uid, submission_uid, attachment_uid, locator, note, sha256_at_submit,
|
|
161
|
+
submitted_by, submitted_by_type, submitted_at
|
|
162
|
+
FROM criterion_evidence WHERE submission_uid = ? ORDER BY rowid`,
|
|
163
|
+
[last[0]]
|
|
164
|
+
).map(toEvidence);
|
|
165
|
+
}
|
|
166
|
+
function latestSignoffOf(criterionUid) {
|
|
167
|
+
const r = rows(
|
|
168
|
+
`SELECT uid, criterion_uid, decision, actor, actor_type, channel, note, created_at, evidence_hashes,
|
|
169
|
+
anchor_attachment_uid, anchor_locator, device
|
|
170
|
+
FROM criterion_signoffs WHERE criterion_uid = ? ORDER BY created_at DESC, rowid DESC LIMIT 1`,
|
|
171
|
+
[criterionUid]
|
|
172
|
+
)[0];
|
|
173
|
+
return r ? toSignoff(r) : null;
|
|
174
|
+
}
|
|
175
|
+
function deriveState(submission, signoff, since = null, evidenceChanged = false) {
|
|
176
|
+
if (since !== null) {
|
|
177
|
+
if (signoff && signoff.createdAt < since) signoff = null;
|
|
178
|
+
if (submission[0] && submission[0].submittedAt < since) submission = [];
|
|
179
|
+
}
|
|
180
|
+
const submittedAt = submission[0]?.submittedAt ?? null;
|
|
181
|
+
if (signoff && (submittedAt === null || signoff.createdAt >= submittedAt)) {
|
|
182
|
+
if (signoff.decision === "approved") return evidenceChanged ? "stale" : "met";
|
|
183
|
+
return "sent_back";
|
|
184
|
+
}
|
|
185
|
+
if (submittedAt !== null) return "submitted";
|
|
186
|
+
return "open";
|
|
187
|
+
}
|
|
188
|
+
function toCriterion(r) {
|
|
189
|
+
const uid = r[0];
|
|
190
|
+
const latestSubmission = latestSubmissionOf(uid);
|
|
191
|
+
const latestSignoff = latestSignoffOf(uid);
|
|
192
|
+
return {
|
|
193
|
+
uid,
|
|
194
|
+
itemUid: r[1],
|
|
195
|
+
sortOrder: r[2],
|
|
196
|
+
text: r[3],
|
|
197
|
+
kind: r[4],
|
|
198
|
+
policy: r[5],
|
|
199
|
+
source: r[6] ?? null,
|
|
200
|
+
author: r[7],
|
|
201
|
+
authorType: r[8],
|
|
202
|
+
createdAt: r[9],
|
|
203
|
+
updatedAt: r[10],
|
|
204
|
+
state: deriveState(
|
|
205
|
+
latestSubmission,
|
|
206
|
+
latestSignoff,
|
|
207
|
+
r[11] ?? null,
|
|
208
|
+
latestSignoff ? evidenceHasChanged(latestSignoff) : false
|
|
209
|
+
),
|
|
210
|
+
latestSubmission,
|
|
211
|
+
latestSignoff
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function evidenceHasChanged(signoff) {
|
|
215
|
+
const recorded = signoff.evidenceHashes ?? {};
|
|
216
|
+
const uids = Object.keys(recorded);
|
|
217
|
+
if (uids.length === 0) return false;
|
|
218
|
+
const now = (0, import_artefact_service.currentHashes)(uids);
|
|
219
|
+
return uids.some((uid) => recorded[uid] !== now[uid]);
|
|
220
|
+
}
|
|
221
|
+
function getCriterion(uid) {
|
|
222
|
+
const r = rows(`SELECT ${CRITERION_COLS} FROM item_criteria WHERE uid = ?`, [uid])[0];
|
|
223
|
+
return r ? toCriterion(r) : null;
|
|
224
|
+
}
|
|
225
|
+
function listCriteria(itemUid) {
|
|
226
|
+
ensureCriteria(itemUid);
|
|
227
|
+
return rows(
|
|
228
|
+
`SELECT ${CRITERION_COLS} FROM item_criteria WHERE item_uid = ? ORDER BY sort_order, created_at`,
|
|
229
|
+
[itemUid]
|
|
230
|
+
).map(toCriterion);
|
|
231
|
+
}
|
|
232
|
+
function listAwaitingPerson(limit = 100) {
|
|
233
|
+
const candidates = rows(
|
|
234
|
+
`SELECT DISTINCT c.uid, i.title, p.uid, p.title
|
|
235
|
+
FROM item_criteria c
|
|
236
|
+
JOIN plan_items i ON i.uid = c.item_uid
|
|
237
|
+
JOIN plans p ON p.uid = i.plan_uid
|
|
238
|
+
WHERE EXISTS (SELECT 1 FROM criterion_evidence e WHERE e.criterion_uid = c.uid)`
|
|
239
|
+
);
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const r of candidates) {
|
|
242
|
+
const criterion = getCriterion(r[0]);
|
|
243
|
+
if (!criterion || criterion.state !== "submitted" && criterion.state !== "stale") continue;
|
|
244
|
+
const at = criterion.state === "stale" ? criterion.latestSignoff?.createdAt ?? 0 : criterion.latestSubmission[0]?.submittedAt ?? 0;
|
|
245
|
+
out.push({
|
|
246
|
+
criterion,
|
|
247
|
+
itemTitle: r[1],
|
|
248
|
+
planUid: r[2],
|
|
249
|
+
planTitle: r[3],
|
|
250
|
+
at
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return out.sort((a, b) => b.at - a.at).slice(0, limit).map(({ at: _at, ...rest }) => rest);
|
|
254
|
+
}
|
|
255
|
+
function listSignoffs(criterionUid) {
|
|
256
|
+
return rows(
|
|
257
|
+
`SELECT uid, criterion_uid, decision, actor, actor_type, channel, note, created_at, evidence_hashes,
|
|
258
|
+
anchor_attachment_uid, anchor_locator, device
|
|
259
|
+
FROM criterion_signoffs WHERE criterion_uid = ? ORDER BY created_at, rowid`,
|
|
260
|
+
[criterionUid]
|
|
261
|
+
).map(toSignoff);
|
|
262
|
+
}
|
|
263
|
+
function unmetHumanCriteria(itemUid) {
|
|
264
|
+
return listCriteria(itemUid).filter((c) => c.policy === "human" && c.state !== "met");
|
|
265
|
+
}
|
|
266
|
+
function parseAcceptanceSection(body) {
|
|
267
|
+
const lines = body.split(/\r?\n/);
|
|
268
|
+
const start = lines.findIndex((l) => /^#{1,6}\s+acceptance criteria\s*$/i.test(l.trim()));
|
|
269
|
+
if (start === -1) return [];
|
|
270
|
+
const section = [];
|
|
271
|
+
for (const line of lines.slice(start + 1)) {
|
|
272
|
+
if (/^#{1,6}\s/.test(line.trim())) break;
|
|
273
|
+
section.push(line);
|
|
274
|
+
}
|
|
275
|
+
const listItem = /^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/;
|
|
276
|
+
const listed = section.map((l) => l.match(listItem)?.[1]).filter((t) => !!t);
|
|
277
|
+
if (listed.length > 0) return listed;
|
|
278
|
+
const prose = section.join("\n").trim();
|
|
279
|
+
return prose ? [prose] : [];
|
|
280
|
+
}
|
|
281
|
+
function writeThroughFor(itemUid) {
|
|
282
|
+
try {
|
|
283
|
+
const r = rows(`SELECT plan_uid FROM plan_items WHERE uid = ?`, [itemUid])[0];
|
|
284
|
+
if (r) _lazy___plan_file_service.scheduleWriteThrough(r[0]);
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function itemRow(itemUid) {
|
|
289
|
+
const r = rows(`SELECT body, requires_approval FROM plan_items WHERE uid = ?`, [itemUid])[0];
|
|
290
|
+
return r ? { body: r[0] ?? "", requiresApproval: !!r[1] } : null;
|
|
291
|
+
}
|
|
292
|
+
function nextSortOrder(itemUid) {
|
|
293
|
+
const r = rows(`SELECT MAX(sort_order) FROM item_criteria WHERE item_uid = ?`, [itemUid])[0];
|
|
294
|
+
return typeof r?.[0] === "number" ? r[0] + 1 : 0;
|
|
295
|
+
}
|
|
296
|
+
function insertCriterion(input) {
|
|
297
|
+
const uid = input.uid ?? (0, import_node_crypto.randomUUID)();
|
|
298
|
+
const now = tick();
|
|
299
|
+
(0, import_database.getDb)().run(
|
|
300
|
+
`INSERT INTO item_criteria (${CRITERION_COLS}) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)`,
|
|
301
|
+
[
|
|
302
|
+
uid,
|
|
303
|
+
input.itemUid,
|
|
304
|
+
input.sortOrder ?? nextSortOrder(input.itemUid),
|
|
305
|
+
input.text,
|
|
306
|
+
input.kind,
|
|
307
|
+
normalisePolicy(input.kind, input.policy),
|
|
308
|
+
input.source,
|
|
309
|
+
input.author,
|
|
310
|
+
input.authorType,
|
|
311
|
+
input.createdAt ?? now,
|
|
312
|
+
now
|
|
313
|
+
]
|
|
314
|
+
);
|
|
315
|
+
writeThroughFor(input.itemUid);
|
|
316
|
+
return uid;
|
|
317
|
+
}
|
|
318
|
+
function ensureCriteria(itemUid) {
|
|
319
|
+
const item = itemRow(itemUid);
|
|
320
|
+
if (!item) return;
|
|
321
|
+
const db = (0, import_database.getDb)();
|
|
322
|
+
let changed = false;
|
|
323
|
+
const migrated = rows(`SELECT 1 FROM item_criteria_migrated WHERE item_uid = ?`, [itemUid]).length > 0;
|
|
324
|
+
if (!migrated) {
|
|
325
|
+
for (const text of parseAcceptanceSection(item.body)) {
|
|
326
|
+
insertCriterion({
|
|
327
|
+
itemUid,
|
|
328
|
+
text,
|
|
329
|
+
kind: "manual",
|
|
330
|
+
policy: "propose",
|
|
331
|
+
source: "migrated",
|
|
332
|
+
author: "migration",
|
|
333
|
+
authorType: "system"
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
db.run(`INSERT OR IGNORE INTO item_criteria_migrated (item_uid, migrated_at) VALUES (?, ?)`, [itemUid, Date.now()]);
|
|
337
|
+
changed = true;
|
|
338
|
+
}
|
|
339
|
+
const gate = rows(`SELECT uid FROM item_criteria WHERE item_uid = ? AND source = 'gate'`, [itemUid])[0];
|
|
340
|
+
if (item.requiresApproval && !gate) {
|
|
341
|
+
insertCriterion({
|
|
342
|
+
itemUid,
|
|
343
|
+
text: GATE_CRITERION_TEXT,
|
|
344
|
+
kind: "manual",
|
|
345
|
+
policy: "human",
|
|
346
|
+
source: "gate",
|
|
347
|
+
author: "gate",
|
|
348
|
+
authorType: "system"
|
|
349
|
+
});
|
|
350
|
+
changed = true;
|
|
351
|
+
} else if (!item.requiresApproval && gate) {
|
|
352
|
+
db.run(`DELETE FROM item_criteria WHERE uid = ?`, [gate[0]]);
|
|
353
|
+
changed = true;
|
|
354
|
+
}
|
|
355
|
+
if (changed) (0, import_persistence.markDirty)();
|
|
356
|
+
}
|
|
357
|
+
function addCriterionAsHuman(itemUid, input, decision) {
|
|
358
|
+
assertHuman(decision);
|
|
359
|
+
requireItem(itemUid);
|
|
360
|
+
const kind = parseKind(input.kind);
|
|
361
|
+
const policy = input.policy === void 0 ? defaultPolicy(kind) : parsePolicy(input.policy);
|
|
362
|
+
const uid = insertCriterion({
|
|
363
|
+
itemUid,
|
|
364
|
+
text: requireText(input.text),
|
|
365
|
+
kind,
|
|
366
|
+
policy,
|
|
367
|
+
source: null,
|
|
368
|
+
author: decision.actor,
|
|
369
|
+
authorType: (0, import_human_decision.actorTypeOf)(decision)
|
|
370
|
+
});
|
|
371
|
+
(0, import_persistence.markDirty)();
|
|
372
|
+
return getCriterion(uid);
|
|
373
|
+
}
|
|
374
|
+
function addCriterionAsAgent(itemUid, input, actor) {
|
|
375
|
+
requireItem(itemUid);
|
|
376
|
+
const kind = parseKind(input.kind);
|
|
377
|
+
const uid = insertCriterion({
|
|
378
|
+
itemUid,
|
|
379
|
+
text: requireText(input.text),
|
|
380
|
+
kind,
|
|
381
|
+
policy: "propose",
|
|
382
|
+
source: null,
|
|
383
|
+
author: actor.author,
|
|
384
|
+
authorType: actor.authorType
|
|
385
|
+
});
|
|
386
|
+
(0, import_persistence.markDirty)();
|
|
387
|
+
return getCriterion(uid);
|
|
388
|
+
}
|
|
389
|
+
function updateCriterion(uid, changes, decision) {
|
|
390
|
+
assertHuman(decision);
|
|
391
|
+
const before = getCriterion(uid);
|
|
392
|
+
if (!before) throw new CriterionError("Criterion not found", 404);
|
|
393
|
+
const sets = [];
|
|
394
|
+
const params = [];
|
|
395
|
+
const now = tick();
|
|
396
|
+
if (changes.text !== void 0) {
|
|
397
|
+
const text = requireText(changes.text);
|
|
398
|
+
if (text !== before.text) {
|
|
399
|
+
sets.push("text = ?", "text_updated_at = ?");
|
|
400
|
+
params.push(text, now);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (changes.policy !== void 0) {
|
|
404
|
+
sets.push("policy = ?");
|
|
405
|
+
params.push(normalisePolicy(before.kind, parsePolicy(changes.policy)));
|
|
406
|
+
}
|
|
407
|
+
if (changes.sortOrder !== void 0) {
|
|
408
|
+
if (typeof changes.sortOrder !== "number" || !Number.isInteger(changes.sortOrder)) {
|
|
409
|
+
throw new CriterionError("sortOrder must be an integer");
|
|
410
|
+
}
|
|
411
|
+
sets.push("sort_order = ?");
|
|
412
|
+
params.push(changes.sortOrder);
|
|
413
|
+
}
|
|
414
|
+
if (sets.length === 0) return before;
|
|
415
|
+
sets.push("updated_at = ?");
|
|
416
|
+
params.push(now);
|
|
417
|
+
(0, import_database.getDb)().run(`UPDATE item_criteria SET ${sets.join(", ")} WHERE uid = ?`, [...params, uid]);
|
|
418
|
+
writeThroughFor(before.itemUid);
|
|
419
|
+
(0, import_persistence.markDirty)();
|
|
420
|
+
return getCriterion(uid);
|
|
421
|
+
}
|
|
422
|
+
function deleteCriterion(uid, decision) {
|
|
423
|
+
assertHuman(decision);
|
|
424
|
+
const existing = getCriterion(uid);
|
|
425
|
+
if (!existing) return false;
|
|
426
|
+
(0, import_database.getDb)().run(`DELETE FROM item_criteria WHERE uid = ?`, [uid]);
|
|
427
|
+
writeThroughFor(existing.itemUid);
|
|
428
|
+
(0, import_persistence.markDirty)();
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
function submitCriterion(criterionUid, input, actor) {
|
|
432
|
+
const criterion = getCriterion(criterionUid);
|
|
433
|
+
if (!criterion) throw new CriterionError("Criterion not found", 404);
|
|
434
|
+
const evidence = input.evidence ?? [];
|
|
435
|
+
const note = typeof input.note === "string" && input.note.trim() ? input.note.trim() : null;
|
|
436
|
+
if (evidence.length === 0 && !note) {
|
|
437
|
+
throw new CriterionError("A submission needs evidence, a note, or both \u2014 say what shows this is met.");
|
|
438
|
+
}
|
|
439
|
+
for (const e of evidence) {
|
|
440
|
+
const owner = rows(
|
|
441
|
+
`SELECT target_type, target_uid FROM attachments WHERE uid = ?`,
|
|
442
|
+
[e.attachmentUid]
|
|
443
|
+
)[0];
|
|
444
|
+
if (!owner) throw new CriterionError(`Attachment ${e.attachmentUid} not found`, 404);
|
|
445
|
+
if (owner[1] !== criterion.itemUid) {
|
|
446
|
+
throw new CriterionError(`Attachment ${e.attachmentUid} belongs to another item; evidence must be attached to this one`);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const db = (0, import_database.getDb)();
|
|
450
|
+
const submissionUid = (0, import_node_crypto.randomUUID)();
|
|
451
|
+
const at = tick();
|
|
452
|
+
const pieces = evidence.length > 0 ? evidence : [null];
|
|
453
|
+
for (const e of pieces) {
|
|
454
|
+
db.run(
|
|
455
|
+
`INSERT INTO criterion_evidence
|
|
456
|
+
(uid, criterion_uid, submission_uid, attachment_uid, locator, note, sha256_at_submit,
|
|
457
|
+
submitted_by, submitted_by_type, submitted_at)
|
|
458
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
459
|
+
[
|
|
460
|
+
(0, import_node_crypto.randomUUID)(),
|
|
461
|
+
criterionUid,
|
|
462
|
+
submissionUid,
|
|
463
|
+
e?.attachmentUid ?? null,
|
|
464
|
+
e?.locator === void 0 || e?.locator === null ? null : JSON.stringify(e.locator),
|
|
465
|
+
note,
|
|
466
|
+
e ? (0, import_artefact_service.currentHashes)([e.attachmentUid])[e.attachmentUid] : null,
|
|
467
|
+
actor.author,
|
|
468
|
+
actor.authorType,
|
|
469
|
+
at
|
|
470
|
+
]
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
if (criterion.policy === "agent") {
|
|
474
|
+
appendSignoff(criterionUid, "approved", actor.author, actor.authorType, "mcp", note);
|
|
475
|
+
}
|
|
476
|
+
(0, import_persistence.markDirty)();
|
|
477
|
+
return getCriterion(criterionUid);
|
|
478
|
+
}
|
|
479
|
+
function decideCriterion(criterionUid, input, decision) {
|
|
480
|
+
assertHuman(decision);
|
|
481
|
+
const criterion = getCriterion(criterionUid);
|
|
482
|
+
if (!criterion) throw new CriterionError("Criterion not found", 404);
|
|
483
|
+
if (input.decision !== "approved" && input.decision !== "sent_back") {
|
|
484
|
+
throw new CriterionError("decision must be approved or sent_back");
|
|
485
|
+
}
|
|
486
|
+
const note = typeof input.note === "string" && input.note.trim() ? input.note.trim() : null;
|
|
487
|
+
if (input.decision === "sent_back" && !note) {
|
|
488
|
+
throw new CriterionError("Say what is wrong \u2014 a send-back note is what the agent reads next.");
|
|
489
|
+
}
|
|
490
|
+
const anchor = input.decision === "sent_back" ? parseAnchor(input.anchor, criterion.itemUid) : null;
|
|
491
|
+
const signed = appendSignoff(criterionUid, input.decision, decision.actor, (0, import_human_decision.actorTypeOf)(decision), decision.channel, note, anchor, decision.device ?? null);
|
|
492
|
+
(0, import_persistence.markDirty)();
|
|
493
|
+
if (input.decision === "approved" && (0, import_human_decision.isHumanDecision)(decision)) signPersonsApproval(criterion, signed);
|
|
494
|
+
return getCriterion(criterionUid);
|
|
495
|
+
}
|
|
496
|
+
function parseAnchor(raw, itemUid) {
|
|
497
|
+
if (raw === void 0 || raw === null) return null;
|
|
498
|
+
if (typeof raw !== "object" || Array.isArray(raw)) throw new CriterionError("anchor must be {attachmentUid, locator}");
|
|
499
|
+
const a = raw;
|
|
500
|
+
if (typeof a.attachmentUid !== "string") throw new CriterionError("anchor.attachmentUid is required");
|
|
501
|
+
const owner = rows(`SELECT target_uid FROM attachments WHERE uid = ?`, [a.attachmentUid])[0];
|
|
502
|
+
if (!owner) throw new CriterionError("The anchored file is not an attachment", 404);
|
|
503
|
+
if (owner[0] !== itemUid) throw new CriterionError("The anchored file belongs to another item");
|
|
504
|
+
const locator = a.locator ?? null;
|
|
505
|
+
if (locator !== null && (typeof locator !== "object" || Array.isArray(locator) || JSON.stringify(locator).length > 2e3)) {
|
|
506
|
+
throw new CriterionError('anchor.locator must be a small object such as {"lines": "4-6"}');
|
|
507
|
+
}
|
|
508
|
+
return { attachmentUid: a.attachmentUid, locator };
|
|
509
|
+
}
|
|
510
|
+
function appendSignoff(criterionUid, decision, actor, actorType, channel, note, anchor = null, device = null) {
|
|
511
|
+
let evidenceHashes = {};
|
|
512
|
+
if (decision === "approved") {
|
|
513
|
+
const uids = latestSubmissionOf(criterionUid).map((e) => e.attachmentUid).filter((u) => !!u);
|
|
514
|
+
evidenceHashes = (0, import_artefact_service.currentHashes)(uids);
|
|
515
|
+
}
|
|
516
|
+
const uid = (0, import_node_crypto.randomUUID)();
|
|
517
|
+
const at = tick();
|
|
518
|
+
(0, import_database.getDb)().run(
|
|
519
|
+
`INSERT INTO criterion_signoffs
|
|
520
|
+
(uid, criterion_uid, decision, actor, actor_type, channel, note, evidence_hashes, created_at,
|
|
521
|
+
anchor_attachment_uid, anchor_locator, device)
|
|
522
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
523
|
+
[
|
|
524
|
+
uid,
|
|
525
|
+
criterionUid,
|
|
526
|
+
decision,
|
|
527
|
+
actor,
|
|
528
|
+
actorType,
|
|
529
|
+
channel,
|
|
530
|
+
note,
|
|
531
|
+
JSON.stringify(evidenceHashes),
|
|
532
|
+
at,
|
|
533
|
+
anchor?.attachmentUid ?? null,
|
|
534
|
+
anchor ? JSON.stringify(anchor.locator) : null,
|
|
535
|
+
device
|
|
536
|
+
]
|
|
537
|
+
);
|
|
538
|
+
(0, import_agent_event_log.recordCriterionDecision)({ criterionUid, decision, actor, actorType, channel });
|
|
539
|
+
return { uid, evidenceHashes, at };
|
|
540
|
+
}
|
|
541
|
+
function addVerifiedSignoff(input) {
|
|
542
|
+
if (rows(`SELECT 1 FROM criterion_signoffs WHERE uid = ?`, [input.uid]).length) return false;
|
|
543
|
+
(0, import_database.getDb)().run(
|
|
544
|
+
`INSERT INTO criterion_signoffs (uid, criterion_uid, decision, actor, actor_type, channel, note, evidence_hashes, created_at)
|
|
545
|
+
VALUES (?, ?, 'approved', ?, 'human', 'file', NULL, ?, ?)`,
|
|
546
|
+
[input.uid, input.criterionUid, input.actor, JSON.stringify(input.evidence), input.at]
|
|
547
|
+
);
|
|
548
|
+
(0, import_persistence.markDirty)();
|
|
549
|
+
return true;
|
|
550
|
+
}
|
|
551
|
+
function criteriaForExport(itemUid) {
|
|
552
|
+
return listCriteria(itemUid).filter((c) => c.source !== "gate").map((c) => ({
|
|
553
|
+
uid: c.uid,
|
|
554
|
+
text: c.text,
|
|
555
|
+
kind: c.kind,
|
|
556
|
+
policy: c.policy,
|
|
557
|
+
sortOrder: c.sortOrder,
|
|
558
|
+
author: c.author,
|
|
559
|
+
authorType: c.authorType,
|
|
560
|
+
createdAt: new Date(c.createdAt).toISOString()
|
|
561
|
+
}));
|
|
562
|
+
}
|
|
563
|
+
function importCriteria(itemUid, raw) {
|
|
564
|
+
if (!Array.isArray(raw)) return 0;
|
|
565
|
+
const db = (0, import_database.getDb)();
|
|
566
|
+
let n = 0;
|
|
567
|
+
for (const entry of raw) {
|
|
568
|
+
if (!entry || typeof entry !== "object") continue;
|
|
569
|
+
const c = entry;
|
|
570
|
+
if (typeof c.uid !== "string" || typeof c.text !== "string" || !c.text.trim()) continue;
|
|
571
|
+
const kind = isKind(c.kind) ? c.kind : "manual";
|
|
572
|
+
let policy = isPolicy(c.policy) ? c.policy : defaultPolicy(kind);
|
|
573
|
+
const existing = getCriterion(c.uid);
|
|
574
|
+
if (existing) {
|
|
575
|
+
if (existing.itemUid !== itemUid) continue;
|
|
576
|
+
if (STRICTNESS[policy] < STRICTNESS[existing.policy]) policy = existing.policy;
|
|
577
|
+
const now = tick();
|
|
578
|
+
const reworded = c.text.trim() !== existing.text;
|
|
579
|
+
db.run(
|
|
580
|
+
`UPDATE item_criteria SET text = ?, policy = ?, sort_order = ?, updated_at = ?,
|
|
581
|
+
text_updated_at = CASE WHEN ? THEN ? ELSE text_updated_at END
|
|
582
|
+
WHERE uid = ?`,
|
|
583
|
+
[
|
|
584
|
+
c.text.trim(),
|
|
585
|
+
normalisePolicy(existing.kind, policy),
|
|
586
|
+
typeof c.sortOrder === "number" ? c.sortOrder : existing.sortOrder,
|
|
587
|
+
now,
|
|
588
|
+
reworded ? 1 : 0,
|
|
589
|
+
now,
|
|
590
|
+
c.uid
|
|
591
|
+
]
|
|
592
|
+
);
|
|
593
|
+
} else {
|
|
594
|
+
if (policy === "agent" && kind !== "code") policy = "propose";
|
|
595
|
+
insertCriterion({
|
|
596
|
+
uid: c.uid,
|
|
597
|
+
itemUid,
|
|
598
|
+
text: c.text.trim(),
|
|
599
|
+
kind,
|
|
600
|
+
policy,
|
|
601
|
+
source: "import",
|
|
602
|
+
author: typeof c.author === "string" ? c.author : "file-import",
|
|
603
|
+
authorType: "file-import",
|
|
604
|
+
sortOrder: typeof c.sortOrder === "number" ? c.sortOrder : void 0,
|
|
605
|
+
createdAt: typeof c.createdAt === "string" ? Date.parse(c.createdAt) || void 0 : void 0
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
n++;
|
|
609
|
+
}
|
|
610
|
+
db.run(`INSERT OR IGNORE INTO item_criteria_migrated (item_uid, migrated_at) VALUES (?, ?)`, [itemUid, Date.now()]);
|
|
611
|
+
(0, import_persistence.markDirty)();
|
|
612
|
+
return n;
|
|
613
|
+
}
|
|
614
|
+
function seedTemplateCriteria(itemUid, raw, templateId) {
|
|
615
|
+
if (!Array.isArray(raw)) return 0;
|
|
616
|
+
let n = 0;
|
|
617
|
+
for (const entry of raw.slice(0, MAX_TEMPLATE_CRITERIA)) {
|
|
618
|
+
const c = typeof entry === "string" ? { text: entry } : entry;
|
|
619
|
+
if (!c || typeof c !== "object") continue;
|
|
620
|
+
const row = c;
|
|
621
|
+
if (typeof row.text !== "string" || !row.text.trim()) continue;
|
|
622
|
+
const kind = isKind(row.kind) ? row.kind : "manual";
|
|
623
|
+
let policy = isPolicy(row.policy) ? row.policy : defaultPolicy(kind);
|
|
624
|
+
if (policy === "agent" && kind !== "code") policy = "propose";
|
|
625
|
+
insertCriterion({
|
|
626
|
+
itemUid,
|
|
627
|
+
text: row.text.trim().slice(0, MAX_CRITERION_TEXT),
|
|
628
|
+
kind,
|
|
629
|
+
policy,
|
|
630
|
+
source: "template",
|
|
631
|
+
author: `template:${templateId}`,
|
|
632
|
+
authorType: "template"
|
|
633
|
+
});
|
|
634
|
+
n++;
|
|
635
|
+
}
|
|
636
|
+
if (n > 0) {
|
|
637
|
+
(0, import_database.getDb)().run(`INSERT OR IGNORE INTO item_criteria_migrated (item_uid, migrated_at) VALUES (?, ?)`, [itemUid, Date.now()]);
|
|
638
|
+
(0, import_persistence.markDirty)();
|
|
639
|
+
}
|
|
640
|
+
return n;
|
|
641
|
+
}
|
|
642
|
+
const MAX_TEMPLATE_CRITERIA = 50;
|
|
643
|
+
const MAX_CRITERION_TEXT = 2e3;
|
|
644
|
+
function criteriaForTemplate(itemUid) {
|
|
645
|
+
return listCriteria(itemUid).filter((c) => c.source !== "gate").map((c) => ({ text: c.text, kind: c.kind, policy: c.policy }));
|
|
646
|
+
}
|
|
647
|
+
function assertHuman(decision) {
|
|
648
|
+
if (!(0, import_human_decision.isHumanDecision)(decision) && !(0, import_human_decision.isUnverifiedDecision)(decision)) {
|
|
649
|
+
throw new CriterionError("Only a person can do this, from CodeTrellis or a paired phone.", 403);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function requireItem(itemUid) {
|
|
653
|
+
if (!itemRow(itemUid)) throw new CriterionError("Item not found", 404);
|
|
654
|
+
}
|
|
655
|
+
function requireText(text) {
|
|
656
|
+
if (typeof text !== "string" || !text.trim()) throw new CriterionError("text is required");
|
|
657
|
+
return text.trim();
|
|
658
|
+
}
|
|
659
|
+
function parseKind(kind) {
|
|
660
|
+
if (kind === void 0) return "manual";
|
|
661
|
+
if (!isKind(kind)) throw new CriterionError(`kind must be one of: ${CRITERION_KINDS.join(", ")}`);
|
|
662
|
+
return kind;
|
|
663
|
+
}
|
|
664
|
+
function parsePolicy(policy) {
|
|
665
|
+
if (!isPolicy(policy)) throw new CriterionError(`policy must be one of: ${CRITERION_POLICIES.join(", ")}`);
|
|
666
|
+
return policy;
|
|
667
|
+
}
|
|
668
|
+
function signPersonsApproval(criterion, signed) {
|
|
669
|
+
try {
|
|
670
|
+
const planRow = rows(`SELECT plan_uid FROM plan_items WHERE uid = ?`, [criterion.itemUid])[0];
|
|
671
|
+
if (!planRow) return;
|
|
672
|
+
const planUid = planRow[0];
|
|
673
|
+
const plan = (0, import_plan_service.getPlan)(planUid);
|
|
674
|
+
let repo = null;
|
|
675
|
+
try {
|
|
676
|
+
repo = plan?.projectPath ? (0, import_trusted_roots.resolveTrustedProjectRoot)(plan.projectPath, "signed approval") : null;
|
|
677
|
+
} catch {
|
|
678
|
+
repo = null;
|
|
679
|
+
}
|
|
680
|
+
const planDir = repo ? _lazy___plan_file_service.getLinkedPlanDir(planUid, repo) : null;
|
|
681
|
+
(0, import_signed_approvals.signApproval)({
|
|
682
|
+
signoffUid: signed.uid,
|
|
683
|
+
planUid,
|
|
684
|
+
itemUid: criterion.itemUid,
|
|
685
|
+
criterionUid: criterion.uid,
|
|
686
|
+
criterionText: criterion.text,
|
|
687
|
+
evidence: signed.evidenceHashes,
|
|
688
|
+
at: signed.at,
|
|
689
|
+
repo,
|
|
690
|
+
planDir
|
|
691
|
+
});
|
|
692
|
+
} catch {
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
696
|
+
0 && (module.exports = {
|
|
697
|
+
CRITERION_KINDS,
|
|
698
|
+
CRITERION_POLICIES,
|
|
699
|
+
CriterionError,
|
|
700
|
+
GATE_CRITERION_TEXT,
|
|
701
|
+
addCriterionAsAgent,
|
|
702
|
+
addCriterionAsHuman,
|
|
703
|
+
addVerifiedSignoff,
|
|
704
|
+
criteriaForExport,
|
|
705
|
+
criteriaForTemplate,
|
|
706
|
+
decideCriterion,
|
|
707
|
+
defaultPolicy,
|
|
708
|
+
deleteCriterion,
|
|
709
|
+
deriveState,
|
|
710
|
+
ensureCriteria,
|
|
711
|
+
getCriterion,
|
|
712
|
+
importCriteria,
|
|
713
|
+
listAwaitingPerson,
|
|
714
|
+
listCriteria,
|
|
715
|
+
listSignoffs,
|
|
716
|
+
parseAcceptanceSection,
|
|
717
|
+
seedTemplateCriteria,
|
|
718
|
+
submitCriterion,
|
|
719
|
+
unmetHumanCriteria,
|
|
720
|
+
updateCriterion
|
|
721
|
+
});
|