@starci/hfs 4.0.8 → 4.0.10
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +81 -39
- package/emit/contracts.mjs +56 -9
- package/emit/db-types.mjs +169 -0
- package/emit/events.mjs +41 -0
- package/emit/operations-worker.mjs +1 -1
- package/emit/operations.mjs +2 -2
- package/emit/schema-worker.mjs +1 -1
- package/lint/bound-sys.cjs +50 -0
- package/lint/run.mjs +131 -24
- package/package.json +16 -10
- package/report/sonar.mjs +8 -8
- package/runtime/engine/admission.mjs +308 -0
- package/runtime/engine/canonical-json.mjs +10 -0
- package/runtime/engine/config.mjs +486 -0
- package/runtime/engine/db/blob.mjs +315 -0
- package/runtime/engine/db/ledger-paths.mjs +83 -0
- package/runtime/engine/db/ledger.mjs +1118 -0
- package/runtime/engine/db/machine-connection.mjs +135 -0
- package/runtime/engine/db/machine-schema.mjs +84 -0
- package/runtime/engine/db/machine.mjs +1367 -0
- package/runtime/engine/db/migrations/machine/0001-init.sql +924 -0
- package/runtime/engine/db/migrations/runtime/0001-init.sql +1108 -0
- package/runtime/engine/db/provider-reservations.mjs +101 -0
- package/runtime/engine/digest.mjs +16 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/engine/secrets.mjs +130 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +162 -31
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +912 -120
- package/runtime/knowledge/patterns/be/api.yaml +369 -0
- package/runtime/knowledge/patterns/be/cli.yaml +297 -0
- package/runtime/knowledge/patterns/be/domain.yaml +113 -0
- package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
- package/runtime/knowledge/patterns/be/projections.yaml +178 -0
- package/runtime/knowledge/patterns/be/queues.yaml +236 -0
- package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
- package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
- package/runtime/knowledge/patterns/be/saga.yaml +248 -0
- package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
- package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
- package/runtime/knowledge/sonar-gate.yaml +52 -18
- package/runtime/modules/kernel/failure-codes.yaml +611 -51
- package/runtime/modules/models/registry.yaml +553 -0
- package/runtime/modules/models/runtimes.yaml +695 -0
- package/runtime/modules/ops/_labels.yaml +53 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
- package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
- package/runtime/scripts/api/fs/lib.mjs +16 -2
- package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
- package/runtime/scripts/api/git/config-get.mjs +10 -0
- package/runtime/scripts/api/git/diff.mjs +5 -0
- package/runtime/scripts/api/git/lib.mjs +18 -26
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/node/lib.mjs +14 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/process/lib.mjs +111 -0
- package/runtime/scripts/api/process/owned-process.mjs +78 -0
- package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -0
- package/runtime/scripts/api/process/run-program.mjs +7 -0
- package/runtime/scripts/api/process/stop-owned-process.mjs +9 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -12
- package/runtime/scripts/api/sops/lib.mjs +255 -0
- package/runtime/scripts/api/sops/seal.mjs +22 -0
- package/runtime/scripts/connectors/lib.mjs +488 -0
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
- package/runtime/scripts/hfs/architecture/config.mjs +9 -6
- package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
- package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
- package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
- package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -2
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
- package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/hfs/architecture/index.mjs +17 -4
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
- package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
- package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
- package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
- package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
- package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
- package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
- package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
- package/runtime/scripts/hfs/architecture.mjs +4 -4
- package/runtime/scripts/hfs/check.mjs +75 -72
- package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
- package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
- package/runtime/scripts/hfs/edition-slots.mjs +66 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/hfs/path-findings.mjs +1 -1
- package/runtime/scripts/hfs/repo-identity.mjs +9 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
- package/runtime/scripts/hfs/rules/cli.mjs +37 -0
- package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
- package/runtime/scripts/hfs/rules/contract.mjs +10 -9
- package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
- package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
- package/runtime/scripts/hfs/rules/database.mjs +68 -0
- package/runtime/scripts/hfs/rules/deps.mjs +1 -1
- package/runtime/scripts/hfs/rules/docker.mjs +259 -0
- package/runtime/scripts/hfs/rules/edition.mjs +111 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
- package/runtime/scripts/hfs/rules/read.mjs +1 -1
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +209 -0
- package/runtime/scripts/hfs/slots.mjs +78 -108
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/hfs/tree.mjs +5 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
- package/runtime/scripts/hfs/view.mjs +3 -2
- package/runtime/scripts/lib/clip.mjs +19 -0
- package/runtime/scripts/lib/display-names.mjs +259 -0
- package/runtime/scripts/lib/dockerfile.mjs +124 -0
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +103 -0
- package/runtime/scripts/lib/example-refs.mjs +158 -0
- package/runtime/scripts/lib/fs-kind.mjs +14 -4
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/json-schema.mjs +52 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +24 -5
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/mutation-fence.mjs +15 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/process-identity.mjs +6 -0
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/read-yaml.mjs +18 -0
- package/runtime/scripts/lib/redact.mjs +161 -0
- package/runtime/scripts/lib/regex.mjs +4 -0
- package/runtime/scripts/lib/same-text.mjs +5 -0
- package/runtime/scripts/lib/secret-patterns.mjs +4 -3
- package/runtime/scripts/lib/sleep-sync.mjs +2 -1
- package/runtime/scripts/lib/sleep.mjs +7 -0
- package/runtime/scripts/lib/sops-envelope.mjs +112 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -0
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/lib/walk.mjs +4 -3
- package/scaffold/add-app.mjs +174 -0
- package/scaffold/add-cli-lite.mjs +257 -0
- package/scaffold/add-inbox-lite.mjs +314 -0
- package/scaffold/add-table.mjs +344 -0
- package/scaffold/add.mjs +299 -0
- package/scaffold/app.mjs +358 -69
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +32 -26
- package/scaffold/supabase-ports.mjs +26 -0
- package/src/main.mjs +327 -0
- package/sync/cli.mjs +6 -9
- package/sync/format.mjs +1 -1
- package/sync/hygiene.mjs +10 -10
- package/sync/index.mjs +167 -104
- package/sync/managed.mjs +19 -11
- package/sync/sonar-key.mjs +5 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
- package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
- package/templates/app/ci-workflows/sonar-steps.yml +9 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
- package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
- package/templates/app/docker-ignore/dockerignore +50 -0
- package/templates/app/gitignore-lite +35 -0
- package/templates/app/hooks/husky/pre-commit +3 -8
- package/templates/app/hooks/husky/pre-push +45 -6
- package/templates/app/package-scripts/core.json +8 -0
- package/templates/app/package-scripts/package.json +3 -10
- package/templates/app/package-scripts-lite/package.json +15 -0
- package/templates/app/quality-config/codecov.yml +15 -3
- package/templates/app/quality-config/sonar-base.properties +4 -0
- package/templates/app/quality-config/sonar-project.properties +1 -4
- package/templates/app/quality-config-lite/sonar-project.properties +5 -0
- package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
- package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
- package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
- package/templates/app/skeleton/README.md +7 -5
- package/templates/app/skeleton-lite/.sops.yaml +3 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
- package/templates/app/skeleton-lite/README.md +40 -0
- package/templates/app/skeleton-lite/supabase/config.toml +41 -0
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
- package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
- package/templates/app/task-graph/turbo.json +23 -0
- package/templates/app/tool-config-lite/prettierignore +2 -0
- package/templates/app/tool-config-lite/prettierrc +1 -0
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
- package/templates/be/common/main-listen.ts.partial +14 -0
- package/templates/be/common/sql-ident.ts.partial +7 -0
- package/templates/be/common/sql-tag.ts.partial +5 -0
- package/templates/be/image/api/Dockerfile +38 -0
- package/templates/be/image/cli/Dockerfile +38 -0
- package/templates/be/image/worker/Dockerfile +36 -0
- package/templates/be/inbox/database-client.ts.tpl +50 -0
- package/templates/be/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/migration.sql.tpl +29 -0
- package/templates/be/inbox/module-definition.ts.tpl +6 -0
- package/templates/be/inbox/module.ts.tpl +14 -0
- package/templates/be/inbox/options.ts.tpl +2 -0
- package/templates/be/inbox/service.ts.tpl +33 -0
- package/templates/be/inbox/sql.ts.tpl +16 -0
- package/templates/be/inbox/webhook-door.ts.tpl +36 -0
- package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
- package/templates/be/patterns/api/application.command.ts.tpl +10 -0
- package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
- package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
- package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
- package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
- package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
- package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
- package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
- package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/http.module.ts.tpl +7 -0
- package/templates/be/patterns/api/http.request.ts.tpl +10 -0
- package/templates/be/patterns/api/http.response.ts.tpl +5 -0
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
- package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
- package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
- package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
- package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
- package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
- package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
- package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
- package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
- package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
- package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
- package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
- package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
- package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
- package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
- package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
- package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
- package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
- package/templates/be/patterns/projections/migration.ts.tpl +18 -0
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
- package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
- package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
- package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
- package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
- package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
- package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
- package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
- package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
- package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
- package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
- package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
- package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
- package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
- package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
- package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
- package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
- package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
- package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
- package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
- package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
- package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
- package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
- package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/index.ts +1 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
- package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
- package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
- package/templates/be/table/error.ts.tpl +16 -0
- package/templates/be/table/index.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +51 -0
- package/templates/be/table/module-definition.ts.tpl +7 -0
- package/templates/be/table/module.ts.tpl +14 -0
- package/templates/be/table/options.ts.tpl +2 -0
- package/templates/be/table/rows.ts.tpl +4 -0
- package/templates/be/table/service.ts.tpl +23 -0
- package/templates/be/table/sql.ts.tpl +4 -0
- package/templates/be/tool-config/jest.config.js +3 -1
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
- package/templates/common/gitignore.base +1 -2
- package/templates/fe/app/package.json.tpl +1 -0
- package/templates/fe/app/tsconfig.json +12 -0
- package/templates/fe/common/app-layout.tsx.partial +8 -0
- package/templates/fe/common/site-shell.tsx.partial +9 -0
- package/templates/fe/image/next/Dockerfile +42 -0
- package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
- package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
- package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
- package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
- package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
- package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
- package/templates/fe/table/read.ts.tpl +16 -0
- package/templates/fe/table/write.ts.tpl +16 -0
- package/upgrade/check.mjs +137 -0
- package/upgrade/index.mjs +524 -0
- package/bin/hfs.mjs +0 -233
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
- package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
- /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
|
@@ -0,0 +1,1108 @@
|
|
|
1
|
+
-- ############################################################################################################
|
|
2
|
+
-- runtime.sqlite (one per project) - this file is the single schema step of the database.
|
|
3
|
+
-- Tables: meta/schema_migrations - identity and migration journal; ui_states/ui_state_map - display vocabulary;
|
|
4
|
+
-- blob_ref_columns - blob-referencing columns; workflow_transitions/job_transitions - legal state changes;
|
|
5
|
+
-- blobs - content-store index; workflows/lifecycle_changes/goals/goal_inputs/workflow_purges - workflow and goal;
|
|
6
|
+
-- work_graph_versions/work_units/unit_edges - work graph; jobs/op_attempts/contracts/resources/leases/
|
|
7
|
+
-- api_requests - tries, dispatches, fencing, idempotency; reports/check_runs/settle_tails/product_lands/
|
|
8
|
+
-- job_artifacts/attempt_transcript_snapshots/report_attachments/artifact_proofs/work_citations/
|
|
9
|
+
-- interface_audits/llm_usage - outcomes and evidence; inbox/decision_items/decisions/conditions/incidents/
|
|
10
|
+
-- foundations/foundation_declarations/path_transfers/record_changes/signals - coordination; events/logs/
|
|
11
|
+
-- log_cursors - append-only journal and typed logs; v_* - durable read views.
|
|
12
|
+
-- ############################################################################################################
|
|
13
|
+
|
|
14
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
15
|
+
-- A0. Identity, version, vocabulary
|
|
16
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
17
|
+
-- meta: ledger_id (UUID, key of machine.ledgers), schema='starci/runtime@1', created_at, journal_mode, product,
|
|
18
|
+
-- repo_root, blob_root, runtime_rev, sqlite_version.
|
|
19
|
+
CREATE TABLE IF NOT EXISTS meta(
|
|
20
|
+
key TEXT PRIMARY KEY,
|
|
21
|
+
value TEXT NOT NULL) STRICT;
|
|
22
|
+
|
|
23
|
+
-- schema_migrations: 0001-init writes the first row; each later step: backup VACUUM INTO + integrity_check first,
|
|
24
|
+
-- foreign_key_check + quick_check before COMMIT. Code refuses to WRITE when user_version > CODE_VERSION.
|
|
25
|
+
CREATE TABLE IF NOT EXISTS schema_migrations(
|
|
26
|
+
version INTEGER PRIMARY KEY,
|
|
27
|
+
name TEXT NOT NULL, -- '0001-init'
|
|
28
|
+
runtime_rev TEXT,
|
|
29
|
+
sql_sha256 TEXT NOT NULL, -- digest of the migration file that ran
|
|
30
|
+
backup_path TEXT, backup_sha256 TEXT,
|
|
31
|
+
started_at INTEGER NOT NULL,
|
|
32
|
+
finished_at INTEGER,
|
|
33
|
+
status TEXT NOT NULL CHECK(status IN ('running','done','failed'))) STRICT;
|
|
34
|
+
|
|
35
|
+
-- ui_states: the ONLY set of display states (UI-API sec. 3.4).
|
|
36
|
+
CREATE TABLE IF NOT EXISTS ui_states(
|
|
37
|
+
ui TEXT PRIMARY KEY CHECK(ui IN ('bad','warn','running','waiting','ok','done','unknown')),
|
|
38
|
+
rank INTEGER NOT NULL) STRICT; -- sort order: bad first
|
|
39
|
+
INSERT OR IGNORE INTO ui_states VALUES
|
|
40
|
+
('bad',0),('warn',1),('running',2),('waiting',3),('ok',4),('done',5),('unknown',6);
|
|
41
|
+
|
|
42
|
+
-- ui_state_map: static mapping of native state -> ui. Time-based rules (overdue, SLA) live in views/functions and
|
|
43
|
+
-- may only RAISE the level (waiting->warn->bad), never lower it.
|
|
44
|
+
CREATE TABLE IF NOT EXISTS ui_state_map(
|
|
45
|
+
entity TEXT NOT NULL, native TEXT NOT NULL,
|
|
46
|
+
ui TEXT NOT NULL REFERENCES ui_states(ui),
|
|
47
|
+
PRIMARY KEY(entity,native)) STRICT;
|
|
48
|
+
INSERT OR IGNORE INTO ui_state_map VALUES
|
|
49
|
+
('workflow','awaiting-approval','waiting'),('workflow','queued','waiting'),('workflow','running','running'),
|
|
50
|
+
('workflow','paused','waiting'),('workflow','stopped','done'),('workflow','finished','done'),('workflow','archived','done'),
|
|
51
|
+
('unit','planned','waiting'),('unit','queued','waiting'),('unit','deciding','waiting'),('unit','running','running'),
|
|
52
|
+
('unit','reported','running'),('unit','failed','bad'),('unit','done','done'),('unit','dropped','done'),
|
|
53
|
+
('job','queued','waiting'),('job','ready','waiting'),('job','leased','running'),('job','running','running'),
|
|
54
|
+
('job','answering','waiting'),('job','reported','running'),('job','deciding','waiting'),('job','awaiting_owner','waiting'),
|
|
55
|
+
('job','effect_unknown','bad'),('job','succeeded','done'),('job','failed','bad'),('job','cancelled','done'),
|
|
56
|
+
('attempt','routed','waiting'),('attempt','in-flight','running'),('attempt','pass','done'),('attempt','partial','warn'),
|
|
57
|
+
('attempt','fail','bad'),('attempt','blocked','bad'),('attempt','worker-dead','bad'),('attempt','effect-unknown','bad'),
|
|
58
|
+
('attempt','requeued','done'),('attempt','cancelled','done'),('attempt','dropped','done'),
|
|
59
|
+
('check','pass','ok'),('check','fail','bad'),('check','error','bad'),('check','unavailable','warn'),('check','skipped','waiting'),
|
|
60
|
+
('decision','open','waiting'),('decision','claimed','running'),('decision','escalated','warn'),('decision','expired','warn'),
|
|
61
|
+
('decision','resolved','done'),('decision','superseded','done'),
|
|
62
|
+
('incident','open','bad'),('incident','resolved','done'),('incident','superseded','done'),
|
|
63
|
+
('condition','True','ok'),('condition','False','bad'),('condition','Unknown','waiting'),
|
|
64
|
+
('settle-tail','queued','waiting'),('settle-tail','running','running'),('settle-tail','done','done'),('settle-tail','failed','bad'),
|
|
65
|
+
('product-land','queued','waiting'),('product-land','landed','done'),('product-land','failed','bad'),('product-land','conflict','bad'),
|
|
66
|
+
('product-land','red','bad'),('product-land','busy','warn'),('product-land','main-moving','warn');
|
|
67
|
+
|
|
68
|
+
-- blob_ref_columns: the ONLY list of blob-referencing columns (GC mark reads this table; a spec diffs every
|
|
69
|
+
-- *_sha / sha256 column against sqlite_master so no new column is missed).
|
|
70
|
+
CREATE TABLE IF NOT EXISTS blob_ref_columns(
|
|
71
|
+
table_name TEXT NOT NULL, column_name TEXT NOT NULL, PRIMARY KEY(table_name,column_name)) STRICT;
|
|
72
|
+
INSERT OR IGNORE INTO blob_ref_columns VALUES
|
|
73
|
+
('goal_inputs','sha256'),('op_attempts','prompt_sha'),('op_attempts','transcript_sha'),('op_attempts','session_sha'),
|
|
74
|
+
('attempt_transcript_snapshots','sha256'),
|
|
75
|
+
('check_runs','stdout_sha'),('check_runs','stderr_sha'),('check_runs','output_sha'),('job_artifacts','sha256'),
|
|
76
|
+
('work_citations','sha256'),('product_lands','output_sha'),('events','payload_sha');
|
|
77
|
+
|
|
78
|
+
-- Valid state-transition tables (data, not code). Triggers in A2/A3/A4 refuse any pair not listed here.
|
|
79
|
+
CREATE TABLE IF NOT EXISTS workflow_transitions(
|
|
80
|
+
from_phase TEXT NOT NULL, to_phase TEXT NOT NULL, PRIMARY KEY(from_phase,to_phase)) STRICT;
|
|
81
|
+
INSERT OR IGNORE INTO workflow_transitions VALUES
|
|
82
|
+
('awaiting-approval','queued'),('awaiting-approval','stopped'),
|
|
83
|
+
('queued','running'),('queued','stopped'),
|
|
84
|
+
('running','paused'),('running','stopped'),('running','finished'),
|
|
85
|
+
('paused','running'),('paused','stopped'),
|
|
86
|
+
('stopped','queued'), -- only the owner verb `starci kernel lifecycle --resume`; a controller NEVER does this (MB-08)
|
|
87
|
+
('stopped','archived'),('finished','archived');
|
|
88
|
+
CREATE TABLE IF NOT EXISTS job_transitions(
|
|
89
|
+
from_status TEXT NOT NULL, to_status TEXT NOT NULL, PRIMARY KEY(from_status,to_status)) STRICT;
|
|
90
|
+
INSERT OR IGNORE INTO job_transitions VALUES
|
|
91
|
+
('queued','ready'),('queued','cancelled'),
|
|
92
|
+
('ready','queued'),('ready','leased'),('ready','cancelled'),
|
|
93
|
+
('leased','running'),('leased','ready'),('leased','cancelled'), -- leased->ready: dispatch refused before the op accepts the contract (H13: spends no try)
|
|
94
|
+
('leased','failed'), -- leased->failed: settling a dispatch that died mid-launch after terminal/attempt were created
|
|
95
|
+
('running','answering'),('answering','running'),
|
|
96
|
+
('running','reported'),('answering','reported'),
|
|
97
|
+
('running','effect_unknown'),('running','ready'),('running','failed'),('running','cancelled'), -- running->ready: requeue after worker death
|
|
98
|
+
('effect_unknown','running'),('effect_unknown','ready'),('effect_unknown','failed'),
|
|
99
|
+
('reported','deciding'),('reported','succeeded'),('reported','failed'), -- reported NEVER -> cancelled (H9: archive does not drop reported work)
|
|
100
|
+
('deciding','succeeded'),('deciding','failed'),('deciding','cancelled'),
|
|
101
|
+
('reported','awaiting_owner'),('deciding','awaiting_owner'); -- an op may end with a question: waiting on the owner, not a failure (awaiting_owner is terminal, like failed)
|
|
102
|
+
|
|
103
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
104
|
+
-- A1. Blob - index of the content store (content is NOT in SQLite)
|
|
105
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
106
|
+
CREATE TABLE IF NOT EXISTS blobs(
|
|
107
|
+
sha256 TEXT PRIMARY KEY CHECK(length(sha256)=64),
|
|
108
|
+
bytes INTEGER NOT NULL CHECK(bytes>=0),
|
|
109
|
+
media_type TEXT NOT NULL, -- image/png, video/webm, application/json, text/plain, text/x-diff ...
|
|
110
|
+
encoding TEXT CHECK(encoding IS NULL OR encoding='gzip'), -- sha256 is always of the ORIGINAL bytes
|
|
111
|
+
redaction TEXT CHECK(redaction IS NULL OR redaction IN ('v1','binary')), -- 'v1' = text passed through secret filter v1
|
|
112
|
+
-- (transcript, log); 'binary' = binary that cannot be filtered; NULL = not needed
|
|
113
|
+
file_uri TEXT NOT NULL, -- absolute path, e.g. <blob_root>/ab/ab12...
|
|
114
|
+
http_path TEXT GENERATED ALWAYS AS ('/api/blob/' || sha256) VIRTUAL, -- served by the harness; cannot drift
|
|
115
|
+
created_at INTEGER NOT NULL, -- GC never touches a blob younger than 24 hours (put-before-insert grace)
|
|
116
|
+
pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0,1)), -- 1 once a Work record cites it: never swept
|
|
117
|
+
archived_at INTEGER, -- already inside a verified zip in the archive store
|
|
118
|
+
archive_ref TEXT) STRICT; -- '<zip path>!<entry>'
|
|
119
|
+
|
|
120
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
121
|
+
-- A2. Workflow and goal
|
|
122
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
123
|
+
CREATE TABLE IF NOT EXISTS workflows(
|
|
124
|
+
workflow_id TEXT PRIMARY KEY, -- wf-<slug>-<base36 time>
|
|
125
|
+
trace_id TEXT NOT NULL UNIQUE CHECK(length(trace_id)=32), -- W3C trace id, minted at creation, kept across revisions
|
|
126
|
+
title TEXT,
|
|
127
|
+
display_name TEXT, -- '<Product> - <what>' (Vietnamese)
|
|
128
|
+
phase TEXT NOT NULL CHECK(phase IN ('awaiting-approval','queued','running','paused','stopped','finished','archived')),
|
|
129
|
+
phase_reason TEXT, -- reason for the latest phase change (detail in lifecycle_changes)
|
|
130
|
+
ledger_mode TEXT,
|
|
131
|
+
source_roots_json TEXT CHECK(source_roots_json IS NULL OR json_valid(source_roots_json)),
|
|
132
|
+
generation INTEGER NOT NULL DEFAULT 0, -- spec: bumped when the goal/graph changes
|
|
133
|
+
observed_generation INTEGER NOT NULL DEFAULT 0, -- status: the generation the Kernel has fully processed (K8s)
|
|
134
|
+
goal_identity TEXT,
|
|
135
|
+
pin_digest TEXT,
|
|
136
|
+
allowed_parallel INTEGER,
|
|
137
|
+
finished_json TEXT CHECK(finished_json IS NULL OR json_valid(finished_json)),
|
|
138
|
+
created_at INTEGER NOT NULL,
|
|
139
|
+
updated_at INTEGER NOT NULL,
|
|
140
|
+
finished_at INTEGER,
|
|
141
|
+
archived_at INTEGER) STRICT;
|
|
142
|
+
|
|
143
|
+
-- lifecycle_changes: phase-change history, append-only (MB-08, G10). Sole writer: the `starci kernel lifecycle` verb
|
|
144
|
+
-- (define-goal, start, pause, stop, resume, finish, archive) - same transaction as UPDATE workflows.phase.
|
|
145
|
+
CREATE TABLE IF NOT EXISTS lifecycle_changes(
|
|
146
|
+
change_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
147
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
148
|
+
from_phase TEXT, to_phase TEXT NOT NULL,
|
|
149
|
+
by TEXT NOT NULL, -- owner | kernel:<wf> | supervisor
|
|
150
|
+
reason TEXT NOT NULL,
|
|
151
|
+
at INTEGER NOT NULL) STRICT;
|
|
152
|
+
CREATE TRIGGER IF NOT EXISTS lifecycle_changes_append_only BEFORE UPDATE ON lifecycle_changes BEGIN
|
|
153
|
+
SELECT RAISE(ABORT,'lifecycle_changes are append-only');
|
|
154
|
+
END;
|
|
155
|
+
-- Phase may only change per workflow_transitions AND a matching lifecycle_changes row must be written just before, in the same transaction.
|
|
156
|
+
CREATE TRIGGER IF NOT EXISTS workflows_phase_guard BEFORE UPDATE OF phase ON workflows
|
|
157
|
+
WHEN NEW.phase <> OLD.phase BEGIN
|
|
158
|
+
SELECT RAISE(ABORT,'workflow-transition-refused')
|
|
159
|
+
WHERE NOT EXISTS(SELECT 1 FROM workflow_transitions t WHERE t.from_phase=OLD.phase AND t.to_phase=NEW.phase);
|
|
160
|
+
SELECT RAISE(ABORT,'workflow-transition-unrecorded')
|
|
161
|
+
WHERE NOT EXISTS(SELECT 1 FROM lifecycle_changes c WHERE c.workflow_id=NEW.workflow_id
|
|
162
|
+
AND c.from_phase IS OLD.phase AND c.to_phase=NEW.phase
|
|
163
|
+
AND c.change_id=(SELECT max(change_id) FROM lifecycle_changes WHERE workflow_id=NEW.workflow_id));
|
|
164
|
+
END;
|
|
165
|
+
|
|
166
|
+
CREATE TABLE IF NOT EXISTS goals(
|
|
167
|
+
goal_seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
168
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
169
|
+
revision INTEGER NOT NULL,
|
|
170
|
+
goal_identity TEXT NOT NULL,
|
|
171
|
+
markdown TEXT NOT NULL, -- goal text the owner approved (relaunch source)
|
|
172
|
+
json TEXT NOT NULL CHECK(json_valid(json)), -- derived op chain
|
|
173
|
+
amendment_json TEXT CHECK(amendment_json IS NULL OR json_valid(amendment_json)),
|
|
174
|
+
approved_by TEXT, approval_ref TEXT,
|
|
175
|
+
created_at INTEGER NOT NULL,
|
|
176
|
+
UNIQUE(workflow_id,revision)) STRICT;
|
|
177
|
+
|
|
178
|
+
CREATE TABLE IF NOT EXISTS goal_inputs(
|
|
179
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
180
|
+
key TEXT NOT NULL, -- '<index>-<basename>'
|
|
181
|
+
goal_revision INTEGER NOT NULL,
|
|
182
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
183
|
+
origin TEXT NOT NULL,
|
|
184
|
+
created_at INTEGER NOT NULL,
|
|
185
|
+
PRIMARY KEY(workflow_id,key)) STRICT;
|
|
186
|
+
|
|
187
|
+
-- workflow_purges: tombstone of the single delete path (no FK: survives the purge).
|
|
188
|
+
CREATE TABLE IF NOT EXISTS workflow_purges(
|
|
189
|
+
workflow_id TEXT PRIMARY KEY,
|
|
190
|
+
state TEXT NOT NULL CHECK(state IN ('planned','archived','deleting','purged')),
|
|
191
|
+
approved_by TEXT, approval_ref TEXT,
|
|
192
|
+
archive_path TEXT, archive_sha256 TEXT, archive_bytes INTEGER, manifest_sha256 TEXT,
|
|
193
|
+
events_head TEXT,
|
|
194
|
+
counts_json TEXT CHECK(counts_json IS NULL OR json_valid(counts_json)),
|
|
195
|
+
created_at INTEGER NOT NULL, archived_at INTEGER, verified_at INTEGER, purged_at INTEGER,
|
|
196
|
+
CHECK(state IN ('planned','archived') OR (approved_by IS NOT NULL AND approval_ref IS NOT NULL
|
|
197
|
+
AND archive_path IS NOT NULL AND archive_sha256 IS NOT NULL AND verified_at IS NOT NULL))) STRICT;
|
|
198
|
+
|
|
199
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
200
|
+
-- A3. Graph: units (steps) and edges
|
|
201
|
+
-- Vocabulary: unit = logical step; job = one try with concrete input (try_no); attempt = one dispatch (op_attempts).
|
|
202
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
203
|
+
CREATE TABLE IF NOT EXISTS work_graph_versions(
|
|
204
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
205
|
+
version INTEGER NOT NULL CHECK(version>=0),
|
|
206
|
+
event TEXT NOT NULL CHECK(event IN ('draw','revise','cut','edit')),
|
|
207
|
+
graph_json TEXT NOT NULL CHECK(json_valid(graph_json)),
|
|
208
|
+
diff_json TEXT NOT NULL CHECK(json_valid(diff_json)),
|
|
209
|
+
colors_json TEXT NOT NULL CHECK(json_valid(colors_json)),
|
|
210
|
+
reason TEXT NOT NULL, author_op TEXT NOT NULL, author_job TEXT, digest TEXT NOT NULL,
|
|
211
|
+
created_at INTEGER NOT NULL,
|
|
212
|
+
PRIMARY KEY(workflow_id,version)) STRICT;
|
|
213
|
+
|
|
214
|
+
CREATE TABLE IF NOT EXISTS work_units(
|
|
215
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
216
|
+
unit_id TEXT NOT NULL, -- = job_id of the first job in the family
|
|
217
|
+
op_id TEXT NOT NULL,
|
|
218
|
+
subject_key TEXT NOT NULL, -- work identity, normalized: '<cut_id>#<ordinal>' or digest(records + owned_paths)
|
|
219
|
+
goal_revision INTEGER NOT NULL,
|
|
220
|
+
title TEXT,
|
|
221
|
+
cut_id TEXT, cut_ordinal INTEGER, cut_total INTEGER,
|
|
222
|
+
repository TEXT,
|
|
223
|
+
state TEXT NOT NULL CHECK(state IN ('planned','queued','running','reported','deciding','done','failed','dropped')),
|
|
224
|
+
current_job_id TEXT,
|
|
225
|
+
tries INTEGER NOT NULL DEFAULT 0, -- number of jobs in the family
|
|
226
|
+
dispatches INTEGER NOT NULL DEFAULT 0, -- cumulative dispatch count
|
|
227
|
+
try_budget INTEGER NOT NULL DEFAULT 5 CHECK(try_budget>=1), -- H3: exceeding it makes INSERT jobs refuse
|
|
228
|
+
budget_raised_by TEXT, -- who raised the budget (owner | supervisor) ...
|
|
229
|
+
budget_raised_ref TEXT, -- ... and why (di_id / incident_id); required when try_budget > default
|
|
230
|
+
reopen_reason TEXT, reopened_by TEXT, reopened_at INTEGER, -- H5: the only way to rerun a done unit
|
|
231
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, done_at INTEGER,
|
|
232
|
+
PRIMARY KEY(workflow_id,unit_id),
|
|
233
|
+
UNIQUE(workflow_id,op_id,subject_key,goal_revision), -- H3/H5: cannot "create a new unit" for the same work
|
|
234
|
+
CHECK(try_budget<=5 OR (budget_raised_by IS NOT NULL AND budget_raised_ref IS NOT NULL))) STRICT;
|
|
235
|
+
CREATE INDEX IF NOT EXISTS ix_units_state ON work_units(workflow_id,state);
|
|
236
|
+
CREATE INDEX IF NOT EXISTS ix_units_op ON work_units(workflow_id,op_id);
|
|
237
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_units_cut ON work_units(workflow_id,cut_id,cut_ordinal,goal_revision) WHERE cut_id IS NOT NULL;
|
|
238
|
+
-- A done unit leaves 'done' only via a reopen with a fresh reason (reopened_at changes) - no shortcut.
|
|
239
|
+
CREATE TRIGGER IF NOT EXISTS work_units_done_guard BEFORE UPDATE OF state ON work_units
|
|
240
|
+
WHEN OLD.state='done' AND NEW.state<>'done' BEGIN
|
|
241
|
+
SELECT RAISE(ABORT,'unit-already-passed: reopen needs reopen_reason, reopened_by and a new reopened_at')
|
|
242
|
+
WHERE NEW.reopen_reason IS NULL OR NEW.reopened_by IS NULL OR NEW.reopened_at IS NULL OR NEW.reopened_at IS OLD.reopened_at;
|
|
243
|
+
END;
|
|
244
|
+
|
|
245
|
+
CREATE TABLE IF NOT EXISTS unit_edges(
|
|
246
|
+
workflow_id TEXT NOT NULL,
|
|
247
|
+
from_unit TEXT NOT NULL,
|
|
248
|
+
to_unit TEXT NOT NULL,
|
|
249
|
+
kind TEXT NOT NULL CHECK(kind IN ('after','seam','dependsOn','peer-wait')),
|
|
250
|
+
source TEXT NOT NULL CHECK(source IN ('plan','graph','kernel-graph-edit','peer-wait','seam')),
|
|
251
|
+
created_at INTEGER NOT NULL,
|
|
252
|
+
PRIMARY KEY(workflow_id,from_unit,to_unit,kind),
|
|
253
|
+
FOREIGN KEY(workflow_id,from_unit) REFERENCES work_units(workflow_id,unit_id) ON DELETE CASCADE,
|
|
254
|
+
FOREIGN KEY(workflow_id,to_unit) REFERENCES work_units(workflow_id,unit_id) ON DELETE CASCADE) STRICT;
|
|
255
|
+
CREATE INDEX IF NOT EXISTS ix_unit_edges_to ON unit_edges(workflow_id,to_unit);
|
|
256
|
+
|
|
257
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
258
|
+
-- A4. Job (try), attempt (dispatch), contract, lease, idempotency
|
|
259
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
260
|
+
CREATE TABLE IF NOT EXISTS jobs(
|
|
261
|
+
job_id TEXT PRIMARY KEY, -- op-<op>-<token10> | kernel-<wf>
|
|
262
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
263
|
+
unit_id TEXT, -- NULL for kernel jobs
|
|
264
|
+
op_id TEXT,
|
|
265
|
+
try_no INTEGER NOT NULL CHECK(try_no>=1), -- which try OF THE UNIT
|
|
266
|
+
retry_of TEXT REFERENCES jobs(job_id), -- previous job in the family (a retry may change shape)
|
|
267
|
+
resume_of TEXT REFERENCES jobs(job_id),
|
|
268
|
+
retry_class TEXT CHECK(retry_class IS NULL OR retry_class IN ('business','infra','resume','follow-up')),
|
|
269
|
+
generation INTEGER NOT NULL,
|
|
270
|
+
kind TEXT NOT NULL CHECK(kind IN ('op','kernel')),
|
|
271
|
+
role TEXT,
|
|
272
|
+
status TEXT NOT NULL CHECK(status IN ('queued','ready','leased','running','answering','reported','deciding',
|
|
273
|
+
'effect_unknown','awaiting_owner','succeeded','failed','cancelled')),
|
|
274
|
+
priority_json TEXT CHECK(priority_json IS NULL OR json_valid(priority_json)),
|
|
275
|
+
lease_token TEXT,
|
|
276
|
+
worker_id TEXT,
|
|
277
|
+
deadline INTEGER,
|
|
278
|
+
payload_json TEXT CHECK(payload_json IS NULL OR json_valid(payload_json)), -- input ONLY: records, owned_paths, params, goal_binding, cut, after
|
|
279
|
+
created_at INTEGER NOT NULL,
|
|
280
|
+
updated_at INTEGER NOT NULL,
|
|
281
|
+
FOREIGN KEY(workflow_id,unit_id) REFERENCES work_units(workflow_id,unit_id)) STRICT;
|
|
282
|
+
CREATE INDEX IF NOT EXISTS jobs_queue ON jobs(status,kind,created_at,job_id);
|
|
283
|
+
CREATE INDEX IF NOT EXISTS jobs_op ON jobs(workflow_id,op_id);
|
|
284
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_jobs_unit_try ON jobs(workflow_id,unit_id,try_no) WHERE unit_id IS NOT NULL;
|
|
285
|
+
|
|
286
|
+
-- Enqueue gate (H3, H4, H5, H9): every op job needs a unit; unit not done; budget left; valid lineage; workflow accepts work.
|
|
287
|
+
CREATE TRIGGER IF NOT EXISTS jobs_enqueue_guard BEFORE INSERT ON jobs WHEN NEW.kind='op' BEGIN
|
|
288
|
+
SELECT RAISE(ABORT,'workflow-not-accepting-work')
|
|
289
|
+
WHERE (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id) NOT IN ('queued','running');
|
|
290
|
+
SELECT RAISE(ABORT,'unit-required') WHERE NEW.unit_id IS NULL;
|
|
291
|
+
SELECT RAISE(ABORT,'unit-already-passed')
|
|
292
|
+
WHERE EXISTS(SELECT 1 FROM work_units u WHERE u.workflow_id=NEW.workflow_id AND u.unit_id=NEW.unit_id AND u.state='done');
|
|
293
|
+
-- A try that ended awaiting_owner asked a question and did not fail: it spends no try of the budget.
|
|
294
|
+
SELECT RAISE(ABORT,'unit-try-budget-exhausted')
|
|
295
|
+
WHERE NEW.try_no - (SELECT count(*) FROM jobs w WHERE w.workflow_id=NEW.workflow_id AND w.unit_id=NEW.unit_id AND w.status='awaiting_owner')
|
|
296
|
+
> (SELECT try_budget FROM work_units u WHERE u.workflow_id=NEW.workflow_id AND u.unit_id=NEW.unit_id);
|
|
297
|
+
-- H5: the try after a reopen follows a PASSED try, so it has no retry_of/resume_of; it is admitted only as the
|
|
298
|
+
-- next try of a unit reopenUnit just moved out of 'done' (reopened_at set, state no longer done).
|
|
299
|
+
SELECT RAISE(ABORT,'first-try-must-be-1-without-lineage')
|
|
300
|
+
WHERE NEW.retry_of IS NULL AND NEW.resume_of IS NULL AND NEW.try_no<>1
|
|
301
|
+
AND NOT EXISTS(SELECT 1 FROM work_units u WHERE u.workflow_id=NEW.workflow_id AND u.unit_id=NEW.unit_id
|
|
302
|
+
AND u.reopened_at IS NOT NULL AND u.state<>'done' AND NEW.try_no=u.tries+1);
|
|
303
|
+
SELECT RAISE(ABORT,'retry-lineage-invalid: retry_of must be a FAILED or awaiting_owner job of the same unit with try_no-1')
|
|
304
|
+
WHERE NEW.retry_of IS NOT NULL AND NOT EXISTS(SELECT 1 FROM jobs p WHERE p.job_id=NEW.retry_of
|
|
305
|
+
AND p.workflow_id=NEW.workflow_id AND p.unit_id=NEW.unit_id AND p.status IN ('failed','awaiting_owner') AND p.try_no=NEW.try_no-1);
|
|
306
|
+
SELECT RAISE(ABORT,'resume-lineage-invalid: resume_of must be a failed/cancelled job of the same unit')
|
|
307
|
+
WHERE NEW.resume_of IS NOT NULL AND NOT EXISTS(SELECT 1 FROM jobs p WHERE p.job_id=NEW.resume_of
|
|
308
|
+
AND p.workflow_id=NEW.workflow_id AND p.unit_id=NEW.unit_id AND p.status IN ('failed','awaiting_owner','cancelled'));
|
|
309
|
+
END;
|
|
310
|
+
-- Job state machine (H9): only pairs in job_transitions; cancelled/succeeded/failed are terminal.
|
|
311
|
+
CREATE TRIGGER IF NOT EXISTS jobs_status_guard BEFORE UPDATE OF status ON jobs
|
|
312
|
+
WHEN NEW.status<>OLD.status AND NOT EXISTS(SELECT 1 FROM job_transitions t WHERE t.from_status=OLD.status AND t.to_status=NEW.status) BEGIN
|
|
313
|
+
SELECT RAISE(ABORT,'job-transition-refused');
|
|
314
|
+
END;
|
|
315
|
+
-- When a job ends, its leases vanish in the same transaction (H12: no leaked leases).
|
|
316
|
+
CREATE TRIGGER IF NOT EXISTS jobs_release_leases AFTER UPDATE OF status ON jobs
|
|
317
|
+
WHEN NEW.status IN ('succeeded','failed','awaiting_owner','cancelled') BEGIN
|
|
318
|
+
DELETE FROM leases WHERE job_id=NEW.job_id;
|
|
319
|
+
END;
|
|
320
|
+
|
|
321
|
+
-- op_attempts: ONE ROW PER DISPATCH (requeue after worker death -> dispatch_seq+1, same job).
|
|
322
|
+
CREATE TABLE IF NOT EXISTS op_attempts(
|
|
323
|
+
attempt_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
324
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
325
|
+
job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,
|
|
326
|
+
unit_id TEXT,
|
|
327
|
+
op_id TEXT NOT NULL,
|
|
328
|
+
try_no INTEGER NOT NULL, -- = jobs.try_no
|
|
329
|
+
dispatch_seq INTEGER NOT NULL DEFAULT 1 CHECK(dispatch_seq>=1),
|
|
330
|
+
dispatch_id TEXT NOT NULL, -- ctx_... ; STARCI_DISPATCH_ID
|
|
331
|
+
-- correlation
|
|
332
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
333
|
+
parent_span_id TEXT, -- span of the decision / Kernel turn that dispatched
|
|
334
|
+
-- who ran
|
|
335
|
+
agent TEXT CHECK(agent IS NULL OR agent IN ('devin','codex','claude')),
|
|
336
|
+
provider TEXT,
|
|
337
|
+
model TEXT, -- attested model id (gen_ai.response.model)
|
|
338
|
+
request_model TEXT, -- requested model (gen_ai.request.model)
|
|
339
|
+
model_profile TEXT, pool TEXT, effort TEXT, difficulty TEXT,
|
|
340
|
+
routed_by TEXT CHECK(routed_by IS NULL OR routed_by IN ('route','config','flag','kernel-override')),
|
|
341
|
+
route_chain_json TEXT CHECK(route_chain_json IS NULL OR json_valid(route_chain_json)),
|
|
342
|
+
route_rejected_json TEXT CHECK(route_rejected_json IS NULL OR json_valid(route_rejected_json)),
|
|
343
|
+
host TEXT NOT NULL DEFAULT 'orca',
|
|
344
|
+
run_id TEXT, task_id TEXT, terminal_handle TEXT, worker_pid INTEGER,
|
|
345
|
+
managed INTEGER NOT NULL DEFAULT 0 CHECK(managed IN (0,1)),
|
|
346
|
+
scratch_dir TEXT, -- STARCI_JOB_SCRATCH
|
|
347
|
+
-- replay (Bazel action key): enough to rebuild the exact try
|
|
348
|
+
runtime_rev TEXT, -- commit .claude
|
|
349
|
+
cli_name TEXT, cli_version TEXT, -- devin 2.x, codex 0.x ...
|
|
350
|
+
contract_sha TEXT, -- digest contracts.markdown
|
|
351
|
+
config_sha TEXT, -- digest of the effective config.yaml
|
|
352
|
+
prompt_sha TEXT REFERENCES blobs(sha256), -- the real prompt sent to the terminal
|
|
353
|
+
transcript_sha TEXT REFERENCES blobs(sha256), -- the WHOLE terminal scrollback at end, redacted (UI-API sec. 2.10);
|
|
354
|
+
-- while running: attempt_transcript_snapshots (every 60 seconds)
|
|
355
|
+
session_sha TEXT REFERENCES blobs(sha256), -- the CLI session file (~/.claude/projects/*.jsonl, ~/.codex/sessions/...), redacted
|
|
356
|
+
-- where
|
|
357
|
+
repo_root TEXT, worktree_path TEXT, branch TEXT, wf_branch TEXT,
|
|
358
|
+
base_sha TEXT, head_sha TEXT, integrated_sha TEXT,
|
|
359
|
+
-- when
|
|
360
|
+
routed_at INTEGER, dispatched_at INTEGER, started_at INTEGER, attested_at INTEGER, reported_at INTEGER,
|
|
361
|
+
consumed_at INTEGER, checked_at INTEGER, settled_at INTEGER, released_at INTEGER,
|
|
362
|
+
terminal_closed_at INTEGER, task_closed_at INTEGER, worktree_removed_at INTEGER,
|
|
363
|
+
wall_ms INTEGER, -- dispatch -> settle, monotonic
|
|
364
|
+
-- outcome: the op's word and the verdict are TWO fields
|
|
365
|
+
report_outcome TEXT CHECK(report_outcome IS NULL OR report_outcome IN ('done','partial','failed','ask','blocked')),
|
|
366
|
+
verdict TEXT CHECK(verdict IS NULL OR verdict IN ('pass','fail','partial','blocked','dropped','cancelled')),
|
|
367
|
+
settled_by TEXT CHECK(settled_by IS NULL OR settled_by IN ('settler','kernel','supervisor','reconcile')),
|
|
368
|
+
decision_id TEXT,
|
|
369
|
+
failure_class TEXT, effect_state TEXT, next_step TEXT,
|
|
370
|
+
settle_json TEXT CHECK(settle_json IS NULL OR json_valid(settle_json)),
|
|
371
|
+
end_state TEXT CHECK(end_state IS NULL OR end_state IN ('settled','worker-dead','requeued','cancelled','effect-unknown')),
|
|
372
|
+
-- summarized cost (detail in llm_usage); NULL when unmeasurable
|
|
373
|
+
tokens_in INTEGER, tokens_out INTEGER, cost_usd REAL,
|
|
374
|
+
usage_source TEXT CHECK(usage_source IS NULL OR usage_source IN ('cli-transcript','provider-report','unavailable')),
|
|
375
|
+
usage_reason TEXT, -- why usage is 'unavailable' (the agent has no usage adapter, or no session file was found)
|
|
376
|
+
-- the owner-facing reason an attempt failed, was blocked, refused or is waiting (starci/why@1, scripts/kernel/why.mjs, docs/why.md), written with the settle
|
|
377
|
+
why_json TEXT CHECK(why_json IS NULL OR json_valid(why_json)),
|
|
378
|
+
UNIQUE(workflow_id,dispatch_id),
|
|
379
|
+
UNIQUE(job_id,dispatch_seq)) STRICT;
|
|
380
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_unit ON op_attempts(workflow_id,unit_id,attempt_id);
|
|
381
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_op ON op_attempts(workflow_id,op_id);
|
|
382
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_time ON op_attempts(workflow_id,dispatched_at);
|
|
383
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_model ON op_attempts(agent,model,op_id);
|
|
384
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_terminal ON op_attempts(terminal_handle);
|
|
385
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_open ON op_attempts(settled_at,end_state);
|
|
386
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_head ON op_attempts(head_sha);
|
|
387
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_reported ON op_attempts(reported_at) WHERE settled_at IS NULL;
|
|
388
|
+
-- Dispatch gate (H9): an attempt row may only be created when the job just went ready->leased (job_transitions
|
|
389
|
+
-- only allows leased from ready) and the workflow is running. A cancelled/archived job cannot be dispatched.
|
|
390
|
+
CREATE TRIGGER IF NOT EXISTS op_attempts_dispatch_guard BEFORE INSERT ON op_attempts BEGIN
|
|
391
|
+
SELECT RAISE(ABORT,'dispatch-requires-ready-then-leased')
|
|
392
|
+
WHERE (SELECT status FROM jobs WHERE job_id=NEW.job_id) IS NOT 'leased';
|
|
393
|
+
SELECT RAISE(ABORT,'dispatch-requires-running-workflow')
|
|
394
|
+
WHERE (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id) IS NOT 'running';
|
|
395
|
+
SELECT RAISE(ABORT,'dispatch-previous-attempt-open')
|
|
396
|
+
WHERE EXISTS(SELECT 1 FROM op_attempts a WHERE a.job_id=NEW.job_id AND a.end_state IS NULL AND a.settled_at IS NULL);
|
|
397
|
+
END;
|
|
398
|
+
|
|
399
|
+
-- contracts: kernel -> op. Keyed by attempt_id: a re-dispatch does NOT overwrite the previous contract.
|
|
400
|
+
CREATE TABLE IF NOT EXISTS contracts(
|
|
401
|
+
attempt_id INTEGER PRIMARY KEY REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
402
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
403
|
+
job_id TEXT NOT NULL,
|
|
404
|
+
contract_rev TEXT, -- admitted contract version (modules/kernel/contract-changes)
|
|
405
|
+
markdown TEXT NOT NULL,
|
|
406
|
+
context_json TEXT CHECK(context_json IS NULL OR json_valid(context_json)),
|
|
407
|
+
created_at INTEGER NOT NULL) STRICT;
|
|
408
|
+
|
|
409
|
+
-- resources / leases: fencing along the write path. A missing resources row = capacity 1 (not pre-seeded).
|
|
410
|
+
CREATE TABLE IF NOT EXISTS resources(
|
|
411
|
+
resource_key TEXT PRIMARY KEY,
|
|
412
|
+
capacity INTEGER NOT NULL CHECK(capacity>=0),
|
|
413
|
+
declared_by TEXT, declared_at INTEGER) STRICT;
|
|
414
|
+
CREATE TABLE IF NOT EXISTS leases(
|
|
415
|
+
resource_key TEXT NOT NULL,
|
|
416
|
+
job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,
|
|
417
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
418
|
+
op_id TEXT, try_no INTEGER NOT NULL, generation INTEGER NOT NULL,
|
|
419
|
+
token TEXT NOT NULL, -- = jobs.lease_token
|
|
420
|
+
units INTEGER NOT NULL CHECK(units>0),
|
|
421
|
+
attempt_id INTEGER, -- owner: the dispatch holding it (NULL between lease and dispatch)
|
|
422
|
+
holder TEXT, -- holding terminal handle / pid - GC can ask "who holds it, still alive"
|
|
423
|
+
acquired_at INTEGER NOT NULL,
|
|
424
|
+
renewed_at INTEGER,
|
|
425
|
+
expires_at INTEGER NOT NULL CHECK(expires_at>acquired_at), -- mandatory TTL; expiry -> dead-worker flow (H12)
|
|
426
|
+
PRIMARY KEY(resource_key,job_id)) STRICT;
|
|
427
|
+
CREATE INDEX IF NOT EXISTS leases_expiry ON leases(expires_at);
|
|
428
|
+
CREATE TRIGGER IF NOT EXISTS leases_match_job BEFORE INSERT ON leases BEGIN
|
|
429
|
+
SELECT RAISE(ABORT,'lease-identity-drift') WHERE NOT EXISTS(
|
|
430
|
+
SELECT 1 FROM jobs j WHERE j.job_id=NEW.job_id AND j.workflow_id=NEW.workflow_id AND j.op_id IS NEW.op_id
|
|
431
|
+
AND j.try_no=NEW.try_no AND j.generation=NEW.generation AND j.lease_token=NEW.token);
|
|
432
|
+
END;
|
|
433
|
+
CREATE TRIGGER IF NOT EXISTS leases_match_job_update BEFORE UPDATE ON leases BEGIN
|
|
434
|
+
SELECT RAISE(ABORT,'lease-identity-drift') WHERE NOT EXISTS(
|
|
435
|
+
SELECT 1 FROM jobs j WHERE j.job_id=NEW.job_id AND j.workflow_id=NEW.workflow_id AND j.op_id IS NEW.op_id
|
|
436
|
+
AND j.try_no=NEW.try_no AND j.generation=NEW.generation AND j.lease_token=NEW.token);
|
|
437
|
+
END;
|
|
438
|
+
|
|
439
|
+
-- api_requests: idempotency for every write verb that may be re-invoked (report, settle, decide, enqueue, drop, decisions --resolve).
|
|
440
|
+
-- request_id = --request-id or sha(verb + dispatch_id + args). A repeat call returns the old result. The harness does NOT write (UI reads only).
|
|
441
|
+
CREATE TABLE IF NOT EXISTS api_requests(
|
|
442
|
+
request_id TEXT PRIMARY KEY,
|
|
443
|
+
verb TEXT NOT NULL,
|
|
444
|
+
caller TEXT, -- kernel:<wf> | op:<attempt_id> | settler | reconciler | supervisor | owner-cli
|
|
445
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
446
|
+
attempt_id INTEGER,
|
|
447
|
+
args_sha TEXT NOT NULL,
|
|
448
|
+
status TEXT NOT NULL CHECK(status IN ('running','done','failed')),
|
|
449
|
+
result_json TEXT CHECK(result_json IS NULL OR json_valid(result_json)),
|
|
450
|
+
created_at INTEGER NOT NULL, finished_at INTEGER) STRICT;
|
|
451
|
+
CREATE INDEX IF NOT EXISTS ix_api_requests_attempt ON api_requests(attempt_id);
|
|
452
|
+
|
|
453
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
454
|
+
-- A5. Reports, checks, settle, lands, artifacts, evidence, LLM cost
|
|
455
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
456
|
+
CREATE TABLE IF NOT EXISTS reports(
|
|
457
|
+
report_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
458
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
459
|
+
attempt_id INTEGER NOT NULL UNIQUE REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
460
|
+
dispatch_id TEXT NOT NULL,
|
|
461
|
+
job_id TEXT NOT NULL,
|
|
462
|
+
outcome TEXT NOT NULL CHECK(outcome IN ('done','partial','failed','ask','blocked')),
|
|
463
|
+
report_json TEXT NOT NULL CHECK(json_valid(report_json)), -- starci/op-report@1, the ONLY version
|
|
464
|
+
summary TEXT GENERATED ALWAYS AS (json_extract(report_json,'$.summary')) VIRTUAL,
|
|
465
|
+
from_terminal TEXT,
|
|
466
|
+
consumed_at INTEGER,
|
|
467
|
+
created_at INTEGER NOT NULL,
|
|
468
|
+
UNIQUE(workflow_id,dispatch_id)) STRICT;
|
|
469
|
+
-- Reports are immutable (H10): resubmit identical content -> api_requests returns the old result; different content -> refused.
|
|
470
|
+
CREATE TRIGGER IF NOT EXISTS reports_immutable BEFORE UPDATE OF report_json, outcome, attempt_id, dispatch_id ON reports BEGIN
|
|
471
|
+
SELECT RAISE(ABORT,'report-already-filed: reports are immutable');
|
|
472
|
+
END;
|
|
473
|
+
|
|
474
|
+
-- check_runs: each run of one check of one try. run_seq = which rerun of the same check.
|
|
475
|
+
CREATE TABLE IF NOT EXISTS check_runs(
|
|
476
|
+
check_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
477
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
478
|
+
attempt_id INTEGER NOT NULL REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
479
|
+
job_id TEXT NOT NULL,
|
|
480
|
+
op_id TEXT NOT NULL,
|
|
481
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
482
|
+
parent_span_id TEXT, -- = op_attempts.span_id
|
|
483
|
+
name TEXT NOT NULL, -- canon-scan, check-scoped-lint, tsc-app, canon-parity, lint-check ...
|
|
484
|
+
phase TEXT NOT NULL CHECK(phase IN ('before','after','verify','parity','integrate')),
|
|
485
|
+
runner TEXT NOT NULL CHECK(runner IN ('op','settler','kernel','parity','integrate')),
|
|
486
|
+
run_seq INTEGER NOT NULL DEFAULT 1,
|
|
487
|
+
command TEXT, -- never holds secrets
|
|
488
|
+
cwd TEXT,
|
|
489
|
+
input_digest TEXT, -- base sha + owned paths + tool rev: the reuse key (replaces settle-parity/*.json)
|
|
490
|
+
authority TEXT NOT NULL CHECK(authority IN ('runtime','declared')), -- H8: 'runtime' = the canonical command owned
|
|
491
|
+
-- and run by the runtime; 'declared' = self-reported by the op, evidence only, does not count toward the verdict
|
|
492
|
+
exit_code INTEGER, -- the RAW exit code the runner observed (never coerced to 0)
|
|
493
|
+
declared_exit_code INTEGER, -- the exit code the op SELF-DECLARED in its report (may differ from exit_code - that is a signal)
|
|
494
|
+
attribution_json TEXT CHECK(attribution_json IS NULL OR json_valid(attribution_json)), -- structured record of the error portion outside the slice
|
|
495
|
+
status TEXT NOT NULL CHECK(status IN ('pass','fail','unavailable','error','skipped')),
|
|
496
|
+
started_at INTEGER, finished_at INTEGER, wall_ms INTEGER,
|
|
497
|
+
stdout_sha TEXT REFERENCES blobs(sha256),
|
|
498
|
+
stderr_sha TEXT REFERENCES blobs(sha256),
|
|
499
|
+
output_sha TEXT REFERENCES blobs(sha256), -- parsed JSON (canon-scan --json ...)
|
|
500
|
+
summary_json TEXT CHECK(summary_json IS NULL OR json_valid(summary_json)),
|
|
501
|
+
note TEXT,
|
|
502
|
+
created_at INTEGER NOT NULL,
|
|
503
|
+
UNIQUE(attempt_id,runner,phase,name,run_seq),
|
|
504
|
+
CHECK(NOT (status='pass' AND exit_code IS NOT NULL AND exit_code<>0)), -- H8: no "pass" with a raw exit != 0
|
|
505
|
+
CHECK(runner<>'op' OR authority='declared')) STRICT; -- an op-run check is always evidence only
|
|
506
|
+
CREATE INDEX IF NOT EXISTS ix_checks_name ON check_runs(workflow_id,name,status);
|
|
507
|
+
CREATE INDEX IF NOT EXISTS ix_checks_cache ON check_runs(name,input_digest);
|
|
508
|
+
|
|
509
|
+
-- settle_tails: the queue of tail work after settle (replaces <ledger dir>/settle-tail/<job>.json).
|
|
510
|
+
CREATE TABLE IF NOT EXISTS settle_tails(
|
|
511
|
+
attempt_id INTEGER PRIMARY KEY REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
512
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
513
|
+
state TEXT NOT NULL CHECK(state IN ('queued','running','done','failed')),
|
|
514
|
+
tries INTEGER NOT NULL DEFAULT 0,
|
|
515
|
+
due_at INTEGER, last_error TEXT,
|
|
516
|
+
queued_at INTEGER NOT NULL, started_at INTEGER, done_at INTEGER) STRICT;
|
|
517
|
+
|
|
518
|
+
-- product_lands: api product-land (merge wf/<wf> into the product repo main; separate from settle).
|
|
519
|
+
CREATE TABLE IF NOT EXISTS product_lands(
|
|
520
|
+
land_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
521
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
522
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
523
|
+
repo_root TEXT NOT NULL,
|
|
524
|
+
wf_branch TEXT NOT NULL,
|
|
525
|
+
main_before TEXT, merged_sha TEXT,
|
|
526
|
+
result TEXT NOT NULL CHECK(result IN ('queued','landed','failed','conflict','red','busy','main-moving')),
|
|
527
|
+
reason TEXT,
|
|
528
|
+
checks_json TEXT CHECK(checks_json IS NULL OR json_valid(checks_json)), -- land checks + import scan (summary)
|
|
529
|
+
output_sha TEXT REFERENCES blobs(sha256),
|
|
530
|
+
pushed INTEGER NOT NULL DEFAULT 0 CHECK(pushed IN (0,1)),
|
|
531
|
+
started_at INTEGER NOT NULL, finished_at INTEGER) STRICT;
|
|
532
|
+
CREATE INDEX IF NOT EXISTS ix_lands_wf ON product_lands(workflow_id,started_at);
|
|
533
|
+
|
|
534
|
+
-- job_artifacts: every output file, bytes in the blob store. Kernel artifacts (scan, dispatch-ready) have no attempt.
|
|
535
|
+
CREATE TABLE IF NOT EXISTS job_artifacts(
|
|
536
|
+
artifact_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
537
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
538
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
539
|
+
job_id TEXT,
|
|
540
|
+
op_id TEXT,
|
|
541
|
+
cut TEXT,
|
|
542
|
+
role TEXT NOT NULL CHECK(role IN (
|
|
543
|
+
'check-output','check-stdout','check-stderr','patch','diff','report-attachment','log',
|
|
544
|
+
'direction','prompt','render','redline','critique',
|
|
545
|
+
'capture','dom','screenshot','video','trace','uat-run','metrics','salvage','scan','other')),
|
|
546
|
+
kind TEXT NOT NULL CHECK(kind IN ('diff','patch','image','video','report','log','trace','file')),
|
|
547
|
+
subkind TEXT CHECK(subkind IS NULL OR subkind IN ('draw-render','asset-gen','app-capture','e2e-capture','uat-capture',
|
|
548
|
+
'uat-video','e2e-video','playwright-trace','patch','patch-json','diff','report','log','critique','metrics',
|
|
549
|
+
'grammar-proposal','asset-request','terminal-transcript','cli-transcript')),
|
|
550
|
+
name TEXT NOT NULL, -- logical name: 'checks/2-canon-scan/output', 'patch.diff', 'rounds/2/desktop-light.png'
|
|
551
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
552
|
+
bytes INTEGER NOT NULL,
|
|
553
|
+
media_type TEXT NOT NULL,
|
|
554
|
+
label TEXT, -- XBase#state@viewport ...
|
|
555
|
+
scope_ref TEXT, -- id of the Work record the artifact is about
|
|
556
|
+
round INTEGER, -- draw/audit round
|
|
557
|
+
run_id TEXT, -- runId of uat.verify
|
|
558
|
+
origin TEXT NOT NULL CHECK(origin IN ('op','settler','checker','kernel')),
|
|
559
|
+
base_sha TEXT, head_sha TEXT, integrated_sha TEXT,
|
|
560
|
+
created_at INTEGER NOT NULL,
|
|
561
|
+
CHECK(attempt_id IS NOT NULL OR origin='kernel')) STRICT;
|
|
562
|
+
-- Artifacts are immutable (H10): key (attempt_id, name), bytes keyed by sha; no UPDATE of content/name, no sha overwrite.
|
|
563
|
+
CREATE TRIGGER IF NOT EXISTS job_artifacts_immutable BEFORE UPDATE OF sha256, bytes, name, attempt_id, media_type ON job_artifacts BEGIN
|
|
564
|
+
SELECT RAISE(ABORT,'artifacts are immutable: file a new name');
|
|
565
|
+
END;
|
|
566
|
+
|
|
567
|
+
-- attempt_transcript_snapshots (UI-API sec. 2.10): periodic (60 s) redacted scrollback snapshots of a running op terminal.
|
|
568
|
+
-- No new row when the scrollback is unchanged (UNIQUE attempt+sha). The final one is op_attempts.transcript_sha.
|
|
569
|
+
CREATE TABLE IF NOT EXISTS attempt_transcript_snapshots(
|
|
570
|
+
snapshot_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
571
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
572
|
+
attempt_id INTEGER NOT NULL REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
573
|
+
at INTEGER NOT NULL,
|
|
574
|
+
lines INTEGER NOT NULL,
|
|
575
|
+
bytes INTEGER NOT NULL,
|
|
576
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
577
|
+
UNIQUE(attempt_id,sha256)) STRICT;
|
|
578
|
+
CREATE INDEX IF NOT EXISTS ix_transcript_snap ON attempt_transcript_snapshots(attempt_id,at);
|
|
579
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_artifacts_attempt_name ON job_artifacts(attempt_id,name) WHERE attempt_id IS NOT NULL;
|
|
580
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_artifacts_kernel_name ON job_artifacts(workflow_id,name) WHERE attempt_id IS NULL;
|
|
581
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_blob ON job_artifacts(sha256);
|
|
582
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_role ON job_artifacts(workflow_id,role);
|
|
583
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_scope ON job_artifacts(scope_ref);
|
|
584
|
+
|
|
585
|
+
CREATE TABLE IF NOT EXISTS report_attachments(
|
|
586
|
+
report_id INTEGER NOT NULL REFERENCES reports(report_id) ON DELETE CASCADE,
|
|
587
|
+
artifact_id INTEGER NOT NULL REFERENCES job_artifacts(artifact_id) ON DELETE CASCADE,
|
|
588
|
+
PRIMARY KEY(report_id,artifact_id)) STRICT;
|
|
589
|
+
|
|
590
|
+
CREATE TABLE IF NOT EXISTS artifact_proofs(
|
|
591
|
+
artifact_id INTEGER PRIMARY KEY REFERENCES job_artifacts(artifact_id) ON DELETE CASCADE,
|
|
592
|
+
claims_json TEXT NOT NULL CHECK(json_valid(claims_json)), -- {frs[], cases[], shapes[], specs[]}
|
|
593
|
+
code_sha TEXT,
|
|
594
|
+
deps_json TEXT NOT NULL CHECK(json_valid(deps_json)), -- [{path, kind: code|work, digest}]
|
|
595
|
+
created_at INTEGER NOT NULL) STRICT;
|
|
596
|
+
|
|
597
|
+
-- work_citations: which artifact/blob a Work (git) record cites - pins the blob, renders the record's images, detects broken citations.
|
|
598
|
+
CREATE TABLE IF NOT EXISTS work_citations(
|
|
599
|
+
record_id TEXT NOT NULL, -- uat.<feature>.<flow>, ui.<feature>.<screen>, impl.<...>, brand
|
|
600
|
+
record_path TEXT NOT NULL, -- features/<f>/<family>/<name>/evidence.yaml
|
|
601
|
+
field TEXT NOT NULL, -- 'run.screens[0]', 'assets[3]'
|
|
602
|
+
artifact_id INTEGER REFERENCES job_artifacts(artifact_id) ON DELETE RESTRICT,
|
|
603
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256) ON DELETE RESTRICT,
|
|
604
|
+
role TEXT CHECK(role IS NULL OR role IN ('direction','capture','uat-screen','uat-video','uat-result','render','layout-capture')),
|
|
605
|
+
record_rev TEXT,
|
|
606
|
+
created_at INTEGER NOT NULL,
|
|
607
|
+
PRIMARY KEY(record_id,field)) STRICT;
|
|
608
|
+
CREATE INDEX IF NOT EXISTS ix_citations_blob ON work_citations(sha256);
|
|
609
|
+
CREATE INDEX IF NOT EXISTS ix_citations_path ON work_citations(record_path);
|
|
610
|
+
|
|
611
|
+
-- interface_audits: scope + verdict of interface.audit (replaces features/<f>/operations/<name>/index.yaml + E/**).
|
|
612
|
+
CREATE TABLE IF NOT EXISTS interface_audits(
|
|
613
|
+
audit_id TEXT PRIMARY KEY, -- operation.<feature>.<name>
|
|
614
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
615
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE SET NULL,
|
|
616
|
+
feature TEXT NOT NULL,
|
|
617
|
+
scope_json TEXT NOT NULL CHECK(json_valid(scope_json)),
|
|
618
|
+
verdict TEXT CHECK(verdict IS NULL OR verdict IN ('pass','fail','partial')),
|
|
619
|
+
findings_json TEXT CHECK(findings_json IS NULL OR json_valid(findings_json)),
|
|
620
|
+
route_to TEXT CHECK(route_to IS NULL OR route_to IN ('interface.implement','interface.draw')),
|
|
621
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL) STRICT;
|
|
622
|
+
|
|
623
|
+
-- llm_usage: LLM cost per OTel GenAI semconv - one row per (attempt | Kernel turn) x model.
|
|
624
|
+
-- Supervisor and [Worker] cost lives in machine.llm_usage.
|
|
625
|
+
CREATE TABLE IF NOT EXISTS llm_usage(
|
|
626
|
+
usage_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
627
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
628
|
+
subject_type TEXT NOT NULL CHECK(subject_type IN ('attempt','kernel-turn')),
|
|
629
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE CASCADE, -- when subject_type='attempt'
|
|
630
|
+
turn_ref TEXT, -- when 'kernel-turn': '<seat>:<n>' or a decision_id
|
|
631
|
+
span_id TEXT,
|
|
632
|
+
provider TEXT NOT NULL, -- gen_ai.provider.name
|
|
633
|
+
request_model TEXT, response_model TEXT, -- gen_ai.request.model / gen_ai.response.model
|
|
634
|
+
input_tokens INTEGER, output_tokens INTEGER, cache_read_tokens INTEGER, cache_write_tokens INTEGER,
|
|
635
|
+
reasoning_tokens INTEGER,
|
|
636
|
+
cost_usd REAL,
|
|
637
|
+
turns INTEGER, tool_calls INTEGER, tool_errors INTEGER,
|
|
638
|
+
source TEXT NOT NULL CHECK(source IN ('cli-transcript','provider-report')),
|
|
639
|
+
at INTEGER NOT NULL,
|
|
640
|
+
CHECK((subject_type='attempt' AND attempt_id IS NOT NULL) OR (subject_type='kernel-turn' AND turn_ref IS NOT NULL))) STRICT;
|
|
641
|
+
CREATE INDEX IF NOT EXISTS ix_usage_attempt ON llm_usage(attempt_id);
|
|
642
|
+
CREATE INDEX IF NOT EXISTS ix_usage_wf ON llm_usage(workflow_id,subject_type,at);
|
|
643
|
+
|
|
644
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
645
|
+
-- A6. Inbox, decision items, decisions, conditions, incidents, shared foundations, signals
|
|
646
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
647
|
+
CREATE TABLE IF NOT EXISTS inbox(
|
|
648
|
+
inbox_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
649
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
650
|
+
kind TEXT NOT NULL CHECK(kind IN ('goal','goal-revision','peer-message','worker-question','owner-answer','amendment')),
|
|
651
|
+
key TEXT,
|
|
652
|
+
from_ref TEXT, -- owner | wf-<peer> | attempt:<id>
|
|
653
|
+
attempt_id INTEGER, -- a worker's question: which attempt asked
|
|
654
|
+
payload_json TEXT NOT NULL CHECK(json_valid(payload_json)),
|
|
655
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','claimed','applied','rejected','done','superseded')),
|
|
656
|
+
disposition_json TEXT CHECK(disposition_json IS NULL OR json_valid(disposition_json)),
|
|
657
|
+
created_at INTEGER NOT NULL,
|
|
658
|
+
applied_at INTEGER) STRICT;
|
|
659
|
+
CREATE INDEX IF NOT EXISTS ix_inbox_open ON inbox(workflow_id,kind,status);
|
|
660
|
+
|
|
661
|
+
CREATE TABLE IF NOT EXISTS decision_items(
|
|
662
|
+
di_id TEXT PRIMARY KEY, -- di-<sha8>
|
|
663
|
+
-- MB-07: key = '<kind>:<entity>:<error signature>:<head|rev>' - EVERY part mandatory, no empty part. Signature
|
|
664
|
+
-- changes -> new DI and the old DI superseded (superseded_by), never merged forever into the old DI.
|
|
665
|
+
idempotency_key TEXT NOT NULL UNIQUE CHECK(idempotency_key NOT LIKE '%::%' AND idempotency_key NOT LIKE '%:'
|
|
666
|
+
AND idempotency_key NOT LIKE ':%' AND length(idempotency_key)>=5),
|
|
667
|
+
key_parts_json TEXT NOT NULL CHECK(json_valid(key_parts_json) AND json_type(key_parts_json)='object'),
|
|
668
|
+
superseded_by TEXT REFERENCES decision_items(di_id),
|
|
669
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
670
|
+
kind TEXT NOT NULL, -- settle-nongreen | worker-question | progress-stall | stale-gate | ...
|
|
671
|
+
decider TEXT NOT NULL CHECK(decider IN ('kernel','supervisor','owner')),
|
|
672
|
+
entity_type TEXT CHECK(entity_type IS NULL OR entity_type IN ('job','unit','attempt','workflow','lane','service','seat')),
|
|
673
|
+
entity_id TEXT,
|
|
674
|
+
job_id TEXT, unit_id TEXT, attempt_id INTEGER,
|
|
675
|
+
summary TEXT NOT NULL,
|
|
676
|
+
status TEXT NOT NULL CHECK(status IN ('open','claimed','resolved','escalated','expired','superseded')),
|
|
677
|
+
opened_by TEXT NOT NULL,
|
|
678
|
+
opened_at INTEGER NOT NULL,
|
|
679
|
+
due_at INTEGER,
|
|
680
|
+
escalate_to TEXT, escalations INTEGER NOT NULL DEFAULT 0,
|
|
681
|
+
claim_by TEXT, claim_at INTEGER, claim_ttl_ms INTEGER,
|
|
682
|
+
resolved_by TEXT, resolved_at INTEGER, resolution_verb TEXT, decision_id TEXT,
|
|
683
|
+
evidence_json TEXT CHECK(evidence_json IS NULL OR json_valid(evidence_json)),
|
|
684
|
+
options_json TEXT CHECK(options_json IS NULL OR json_valid(options_json)),
|
|
685
|
+
allowed_verbs_json TEXT CHECK(allowed_verbs_json IS NULL OR json_valid(allowed_verbs_json)),
|
|
686
|
+
payload_json TEXT NOT NULL CHECK(json_valid(payload_json))) STRICT;
|
|
687
|
+
CREATE INDEX IF NOT EXISTS ix_di_open ON decision_items(decider,status,due_at);
|
|
688
|
+
CREATE INDEX IF NOT EXISTS ix_di_wf ON decision_items(workflow_id,status);
|
|
689
|
+
CREATE INDEX IF NOT EXISTS ix_di_ent ON decision_items(entity_type,entity_id);
|
|
690
|
+
|
|
691
|
+
CREATE TABLE IF NOT EXISTS decisions(
|
|
692
|
+
decision_id TEXT PRIMARY KEY,
|
|
693
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
694
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16), -- child span of the Kernel turn; dispatched attempts take it as parent
|
|
695
|
+
parent_span_id TEXT,
|
|
696
|
+
decider TEXT NOT NULL, -- 'kernel:<wf>' | 'supervisor' | 'owner'
|
|
697
|
+
di_id TEXT REFERENCES decision_items(di_id),
|
|
698
|
+
subject_type TEXT CHECK(subject_type IS NULL OR subject_type IN ('job','unit','attempt','graph','workflow')),
|
|
699
|
+
subject_id TEXT,
|
|
700
|
+
choice TEXT NOT NULL,
|
|
701
|
+
rationale TEXT,
|
|
702
|
+
result_json TEXT CHECK(result_json IS NULL OR json_valid(result_json)),
|
|
703
|
+
decided_at INTEGER NOT NULL) STRICT;
|
|
704
|
+
CREATE INDEX IF NOT EXISTS ix_decisions_wf ON decisions(workflow_id,decided_at);
|
|
705
|
+
CREATE INDEX IF NOT EXISTS ix_decisions_subj ON decisions(subject_type,subject_id);
|
|
706
|
+
|
|
707
|
+
-- conditions (K8s metav1.Condition): observed status per facet; "why stuck" = the first condition not yet True.
|
|
708
|
+
-- A controller writes 'Unknown' on first sight of an entity. Each status change -> a 'condition-changed' event.
|
|
709
|
+
CREATE TABLE IF NOT EXISTS conditions(
|
|
710
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
711
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('workflow','unit','job','attempt')),
|
|
712
|
+
entity_id TEXT NOT NULL,
|
|
713
|
+
type TEXT NOT NULL, -- Routed, Dispatched, WorkerAlive, Reported, ChecksPassed, Settled, Released, WorktreeClean, GoalObserved
|
|
714
|
+
status TEXT NOT NULL CHECK(status IN ('True','False','Unknown')),
|
|
715
|
+
reason TEXT NOT NULL, -- CamelCase: ProviderCircuitOpen, RamThrottled, NoReportAfterSla, CheckFailed ...
|
|
716
|
+
message TEXT,
|
|
717
|
+
owner TEXT CHECK(owner IS NULL OR owner IN ('owner','supervisor','kernel','controller','op')), -- who must clear it
|
|
718
|
+
observed_generation INTEGER,
|
|
719
|
+
last_transition_at INTEGER NOT NULL,
|
|
720
|
+
updated_at INTEGER NOT NULL,
|
|
721
|
+
PRIMARY KEY(entity_type,entity_id,type)) STRICT;
|
|
722
|
+
CREATE INDEX IF NOT EXISTS ix_conditions_open ON conditions(workflow_id,status);
|
|
723
|
+
|
|
724
|
+
-- incidents: kind is an enum (today hundreds of free-form kinds), with owner and deadline (GC can ask "which incident is overdue, who must clear it").
|
|
725
|
+
CREATE TABLE IF NOT EXISTS incidents(
|
|
726
|
+
incident_id TEXT PRIMARY KEY,
|
|
727
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
728
|
+
op_id TEXT, job_id TEXT, attempt_id INTEGER,
|
|
729
|
+
kind TEXT NOT NULL CHECK(kind IN ('infra-provider','config-defect','owner-ask','credential-missing','safety-block',
|
|
730
|
+
'runtime-defect','evidence-missing','scope-change','partial-effect','other')),
|
|
731
|
+
detail TEXT,
|
|
732
|
+
owner TEXT NOT NULL CHECK(owner IN ('kernel','supervisor','owner')),
|
|
733
|
+
due_at INTEGER, -- overdue -> DI escalation
|
|
734
|
+
attempts INTEGER NOT NULL DEFAULT 0, model_calls INTEGER NOT NULL DEFAULT 0,
|
|
735
|
+
tokens INTEGER NOT NULL DEFAULT 0, elapsed_ms INTEGER NOT NULL DEFAULT 0,
|
|
736
|
+
last_progress TEXT,
|
|
737
|
+
status TEXT NOT NULL CHECK(status IN ('open','resolved','superseded')),
|
|
738
|
+
resolved_reason TEXT CHECK(resolved_reason IS NULL OR resolved_reason IN ('fixed','answered','workflow-ended','superseded','dropped')),
|
|
739
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, resolved_at INTEGER,
|
|
740
|
+
CHECK(status='open' OR (resolved_at IS NOT NULL AND resolved_reason IS NOT NULL))) STRICT;
|
|
741
|
+
CREATE INDEX IF NOT EXISTS ix_incidents_open ON incidents(status,due_at);
|
|
742
|
+
-- When a workflow ends, its incidents close in the same transaction (H12: 405 orphaned incidents).
|
|
743
|
+
CREATE TRIGGER IF NOT EXISTS workflows_close_incidents AFTER UPDATE OF phase ON workflows
|
|
744
|
+
WHEN NEW.phase IN ('finished','archived') BEGIN
|
|
745
|
+
UPDATE incidents SET status='resolved', resolved_reason='workflow-ended', resolved_at=NEW.updated_at, updated_at=NEW.updated_at
|
|
746
|
+
WHERE workflow_id=NEW.workflow_id AND status='open';
|
|
747
|
+
END;
|
|
748
|
+
|
|
749
|
+
CREATE TABLE IF NOT EXISTS foundations(
|
|
750
|
+
name TEXT PRIMARY KEY,
|
|
751
|
+
kind TEXT NOT NULL CHECK(kind IN ('brand','grammar','layout-tree','shell','module','contract','other')),
|
|
752
|
+
state TEXT NOT NULL CHECK(state IN ('unclaimed','claimed','published','retired')),
|
|
753
|
+
owner_workflow TEXT, version TEXT, detail TEXT, work_ref TEXT,
|
|
754
|
+
updated_at INTEGER NOT NULL) STRICT;
|
|
755
|
+
CREATE TABLE IF NOT EXISTS foundation_declarations(
|
|
756
|
+
workflow_id TEXT PRIMARY KEY REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
757
|
+
builds_none INTEGER NOT NULL CHECK(builds_none IN (0,1)), detail TEXT, at INTEGER NOT NULL) STRICT;
|
|
758
|
+
CREATE TABLE IF NOT EXISTS path_transfers(
|
|
759
|
+
path TEXT PRIMARY KEY,
|
|
760
|
+
from_workflow TEXT, to_workflow TEXT, bridge_id TEXT,
|
|
761
|
+
state TEXT NOT NULL CHECK(state IN ('proposed','applied','reverted')),
|
|
762
|
+
detail_json TEXT CHECK(detail_json IS NULL OR json_valid(detail_json)), at INTEGER NOT NULL) STRICT;
|
|
763
|
+
CREATE TABLE IF NOT EXISTS record_changes(
|
|
764
|
+
change_id TEXT PRIMARY KEY,
|
|
765
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
766
|
+
record_id TEXT NOT NULL, record_path TEXT, reason TEXT, by TEXT, at INTEGER NOT NULL) STRICT;
|
|
767
|
+
|
|
768
|
+
-- signals: process locks/fences ONLY.
|
|
769
|
+
CREATE TABLE IF NOT EXISTS signals(
|
|
770
|
+
scope TEXT NOT NULL CHECK(scope IN ('kernel','stop','launch','decision-doorbell')),
|
|
771
|
+
key TEXT NOT NULL,
|
|
772
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE, -- a signal does not outlive its workflow (data #18)
|
|
773
|
+
holder_pid INTEGER, token TEXT,
|
|
774
|
+
value_json TEXT CHECK(value_json IS NULL OR json_valid(value_json)),
|
|
775
|
+
at INTEGER NOT NULL, expires_at INTEGER,
|
|
776
|
+
PRIMARY KEY(scope,key)) STRICT;
|
|
777
|
+
|
|
778
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
779
|
+
-- A7. Events and logs (append-only, FTS)
|
|
780
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
781
|
+
-- events: the audit journal. prev_digest/digest are computed by the JS writer inside BEGIN IMMEDIATE:
|
|
782
|
+
-- digest = sha256(prev_digest || event_id || kind || coalesce(payload_json,'') || created_at)
|
|
783
|
+
CREATE TABLE IF NOT EXISTS events(
|
|
784
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
785
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
786
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
787
|
+
generation INTEGER NOT NULL,
|
|
788
|
+
entity_type TEXT NOT NULL,
|
|
789
|
+
entity_id TEXT NOT NULL,
|
|
790
|
+
attempt_id INTEGER, -- when the event belongs to a try
|
|
791
|
+
span_id TEXT,
|
|
792
|
+
kind TEXT NOT NULL,
|
|
793
|
+
payload_json TEXT CHECK(payload_json IS NULL OR (json_valid(payload_json) AND length(payload_json)<=16384)),
|
|
794
|
+
payload_sha TEXT REFERENCES blobs(sha256), -- payload larger than 16 KiB
|
|
795
|
+
prev_digest TEXT,
|
|
796
|
+
digest TEXT NOT NULL CHECK(length(digest)=64),
|
|
797
|
+
occurred_at INTEGER NOT NULL, -- when it happened (data #12: split from write time)
|
|
798
|
+
created_at INTEGER NOT NULL) STRICT; -- when written (recorded_at)
|
|
799
|
+
CREATE INDEX IF NOT EXISTS events_entity ON events(workflow_id,entity_type,entity_id,seq);
|
|
800
|
+
CREATE INDEX IF NOT EXISTS events_kind ON events(workflow_id,kind,seq);
|
|
801
|
+
CREATE INDEX IF NOT EXISTS events_workflow_seq ON events(workflow_id,seq);
|
|
802
|
+
CREATE INDEX IF NOT EXISTS events_attempt ON events(attempt_id,seq);
|
|
803
|
+
CREATE TRIGGER IF NOT EXISTS events_append_only BEFORE UPDATE ON events BEGIN
|
|
804
|
+
SELECT RAISE(ABORT,'events are append-only');
|
|
805
|
+
END;
|
|
806
|
+
-- H9: an archived workflow accepts no more events (today 1,840 events were written after archive), except purge events.
|
|
807
|
+
CREATE TRIGGER IF NOT EXISTS events_refuse_archived BEFORE INSERT ON events
|
|
808
|
+
WHEN (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id)='archived' AND NEW.kind NOT LIKE 'purge-%' BEGIN
|
|
809
|
+
SELECT RAISE(ABORT,'workflow-archived: no further writes');
|
|
810
|
+
END;
|
|
811
|
+
CREATE TRIGGER IF NOT EXISTS events_delete_only_by_purge BEFORE DELETE ON events
|
|
812
|
+
WHEN NOT EXISTS(SELECT 1 FROM workflow_purges p WHERE p.workflow_id=OLD.workflow_id AND p.state='deleting') BEGIN
|
|
813
|
+
SELECT RAISE(ABORT,'events are append-only: only the owner-approved workflow purge deletes them');
|
|
814
|
+
END;
|
|
815
|
+
|
|
816
|
+
-- logs: typed logs. Ops write via `starci kernel log` (no more log.jsonl in .starciwork); UI reads only.
|
|
817
|
+
-- 'debug' is NOT written to the DB by default (debug lives in the try's raw log blob); per-workflow opt-in keeps 14 days.
|
|
818
|
+
CREATE TABLE IF NOT EXISTS logs(
|
|
819
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
820
|
+
at INTEGER NOT NULL,
|
|
821
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
822
|
+
job_id TEXT,
|
|
823
|
+
attempt_id INTEGER,
|
|
824
|
+
trace_id TEXT, span_id TEXT,
|
|
825
|
+
actor TEXT NOT NULL CHECK(actor IN ('kernel','op','runtime','check','land','settler','reconciler')),
|
|
826
|
+
node_id TEXT,
|
|
827
|
+
level TEXT NOT NULL CHECK(level IN ('debug','info','warn','error')),
|
|
828
|
+
kind TEXT NOT NULL, -- typed-logs.mjs LOG_KINDS
|
|
829
|
+
msg TEXT NOT NULL,
|
|
830
|
+
data_json TEXT CHECK(data_json IS NULL OR json_valid(data_json)),
|
|
831
|
+
refs_json TEXT CHECK(refs_json IS NULL OR json_valid(refs_json)),
|
|
832
|
+
src TEXT UNIQUE) STRICT; -- idempotent derivation key (ev:<seq> ...)
|
|
833
|
+
CREATE INDEX IF NOT EXISTS logs_workflow ON logs(workflow_id,seq);
|
|
834
|
+
CREATE INDEX IF NOT EXISTS logs_attempt ON logs(attempt_id,seq);
|
|
835
|
+
CREATE INDEX IF NOT EXISTS logs_job ON logs(job_id,seq);
|
|
836
|
+
CREATE INDEX IF NOT EXISTS logs_kind ON logs(workflow_id,kind,seq);
|
|
837
|
+
CREATE TRIGGER IF NOT EXISTS logs_append_only_update BEFORE UPDATE ON logs BEGIN
|
|
838
|
+
SELECT RAISE(ABORT,'logs are append-only');
|
|
839
|
+
END;
|
|
840
|
+
CREATE TRIGGER IF NOT EXISTS logs_delete_guard BEFORE DELETE ON logs
|
|
841
|
+
WHEN NOT EXISTS(SELECT 1 FROM workflow_purges p WHERE p.workflow_id=OLD.workflow_id AND p.state='deleting')
|
|
842
|
+
AND NOT (OLD.level='debug' AND OLD.at < CAST(unixepoch('subsec')*1000 AS INTEGER) - 1209600000) BEGIN
|
|
843
|
+
SELECT RAISE(ABORT,'logs are append-only: only the workflow purge (or debug retention > 14 days) deletes them');
|
|
844
|
+
END;
|
|
845
|
+
|
|
846
|
+
-- logs_fts: FTS5 external-content over msg/kind (data_json not indexed). Search: logs_fts MATCH '"EADDRINUSE"'.
|
|
847
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS logs_fts USING fts5(msg, kind, content='logs', content_rowid='seq',
|
|
848
|
+
tokenize='unicode61 remove_diacritics 2');
|
|
849
|
+
CREATE TRIGGER IF NOT EXISTS logs_fts_insert AFTER INSERT ON logs BEGIN
|
|
850
|
+
INSERT INTO logs_fts(rowid,msg,kind) VALUES (NEW.seq,NEW.msg,NEW.kind);
|
|
851
|
+
END;
|
|
852
|
+
CREATE TRIGGER IF NOT EXISTS logs_fts_delete AFTER DELETE ON logs BEGIN
|
|
853
|
+
INSERT INTO logs_fts(logs_fts,rowid,msg,kind) VALUES ('delete',OLD.seq,OLD.msg,OLD.kind);
|
|
854
|
+
END;
|
|
855
|
+
|
|
856
|
+
CREATE TABLE IF NOT EXISTS log_cursors(name TEXT PRIMARY KEY, value INTEGER NOT NULL) STRICT; -- events:<ledger> -> last derived seq
|
|
857
|
+
|
|
858
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
859
|
+
-- A8. Query views (durable)
|
|
860
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
861
|
+
-- Native state of a try (computed in one place, used for ui).
|
|
862
|
+
CREATE VIEW IF NOT EXISTS v_attempt_state AS
|
|
863
|
+
SELECT a.attempt_id,
|
|
864
|
+
CASE WHEN a.end_state='worker-dead' THEN 'worker-dead' WHEN a.end_state='effect-unknown' THEN 'effect-unknown'
|
|
865
|
+
WHEN a.end_state='requeued' AND json_extract(a.settle_json,'$.reason')='dispatch-rejected' THEN 'rejected'
|
|
866
|
+
WHEN a.end_state='requeued' THEN 'requeued'
|
|
867
|
+
WHEN (a.verdict='blocked' AND a.report_outcome='ask')
|
|
868
|
+
OR (a.verdict IS NOT NULL AND (SELECT j.status FROM jobs j WHERE j.job_id=a.job_id)='awaiting_owner') THEN 'awaiting-owner'
|
|
869
|
+
WHEN a.verdict IS NOT NULL THEN a.verdict
|
|
870
|
+
WHEN a.dispatched_at IS NULL THEN 'routed' ELSE 'in-flight' END AS native
|
|
871
|
+
FROM op_attempts a;
|
|
872
|
+
|
|
873
|
+
-- Op history: one row per dispatch (UI /api/attempts).
|
|
874
|
+
CREATE VIEW IF NOT EXISTS v_op_history AS
|
|
875
|
+
SELECT a.attempt_id, a.workflow_id, a.unit_id, a.job_id, a.op_id, a.try_no, a.dispatch_seq, a.dispatch_id, a.span_id,
|
|
876
|
+
a.agent, a.provider, a.model, a.model_profile, a.pool, a.effort, a.cli_version, a.runtime_rev,
|
|
877
|
+
a.terminal_handle, a.worktree_path, a.branch, a.base_sha, a.head_sha, a.integrated_sha,
|
|
878
|
+
a.routed_at, a.dispatched_at, a.reported_at, a.settled_at, a.released_at, a.worktree_removed_at,
|
|
879
|
+
COALESCE(a.wall_ms, a.settled_at - a.dispatched_at) AS cycle_ms,
|
|
880
|
+
a.report_outcome, a.verdict, a.settled_by, a.failure_class, a.end_state, a.why_json,
|
|
881
|
+
a.tokens_in, a.tokens_out, a.cost_usd, a.usage_source,
|
|
882
|
+
a.prompt_sha, a.transcript_sha, a.session_sha,
|
|
883
|
+
s.native AS attempt_state,
|
|
884
|
+
CASE WHEN s.native IN ('awaiting-owner','rejected') THEN s.native ELSE COALESCE(m.ui,'unknown') END AS ui,
|
|
885
|
+
j.status AS job_status, r.report_id, r.summary AS report_summary,
|
|
886
|
+
(SELECT count(*) FROM check_runs c WHERE c.attempt_id=a.attempt_id) AS checks,
|
|
887
|
+
(SELECT count(*) FROM check_runs c WHERE c.attempt_id=a.attempt_id AND c.status IN ('fail','error')) AS checks_red,
|
|
888
|
+
(SELECT count(*) FROM job_artifacts x WHERE x.attempt_id=a.attempt_id) AS artifacts
|
|
889
|
+
FROM op_attempts a
|
|
890
|
+
JOIN v_attempt_state s ON s.attempt_id=a.attempt_id
|
|
891
|
+
LEFT JOIN ui_state_map m ON m.entity='attempt' AND m.native=s.native
|
|
892
|
+
LEFT JOIN jobs j ON j.job_id=a.job_id
|
|
893
|
+
LEFT JOIN reports r ON r.attempt_id=a.attempt_id;
|
|
894
|
+
|
|
895
|
+
-- Unit rows with the latest try (UI /units, /graph).
|
|
896
|
+
CREATE VIEW IF NOT EXISTS v_units AS
|
|
897
|
+
SELECT u.*, COALESCE(m.ui,'unknown') AS ui,
|
|
898
|
+
(SELECT max(attempt_id) FROM op_attempts a WHERE a.workflow_id=u.workflow_id AND a.unit_id=u.unit_id) AS last_attempt_id
|
|
899
|
+
FROM work_units u LEFT JOIN ui_state_map m ON m.entity='unit' AND m.native=u.state;
|
|
900
|
+
|
|
901
|
+
-- Checks with ui.
|
|
902
|
+
CREATE VIEW IF NOT EXISTS v_checks AS
|
|
903
|
+
SELECT c.*, COALESCE(m.ui,'unknown') AS ui FROM check_runs c LEFT JOIN ui_state_map m ON m.entity='check' AND m.native=c.status;
|
|
904
|
+
|
|
905
|
+
-- An ENDED workflow (phase archived|finished) surfaces no live leftovers: the predicate of scripts/machine/decisions.mjs listDecisions,
|
|
906
|
+
-- applied per leg here (a missing workflow row survives; v_decision_rows keeps an ended workflow's resolved rows as history).
|
|
907
|
+
-- Decision items with a time-rule ui: overdue or escalations >= 2 -> bad; under 20% of the window left -> warn.
|
|
908
|
+
CREATE VIEW IF NOT EXISTS v_decision_rows AS
|
|
909
|
+
SELECT d.*,
|
|
910
|
+
(d.due_at IS NOT NULL AND d.status IN ('open','claimed','escalated') AND d.due_at < CAST(unixepoch('subsec')*1000 AS INTEGER)) AS overdue,
|
|
911
|
+
CASE WHEN d.status IN ('open','claimed','escalated')
|
|
912
|
+
AND ((d.due_at IS NOT NULL AND d.due_at < CAST(unixepoch('subsec')*1000 AS INTEGER)) OR d.escalations>=2) THEN 'bad'
|
|
913
|
+
WHEN d.status='open' AND d.due_at IS NOT NULL
|
|
914
|
+
AND (d.due_at - CAST(unixepoch('subsec')*1000 AS INTEGER)) < 0.2*(d.due_at - d.opened_at) THEN 'warn'
|
|
915
|
+
ELSE COALESCE(m.ui,'unknown') END AS ui
|
|
916
|
+
FROM decision_items d LEFT JOIN ui_state_map m ON m.entity='decision' AND m.native=d.status
|
|
917
|
+
LEFT JOIN workflows w ON w.workflow_id=d.workflow_id
|
|
918
|
+
WHERE d.status NOT IN ('open','claimed','escalated') OR w.phase IS NULL OR w.phase NOT IN ('archived','finished');
|
|
919
|
+
|
|
920
|
+
-- Media with link.
|
|
921
|
+
CREATE VIEW IF NOT EXISTS v_media AS
|
|
922
|
+
SELECT x.artifact_id, x.workflow_id, x.job_id, x.attempt_id, x.op_id, x.role, x.kind, x.subkind,
|
|
923
|
+
x.name, x.label, x.scope_ref, x.round, x.run_id, x.sha256, x.bytes, x.media_type, x.created_at,
|
|
924
|
+
b.http_path, b.file_uri, b.pinned, b.archived_at
|
|
925
|
+
FROM job_artifacts x JOIN blobs b ON b.sha256=x.sha256
|
|
926
|
+
WHERE x.role IN ('direction','render','redline','capture','dom','screenshot','video','trace','uat-run')
|
|
927
|
+
OR x.media_type LIKE 'image/%' OR x.media_type LIKE 'video/%';
|
|
928
|
+
|
|
929
|
+
-- Evidence of a Work record.
|
|
930
|
+
CREATE VIEW IF NOT EXISTS v_record_evidence AS
|
|
931
|
+
SELECT c.record_id, c.record_path, c.field, c.role, c.record_rev, c.sha256,
|
|
932
|
+
b.http_path, b.file_uri, b.media_type, b.bytes,
|
|
933
|
+
x.artifact_id, x.workflow_id, x.attempt_id, x.job_id, x.op_id, x.label, x.run_id, x.created_at AS produced_at
|
|
934
|
+
FROM work_citations c JOIN blobs b ON b.sha256=c.sha256 LEFT JOIN job_artifacts x ON x.artifact_id=c.artifact_id;
|
|
935
|
+
|
|
936
|
+
-- Timeline: event + log + try milestones + check + decision (ordered by at, then seq).
|
|
937
|
+
CREATE VIEW IF NOT EXISTS v_timeline AS
|
|
938
|
+
SELECT workflow_id, created_at AS at, seq, 'event' AS source, kind, entity_type, entity_id, attempt_id, payload_json AS detail
|
|
939
|
+
FROM events
|
|
940
|
+
UNION ALL
|
|
941
|
+
SELECT workflow_id, at, seq, 'log', level||':'||kind, actor, node_id, attempt_id, msg FROM logs
|
|
942
|
+
UNION ALL
|
|
943
|
+
SELECT workflow_id, COALESCE(settled_at, reported_at, started_at, dispatched_at, routed_at), attempt_id, 'attempt',
|
|
944
|
+
COALESCE(verdict, report_outcome, 'active'), 'attempt', CAST(attempt_id AS TEXT), attempt_id,
|
|
945
|
+
json_object('op',op_id,'try',try_no,'seq',dispatch_seq,'agent',agent,'model',model,'verdict',verdict)
|
|
946
|
+
FROM op_attempts WHERE COALESCE(settled_at, reported_at, started_at, dispatched_at, routed_at) IS NOT NULL
|
|
947
|
+
UNION ALL
|
|
948
|
+
SELECT workflow_id, COALESCE(started_at, created_at), check_id, 'check', name||' '||status, 'check', CAST(check_id AS TEXT),
|
|
949
|
+
attempt_id, command FROM check_runs
|
|
950
|
+
UNION ALL
|
|
951
|
+
SELECT workflow_id, decided_at, 0, 'decision', choice, subject_type, subject_id, NULL, rationale FROM decisions;
|
|
952
|
+
|
|
953
|
+
-- Progress per unit.
|
|
954
|
+
CREATE VIEW IF NOT EXISTS v_workflow_progress AS
|
|
955
|
+
SELECT w.workflow_id, w.display_name, w.phase, w.trace_id, w.allowed_parallel,
|
|
956
|
+
count(u.unit_id) AS units_total,
|
|
957
|
+
coalesce(sum(u.state='done'),0) AS units_done,
|
|
958
|
+
coalesce(sum(u.state IN ('running','reported','deciding')),0) AS units_active,
|
|
959
|
+
coalesce(sum(u.state IN ('planned','queued')),0) AS units_waiting,
|
|
960
|
+
coalesce(sum(u.state='failed'),0) AS units_failed,
|
|
961
|
+
coalesce(sum(u.state='dropped'),0) AS units_dropped,
|
|
962
|
+
CASE WHEN count(u.unit_id)=0 THEN 0.0 ELSE round(1.0*sum(u.state='done')/count(u.unit_id),3) END AS completion_rate,
|
|
963
|
+
coalesce(sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000),0) AS done_last_6h,
|
|
964
|
+
max(u.done_at) AS last_unit_at,
|
|
965
|
+
CASE WHEN coalesce(sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000),0)=0 THEN NULL
|
|
966
|
+
ELSE CAST(unixepoch('subsec')*1000 AS INTEGER) + CAST(21600000.0 * (count(u.unit_id) - sum(u.state IN ('done','dropped')))
|
|
967
|
+
/ sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000) AS INTEGER) END AS eta_at
|
|
968
|
+
FROM workflows w LEFT JOIN work_units u ON u.workflow_id=w.workflow_id
|
|
969
|
+
GROUP BY w.workflow_id;
|
|
970
|
+
|
|
971
|
+
-- Model x op performance.
|
|
972
|
+
CREATE VIEW IF NOT EXISTS v_model_scorecard AS
|
|
973
|
+
SELECT agent, model, op_id, count(*) AS attempts,
|
|
974
|
+
sum(verdict='pass') AS pass, sum(verdict='fail') AS fail, sum(verdict='blocked') AS blocked,
|
|
975
|
+
sum(end_state='worker-dead') AS worker_dead,
|
|
976
|
+
round(1.0*sum(verdict='pass')/NULLIF(sum(verdict IS NOT NULL),0),3) AS pass_rate,
|
|
977
|
+
avg(COALESCE(wall_ms, settled_at - dispatched_at)) AS avg_cycle_ms,
|
|
978
|
+
sum(tokens_in) AS tokens_in, sum(tokens_out) AS tokens_out, sum(cost_usd) AS cost_usd
|
|
979
|
+
FROM op_attempts GROUP BY agent, model, op_id;
|
|
980
|
+
|
|
981
|
+
-- "Why is X stuck": every open blocker, one row each, with who must clear it (UI blockedBy).
|
|
982
|
+
CREATE VIEW IF NOT EXISTS v_blocking AS
|
|
983
|
+
SELECT e.workflow_id, 'unit' AS entity_type, e.to_unit AS entity_id, 'unit' AS blocker_type, e.from_unit AS blocker_id,
|
|
984
|
+
'UpstreamNotDone' AS reason_code, p.state AS detail, p.updated_at AS since, 'kernel' AS who
|
|
985
|
+
FROM unit_edges e JOIN work_units t ON t.workflow_id=e.workflow_id AND t.unit_id=e.to_unit
|
|
986
|
+
JOIN work_units p ON p.workflow_id=e.workflow_id AND p.unit_id=e.from_unit
|
|
987
|
+
LEFT JOIN workflows w ON w.workflow_id=e.workflow_id
|
|
988
|
+
WHERE t.state IN ('planned','queued') AND p.state NOT IN ('done','dropped')
|
|
989
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
990
|
+
UNION ALL
|
|
991
|
+
SELECT d.workflow_id, COALESCE(d.entity_type,'workflow'), COALESCE(d.entity_id,d.workflow_id), 'decision', d.di_id,
|
|
992
|
+
'DecisionOpen:'||d.kind, d.summary, d.opened_at, d.decider
|
|
993
|
+
FROM decision_items d LEFT JOIN workflows w ON w.workflow_id=d.workflow_id
|
|
994
|
+
WHERE d.status IN ('open','claimed','escalated')
|
|
995
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
996
|
+
UNION ALL
|
|
997
|
+
SELECT c.workflow_id, c.entity_type, c.entity_id, 'condition', c.type, c.reason, c.message, c.last_transition_at, COALESCE(c.owner,'controller')
|
|
998
|
+
FROM conditions c LEFT JOIN workflows w ON w.workflow_id=c.workflow_id
|
|
999
|
+
WHERE c.status<>'True'
|
|
1000
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1001
|
+
UNION ALL
|
|
1002
|
+
SELECT i.workflow_id, 'job', COALESCE(i.job_id,i.workflow_id), 'incident', i.incident_id, 'IncidentOpen:'||i.kind,
|
|
1003
|
+
COALESCE(i.detail,i.last_progress), i.created_at, i.owner
|
|
1004
|
+
FROM incidents i LEFT JOIN workflows w ON w.workflow_id=i.workflow_id
|
|
1005
|
+
WHERE i.status='open'
|
|
1006
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1007
|
+
UNION ALL
|
|
1008
|
+
SELECT x.workflow_id, 'attempt', CAST(x.attempt_id AS TEXT), 'question', CAST(x.inbox_id AS TEXT), 'WorkerQuestionPending',
|
|
1009
|
+
json_extract(x.payload_json,'$.question'), x.created_at, 'kernel'
|
|
1010
|
+
FROM inbox x LEFT JOIN workflows w ON w.workflow_id=x.workflow_id
|
|
1011
|
+
WHERE x.kind='worker-question' AND x.status IN ('pending','claimed')
|
|
1012
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1013
|
+
UNION ALL
|
|
1014
|
+
SELECT s.workflow_id, 'attempt', CAST(s.attempt_id AS TEXT), 'settle-tail', CAST(s.attempt_id AS TEXT), 'SettleTailFailed', s.last_error,
|
|
1015
|
+
COALESCE(s.started_at,s.queued_at), 'controller'
|
|
1016
|
+
FROM settle_tails s LEFT JOIN workflows w ON w.workflow_id=s.workflow_id
|
|
1017
|
+
WHERE s.state='failed'
|
|
1018
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1019
|
+
UNION ALL
|
|
1020
|
+
SELECT l.workflow_id, 'workflow', l.workflow_id, 'product-land', CAST(l.land_id AS TEXT), 'ProductLand:'||l.result, l.reason,
|
|
1021
|
+
l.started_at, 'kernel'
|
|
1022
|
+
FROM product_lands l LEFT JOIN workflows w ON w.workflow_id=l.workflow_id
|
|
1023
|
+
WHERE l.result IN ('failed','conflict','red')
|
|
1024
|
+
AND NOT EXISTS(SELECT 1 FROM product_lands k WHERE k.workflow_id=l.workflow_id AND k.land_id>l.land_id AND k.result='landed')
|
|
1025
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1026
|
+
UNION ALL
|
|
1027
|
+
-- H1: a filed report nobody settled within its SLA (the same rows as v_settle_overdue) blocks its job.
|
|
1028
|
+
SELECT a.workflow_id, 'attempt', CAST(a.attempt_id AS TEXT), 'settle', CAST(a.attempt_id AS TEXT),
|
|
1029
|
+
'SettleOverdue:'||COALESCE(a.report_outcome,'?'), a.job_id, a.reported_at,
|
|
1030
|
+
CASE WHEN a.report_outcome='done' THEN 'settler' ELSE 'kernel' END
|
|
1031
|
+
FROM op_attempts a LEFT JOIN workflows w ON w.workflow_id=a.workflow_id
|
|
1032
|
+
WHERE a.reported_at IS NOT NULL AND a.settled_at IS NULL AND a.end_state IS NULL
|
|
1033
|
+
AND (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) > CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END
|
|
1034
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'));
|
|
1035
|
+
|
|
1036
|
+
-- Open work (DIs + jobs holding a lease/running).
|
|
1037
|
+
CREATE VIEW IF NOT EXISTS v_open_work AS
|
|
1038
|
+
SELECT 'decision' AS kind, d.workflow_id, d.di_id AS ref, d.summary AS what, d.opened_at AS since, d.due_at
|
|
1039
|
+
FROM decision_items d LEFT JOIN workflows w ON w.workflow_id=d.workflow_id
|
|
1040
|
+
WHERE d.status IN ('open','claimed','escalated')
|
|
1041
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'))
|
|
1042
|
+
UNION ALL
|
|
1043
|
+
SELECT 'job-'||j.status, j.workflow_id, j.job_id, j.op_id, j.updated_at, j.deadline
|
|
1044
|
+
FROM jobs j LEFT JOIN workflows w ON w.workflow_id=j.workflow_id
|
|
1045
|
+
WHERE j.status IN ('leased','running','answering','effect_unknown')
|
|
1046
|
+
AND (w.phase IS NULL OR w.phase NOT IN ('archived','finished'));
|
|
1047
|
+
|
|
1048
|
+
-- H1: a filed report unsettled past SLA (green <= 3 min for settler; non-green <= 15 min for Kernel, then DI escalation).
|
|
1049
|
+
-- A job in 'reported'/'deciding' past SLA does NOT count as holding a worker slot (census reads this view to exclude it).
|
|
1050
|
+
CREATE VIEW IF NOT EXISTS v_settle_overdue AS
|
|
1051
|
+
SELECT a.workflow_id, a.attempt_id, a.job_id, a.unit_id, a.op_id, a.report_outcome, j.status AS job_status,
|
|
1052
|
+
a.reported_at, (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) AS waiting_ms,
|
|
1053
|
+
CASE WHEN a.report_outcome='done' THEN 'settler' ELSE 'kernel' END AS who,
|
|
1054
|
+
CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END AS sla_ms,
|
|
1055
|
+
(SELECT di_id FROM decision_items d WHERE d.attempt_id=a.attempt_id AND d.status IN ('open','claimed','escalated')) AS open_di
|
|
1056
|
+
FROM op_attempts a JOIN jobs j ON j.job_id=a.job_id
|
|
1057
|
+
WHERE a.reported_at IS NOT NULL AND a.settled_at IS NULL AND a.end_state IS NULL
|
|
1058
|
+
AND (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) > CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END;
|
|
1059
|
+
|
|
1060
|
+
-- Leaks: expired leases (with holder) and overdue open incidents - GC/Resource ctrl scan this view (H12).
|
|
1061
|
+
CREATE VIEW IF NOT EXISTS v_ledger_leaks AS
|
|
1062
|
+
SELECT 'lease' AS kind, l.resource_key AS target, l.workflow_id, l.job_id, l.attempt_id, l.holder AS owner, l.expires_at AS due_at
|
|
1063
|
+
FROM leases l WHERE l.expires_at < CAST(unixepoch('subsec')*1000 AS INTEGER)
|
|
1064
|
+
UNION ALL
|
|
1065
|
+
SELECT 'incident', i.incident_id, i.workflow_id, i.job_id, i.attempt_id, i.owner, i.due_at
|
|
1066
|
+
FROM incidents i WHERE i.status='open' AND i.due_at < CAST(unixepoch('subsec')*1000 AS INTEGER)
|
|
1067
|
+
UNION ALL
|
|
1068
|
+
SELECT 'attempt-no-transcript', CAST(a.attempt_id AS TEXT), a.workflow_id, a.job_id, a.attempt_id, a.terminal_handle, a.terminal_closed_at + 120000
|
|
1069
|
+
FROM op_attempts a WHERE a.terminal_closed_at IS NOT NULL AND a.transcript_sha IS NULL
|
|
1070
|
+
AND a.terminal_closed_at < CAST(unixepoch('subsec')*1000 AS INTEGER) - 120000; -- SLA TRANSCRIPT_MISSING
|
|
1071
|
+
|
|
1072
|
+
-- Search by id across every entity (UI /api/search, key part; the text part uses logs_fts).
|
|
1073
|
+
CREATE VIEW IF NOT EXISTS v_search_ids AS
|
|
1074
|
+
SELECT 'workflow' AS kind, workflow_id AS id, workflow_id, COALESCE(display_name,title) AS title FROM workflows
|
|
1075
|
+
UNION ALL SELECT 'trace', trace_id, workflow_id, COALESCE(display_name,title) FROM workflows
|
|
1076
|
+
UNION ALL SELECT 'unit', unit_id, workflow_id, COALESCE(title,op_id) FROM work_units
|
|
1077
|
+
UNION ALL SELECT 'job', job_id, workflow_id, op_id FROM jobs
|
|
1078
|
+
UNION ALL SELECT 'attempt', CAST(attempt_id AS TEXT), workflow_id, op_id||' #'||try_no||'.'||dispatch_seq FROM op_attempts
|
|
1079
|
+
UNION ALL SELECT 'dispatch', dispatch_id, workflow_id, op_id FROM op_attempts
|
|
1080
|
+
UNION ALL SELECT 'span', span_id, workflow_id, op_id FROM op_attempts
|
|
1081
|
+
UNION ALL SELECT 'terminal', terminal_handle, workflow_id, op_id FROM op_attempts WHERE terminal_handle IS NOT NULL
|
|
1082
|
+
UNION ALL SELECT 'commit', head_sha, workflow_id, op_id FROM op_attempts WHERE head_sha IS NOT NULL
|
|
1083
|
+
UNION ALL SELECT 'decision', di_id, workflow_id, summary FROM decision_items
|
|
1084
|
+
UNION ALL SELECT 'incident', incident_id, workflow_id, kind FROM incidents
|
|
1085
|
+
UNION ALL SELECT 'artifact', CAST(artifact_id AS TEXT), workflow_id, name FROM job_artifacts
|
|
1086
|
+
UNION ALL SELECT 'blob', sha256, NULL, media_type FROM blobs;
|
|
1087
|
+
|
|
1088
|
+
-- Invalidation marks for SSE /api/live (server compares with previous; ETag also uses PRAGMA data_version).
|
|
1089
|
+
CREATE VIEW IF NOT EXISTS v_live_marks AS
|
|
1090
|
+
SELECT 'events' AS topic, COALESCE(max(seq),0) AS mark FROM events
|
|
1091
|
+
UNION ALL SELECT 'logs', COALESCE(max(seq),0) FROM logs
|
|
1092
|
+
UNION ALL SELECT 'attempts', COALESCE(max(max(attempt_id), max(COALESCE(settled_at,reported_at,dispatched_at,0))),0) FROM op_attempts
|
|
1093
|
+
UNION ALL SELECT 'decisions', COALESCE(max(max(opened_at), max(COALESCE(resolved_at,0))),0) FROM decision_items;
|
|
1094
|
+
|
|
1095
|
+
-- Blob references in this ledger (convenient lookup; GC uses blob_ref_columns, not this view).
|
|
1096
|
+
CREATE VIEW IF NOT EXISTS v_blob_refs AS
|
|
1097
|
+
SELECT b.sha256, b.bytes, b.pinned, b.archived_at, b.created_at,
|
|
1098
|
+
(SELECT count(*) FROM job_artifacts x WHERE x.sha256=b.sha256)
|
|
1099
|
+
+ (SELECT count(*) FROM check_runs c WHERE b.sha256 IN (c.stdout_sha,c.stderr_sha,c.output_sha))
|
|
1100
|
+
+ (SELECT count(*) FROM op_attempts a WHERE b.sha256 IN (a.prompt_sha,a.transcript_sha,a.session_sha))
|
|
1101
|
+
+ (SELECT count(*) FROM attempt_transcript_snapshots t WHERE t.sha256=b.sha256)
|
|
1102
|
+
+ (SELECT count(*) FROM work_citations w WHERE w.sha256=b.sha256)
|
|
1103
|
+
+ (SELECT count(*) FROM goal_inputs g WHERE g.sha256=b.sha256)
|
|
1104
|
+
+ (SELECT count(*) FROM product_lands l WHERE l.output_sha=b.sha256)
|
|
1105
|
+
+ (SELECT count(*) FROM events e WHERE e.payload_sha=b.sha256) AS refs
|
|
1106
|
+
FROM blobs b;
|
|
1107
|
+
|
|
1108
|
+
|