@starci/hfs 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/README.md +110 -12
- package/bin/hfs.mjs +119 -15
- package/emit/compiler.mjs +35 -0
- package/emit/contracts.mjs +97 -0
- package/emit/operations-worker.mjs +24 -0
- package/emit/operations.mjs +126 -0
- package/emit/schema-worker.mjs +117 -0
- package/emit/static-graph.mjs +670 -0
- package/emit/type-schema.mjs +145 -0
- package/package.json +4 -1
- package/report/sonar.mjs +180 -0
- package/runtime/engine/admission.mjs +284 -0
- package/runtime/engine/digest.mjs +10 -0
- package/runtime/engine/ledger-db.mjs +1245 -0
- package/runtime/engine/machine-db.mjs +1484 -0
- package/runtime/engine/migrations/machine/0001-init.sql +887 -0
- package/runtime/engine/migrations/runtime/0001-init.sql +1072 -0
- package/runtime/engine/migrations/runtime/0003-usage-unavailable.sql +13 -0
- package/runtime/engine/migrations/runtime/0004-attempt-why.sql +43 -0
- package/runtime/engine/plain-object.mjs +5 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +16 -58
- package/runtime/knowledge/hfs/slots.yaml +405 -137
- package/runtime/knowledge/patterns/fe/folder.yaml +309 -0
- package/runtime/knowledge/sonar-gate.yaml +85 -0
- package/runtime/modules/kernel/failure-codes.yaml +1480 -16
- package/runtime/scripts/checks/architecture/backend.mjs +350 -0
- package/runtime/scripts/checks/architecture/background-unowned.mjs +107 -0
- package/runtime/scripts/checks/architecture/client-reaches-server.mjs +94 -0
- package/runtime/scripts/checks/architecture/clones.mjs +200 -0
- package/runtime/scripts/checks/architecture/config-unread.mjs +35 -0
- package/runtime/scripts/checks/architecture/config.mjs +310 -0
- package/runtime/scripts/checks/architecture/connection-map.mjs +208 -0
- package/runtime/scripts/checks/architecture/constructor-deps.mjs +100 -0
- package/runtime/scripts/checks/architecture/contract-fixture-guard.mjs +128 -0
- package/runtime/scripts/checks/architecture/contracts.mjs +792 -0
- package/runtime/scripts/checks/architecture/cross-app-duplicate.mjs +73 -0
- package/runtime/scripts/checks/architecture/dead-exports.mjs +265 -0
- package/runtime/scripts/checks/architecture/default-deny.mjs +129 -0
- package/runtime/scripts/checks/architecture/doc-language.mjs +39 -0
- package/runtime/scripts/checks/architecture/entrypoint.mjs +57 -0
- package/runtime/scripts/checks/architecture/error-codes.mjs +45 -0
- package/runtime/scripts/checks/architecture/error-masked.mjs +60 -0
- package/runtime/scripts/checks/architecture/fe-slot-allows.mjs +38 -0
- package/runtime/scripts/checks/architecture/feature-shape.mjs +49 -0
- package/runtime/scripts/checks/architecture/framework-pinned.mjs +83 -0
- package/runtime/scripts/checks/architecture/frontend.mjs +995 -0
- package/runtime/scripts/checks/architecture/hfs-graph.mjs +61 -0
- package/runtime/scripts/checks/architecture/hfs.mjs +521 -0
- package/runtime/scripts/checks/architecture/hooks-are-hooks.mjs +88 -0
- package/runtime/scripts/checks/architecture/i18n-keys.mjs +146 -0
- package/runtime/scripts/checks/architecture/index.mjs +316 -0
- package/runtime/scripts/checks/architecture/injection-token-exported.mjs +62 -0
- package/runtime/scripts/checks/architecture/machine-ast.mjs +138 -0
- package/runtime/scripts/checks/architecture/module-per-transport.mjs +130 -0
- package/runtime/scripts/checks/architecture/next-data.mjs +775 -0
- package/runtime/scripts/checks/architecture/owners.mjs +89 -0
- package/runtime/scripts/checks/architecture/package-shape.mjs +63 -0
- package/runtime/scripts/checks/architecture/reachability.mjs +233 -0
- package/runtime/scripts/checks/architecture/register-once.mjs +141 -0
- package/runtime/scripts/checks/architecture/registration.mjs +319 -0
- package/runtime/scripts/checks/architecture/required-files.mjs +172 -0
- package/runtime/scripts/checks/architecture/route-files-thin.mjs +97 -0
- package/runtime/scripts/checks/architecture/schema-owner.mjs +261 -0
- package/runtime/scripts/checks/architecture/size-growth.mjs +73 -0
- package/runtime/scripts/checks/architecture/source-names.mjs +607 -0
- package/runtime/scripts/checks/architecture/sql-owner.mjs +142 -0
- package/runtime/scripts/checks/architecture/sql-tokens.mjs +327 -0
- package/runtime/scripts/checks/architecture/symbols.mjs +193 -0
- package/runtime/scripts/checks/architecture/test-world-files.mjs +163 -0
- package/runtime/scripts/checks/architecture/tiers.mjs +130 -0
- package/runtime/scripts/checks/architecture/transport-owner.mjs +112 -0
- package/runtime/scripts/checks/architecture/typescript.mjs +500 -0
- package/runtime/scripts/checks/architecture/unit-spec-providers.mjs +122 -0
- package/runtime/scripts/checks/architecture.mjs +41 -0
- package/runtime/scripts/checks/common.mjs +37 -0
- package/runtime/scripts/checks/typescript-programs.mjs +82 -0
- package/runtime/scripts/lib/artifact-hold.mjs +89 -0
- package/runtime/scripts/lib/artifact-store.mjs +103 -0
- package/runtime/scripts/lib/fs-kind.mjs +10 -0
- package/runtime/scripts/lib/git.mjs +53 -0
- package/runtime/scripts/lib/hfs-allows.mjs +57 -0
- package/runtime/scripts/lib/hfs-check.mjs +254 -28
- package/runtime/scripts/lib/hfs-rules/contract.mjs +126 -0
- package/runtime/scripts/lib/hfs-rules/deps.mjs +63 -0
- package/runtime/scripts/lib/hfs-rules/fe-no-tests.mjs +47 -0
- package/runtime/scripts/lib/hfs-rules/frontend.mjs +124 -0
- package/runtime/scripts/lib/hfs-rules/lint-suppression.mjs +34 -0
- package/runtime/scripts/lib/hfs-rules/pipeline.mjs +51 -0
- package/runtime/scripts/lib/hfs-rules/proof-commands.mjs +64 -0
- package/runtime/scripts/lib/hfs-rules/read.mjs +28 -0
- package/runtime/scripts/lib/hfs-rules/repo-local-checks.mjs +32 -0
- package/runtime/scripts/lib/hfs-rules/secrets.mjs +54 -0
- package/runtime/scripts/lib/hfs-rules/spec-placement.mjs +31 -0
- package/runtime/scripts/lib/hfs-rules/stacks.mjs +54 -0
- package/runtime/scripts/lib/hfs-rules/test-topology.mjs +31 -0
- package/runtime/scripts/lib/hfs-slots.mjs +88 -40
- package/runtime/scripts/lib/hfs-tree.mjs +80 -0
- package/runtime/scripts/lib/hfs-view.mjs +68 -0
- package/runtime/scripts/lib/json.mjs +22 -0
- package/runtime/scripts/lib/language.mjs +107 -0
- package/runtime/scripts/lib/path-key.mjs +2 -0
- package/runtime/scripts/lib/redact.mjs +148 -0
- package/runtime/scripts/lib/repo-identity.mjs +50 -0
- package/runtime/scripts/lib/safe-remove.mjs +179 -0
- package/runtime/scripts/lib/secret-patterns.mjs +44 -0
- package/runtime/scripts/lib/sleep-sync.mjs +17 -0
- package/runtime/scripts/lib/stack-declaration.mjs +52 -0
- package/runtime/scripts/lib/stack-services.mjs +217 -0
- package/runtime/scripts/lib/test-secrets.mjs +120 -0
- package/scaffold/service.mjs +333 -0
- package/sync/format.mjs +46 -0
- package/sync/hygiene.mjs +56 -24
- package/sync/index.mjs +133 -41
- package/sync/managed.mjs +170 -0
- package/sync/skeleton.mjs +32 -10
- package/sync/sonar-key.mjs +13 -0
- package/sync/ts-strict.mjs +48 -0
- package/templates/{common → be/ci-workflows/github/workflows}/ci.yml +13 -13
- package/templates/be/{e2e.yml → ci-workflows/github/workflows/e2e.yml} +1 -0
- package/templates/be/hooks/husky/pre-commit +13 -0
- package/templates/be/hooks/husky/pre-push +7 -0
- package/templates/be/package-scripts/package.json +22 -0
- package/templates/be/{sonar-project.properties → quality-config/sonar-project.properties} +2 -3
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +14 -4
- package/templates/be/skeleton/apps/__app__/src/main.ts +9 -6
- package/templates/be/skeleton/scripts/.gitkeep +0 -0
- package/templates/be/skeleton/src/features/system-health/application/check-liveness.contracts.ts +2 -0
- package/templates/be/skeleton/src/features/system-health/application/check-liveness.handler.ts +22 -0
- package/templates/be/skeleton/src/features/system-health/application/check-liveness.query.ts +11 -0
- package/templates/be/skeleton/src/features/system-health/index.ts +1 -1
- package/templates/be/skeleton/src/features/system-health/system-health.module.ts +3 -3
- package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.ts +8 -9
- package/templates/be/skeleton/src/features/system-health/transport/http/system-health-http.module.ts +3 -6
- package/templates/be/skeleton/src/modules/domain/liveness/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.contracts.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.module.ts +14 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.service.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/liveness/liveness.service.ts +26 -0
- package/templates/be/skeleton/src/modules/platform/clock/clock.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/clock/clock.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/clock/clock.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/clock/clock.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/clock/clock.port.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/clock/index.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/clock/system-clock.service.spec.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/clock/system-clock.service.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/composition/composition.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/composition/index.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/config/config.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +2 -3
- package/templates/be/skeleton/src/modules/platform/config/server.config.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/config/server.options.ts +0 -3
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.handler.ts +27 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.log-events.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.module.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/errors/error.filter.ts +8 -12
- package/templates/be/skeleton/src/modules/platform/errors/errors.log-events.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/errors/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/logging/index.ts +4 -4
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +99 -0
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +38 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +23 -10
- package/templates/be/skeleton/src/modules/platform/logging/logging.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.port.ts +15 -0
- package/templates/be/tool-config/eslint.config.mjs +3 -0
- package/templates/be/tool-config/jest.config.js +1 -0
- package/templates/be/tool-config/prettierignore +8 -0
- package/templates/be/tool-config/prettierrc +1 -0
- package/templates/be/tool-config/src/tests/tsconfig.json +5 -0
- package/templates/be/tool-config/tsconfig.build.json +5 -0
- package/templates/be/tool-config/tsconfig.json +11 -0
- package/templates/common/gitignore.base +1 -1
- package/templates/fe/ci-workflows/github/workflows/ci.yml +54 -0
- package/templates/fe/hooks/husky/pre-commit +16 -0
- package/templates/fe/hooks/husky/pre-push +6 -0
- package/templates/fe/package-scripts/package.json +17 -0
- package/templates/fe/parts/api-client.ts +44 -0
- package/templates/fe/parts/api-outcome.ts +7 -0
- package/templates/fe/quality-config/sonar-project.properties +8 -0
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +1 -1
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/not-found.tsx +1 -1
- package/templates/fe/skeleton/apps/__app__/src/app/global-error.tsx +1 -1
- package/templates/fe/skeleton/scripts/.gitkeep +0 -0
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/client.ts +1 -0
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/index.ts +3 -0
- package/templates/fe/skeleton-app/apps/__app__/src/modules/api/outcome.ts +1 -0
- package/templates/fe/skeleton-app/apps/__app__/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton-shared/apps/__app__/next.config.ts +12 -0
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/api/index.ts +2 -0
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/index.ts +9 -0
- package/templates/fe/skeleton-shared/apps/__app__/src/modules/i18n/request.ts +5 -0
- package/templates/fe/skeleton-shared/apps/__app__/src/proxy.ts +5 -0
- package/templates/fe/skeleton-shared/packages/__family__-api/package.json +12 -0
- package/templates/fe/skeleton-shared/packages/__family__-api/src/client.ts +1 -0
- package/templates/fe/skeleton-shared/packages/__family__-api/src/index.ts +3 -0
- package/templates/fe/skeleton-shared/packages/__family__-api/src/outcome.ts +1 -0
- package/templates/fe/skeleton-shared/packages/__family__-api/tsconfig.json +5 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/package.json +18 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/app.ts +19 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/index.ts +2 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/proxy.ts +12 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/src/request.ts +15 -0
- package/templates/fe/skeleton-shared/packages/__family__-i18n/tsconfig.json +5 -0
- package/templates/fe/tool-config/eslint.config.mjs +3 -0
- package/templates/fe/tool-config/prettierignore +10 -0
- package/templates/fe/tool-config/prettierrc +1 -0
- package/templates/fe/tool-config/stylelint.config.mjs +3 -0
- package/templates/fe/tool-config/tsconfig.json +4 -0
- package/templates/be/pre-commit +0 -8
- package/templates/be/skeleton/apps/__app__/src/__app__.composition.spec.ts +0 -31
- package/templates/be/skeleton/src/features/system-health/transport/http/live.controller.spec.ts +0 -13
- package/templates/be/skeleton/src/modules/platform/config/env-source.spec.ts +0 -36
- package/templates/be/skeleton/src/modules/platform/config/server.config.spec.ts +0 -19
- package/templates/be/skeleton/src/modules/platform/errors/domain-error.spec.ts +0 -15
- package/templates/be/skeleton/src/modules/platform/errors/error.filter.spec.ts +0 -38
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.spec.ts +0 -33
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.ts +0 -35
- package/templates/be/skeleton/src/modules/platform/logging/log-id.ts +0 -9
- package/templates/be/skeleton/src/modules/platform/logging/logger.port.ts +0 -19
- package/templates/common/codecov.yml +0 -13
- package/templates/common/pre-push +0 -5
- package/templates/fe/e2e.yml +0 -22
- package/templates/fe/pre-commit +0 -7
- package/templates/fe/skeleton/apps/__app__/src/app/health/live/route.spec.ts +0 -10
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages.spec.ts +0 -27
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.spec.ts +0 -10
- package/templates/fe/sonar-project.properties +0 -11
- /package/templates/be/skeleton/src/modules/platform/config/{env-source.ts → env-source.config.ts} +0 -0
- /package/templates/be/skeleton/src/modules/platform/errors/{domain-error.ts → domain.error.ts} +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/next.config.ts +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/config.ts +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/navigation.ts +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/request.ts +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/modules/i18n/routing.ts +0 -0
- /package/templates/fe/{skeleton → skeleton-app}/apps/__app__/src/proxy.ts +0 -0
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
-- ############################################################################################################
|
|
2
|
+
-- PHẦN A — runtime.sqlite (mỗi dự án) → engine/migrations/runtime/0001-init.sql
|
|
3
|
+
-- ############################################################################################################
|
|
4
|
+
|
|
5
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
6
|
+
-- A0. Danh tính, phiên bản, từ vựng
|
|
7
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
8
|
+
-- meta: ledger_id (UUID, khoá của machine.ledgers), schema='starci/runtime@1', created_at, journal_mode, product,
|
|
9
|
+
-- repo_root, blob_root, runtime_rev, sqlite_version.
|
|
10
|
+
CREATE TABLE IF NOT EXISTS meta(
|
|
11
|
+
key TEXT PRIMARY KEY,
|
|
12
|
+
value TEXT NOT NULL) STRICT;
|
|
13
|
+
|
|
14
|
+
-- schema_migrations: 0001-init ghi dòng đầu; mỗi bước sau: backup VACUUM INTO + integrity_check trước,
|
|
15
|
+
-- foreign_key_check + quick_check trước COMMIT. Code từ chối GHI khi user_version > CODE_VERSION.
|
|
16
|
+
CREATE TABLE IF NOT EXISTS schema_migrations(
|
|
17
|
+
version INTEGER PRIMARY KEY,
|
|
18
|
+
name TEXT NOT NULL, -- '0001-init'
|
|
19
|
+
runtime_rev TEXT,
|
|
20
|
+
sql_sha256 TEXT NOT NULL, -- digest file migration đã chạy
|
|
21
|
+
backup_path TEXT, backup_sha256 TEXT,
|
|
22
|
+
started_at INTEGER NOT NULL,
|
|
23
|
+
finished_at INTEGER,
|
|
24
|
+
status TEXT NOT NULL CHECK(status IN ('running','done','failed'))) STRICT;
|
|
25
|
+
|
|
26
|
+
-- ui_states: bộ trạng thái hiển thị DUY NHẤT (UI-API §3.4).
|
|
27
|
+
CREATE TABLE IF NOT EXISTS ui_states(
|
|
28
|
+
ui TEXT PRIMARY KEY CHECK(ui IN ('bad','warn','running','waiting','ok','done','unknown')),
|
|
29
|
+
rank INTEGER NOT NULL, -- thứ tự xếp: bad trước
|
|
30
|
+
label_vi TEXT NOT NULL) STRICT;
|
|
31
|
+
INSERT OR IGNORE INTO ui_states VALUES
|
|
32
|
+
('bad',0,'Hỏng / Kẹt'),('warn',1,'Chậm / Cảnh báo'),('running',2,'Đang chạy'),('waiting',3,'Chờ'),
|
|
33
|
+
('ok',4,'Ổn'),('done',5,'Xong'),('unknown',6,'Chưa rõ');
|
|
34
|
+
|
|
35
|
+
-- ui_state_map: ánh xạ tĩnh trạng thái gốc → ui. Quy tắc theo thời gian (quá hạn, SLA) nằm trong view/hàm và chỉ
|
|
36
|
+
-- được NÂNG mức (waiting→warn→bad), không hạ.
|
|
37
|
+
CREATE TABLE IF NOT EXISTS ui_state_map(
|
|
38
|
+
entity TEXT NOT NULL, native TEXT NOT NULL,
|
|
39
|
+
ui TEXT NOT NULL REFERENCES ui_states(ui),
|
|
40
|
+
PRIMARY KEY(entity,native)) STRICT;
|
|
41
|
+
INSERT OR IGNORE INTO ui_state_map VALUES
|
|
42
|
+
('workflow','awaiting-approval','waiting'),('workflow','queued','waiting'),('workflow','running','running'),
|
|
43
|
+
('workflow','paused','waiting'),('workflow','stopped','done'),('workflow','finished','done'),('workflow','archived','done'),
|
|
44
|
+
('unit','planned','waiting'),('unit','queued','waiting'),('unit','deciding','waiting'),('unit','running','running'),
|
|
45
|
+
('unit','reported','running'),('unit','failed','bad'),('unit','done','done'),('unit','dropped','done'),
|
|
46
|
+
('job','queued','waiting'),('job','ready','waiting'),('job','leased','running'),('job','running','running'),
|
|
47
|
+
('job','answering','waiting'),('job','reported','running'),('job','deciding','waiting'),('job','awaiting_owner','waiting'),
|
|
48
|
+
('job','effect_unknown','bad'),('job','succeeded','done'),('job','failed','bad'),('job','cancelled','done'),
|
|
49
|
+
('attempt','routed','waiting'),('attempt','in-flight','running'),('attempt','pass','done'),('attempt','partial','warn'),
|
|
50
|
+
('attempt','fail','bad'),('attempt','blocked','bad'),('attempt','worker-dead','bad'),('attempt','effect-unknown','bad'),
|
|
51
|
+
('attempt','requeued','done'),('attempt','cancelled','done'),('attempt','dropped','done'),
|
|
52
|
+
('check','pass','ok'),('check','fail','bad'),('check','error','bad'),('check','unavailable','warn'),('check','skipped','waiting'),
|
|
53
|
+
('decision','open','waiting'),('decision','claimed','running'),('decision','escalated','warn'),('decision','expired','warn'),
|
|
54
|
+
('decision','resolved','done'),('decision','superseded','done'),
|
|
55
|
+
('incident','open','bad'),('incident','resolved','done'),('incident','superseded','done'),
|
|
56
|
+
('condition','True','ok'),('condition','False','bad'),('condition','Unknown','waiting'),
|
|
57
|
+
('settle-tail','queued','waiting'),('settle-tail','running','running'),('settle-tail','done','done'),('settle-tail','failed','bad'),
|
|
58
|
+
('product-land','queued','waiting'),('product-land','landed','done'),('product-land','failed','bad'),('product-land','conflict','bad'),
|
|
59
|
+
('product-land','red','bad'),('product-land','busy','warn'),('product-land','main-moving','warn');
|
|
60
|
+
|
|
61
|
+
-- blob_ref_columns: danh sách DUY NHẤT các cột tham chiếu blob (GC mark đọc bảng này; spec so với sqlite_master mọi
|
|
62
|
+
-- cột *_sha / sha256 để không bỏ sót cột mới).
|
|
63
|
+
CREATE TABLE IF NOT EXISTS blob_ref_columns(
|
|
64
|
+
table_name TEXT NOT NULL, column_name TEXT NOT NULL, PRIMARY KEY(table_name,column_name)) STRICT;
|
|
65
|
+
INSERT OR IGNORE INTO blob_ref_columns VALUES
|
|
66
|
+
('goal_inputs','sha256'),('op_attempts','prompt_sha'),('op_attempts','transcript_sha'),('op_attempts','session_sha'),
|
|
67
|
+
('attempt_transcript_snapshots','sha256'),
|
|
68
|
+
('check_runs','stdout_sha'),('check_runs','stderr_sha'),('check_runs','output_sha'),('job_artifacts','sha256'),
|
|
69
|
+
('work_citations','sha256'),('product_lands','output_sha'),('events','payload_sha');
|
|
70
|
+
|
|
71
|
+
-- Bảng chuyển trạng thái hợp lệ (dữ liệu, không phải code). Trigger ở A2/A3/A4 từ chối mọi cặp không có ở đây.
|
|
72
|
+
CREATE TABLE IF NOT EXISTS workflow_transitions(
|
|
73
|
+
from_phase TEXT NOT NULL, to_phase TEXT NOT NULL, PRIMARY KEY(from_phase,to_phase)) STRICT;
|
|
74
|
+
INSERT OR IGNORE INTO workflow_transitions VALUES
|
|
75
|
+
('awaiting-approval','queued'),('awaiting-approval','stopped'),
|
|
76
|
+
('queued','running'),('queued','stopped'),
|
|
77
|
+
('running','paused'),('running','stopped'),('running','finished'),
|
|
78
|
+
('paused','running'),('paused','stopped'),
|
|
79
|
+
('stopped','queued'), -- chỉ verb owner `api lifecycle --resume`; controller KHÔNG BAO GIỜ làm (MB-08)
|
|
80
|
+
('stopped','archived'),('finished','archived');
|
|
81
|
+
CREATE TABLE IF NOT EXISTS job_transitions(
|
|
82
|
+
from_status TEXT NOT NULL, to_status TEXT NOT NULL, PRIMARY KEY(from_status,to_status)) STRICT;
|
|
83
|
+
INSERT OR IGNORE INTO job_transitions VALUES
|
|
84
|
+
('queued','ready'),('queued','cancelled'),
|
|
85
|
+
('ready','queued'),('ready','leased'),('ready','cancelled'),
|
|
86
|
+
('leased','running'),('leased','ready'),('leased','cancelled'), -- leased→ready: dispatch bị từ chối trước khi op nhận hợp đồng (H13: không tiêu lần thử)
|
|
87
|
+
('leased','failed'), -- leased→failed: settle một dispatch chết giữa launch đã tạo terminal/attempt
|
|
88
|
+
('running','answering'),('answering','running'),
|
|
89
|
+
('running','reported'),('answering','reported'),
|
|
90
|
+
('running','effect_unknown'),('running','ready'),('running','failed'),('running','cancelled'), -- running→ready: requeue sau worker chết
|
|
91
|
+
('effect_unknown','running'),('effect_unknown','ready'),('effect_unknown','failed'),
|
|
92
|
+
('reported','deciding'),('reported','succeeded'),('reported','failed'), -- reported KHÔNG → cancelled (H9: archive không bỏ việc đã report)
|
|
93
|
+
('deciding','succeeded'),('deciding','failed'),('deciding','cancelled'),
|
|
94
|
+
('reported','awaiting_owner'),('deciding','awaiting_owner'); -- một op kết thúc bằng câu hỏi: chờ owner, không phải lỗi (awaiting_owner là trạng thái kết, như failed)
|
|
95
|
+
|
|
96
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
97
|
+
-- A1. Blob — chỉ mục kho nội dung (nội dung KHÔNG nằm trong SQLite)
|
|
98
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
99
|
+
CREATE TABLE IF NOT EXISTS blobs(
|
|
100
|
+
sha256 TEXT PRIMARY KEY CHECK(length(sha256)=64),
|
|
101
|
+
bytes INTEGER NOT NULL CHECK(bytes>=0),
|
|
102
|
+
media_type TEXT NOT NULL, -- image/png, video/webm, application/json, text/plain, text/x-diff ...
|
|
103
|
+
encoding TEXT CHECK(encoding IS NULL OR encoding='gzip'), -- sha256 luôn là của byte GỐC
|
|
104
|
+
redaction TEXT CHECK(redaction IS NULL OR redaction IN ('v1','binary')), -- 'v1' = văn bản đã qua bộ lọc bí mật v1
|
|
105
|
+
-- (transcript, log); 'binary' = nhị phân không lọc được; NULL = không cần
|
|
106
|
+
file_uri TEXT NOT NULL, -- đường tuyệt đối, ví dụ C:/Users/Hi/.starci/artifacts/ab/ab12...
|
|
107
|
+
http_path TEXT GENERATED ALWAYS AS ('/api/blob/' || sha256) VIRTUAL, -- harness 4547 phục vụ; không thể lệch
|
|
108
|
+
created_at INTEGER NOT NULL, -- GC không đụng blob trẻ hơn 24 giờ (grace put-trước-insert)
|
|
109
|
+
pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0,1)), -- 1 khi một bản ghi Work trích: không bao giờ bị sweep
|
|
110
|
+
archived_at INTEGER, -- đã nằm trong một zip D:/starci-archive đã kiểm chứng
|
|
111
|
+
archive_ref TEXT) STRICT; -- '<zip path>!<entry>'
|
|
112
|
+
|
|
113
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
114
|
+
-- A2. Workflow và goal
|
|
115
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
116
|
+
CREATE TABLE IF NOT EXISTS workflows(
|
|
117
|
+
workflow_id TEXT PRIMARY KEY, -- wf-<slug>-<base36 time>
|
|
118
|
+
trace_id TEXT NOT NULL UNIQUE CHECK(length(trace_id)=32), -- W3C trace id, sinh lúc tạo, giữ qua mọi revision
|
|
119
|
+
title TEXT,
|
|
120
|
+
display_name TEXT, -- '<Sản phẩm> · <việc gì>' (tiếng Việt)
|
|
121
|
+
phase TEXT NOT NULL CHECK(phase IN ('awaiting-approval','queued','running','paused','stopped','finished','archived')),
|
|
122
|
+
phase_reason TEXT, -- lý do đổi phase gần nhất (chi tiết ở lifecycle_changes)
|
|
123
|
+
ledger_mode TEXT,
|
|
124
|
+
source_roots_json TEXT CHECK(source_roots_json IS NULL OR json_valid(source_roots_json)),
|
|
125
|
+
generation INTEGER NOT NULL DEFAULT 0, -- spec: tăng khi goal/đồ thị đổi
|
|
126
|
+
observed_generation INTEGER NOT NULL DEFAULT 0, -- status: generation mà Kernel đã xử lý xong (K8s)
|
|
127
|
+
goal_identity TEXT,
|
|
128
|
+
pin_digest TEXT,
|
|
129
|
+
allowed_parallel INTEGER,
|
|
130
|
+
finished_json TEXT CHECK(finished_json IS NULL OR json_valid(finished_json)),
|
|
131
|
+
created_at INTEGER NOT NULL,
|
|
132
|
+
updated_at INTEGER NOT NULL,
|
|
133
|
+
finished_at INTEGER,
|
|
134
|
+
archived_at INTEGER) STRICT;
|
|
135
|
+
|
|
136
|
+
-- lifecycle_changes: lịch sử đổi phase, append-only (MB-08, G10). Chủ ghi duy nhất: verb `api lifecycle`
|
|
137
|
+
-- (define-goal, start, pause, stop, resume, finish, archive) — cùng giao dịch với UPDATE workflows.phase.
|
|
138
|
+
CREATE TABLE IF NOT EXISTS lifecycle_changes(
|
|
139
|
+
change_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
141
|
+
from_phase TEXT, to_phase TEXT NOT NULL,
|
|
142
|
+
by TEXT NOT NULL, -- owner | kernel:<wf> | supervisor
|
|
143
|
+
reason TEXT NOT NULL,
|
|
144
|
+
at INTEGER NOT NULL) STRICT;
|
|
145
|
+
CREATE TRIGGER IF NOT EXISTS lifecycle_changes_append_only BEFORE UPDATE ON lifecycle_changes BEGIN
|
|
146
|
+
SELECT RAISE(ABORT,'lifecycle_changes are append-only');
|
|
147
|
+
END;
|
|
148
|
+
-- Phase chỉ đổi theo workflow_transitions VÀ phải có dòng lifecycle_changes khớp được ghi ngay trước trong cùng giao dịch.
|
|
149
|
+
CREATE TRIGGER IF NOT EXISTS workflows_phase_guard BEFORE UPDATE OF phase ON workflows
|
|
150
|
+
WHEN NEW.phase <> OLD.phase BEGIN
|
|
151
|
+
SELECT RAISE(ABORT,'workflow-transition-refused')
|
|
152
|
+
WHERE NOT EXISTS(SELECT 1 FROM workflow_transitions t WHERE t.from_phase=OLD.phase AND t.to_phase=NEW.phase);
|
|
153
|
+
SELECT RAISE(ABORT,'workflow-transition-unrecorded')
|
|
154
|
+
WHERE NOT EXISTS(SELECT 1 FROM lifecycle_changes c WHERE c.workflow_id=NEW.workflow_id
|
|
155
|
+
AND c.from_phase IS OLD.phase AND c.to_phase=NEW.phase
|
|
156
|
+
AND c.change_id=(SELECT max(change_id) FROM lifecycle_changes WHERE workflow_id=NEW.workflow_id));
|
|
157
|
+
END;
|
|
158
|
+
|
|
159
|
+
CREATE TABLE IF NOT EXISTS goals(
|
|
160
|
+
goal_seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
161
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
162
|
+
revision INTEGER NOT NULL,
|
|
163
|
+
goal_identity TEXT NOT NULL,
|
|
164
|
+
markdown TEXT NOT NULL, -- goal text thầy duyệt (nguồn relaunch)
|
|
165
|
+
json TEXT NOT NULL CHECK(json_valid(json)), -- op chain đã derive
|
|
166
|
+
amendment_json TEXT CHECK(amendment_json IS NULL OR json_valid(amendment_json)),
|
|
167
|
+
approved_by TEXT, approval_ref TEXT,
|
|
168
|
+
created_at INTEGER NOT NULL,
|
|
169
|
+
UNIQUE(workflow_id,revision)) STRICT;
|
|
170
|
+
|
|
171
|
+
CREATE TABLE IF NOT EXISTS goal_inputs(
|
|
172
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
173
|
+
key TEXT NOT NULL, -- '<index>-<basename>'
|
|
174
|
+
goal_revision INTEGER NOT NULL,
|
|
175
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
176
|
+
origin TEXT NOT NULL,
|
|
177
|
+
created_at INTEGER NOT NULL,
|
|
178
|
+
PRIMARY KEY(workflow_id,key)) STRICT;
|
|
179
|
+
|
|
180
|
+
-- workflow_purges: bia mộ của đường xoá duy nhất (không FK: sống sót sau purge).
|
|
181
|
+
CREATE TABLE IF NOT EXISTS workflow_purges(
|
|
182
|
+
workflow_id TEXT PRIMARY KEY,
|
|
183
|
+
state TEXT NOT NULL CHECK(state IN ('planned','archived','deleting','purged')),
|
|
184
|
+
approved_by TEXT, approval_ref TEXT,
|
|
185
|
+
archive_path TEXT, archive_sha256 TEXT, archive_bytes INTEGER, manifest_sha256 TEXT,
|
|
186
|
+
events_head TEXT,
|
|
187
|
+
counts_json TEXT CHECK(counts_json IS NULL OR json_valid(counts_json)),
|
|
188
|
+
created_at INTEGER NOT NULL, archived_at INTEGER, verified_at INTEGER, purged_at INTEGER,
|
|
189
|
+
CHECK(state IN ('planned','archived') OR (approved_by IS NOT NULL AND approval_ref IS NOT NULL
|
|
190
|
+
AND archive_path IS NOT NULL AND archive_sha256 IS NOT NULL AND verified_at IS NOT NULL))) STRICT;
|
|
191
|
+
|
|
192
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
193
|
+
-- A3. Đồ thị: unit (bước) và cạnh
|
|
194
|
+
-- Từ vựng: unit = bước logic; job = một lần thử có input cụ thể (try_no); attempt = một lần dispatch (op_attempts).
|
|
195
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
196
|
+
CREATE TABLE IF NOT EXISTS work_graph_versions(
|
|
197
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
198
|
+
version INTEGER NOT NULL CHECK(version>=0),
|
|
199
|
+
event TEXT NOT NULL CHECK(event IN ('draw','revise','cut','edit')),
|
|
200
|
+
graph_json TEXT NOT NULL CHECK(json_valid(graph_json)),
|
|
201
|
+
diff_json TEXT NOT NULL CHECK(json_valid(diff_json)),
|
|
202
|
+
colors_json TEXT NOT NULL CHECK(json_valid(colors_json)),
|
|
203
|
+
reason TEXT NOT NULL, author_op TEXT NOT NULL, author_job TEXT, digest TEXT NOT NULL,
|
|
204
|
+
created_at INTEGER NOT NULL,
|
|
205
|
+
PRIMARY KEY(workflow_id,version)) STRICT;
|
|
206
|
+
|
|
207
|
+
CREATE TABLE IF NOT EXISTS work_units(
|
|
208
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
209
|
+
unit_id TEXT NOT NULL, -- = job_id của job đầu tiên trong họ
|
|
210
|
+
op_id TEXT NOT NULL,
|
|
211
|
+
subject_key TEXT NOT NULL, -- danh tính công việc, chuẩn hoá: '<cut_id>#<ordinal>' hoặc digest(records + owned_paths)
|
|
212
|
+
goal_revision INTEGER NOT NULL,
|
|
213
|
+
title TEXT,
|
|
214
|
+
cut_id TEXT, cut_ordinal INTEGER, cut_total INTEGER,
|
|
215
|
+
repository TEXT,
|
|
216
|
+
state TEXT NOT NULL CHECK(state IN ('planned','queued','running','reported','deciding','done','failed','dropped')),
|
|
217
|
+
current_job_id TEXT,
|
|
218
|
+
tries INTEGER NOT NULL DEFAULT 0, -- số job trong họ
|
|
219
|
+
dispatches INTEGER NOT NULL DEFAULT 0, -- số lần dispatch cộng dồn
|
|
220
|
+
try_budget INTEGER NOT NULL DEFAULT 5 CHECK(try_budget>=1), -- H3: vượt thì INSERT jobs bị từ chối
|
|
221
|
+
budget_raised_by TEXT, -- ai nâng ngân sách (owner | supervisor) ...
|
|
222
|
+
budget_raised_ref TEXT, -- ... và vì sao (di_id / incident_id); bắt buộc khi try_budget > mặc định
|
|
223
|
+
reopen_reason TEXT, reopened_by TEXT, reopened_at INTEGER, -- H5: chỉ cách duy nhất để chạy lại unit đã done
|
|
224
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, done_at INTEGER,
|
|
225
|
+
PRIMARY KEY(workflow_id,unit_id),
|
|
226
|
+
UNIQUE(workflow_id,op_id,subject_key,goal_revision), -- H3/H5: không thể "tạo unit mới" cho cùng một việc
|
|
227
|
+
CHECK(try_budget<=5 OR (budget_raised_by IS NOT NULL AND budget_raised_ref IS NOT NULL))) STRICT;
|
|
228
|
+
CREATE INDEX IF NOT EXISTS ix_units_state ON work_units(workflow_id,state);
|
|
229
|
+
CREATE INDEX IF NOT EXISTS ix_units_op ON work_units(workflow_id,op_id);
|
|
230
|
+
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;
|
|
231
|
+
-- Unit đã done chỉ rời 'done' qua reopen có lý do mới (reopened_at đổi) — không có đường tắt.
|
|
232
|
+
CREATE TRIGGER IF NOT EXISTS work_units_done_guard BEFORE UPDATE OF state ON work_units
|
|
233
|
+
WHEN OLD.state='done' AND NEW.state<>'done' BEGIN
|
|
234
|
+
SELECT RAISE(ABORT,'unit-already-passed: reopen needs reopen_reason, reopened_by and a new reopened_at')
|
|
235
|
+
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;
|
|
236
|
+
END;
|
|
237
|
+
|
|
238
|
+
CREATE TABLE IF NOT EXISTS unit_edges(
|
|
239
|
+
workflow_id TEXT NOT NULL,
|
|
240
|
+
from_unit TEXT NOT NULL,
|
|
241
|
+
to_unit TEXT NOT NULL,
|
|
242
|
+
kind TEXT NOT NULL CHECK(kind IN ('after','seam','dependsOn','peer-wait')),
|
|
243
|
+
source TEXT NOT NULL CHECK(source IN ('plan','graph','kernel-graph-edit','peer-wait','seam')),
|
|
244
|
+
created_at INTEGER NOT NULL,
|
|
245
|
+
PRIMARY KEY(workflow_id,from_unit,to_unit,kind),
|
|
246
|
+
FOREIGN KEY(workflow_id,from_unit) REFERENCES work_units(workflow_id,unit_id) ON DELETE CASCADE,
|
|
247
|
+
FOREIGN KEY(workflow_id,to_unit) REFERENCES work_units(workflow_id,unit_id) ON DELETE CASCADE) STRICT;
|
|
248
|
+
CREATE INDEX IF NOT EXISTS ix_unit_edges_to ON unit_edges(workflow_id,to_unit);
|
|
249
|
+
|
|
250
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
251
|
+
-- A4. Job (try), attempt (dispatch), hợp đồng, lease, idempotency
|
|
252
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
253
|
+
CREATE TABLE IF NOT EXISTS jobs(
|
|
254
|
+
job_id TEXT PRIMARY KEY, -- op-<op>-<token10> | kernel-<wf>
|
|
255
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
256
|
+
unit_id TEXT, -- NULL cho job kernel
|
|
257
|
+
op_id TEXT,
|
|
258
|
+
try_no INTEGER NOT NULL CHECK(try_no>=1), -- lần thử thứ mấy CỦA UNIT
|
|
259
|
+
retry_of TEXT REFERENCES jobs(job_id), -- job trước trong họ (retry đổi hình dạng)
|
|
260
|
+
resume_of TEXT REFERENCES jobs(job_id),
|
|
261
|
+
retry_class TEXT CHECK(retry_class IS NULL OR retry_class IN ('business','infra','resume','follow-up')),
|
|
262
|
+
generation INTEGER NOT NULL,
|
|
263
|
+
kind TEXT NOT NULL CHECK(kind IN ('op','kernel')),
|
|
264
|
+
role TEXT,
|
|
265
|
+
status TEXT NOT NULL CHECK(status IN ('queued','ready','leased','running','answering','reported','deciding',
|
|
266
|
+
'effect_unknown','awaiting_owner','succeeded','failed','cancelled')),
|
|
267
|
+
priority_json TEXT CHECK(priority_json IS NULL OR json_valid(priority_json)),
|
|
268
|
+
lease_token TEXT,
|
|
269
|
+
worker_id TEXT,
|
|
270
|
+
deadline INTEGER,
|
|
271
|
+
payload_json TEXT CHECK(payload_json IS NULL OR json_valid(payload_json)), -- CHỈ input: records, owned_paths, params, goal_binding, cut, after
|
|
272
|
+
created_at INTEGER NOT NULL,
|
|
273
|
+
updated_at INTEGER NOT NULL,
|
|
274
|
+
FOREIGN KEY(workflow_id,unit_id) REFERENCES work_units(workflow_id,unit_id)) STRICT;
|
|
275
|
+
CREATE INDEX IF NOT EXISTS jobs_queue ON jobs(status,kind,created_at,job_id);
|
|
276
|
+
CREATE INDEX IF NOT EXISTS jobs_op ON jobs(workflow_id,op_id);
|
|
277
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_jobs_unit_try ON jobs(workflow_id,unit_id,try_no) WHERE unit_id IS NOT NULL;
|
|
278
|
+
|
|
279
|
+
-- Cổng enqueue (H3, H4, H5, H9): mọi job op phải có unit; unit chưa done; còn ngân sách; lineage hợp lệ; workflow nhận việc.
|
|
280
|
+
CREATE TRIGGER IF NOT EXISTS jobs_enqueue_guard BEFORE INSERT ON jobs WHEN NEW.kind='op' BEGIN
|
|
281
|
+
SELECT RAISE(ABORT,'workflow-not-accepting-work')
|
|
282
|
+
WHERE (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id) NOT IN ('queued','running');
|
|
283
|
+
SELECT RAISE(ABORT,'unit-required') WHERE NEW.unit_id IS NULL;
|
|
284
|
+
SELECT RAISE(ABORT,'unit-already-passed')
|
|
285
|
+
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');
|
|
286
|
+
-- A try that ended awaiting_owner asked a question and did not fail: it spends no try of the budget.
|
|
287
|
+
SELECT RAISE(ABORT,'unit-try-budget-exhausted')
|
|
288
|
+
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')
|
|
289
|
+
> (SELECT try_budget FROM work_units u WHERE u.workflow_id=NEW.workflow_id AND u.unit_id=NEW.unit_id);
|
|
290
|
+
-- H5: the try after a reopen follows a PASSED try, so it has no retry_of/resume_of; it is admitted only as the
|
|
291
|
+
-- next try of a unit reopenUnit just moved out of 'done' (reopened_at set, state no longer done).
|
|
292
|
+
SELECT RAISE(ABORT,'first-try-must-be-1-without-lineage')
|
|
293
|
+
WHERE NEW.retry_of IS NULL AND NEW.resume_of IS NULL AND NEW.try_no<>1
|
|
294
|
+
AND NOT EXISTS(SELECT 1 FROM work_units u WHERE u.workflow_id=NEW.workflow_id AND u.unit_id=NEW.unit_id
|
|
295
|
+
AND u.reopened_at IS NOT NULL AND u.state<>'done' AND NEW.try_no=u.tries+1);
|
|
296
|
+
SELECT RAISE(ABORT,'retry-lineage-invalid: retry_of must be a FAILED or awaiting_owner job of the same unit with try_no-1')
|
|
297
|
+
WHERE NEW.retry_of IS NOT NULL AND NOT EXISTS(SELECT 1 FROM jobs p WHERE p.job_id=NEW.retry_of
|
|
298
|
+
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);
|
|
299
|
+
SELECT RAISE(ABORT,'resume-lineage-invalid: resume_of must be a failed/cancelled job of the same unit')
|
|
300
|
+
WHERE NEW.resume_of IS NOT NULL AND NOT EXISTS(SELECT 1 FROM jobs p WHERE p.job_id=NEW.resume_of
|
|
301
|
+
AND p.workflow_id=NEW.workflow_id AND p.unit_id=NEW.unit_id AND p.status IN ('failed','awaiting_owner','cancelled'));
|
|
302
|
+
END;
|
|
303
|
+
-- Máy trạng thái job (H9): chỉ các cặp trong job_transitions; cancelled/succeeded/failed là kết.
|
|
304
|
+
CREATE TRIGGER IF NOT EXISTS jobs_status_guard BEFORE UPDATE OF status ON jobs
|
|
305
|
+
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
|
|
306
|
+
SELECT RAISE(ABORT,'job-transition-refused');
|
|
307
|
+
END;
|
|
308
|
+
-- Job hết đời thì lease của nó biến mất trong cùng giao dịch (H12: không còn lease rò).
|
|
309
|
+
CREATE TRIGGER IF NOT EXISTS jobs_release_leases AFTER UPDATE OF status ON jobs
|
|
310
|
+
WHEN NEW.status IN ('succeeded','failed','awaiting_owner','cancelled') BEGIN
|
|
311
|
+
DELETE FROM leases WHERE job_id=NEW.job_id;
|
|
312
|
+
END;
|
|
313
|
+
|
|
314
|
+
-- op_attempts: MỘT DÒNG CHO MỖI LẦN DISPATCH (requeue sau worker chết → dispatch_seq+1, cùng job).
|
|
315
|
+
CREATE TABLE IF NOT EXISTS op_attempts(
|
|
316
|
+
attempt_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
317
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
318
|
+
job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,
|
|
319
|
+
unit_id TEXT,
|
|
320
|
+
op_id TEXT NOT NULL,
|
|
321
|
+
try_no INTEGER NOT NULL, -- = jobs.try_no
|
|
322
|
+
dispatch_seq INTEGER NOT NULL DEFAULT 1 CHECK(dispatch_seq>=1),
|
|
323
|
+
dispatch_id TEXT NOT NULL, -- ctx_... ; STARCI_DISPATCH_ID
|
|
324
|
+
-- tương quan
|
|
325
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
326
|
+
parent_span_id TEXT, -- span của decision / lượt Kernel đã dispatch
|
|
327
|
+
-- ai chạy
|
|
328
|
+
agent TEXT CHECK(agent IS NULL OR agent IN ('devin','codex','claude')),
|
|
329
|
+
provider TEXT,
|
|
330
|
+
model TEXT, -- model id đã chứng thực trên màn hình (gen_ai.response.model)
|
|
331
|
+
request_model TEXT, -- model đã yêu cầu (gen_ai.request.model)
|
|
332
|
+
model_profile TEXT, pool TEXT, effort TEXT, difficulty TEXT,
|
|
333
|
+
routed_by TEXT CHECK(routed_by IS NULL OR routed_by IN ('route','config','flag','kernel-override')),
|
|
334
|
+
route_chain_json TEXT CHECK(route_chain_json IS NULL OR json_valid(route_chain_json)),
|
|
335
|
+
route_rejected_json TEXT CHECK(route_rejected_json IS NULL OR json_valid(route_rejected_json)),
|
|
336
|
+
host TEXT NOT NULL DEFAULT 'orca',
|
|
337
|
+
run_id TEXT, task_id TEXT, terminal_handle TEXT, worker_pid INTEGER,
|
|
338
|
+
managed INTEGER NOT NULL DEFAULT 0 CHECK(managed IN (0,1)),
|
|
339
|
+
scratch_dir TEXT, -- STARCI_JOB_SCRATCH
|
|
340
|
+
-- tái lập (Bazel action key): đủ để dựng lại đúng lần thử
|
|
341
|
+
runtime_rev TEXT, -- commit .claude
|
|
342
|
+
cli_name TEXT, cli_version TEXT, -- devin 2.x, codex 0.x ...
|
|
343
|
+
contract_sha TEXT, -- digest contracts.markdown
|
|
344
|
+
config_sha TEXT, -- digest config.yaml hiệu lực
|
|
345
|
+
prompt_sha TEXT REFERENCES blobs(sha256), -- prompt thật đã gửi vào terminal
|
|
346
|
+
transcript_sha TEXT REFERENCES blobs(sha256), -- TOÀN BỘ scrollback terminal lúc kết thúc, đã redact (UI-API §2.10);
|
|
347
|
+
-- trong lúc chạy: attempt_transcript_snapshots (mỗi 60 giây)
|
|
348
|
+
session_sha TEXT REFERENCES blobs(sha256), -- file session của CLI (~/.claude/projects/*.jsonl, ~/.codex/sessions/...), đã redact
|
|
349
|
+
-- ở đâu
|
|
350
|
+
repo_root TEXT, worktree_path TEXT, branch TEXT, wf_branch TEXT,
|
|
351
|
+
base_sha TEXT, head_sha TEXT, integrated_sha TEXT,
|
|
352
|
+
-- khi nào
|
|
353
|
+
routed_at INTEGER, dispatched_at INTEGER, started_at INTEGER, attested_at INTEGER, reported_at INTEGER,
|
|
354
|
+
consumed_at INTEGER, checked_at INTEGER, settled_at INTEGER, released_at INTEGER,
|
|
355
|
+
terminal_closed_at INTEGER, task_closed_at INTEGER, worktree_removed_at INTEGER,
|
|
356
|
+
wall_ms INTEGER, -- dispatch → settle, monotonic
|
|
357
|
+
-- kết quả: lời op và phán quyết là HAI trường
|
|
358
|
+
report_outcome TEXT CHECK(report_outcome IS NULL OR report_outcome IN ('done','partial','failed','ask','blocked')),
|
|
359
|
+
verdict TEXT CHECK(verdict IS NULL OR verdict IN ('pass','fail','partial','blocked','dropped','cancelled')),
|
|
360
|
+
settled_by TEXT CHECK(settled_by IS NULL OR settled_by IN ('settler','kernel','supervisor','reconcile')),
|
|
361
|
+
decision_id TEXT,
|
|
362
|
+
failure_class TEXT, effect_state TEXT, next_step TEXT,
|
|
363
|
+
settle_json TEXT CHECK(settle_json IS NULL OR json_valid(settle_json)),
|
|
364
|
+
end_state TEXT CHECK(end_state IS NULL OR end_state IN ('settled','worker-dead','requeued','cancelled','effect-unknown')),
|
|
365
|
+
-- chi phí tóm tắt (chi tiết ở llm_usage); NULL khi không đo được
|
|
366
|
+
tokens_in INTEGER, tokens_out INTEGER, cost_usd REAL,
|
|
367
|
+
usage_source TEXT CHECK(usage_source IS NULL OR usage_source IN ('cli-transcript','provider-report')),
|
|
368
|
+
UNIQUE(workflow_id,dispatch_id),
|
|
369
|
+
UNIQUE(job_id,dispatch_seq)) STRICT;
|
|
370
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_unit ON op_attempts(workflow_id,unit_id,attempt_id);
|
|
371
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_op ON op_attempts(workflow_id,op_id);
|
|
372
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_time ON op_attempts(workflow_id,dispatched_at);
|
|
373
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_model ON op_attempts(agent,model,op_id);
|
|
374
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_terminal ON op_attempts(terminal_handle);
|
|
375
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_open ON op_attempts(settled_at,end_state);
|
|
376
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_head ON op_attempts(head_sha);
|
|
377
|
+
CREATE INDEX IF NOT EXISTS ix_attempts_reported ON op_attempts(reported_at) WHERE settled_at IS NULL;
|
|
378
|
+
-- Cổng dispatch (H9): dòng attempt chỉ được tạo khi job vừa đi ready→leased (job_transitions chỉ cho leased từ ready)
|
|
379
|
+
-- và workflow đang running. Job đã cancelled/archived không thể được dispatch.
|
|
380
|
+
CREATE TRIGGER IF NOT EXISTS op_attempts_dispatch_guard BEFORE INSERT ON op_attempts BEGIN
|
|
381
|
+
SELECT RAISE(ABORT,'dispatch-requires-ready-then-leased')
|
|
382
|
+
WHERE (SELECT status FROM jobs WHERE job_id=NEW.job_id) IS NOT 'leased';
|
|
383
|
+
SELECT RAISE(ABORT,'dispatch-requires-running-workflow')
|
|
384
|
+
WHERE (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id) IS NOT 'running';
|
|
385
|
+
SELECT RAISE(ABORT,'dispatch-previous-attempt-open')
|
|
386
|
+
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);
|
|
387
|
+
END;
|
|
388
|
+
|
|
389
|
+
-- contracts: kernel → op. Khoá theo attempt_id: lần dispatch lại KHÔNG ghi đè hợp đồng của lần trước.
|
|
390
|
+
CREATE TABLE IF NOT EXISTS contracts(
|
|
391
|
+
attempt_id INTEGER PRIMARY KEY REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
392
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
393
|
+
job_id TEXT NOT NULL,
|
|
394
|
+
contract_rev TEXT, -- phiên bản hợp đồng đã admit (modules/kernel/contract-changes)
|
|
395
|
+
markdown TEXT NOT NULL,
|
|
396
|
+
context_json TEXT CHECK(context_json IS NULL OR json_valid(context_json)),
|
|
397
|
+
created_at INTEGER NOT NULL) STRICT;
|
|
398
|
+
|
|
399
|
+
-- resources / leases: fence theo đường ghi. Thiếu dòng resources = capacity 1 (không seed trước).
|
|
400
|
+
CREATE TABLE IF NOT EXISTS resources(
|
|
401
|
+
resource_key TEXT PRIMARY KEY,
|
|
402
|
+
capacity INTEGER NOT NULL CHECK(capacity>=0),
|
|
403
|
+
declared_by TEXT, declared_at INTEGER) STRICT;
|
|
404
|
+
CREATE TABLE IF NOT EXISTS leases(
|
|
405
|
+
resource_key TEXT NOT NULL,
|
|
406
|
+
job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,
|
|
407
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
408
|
+
op_id TEXT, try_no INTEGER NOT NULL, generation INTEGER NOT NULL,
|
|
409
|
+
token TEXT NOT NULL, -- = jobs.lease_token
|
|
410
|
+
units INTEGER NOT NULL CHECK(units>0),
|
|
411
|
+
attempt_id INTEGER, -- chủ: lần dispatch đang giữ (NULL giữa lease và dispatch)
|
|
412
|
+
holder TEXT, -- terminal handle / pid đang giữ — GC hỏi được "ai giữ, còn sống không"
|
|
413
|
+
acquired_at INTEGER NOT NULL,
|
|
414
|
+
renewed_at INTEGER,
|
|
415
|
+
expires_at INTEGER NOT NULL CHECK(expires_at>acquired_at), -- TTL bắt buộc; hết hạn → luồng dead-worker (H12)
|
|
416
|
+
PRIMARY KEY(resource_key,job_id)) STRICT;
|
|
417
|
+
CREATE INDEX IF NOT EXISTS leases_expiry ON leases(expires_at);
|
|
418
|
+
CREATE TRIGGER IF NOT EXISTS leases_match_job BEFORE INSERT ON leases BEGIN
|
|
419
|
+
SELECT RAISE(ABORT,'lease-identity-drift') WHERE NOT EXISTS(
|
|
420
|
+
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
|
|
421
|
+
AND j.try_no=NEW.try_no AND j.generation=NEW.generation AND j.lease_token=NEW.token);
|
|
422
|
+
END;
|
|
423
|
+
CREATE TRIGGER IF NOT EXISTS leases_match_job_update BEFORE UPDATE ON leases BEGIN
|
|
424
|
+
SELECT RAISE(ABORT,'lease-identity-drift') WHERE NOT EXISTS(
|
|
425
|
+
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
|
|
426
|
+
AND j.try_no=NEW.try_no AND j.generation=NEW.generation AND j.lease_token=NEW.token);
|
|
427
|
+
END;
|
|
428
|
+
|
|
429
|
+
-- api_requests: idempotency cho mọi verb ghi có thể bị gọi lại (report, settle, decide, enqueue, drop, decisions --resolve).
|
|
430
|
+
-- request_id = --request-id hoặc sha(verb + dispatch_id + args). Gọi lại → trả result cũ. Harness KHÔNG ghi (UI chỉ đọc).
|
|
431
|
+
CREATE TABLE IF NOT EXISTS api_requests(
|
|
432
|
+
request_id TEXT PRIMARY KEY,
|
|
433
|
+
verb TEXT NOT NULL,
|
|
434
|
+
caller TEXT, -- kernel:<wf> | op:<attempt_id> | settler | reconciler | supervisor | owner-cli
|
|
435
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
436
|
+
attempt_id INTEGER,
|
|
437
|
+
args_sha TEXT NOT NULL,
|
|
438
|
+
status TEXT NOT NULL CHECK(status IN ('running','done','failed')),
|
|
439
|
+
result_json TEXT CHECK(result_json IS NULL OR json_valid(result_json)),
|
|
440
|
+
created_at INTEGER NOT NULL, finished_at INTEGER) STRICT;
|
|
441
|
+
CREATE INDEX IF NOT EXISTS ix_api_requests_attempt ON api_requests(attempt_id);
|
|
442
|
+
|
|
443
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
444
|
+
-- A5. Báo cáo, check, settle, land, artifact, bằng chứng, chi phí LLM
|
|
445
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
446
|
+
CREATE TABLE IF NOT EXISTS reports(
|
|
447
|
+
report_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
448
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
449
|
+
attempt_id INTEGER NOT NULL UNIQUE REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
450
|
+
dispatch_id TEXT NOT NULL,
|
|
451
|
+
job_id TEXT NOT NULL,
|
|
452
|
+
outcome TEXT NOT NULL CHECK(outcome IN ('done','partial','failed','ask','blocked')),
|
|
453
|
+
report_json TEXT NOT NULL CHECK(json_valid(report_json)), -- starci/op-report@1, bản DUY NHẤT
|
|
454
|
+
summary TEXT GENERATED ALWAYS AS (json_extract(report_json,'$.summary')) VIRTUAL,
|
|
455
|
+
from_terminal TEXT,
|
|
456
|
+
consumed_at INTEGER,
|
|
457
|
+
created_at INTEGER NOT NULL,
|
|
458
|
+
UNIQUE(workflow_id,dispatch_id)) STRICT;
|
|
459
|
+
-- Report bất biến (H10): nộp lại cùng nội dung → api_requests trả kết quả cũ; khác nội dung → từ chối.
|
|
460
|
+
CREATE TRIGGER IF NOT EXISTS reports_immutable BEFORE UPDATE OF report_json, outcome, attempt_id, dispatch_id ON reports BEGIN
|
|
461
|
+
SELECT RAISE(ABORT,'report-already-filed: reports are immutable');
|
|
462
|
+
END;
|
|
463
|
+
|
|
464
|
+
-- check_runs: mỗi lần chạy một check của một lần thử. run_seq = lần chạy lại thứ mấy của cùng check.
|
|
465
|
+
CREATE TABLE IF NOT EXISTS check_runs(
|
|
466
|
+
check_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
467
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
468
|
+
attempt_id INTEGER NOT NULL REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
469
|
+
job_id TEXT NOT NULL,
|
|
470
|
+
op_id TEXT NOT NULL,
|
|
471
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
472
|
+
parent_span_id TEXT, -- = op_attempts.span_id
|
|
473
|
+
name TEXT NOT NULL, -- canon-scan, check-scoped-lint, tsc-app, canon-parity, lint-check ...
|
|
474
|
+
phase TEXT NOT NULL CHECK(phase IN ('before','after','verify','parity','integrate')),
|
|
475
|
+
runner TEXT NOT NULL CHECK(runner IN ('op','settler','kernel','parity','integrate')),
|
|
476
|
+
run_seq INTEGER NOT NULL DEFAULT 1,
|
|
477
|
+
command TEXT, -- không bao giờ chứa bí mật
|
|
478
|
+
cwd TEXT,
|
|
479
|
+
input_digest TEXT, -- base sha + owned paths + tool rev: khoá dùng lại (thay settle-parity/*.json)
|
|
480
|
+
authority TEXT NOT NULL CHECK(authority IN ('runtime','declared')), -- H8: 'runtime' = lệnh chuẩn do runtime sở hữu
|
|
481
|
+
-- và runtime tự chạy; 'declared' = op tự khai, chỉ là bằng chứng, không tính vào verdict
|
|
482
|
+
exit_code INTEGER, -- exit THÔ runner quan sát được (không bao giờ bị hạ về 0)
|
|
483
|
+
declared_exit_code INTEGER, -- exit op TỰ KHAI trong report (có thể khác exit_code — đó là tín hiệu)
|
|
484
|
+
attribution_json TEXT CHECK(attribution_json IS NULL OR json_valid(attribution_json)), -- phần lỗi ngoài slice, có cấu trúc
|
|
485
|
+
status TEXT NOT NULL CHECK(status IN ('pass','fail','unavailable','error','skipped')),
|
|
486
|
+
started_at INTEGER, finished_at INTEGER, wall_ms INTEGER,
|
|
487
|
+
stdout_sha TEXT REFERENCES blobs(sha256),
|
|
488
|
+
stderr_sha TEXT REFERENCES blobs(sha256),
|
|
489
|
+
output_sha TEXT REFERENCES blobs(sha256), -- JSON đã parse (canon-scan --json ...)
|
|
490
|
+
summary_json TEXT CHECK(summary_json IS NULL OR json_valid(summary_json)),
|
|
491
|
+
note TEXT,
|
|
492
|
+
created_at INTEGER NOT NULL,
|
|
493
|
+
UNIQUE(attempt_id,runner,phase,name,run_seq),
|
|
494
|
+
CHECK(NOT (status='pass' AND exit_code IS NOT NULL AND exit_code<>0)), -- H8: không có "pass" với exit thô ≠ 0
|
|
495
|
+
CHECK(runner<>'op' OR authority='declared')) STRICT; -- check op tự chạy luôn chỉ là bằng chứng
|
|
496
|
+
CREATE INDEX IF NOT EXISTS ix_checks_name ON check_runs(workflow_id,name,status);
|
|
497
|
+
CREATE INDEX IF NOT EXISTS ix_checks_cache ON check_runs(name,input_digest);
|
|
498
|
+
|
|
499
|
+
-- settle_tails: hàng việc đuôi sau settle (thay <ledger dir>/settle-tail/<job>.json).
|
|
500
|
+
CREATE TABLE IF NOT EXISTS settle_tails(
|
|
501
|
+
attempt_id INTEGER PRIMARY KEY REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
502
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
503
|
+
state TEXT NOT NULL CHECK(state IN ('queued','running','done','failed')),
|
|
504
|
+
tries INTEGER NOT NULL DEFAULT 0,
|
|
505
|
+
due_at INTEGER, last_error TEXT,
|
|
506
|
+
queued_at INTEGER NOT NULL, started_at INTEGER, done_at INTEGER) STRICT;
|
|
507
|
+
|
|
508
|
+
-- product_lands: api product-land (merge wf/<wf> vào main repo sản phẩm; tách khỏi settle).
|
|
509
|
+
CREATE TABLE IF NOT EXISTS product_lands(
|
|
510
|
+
land_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
511
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
512
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16),
|
|
513
|
+
repo_root TEXT NOT NULL,
|
|
514
|
+
wf_branch TEXT NOT NULL,
|
|
515
|
+
main_before TEXT, merged_sha TEXT,
|
|
516
|
+
result TEXT NOT NULL CHECK(result IN ('queued','landed','failed','conflict','red','busy','main-moving')),
|
|
517
|
+
reason TEXT,
|
|
518
|
+
checks_json TEXT CHECK(checks_json IS NULL OR json_valid(checks_json)), -- land checks + import scan (tóm tắt)
|
|
519
|
+
output_sha TEXT REFERENCES blobs(sha256),
|
|
520
|
+
pushed INTEGER NOT NULL DEFAULT 0 CHECK(pushed IN (0,1)),
|
|
521
|
+
started_at INTEGER NOT NULL, finished_at INTEGER) STRICT;
|
|
522
|
+
CREATE INDEX IF NOT EXISTS ix_lands_wf ON product_lands(workflow_id,started_at);
|
|
523
|
+
|
|
524
|
+
-- job_artifacts: mọi file đầu ra, byte ở kho blob. Artifact của Kernel (scan, dispatch-ready) không có attempt.
|
|
525
|
+
CREATE TABLE IF NOT EXISTS job_artifacts(
|
|
526
|
+
artifact_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
527
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
528
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
529
|
+
job_id TEXT,
|
|
530
|
+
op_id TEXT,
|
|
531
|
+
cut TEXT,
|
|
532
|
+
role TEXT NOT NULL CHECK(role IN (
|
|
533
|
+
'check-output','check-stdout','check-stderr','patch','diff','report-attachment','log',
|
|
534
|
+
'direction','prompt','render','redline','critique',
|
|
535
|
+
'capture','dom','screenshot','video','trace','uat-run','metrics','salvage','scan','other')),
|
|
536
|
+
kind TEXT NOT NULL CHECK(kind IN ('diff','patch','image','video','report','log','trace','file')),
|
|
537
|
+
subkind TEXT CHECK(subkind IS NULL OR subkind IN ('draw-render','asset-gen','app-capture','e2e-capture','uat-capture',
|
|
538
|
+
'uat-video','e2e-video','playwright-trace','patch','patch-json','diff','report','log','critique','metrics',
|
|
539
|
+
'grammar-proposal','asset-request','terminal-transcript','cli-transcript')),
|
|
540
|
+
name TEXT NOT NULL, -- tên logic: 'checks/2-canon-scan/output', 'patch.diff', 'rounds/2/desktop-light.png'
|
|
541
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
542
|
+
bytes INTEGER NOT NULL,
|
|
543
|
+
media_type TEXT NOT NULL,
|
|
544
|
+
label TEXT, -- XBase#state@viewport ...
|
|
545
|
+
scope_ref TEXT, -- id bản ghi Work mà artifact nói về
|
|
546
|
+
round INTEGER, -- vòng vẽ/audit
|
|
547
|
+
run_id TEXT, -- runId của uat.verify
|
|
548
|
+
origin TEXT NOT NULL CHECK(origin IN ('op','settler','checker','kernel')),
|
|
549
|
+
base_sha TEXT, head_sha TEXT, integrated_sha TEXT,
|
|
550
|
+
created_at INTEGER NOT NULL,
|
|
551
|
+
CHECK(attempt_id IS NOT NULL OR origin='kernel')) STRICT;
|
|
552
|
+
-- Artifact bất biến (H10): khoá (attempt_id, name), byte theo sha; không UPDATE nội dung/tên, không ghi đè sha.
|
|
553
|
+
CREATE TRIGGER IF NOT EXISTS job_artifacts_immutable BEFORE UPDATE OF sha256, bytes, name, attempt_id, media_type ON job_artifacts BEGIN
|
|
554
|
+
SELECT RAISE(ABORT,'artifacts are immutable: file a new name');
|
|
555
|
+
END;
|
|
556
|
+
|
|
557
|
+
-- attempt_transcript_snapshots (UI-API §2.10): snapshot scrollback định kỳ (60 giây) của terminal op khi còn chạy, đã redact.
|
|
558
|
+
-- Scrollback không đổi thì không thêm dòng (UNIQUE attempt+sha). Bản cuối là op_attempts.transcript_sha.
|
|
559
|
+
CREATE TABLE IF NOT EXISTS attempt_transcript_snapshots(
|
|
560
|
+
snapshot_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
561
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
562
|
+
attempt_id INTEGER NOT NULL REFERENCES op_attempts(attempt_id) ON DELETE CASCADE,
|
|
563
|
+
at INTEGER NOT NULL,
|
|
564
|
+
lines INTEGER NOT NULL,
|
|
565
|
+
bytes INTEGER NOT NULL,
|
|
566
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256),
|
|
567
|
+
UNIQUE(attempt_id,sha256)) STRICT;
|
|
568
|
+
CREATE INDEX IF NOT EXISTS ix_transcript_snap ON attempt_transcript_snapshots(attempt_id,at);
|
|
569
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_artifacts_attempt_name ON job_artifacts(attempt_id,name) WHERE attempt_id IS NOT NULL;
|
|
570
|
+
CREATE UNIQUE INDEX IF NOT EXISTS ux_artifacts_kernel_name ON job_artifacts(workflow_id,name) WHERE attempt_id IS NULL;
|
|
571
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_blob ON job_artifacts(sha256);
|
|
572
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_role ON job_artifacts(workflow_id,role);
|
|
573
|
+
CREATE INDEX IF NOT EXISTS ix_artifacts_scope ON job_artifacts(scope_ref);
|
|
574
|
+
|
|
575
|
+
CREATE TABLE IF NOT EXISTS report_attachments(
|
|
576
|
+
report_id INTEGER NOT NULL REFERENCES reports(report_id) ON DELETE CASCADE,
|
|
577
|
+
artifact_id INTEGER NOT NULL REFERENCES job_artifacts(artifact_id) ON DELETE CASCADE,
|
|
578
|
+
PRIMARY KEY(report_id,artifact_id)) STRICT;
|
|
579
|
+
|
|
580
|
+
CREATE TABLE IF NOT EXISTS artifact_proofs(
|
|
581
|
+
artifact_id INTEGER PRIMARY KEY REFERENCES job_artifacts(artifact_id) ON DELETE CASCADE,
|
|
582
|
+
claims_json TEXT NOT NULL CHECK(json_valid(claims_json)), -- {frs[], cases[], shapes[], specs[]}
|
|
583
|
+
code_sha TEXT,
|
|
584
|
+
deps_json TEXT NOT NULL CHECK(json_valid(deps_json)), -- [{path, kind: code|work, digest}]
|
|
585
|
+
created_at INTEGER NOT NULL) STRICT;
|
|
586
|
+
|
|
587
|
+
-- work_citations: bản ghi Work (git) trích artifact/blob nào — ghim blob, hiện ảnh của bản ghi, phát hiện trích gãy.
|
|
588
|
+
CREATE TABLE IF NOT EXISTS work_citations(
|
|
589
|
+
record_id TEXT NOT NULL, -- uat.<feature>.<flow>, ui.<feature>.<screen>, impl.<...>, brand
|
|
590
|
+
record_path TEXT NOT NULL, -- features/<f>/<family>/<name>/evidence.yaml
|
|
591
|
+
field TEXT NOT NULL, -- 'run.screens[0]', 'assets[3]'
|
|
592
|
+
artifact_id INTEGER REFERENCES job_artifacts(artifact_id) ON DELETE RESTRICT,
|
|
593
|
+
sha256 TEXT NOT NULL REFERENCES blobs(sha256) ON DELETE RESTRICT,
|
|
594
|
+
role TEXT CHECK(role IS NULL OR role IN ('direction','capture','uat-screen','uat-video','uat-result','render','layout-capture')),
|
|
595
|
+
record_rev TEXT,
|
|
596
|
+
created_at INTEGER NOT NULL,
|
|
597
|
+
PRIMARY KEY(record_id,field)) STRICT;
|
|
598
|
+
CREATE INDEX IF NOT EXISTS ix_citations_blob ON work_citations(sha256);
|
|
599
|
+
CREATE INDEX IF NOT EXISTS ix_citations_path ON work_citations(record_path);
|
|
600
|
+
|
|
601
|
+
-- interface_audits: phạm vi + phán quyết của interface.audit (thay features/<f>/operations/<name>/index.yaml + E/**).
|
|
602
|
+
CREATE TABLE IF NOT EXISTS interface_audits(
|
|
603
|
+
audit_id TEXT PRIMARY KEY, -- operation.<feature>.<name>
|
|
604
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
605
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE SET NULL,
|
|
606
|
+
feature TEXT NOT NULL,
|
|
607
|
+
scope_json TEXT NOT NULL CHECK(json_valid(scope_json)),
|
|
608
|
+
verdict TEXT CHECK(verdict IS NULL OR verdict IN ('pass','fail','partial')),
|
|
609
|
+
findings_json TEXT CHECK(findings_json IS NULL OR json_valid(findings_json)),
|
|
610
|
+
route_to TEXT CHECK(route_to IS NULL OR route_to IN ('interface.implement','interface.draw')),
|
|
611
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL) STRICT;
|
|
612
|
+
|
|
613
|
+
-- llm_usage: chi phí LLM theo OTel GenAI semconv — mỗi (lần thử | lượt Kernel) × model một dòng.
|
|
614
|
+
-- Chi phí của Supervisor và [Worker] nằm ở machine.llm_usage.
|
|
615
|
+
CREATE TABLE IF NOT EXISTS llm_usage(
|
|
616
|
+
usage_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
617
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
618
|
+
subject_type TEXT NOT NULL CHECK(subject_type IN ('attempt','kernel-turn')),
|
|
619
|
+
attempt_id INTEGER REFERENCES op_attempts(attempt_id) ON DELETE CASCADE, -- khi subject_type='attempt'
|
|
620
|
+
turn_ref TEXT, -- khi 'kernel-turn': '<seat>:<n>' hoặc decision_id
|
|
621
|
+
span_id TEXT,
|
|
622
|
+
provider TEXT NOT NULL, -- gen_ai.provider.name
|
|
623
|
+
request_model TEXT, response_model TEXT, -- gen_ai.request.model / gen_ai.response.model
|
|
624
|
+
input_tokens INTEGER, output_tokens INTEGER, cache_read_tokens INTEGER, cache_write_tokens INTEGER,
|
|
625
|
+
reasoning_tokens INTEGER,
|
|
626
|
+
cost_usd REAL,
|
|
627
|
+
turns INTEGER, tool_calls INTEGER, tool_errors INTEGER,
|
|
628
|
+
source TEXT NOT NULL CHECK(source IN ('cli-transcript','provider-report')),
|
|
629
|
+
at INTEGER NOT NULL,
|
|
630
|
+
CHECK((subject_type='attempt' AND attempt_id IS NOT NULL) OR (subject_type='kernel-turn' AND turn_ref IS NOT NULL))) STRICT;
|
|
631
|
+
CREATE INDEX IF NOT EXISTS ix_usage_attempt ON llm_usage(attempt_id);
|
|
632
|
+
CREATE INDEX IF NOT EXISTS ix_usage_wf ON llm_usage(workflow_id,subject_type,at);
|
|
633
|
+
|
|
634
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
635
|
+
-- A6. Hộp thư, Decision Item, quyết định, điều kiện, sự cố, nền dùng chung, tín hiệu
|
|
636
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
637
|
+
CREATE TABLE IF NOT EXISTS inbox(
|
|
638
|
+
inbox_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
639
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
640
|
+
kind TEXT NOT NULL CHECK(kind IN ('goal','goal-revision','peer-message','worker-question','owner-answer','amendment')),
|
|
641
|
+
key TEXT,
|
|
642
|
+
from_ref TEXT, -- owner | wf-<peer> | attempt:<id>
|
|
643
|
+
attempt_id INTEGER, -- câu hỏi của worker: lần thử nào hỏi
|
|
644
|
+
payload_json TEXT NOT NULL CHECK(json_valid(payload_json)),
|
|
645
|
+
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','claimed','applied','rejected','done','superseded')),
|
|
646
|
+
disposition_json TEXT CHECK(disposition_json IS NULL OR json_valid(disposition_json)),
|
|
647
|
+
created_at INTEGER NOT NULL,
|
|
648
|
+
applied_at INTEGER) STRICT;
|
|
649
|
+
CREATE INDEX IF NOT EXISTS ix_inbox_open ON inbox(workflow_id,kind,status);
|
|
650
|
+
|
|
651
|
+
CREATE TABLE IF NOT EXISTS decision_items(
|
|
652
|
+
di_id TEXT PRIMARY KEY, -- di-<sha8>
|
|
653
|
+
-- MB-07: khoá = '<kind>:<entity>:<chữ ký lỗi>:<head|rev>' — MỌI thành phần bắt buộc, không phần rỗng. Chữ ký đổi → DI mới
|
|
654
|
+
-- và DI cũ superseded (superseded_by), không gộp mãi vào DI cũ.
|
|
655
|
+
idempotency_key TEXT NOT NULL UNIQUE CHECK(idempotency_key NOT LIKE '%::%' AND idempotency_key NOT LIKE '%:'
|
|
656
|
+
AND idempotency_key NOT LIKE ':%' AND length(idempotency_key)>=5),
|
|
657
|
+
key_parts_json TEXT NOT NULL CHECK(json_valid(key_parts_json) AND json_type(key_parts_json)='object'),
|
|
658
|
+
superseded_by TEXT REFERENCES decision_items(di_id),
|
|
659
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
660
|
+
kind TEXT NOT NULL, -- settle-nongreen | worker-question | progress-stall | stale-gate | ...
|
|
661
|
+
decider TEXT NOT NULL CHECK(decider IN ('kernel','supervisor','owner')),
|
|
662
|
+
entity_type TEXT CHECK(entity_type IS NULL OR entity_type IN ('job','unit','attempt','workflow','lane','service','seat')),
|
|
663
|
+
entity_id TEXT,
|
|
664
|
+
job_id TEXT, unit_id TEXT, attempt_id INTEGER,
|
|
665
|
+
summary TEXT NOT NULL,
|
|
666
|
+
status TEXT NOT NULL CHECK(status IN ('open','claimed','resolved','escalated','expired','superseded')),
|
|
667
|
+
opened_by TEXT NOT NULL,
|
|
668
|
+
opened_at INTEGER NOT NULL,
|
|
669
|
+
due_at INTEGER,
|
|
670
|
+
escalate_to TEXT, escalations INTEGER NOT NULL DEFAULT 0,
|
|
671
|
+
claim_by TEXT, claim_at INTEGER, claim_ttl_ms INTEGER,
|
|
672
|
+
resolved_by TEXT, resolved_at INTEGER, resolution_verb TEXT, decision_id TEXT,
|
|
673
|
+
evidence_json TEXT CHECK(evidence_json IS NULL OR json_valid(evidence_json)),
|
|
674
|
+
options_json TEXT CHECK(options_json IS NULL OR json_valid(options_json)),
|
|
675
|
+
allowed_verbs_json TEXT CHECK(allowed_verbs_json IS NULL OR json_valid(allowed_verbs_json)),
|
|
676
|
+
payload_json TEXT NOT NULL CHECK(json_valid(payload_json))) STRICT;
|
|
677
|
+
CREATE INDEX IF NOT EXISTS ix_di_open ON decision_items(decider,status,due_at);
|
|
678
|
+
CREATE INDEX IF NOT EXISTS ix_di_wf ON decision_items(workflow_id,status);
|
|
679
|
+
CREATE INDEX IF NOT EXISTS ix_di_ent ON decision_items(entity_type,entity_id);
|
|
680
|
+
|
|
681
|
+
CREATE TABLE IF NOT EXISTS decisions(
|
|
682
|
+
decision_id TEXT PRIMARY KEY,
|
|
683
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
684
|
+
span_id TEXT NOT NULL CHECK(length(span_id)=16), -- span con của lượt Kernel; attempt dispatch từ đây lấy làm parent
|
|
685
|
+
parent_span_id TEXT,
|
|
686
|
+
decider TEXT NOT NULL, -- 'kernel:<wf>' | 'supervisor' | 'owner'
|
|
687
|
+
di_id TEXT REFERENCES decision_items(di_id),
|
|
688
|
+
subject_type TEXT CHECK(subject_type IS NULL OR subject_type IN ('job','unit','attempt','graph','workflow')),
|
|
689
|
+
subject_id TEXT,
|
|
690
|
+
choice TEXT NOT NULL,
|
|
691
|
+
rationale TEXT,
|
|
692
|
+
result_json TEXT CHECK(result_json IS NULL OR json_valid(result_json)),
|
|
693
|
+
decided_at INTEGER NOT NULL) STRICT;
|
|
694
|
+
CREATE INDEX IF NOT EXISTS ix_decisions_wf ON decisions(workflow_id,decided_at);
|
|
695
|
+
CREATE INDEX IF NOT EXISTS ix_decisions_subj ON decisions(subject_type,subject_id);
|
|
696
|
+
|
|
697
|
+
-- conditions (K8s metav1.Condition): trạng thái quan sát theo từng khía cạnh; "vì sao kẹt" = condition đầu tiên
|
|
698
|
+
-- chưa True. Controller ghi 'Unknown' lần đầu thấy entity. Mỗi lần status đổi → event 'condition-changed'.
|
|
699
|
+
CREATE TABLE IF NOT EXISTS conditions(
|
|
700
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
701
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('workflow','unit','job','attempt')),
|
|
702
|
+
entity_id TEXT NOT NULL,
|
|
703
|
+
type TEXT NOT NULL, -- Routed, Dispatched, WorkerAlive, Reported, ChecksPassed, Settled, Released, WorktreeClean, GoalObserved
|
|
704
|
+
status TEXT NOT NULL CHECK(status IN ('True','False','Unknown')),
|
|
705
|
+
reason TEXT NOT NULL, -- CamelCase: ProviderCircuitOpen, RamThrottled, NoReportAfterSla, CheckFailed ...
|
|
706
|
+
message TEXT,
|
|
707
|
+
owner TEXT CHECK(owner IS NULL OR owner IN ('owner','supervisor','kernel','controller','op')), -- ai phải gỡ
|
|
708
|
+
observed_generation INTEGER,
|
|
709
|
+
last_transition_at INTEGER NOT NULL,
|
|
710
|
+
updated_at INTEGER NOT NULL,
|
|
711
|
+
PRIMARY KEY(entity_type,entity_id,type)) STRICT;
|
|
712
|
+
CREATE INDEX IF NOT EXISTS ix_conditions_open ON conditions(workflow_id,status);
|
|
713
|
+
|
|
714
|
+
-- incidents: kind là enum (hôm nay hàng trăm kind tự do), có chủ và hạn (GC hỏi được "sự cố nào quá hạn, ai phải gỡ").
|
|
715
|
+
CREATE TABLE IF NOT EXISTS incidents(
|
|
716
|
+
incident_id TEXT PRIMARY KEY,
|
|
717
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
718
|
+
op_id TEXT, job_id TEXT, attempt_id INTEGER,
|
|
719
|
+
kind TEXT NOT NULL CHECK(kind IN ('infra-provider','config-defect','owner-ask','credential-missing','safety-block',
|
|
720
|
+
'runtime-defect','evidence-missing','scope-change','partial-effect','other')),
|
|
721
|
+
detail TEXT,
|
|
722
|
+
owner TEXT NOT NULL CHECK(owner IN ('kernel','supervisor','owner')),
|
|
723
|
+
due_at INTEGER, -- quá hạn → DI leo thang
|
|
724
|
+
attempts INTEGER NOT NULL DEFAULT 0, model_calls INTEGER NOT NULL DEFAULT 0,
|
|
725
|
+
tokens INTEGER NOT NULL DEFAULT 0, elapsed_ms INTEGER NOT NULL DEFAULT 0,
|
|
726
|
+
last_progress TEXT,
|
|
727
|
+
status TEXT NOT NULL CHECK(status IN ('open','resolved','superseded')),
|
|
728
|
+
resolved_reason TEXT CHECK(resolved_reason IS NULL OR resolved_reason IN ('fixed','answered','workflow-ended','superseded','dropped')),
|
|
729
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, resolved_at INTEGER,
|
|
730
|
+
CHECK(status='open' OR (resolved_at IS NOT NULL AND resolved_reason IS NOT NULL))) STRICT;
|
|
731
|
+
CREATE INDEX IF NOT EXISTS ix_incidents_open ON incidents(status,due_at);
|
|
732
|
+
-- Workflow kết thúc thì sự cố của nó đóng trong cùng giao dịch (H12: 405 sự cố mồ côi).
|
|
733
|
+
CREATE TRIGGER IF NOT EXISTS workflows_close_incidents AFTER UPDATE OF phase ON workflows
|
|
734
|
+
WHEN NEW.phase IN ('finished','archived') BEGIN
|
|
735
|
+
UPDATE incidents SET status='resolved', resolved_reason='workflow-ended', resolved_at=NEW.updated_at, updated_at=NEW.updated_at
|
|
736
|
+
WHERE workflow_id=NEW.workflow_id AND status='open';
|
|
737
|
+
END;
|
|
738
|
+
|
|
739
|
+
CREATE TABLE IF NOT EXISTS foundations(
|
|
740
|
+
name TEXT PRIMARY KEY,
|
|
741
|
+
kind TEXT NOT NULL CHECK(kind IN ('brand','grammar','layout-tree','shell','module','contract','other')),
|
|
742
|
+
state TEXT NOT NULL CHECK(state IN ('unclaimed','claimed','published','retired')),
|
|
743
|
+
owner_workflow TEXT, version TEXT, detail TEXT, work_ref TEXT,
|
|
744
|
+
updated_at INTEGER NOT NULL) STRICT;
|
|
745
|
+
CREATE TABLE IF NOT EXISTS foundation_declarations(
|
|
746
|
+
workflow_id TEXT PRIMARY KEY REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
747
|
+
builds_none INTEGER NOT NULL CHECK(builds_none IN (0,1)), detail TEXT, at INTEGER NOT NULL) STRICT;
|
|
748
|
+
CREATE TABLE IF NOT EXISTS path_transfers(
|
|
749
|
+
path TEXT PRIMARY KEY,
|
|
750
|
+
from_workflow TEXT, to_workflow TEXT, bridge_id TEXT,
|
|
751
|
+
state TEXT NOT NULL CHECK(state IN ('proposed','applied','reverted')),
|
|
752
|
+
detail_json TEXT CHECK(detail_json IS NULL OR json_valid(detail_json)), at INTEGER NOT NULL) STRICT;
|
|
753
|
+
CREATE TABLE IF NOT EXISTS record_changes(
|
|
754
|
+
change_id TEXT PRIMARY KEY,
|
|
755
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
756
|
+
record_id TEXT NOT NULL, record_path TEXT, reason TEXT, by TEXT, at INTEGER NOT NULL) STRICT;
|
|
757
|
+
|
|
758
|
+
-- signals: CHỈ khoá/fence tiến trình.
|
|
759
|
+
CREATE TABLE IF NOT EXISTS signals(
|
|
760
|
+
scope TEXT NOT NULL CHECK(scope IN ('kernel','stop','launch','decision-doorbell')),
|
|
761
|
+
key TEXT NOT NULL,
|
|
762
|
+
workflow_id TEXT REFERENCES workflows(workflow_id) ON DELETE CASCADE, -- tín hiệu không sống lâu hơn workflow (dữ liệu #18)
|
|
763
|
+
holder_pid INTEGER, token TEXT,
|
|
764
|
+
value_json TEXT CHECK(value_json IS NULL OR json_valid(value_json)),
|
|
765
|
+
at INTEGER NOT NULL, expires_at INTEGER,
|
|
766
|
+
PRIMARY KEY(scope,key)) STRICT;
|
|
767
|
+
|
|
768
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
769
|
+
-- A7. Sự kiện và log (append-only, FTS)
|
|
770
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
771
|
+
-- events: nhật ký kiểm toán. prev_digest/digest do writer JS tính trong BEGIN IMMEDIATE:
|
|
772
|
+
-- digest = sha256(prev_digest || event_id || kind || coalesce(payload_json,'') || created_at)
|
|
773
|
+
CREATE TABLE IF NOT EXISTS events(
|
|
774
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
775
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
776
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
777
|
+
generation INTEGER NOT NULL,
|
|
778
|
+
entity_type TEXT NOT NULL,
|
|
779
|
+
entity_id TEXT NOT NULL,
|
|
780
|
+
attempt_id INTEGER, -- khi sự kiện thuộc một lần thử
|
|
781
|
+
span_id TEXT,
|
|
782
|
+
kind TEXT NOT NULL,
|
|
783
|
+
payload_json TEXT CHECK(payload_json IS NULL OR (json_valid(payload_json) AND length(payload_json)<=16384)),
|
|
784
|
+
payload_sha TEXT REFERENCES blobs(sha256), -- payload lớn hơn 16 KiB
|
|
785
|
+
prev_digest TEXT,
|
|
786
|
+
digest TEXT NOT NULL CHECK(length(digest)=64),
|
|
787
|
+
occurred_at INTEGER NOT NULL, -- lúc việc xảy ra (dữ liệu #12: tách khỏi lúc ghi)
|
|
788
|
+
created_at INTEGER NOT NULL) STRICT; -- lúc ghi (recorded_at)
|
|
789
|
+
CREATE INDEX IF NOT EXISTS events_entity ON events(workflow_id,entity_type,entity_id,seq);
|
|
790
|
+
CREATE INDEX IF NOT EXISTS events_kind ON events(workflow_id,kind,seq);
|
|
791
|
+
CREATE INDEX IF NOT EXISTS events_workflow_seq ON events(workflow_id,seq);
|
|
792
|
+
CREATE INDEX IF NOT EXISTS events_attempt ON events(attempt_id,seq);
|
|
793
|
+
CREATE TRIGGER IF NOT EXISTS events_append_only BEFORE UPDATE ON events BEGIN
|
|
794
|
+
SELECT RAISE(ABORT,'events are append-only');
|
|
795
|
+
END;
|
|
796
|
+
-- H9: workflow đã archived không nhận thêm sự kiện (hôm nay 1 840 event ghi sau archive), trừ sự kiện purge.
|
|
797
|
+
CREATE TRIGGER IF NOT EXISTS events_refuse_archived BEFORE INSERT ON events
|
|
798
|
+
WHEN (SELECT phase FROM workflows WHERE workflow_id=NEW.workflow_id)='archived' AND NEW.kind NOT LIKE 'purge-%' BEGIN
|
|
799
|
+
SELECT RAISE(ABORT,'workflow-archived: no further writes');
|
|
800
|
+
END;
|
|
801
|
+
CREATE TRIGGER IF NOT EXISTS events_delete_only_by_purge BEFORE DELETE ON events
|
|
802
|
+
WHEN NOT EXISTS(SELECT 1 FROM workflow_purges p WHERE p.workflow_id=OLD.workflow_id AND p.state='deleting') BEGIN
|
|
803
|
+
SELECT RAISE(ABORT,'events are append-only: only the owner-approved workflow purge deletes them');
|
|
804
|
+
END;
|
|
805
|
+
|
|
806
|
+
-- logs: log có kiểu. Op ghi qua `api log` (không còn log.jsonl trong .starciwork); UI chỉ đọc.
|
|
807
|
+
-- Mặc định KHÔNG ghi 'debug' vào DB (debug ở blob log thô của lần thử); bật theo workflow thì giữ 14 ngày.
|
|
808
|
+
CREATE TABLE IF NOT EXISTS logs(
|
|
809
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
810
|
+
at INTEGER NOT NULL,
|
|
811
|
+
workflow_id TEXT NOT NULL REFERENCES workflows(workflow_id) ON DELETE CASCADE,
|
|
812
|
+
job_id TEXT,
|
|
813
|
+
attempt_id INTEGER,
|
|
814
|
+
trace_id TEXT, span_id TEXT,
|
|
815
|
+
actor TEXT NOT NULL CHECK(actor IN ('kernel','op','runtime','check','land','settler','reconciler')),
|
|
816
|
+
node_id TEXT,
|
|
817
|
+
level TEXT NOT NULL CHECK(level IN ('debug','info','warn','error')),
|
|
818
|
+
kind TEXT NOT NULL, -- typed-logs.mjs LOG_KINDS
|
|
819
|
+
msg TEXT NOT NULL,
|
|
820
|
+
data_json TEXT CHECK(data_json IS NULL OR json_valid(data_json)),
|
|
821
|
+
refs_json TEXT CHECK(refs_json IS NULL OR json_valid(refs_json)),
|
|
822
|
+
src TEXT UNIQUE) STRICT; -- khoá dẫn xuất idempotent (ev:<seq> ...)
|
|
823
|
+
CREATE INDEX IF NOT EXISTS logs_workflow ON logs(workflow_id,seq);
|
|
824
|
+
CREATE INDEX IF NOT EXISTS logs_attempt ON logs(attempt_id,seq);
|
|
825
|
+
CREATE INDEX IF NOT EXISTS logs_job ON logs(job_id,seq);
|
|
826
|
+
CREATE INDEX IF NOT EXISTS logs_kind ON logs(workflow_id,kind,seq);
|
|
827
|
+
CREATE TRIGGER IF NOT EXISTS logs_append_only_update BEFORE UPDATE ON logs BEGIN
|
|
828
|
+
SELECT RAISE(ABORT,'logs are append-only');
|
|
829
|
+
END;
|
|
830
|
+
CREATE TRIGGER IF NOT EXISTS logs_delete_guard BEFORE DELETE ON logs
|
|
831
|
+
WHEN NOT EXISTS(SELECT 1 FROM workflow_purges p WHERE p.workflow_id=OLD.workflow_id AND p.state='deleting')
|
|
832
|
+
AND NOT (OLD.level='debug' AND OLD.at < CAST(unixepoch('subsec')*1000 AS INTEGER) - 1209600000) BEGIN
|
|
833
|
+
SELECT RAISE(ABORT,'logs are append-only: only the workflow purge (or debug retention > 14 days) deletes them');
|
|
834
|
+
END;
|
|
835
|
+
|
|
836
|
+
-- logs_fts: FTS5 external-content trên msg/kind (không đánh data_json). Tìm: logs_fts MATCH '"EADDRINUSE"'.
|
|
837
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS logs_fts USING fts5(msg, kind, content='logs', content_rowid='seq',
|
|
838
|
+
tokenize='unicode61 remove_diacritics 2');
|
|
839
|
+
CREATE TRIGGER IF NOT EXISTS logs_fts_insert AFTER INSERT ON logs BEGIN
|
|
840
|
+
INSERT INTO logs_fts(rowid,msg,kind) VALUES (NEW.seq,NEW.msg,NEW.kind);
|
|
841
|
+
END;
|
|
842
|
+
CREATE TRIGGER IF NOT EXISTS logs_fts_delete AFTER DELETE ON logs BEGIN
|
|
843
|
+
INSERT INTO logs_fts(logs_fts,rowid,msg,kind) VALUES ('delete',OLD.seq,OLD.msg,OLD.kind);
|
|
844
|
+
END;
|
|
845
|
+
|
|
846
|
+
CREATE TABLE IF NOT EXISTS log_cursors(name TEXT PRIMARY KEY, value INTEGER NOT NULL) STRICT; -- events:<ledger> → seq đã dẫn xuất
|
|
847
|
+
|
|
848
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
849
|
+
-- A8. View truy vấn (bền)
|
|
850
|
+
-- ---------------------------------------------------------------------------------------------------------
|
|
851
|
+
-- Trạng thái gốc của một lần thử (một chỗ tính, dùng cho ui).
|
|
852
|
+
CREATE VIEW IF NOT EXISTS v_attempt_state AS
|
|
853
|
+
SELECT attempt_id,
|
|
854
|
+
CASE WHEN end_state='worker-dead' THEN 'worker-dead' WHEN end_state='effect-unknown' THEN 'effect-unknown'
|
|
855
|
+
WHEN end_state='requeued' THEN 'requeued'
|
|
856
|
+
WHEN verdict IS NOT NULL THEN verdict
|
|
857
|
+
WHEN dispatched_at IS NULL THEN 'routed' ELSE 'in-flight' END AS native
|
|
858
|
+
FROM op_attempts;
|
|
859
|
+
|
|
860
|
+
-- Lịch sử op: mỗi lần dispatch (UI /api/attempts).
|
|
861
|
+
CREATE VIEW IF NOT EXISTS v_op_history AS
|
|
862
|
+
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,
|
|
863
|
+
a.agent, a.provider, a.model, a.model_profile, a.pool, a.effort, a.cli_version, a.runtime_rev,
|
|
864
|
+
a.terminal_handle, a.worktree_path, a.branch, a.base_sha, a.head_sha, a.integrated_sha,
|
|
865
|
+
a.routed_at, a.dispatched_at, a.reported_at, a.settled_at, a.released_at, a.worktree_removed_at,
|
|
866
|
+
COALESCE(a.wall_ms, a.settled_at - a.dispatched_at) AS cycle_ms,
|
|
867
|
+
a.report_outcome, a.verdict, a.settled_by, a.failure_class, a.end_state,
|
|
868
|
+
a.tokens_in, a.tokens_out, a.cost_usd, a.usage_source,
|
|
869
|
+
a.prompt_sha, a.transcript_sha, a.session_sha,
|
|
870
|
+
s.native AS attempt_state, COALESCE(m.ui,'unknown') AS ui,
|
|
871
|
+
j.status AS job_status, r.report_id, r.summary AS report_summary,
|
|
872
|
+
(SELECT count(*) FROM check_runs c WHERE c.attempt_id=a.attempt_id) AS checks,
|
|
873
|
+
(SELECT count(*) FROM check_runs c WHERE c.attempt_id=a.attempt_id AND c.status IN ('fail','error')) AS checks_red,
|
|
874
|
+
(SELECT count(*) FROM job_artifacts x WHERE x.attempt_id=a.attempt_id) AS artifacts
|
|
875
|
+
FROM op_attempts a
|
|
876
|
+
JOIN v_attempt_state s ON s.attempt_id=a.attempt_id
|
|
877
|
+
LEFT JOIN ui_state_map m ON m.entity='attempt' AND m.native=s.native
|
|
878
|
+
LEFT JOIN jobs j ON j.job_id=a.job_id
|
|
879
|
+
LEFT JOIN reports r ON r.attempt_id=a.attempt_id;
|
|
880
|
+
|
|
881
|
+
-- Hàng unit với lần thử mới nhất (UI /units, /graph).
|
|
882
|
+
CREATE VIEW IF NOT EXISTS v_units AS
|
|
883
|
+
SELECT u.*, COALESCE(m.ui,'unknown') AS ui,
|
|
884
|
+
(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
|
|
885
|
+
FROM work_units u LEFT JOIN ui_state_map m ON m.entity='unit' AND m.native=u.state;
|
|
886
|
+
|
|
887
|
+
-- Check với ui.
|
|
888
|
+
CREATE VIEW IF NOT EXISTS v_checks AS
|
|
889
|
+
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;
|
|
890
|
+
|
|
891
|
+
-- Decision Item với ui có luật thời gian: quá hạn hoặc leo thang ≥ 2 → bad; còn < 20% thời gian → warn.
|
|
892
|
+
CREATE VIEW IF NOT EXISTS v_decision_rows AS
|
|
893
|
+
SELECT d.*,
|
|
894
|
+
(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,
|
|
895
|
+
CASE WHEN d.status IN ('open','claimed','escalated')
|
|
896
|
+
AND ((d.due_at IS NOT NULL AND d.due_at < CAST(unixepoch('subsec')*1000 AS INTEGER)) OR d.escalations>=2) THEN 'bad'
|
|
897
|
+
WHEN d.status='open' AND d.due_at IS NOT NULL
|
|
898
|
+
AND (d.due_at - CAST(unixepoch('subsec')*1000 AS INTEGER)) < 0.2*(d.due_at - d.opened_at) THEN 'warn'
|
|
899
|
+
ELSE COALESCE(m.ui,'unknown') END AS ui
|
|
900
|
+
FROM decision_items d LEFT JOIN ui_state_map m ON m.entity='decision' AND m.native=d.status;
|
|
901
|
+
|
|
902
|
+
-- Media có link.
|
|
903
|
+
CREATE VIEW IF NOT EXISTS v_media AS
|
|
904
|
+
SELECT x.artifact_id, x.workflow_id, x.job_id, x.attempt_id, x.op_id, x.role, x.kind, x.subkind,
|
|
905
|
+
x.name, x.label, x.scope_ref, x.round, x.run_id, x.sha256, x.bytes, x.media_type, x.created_at,
|
|
906
|
+
b.http_path, b.file_uri, b.pinned, b.archived_at
|
|
907
|
+
FROM job_artifacts x JOIN blobs b ON b.sha256=x.sha256
|
|
908
|
+
WHERE x.role IN ('direction','render','redline','capture','dom','screenshot','video','trace','uat-run')
|
|
909
|
+
OR x.media_type LIKE 'image/%' OR x.media_type LIKE 'video/%';
|
|
910
|
+
|
|
911
|
+
-- Bằng chứng của một bản ghi Work.
|
|
912
|
+
CREATE VIEW IF NOT EXISTS v_record_evidence AS
|
|
913
|
+
SELECT c.record_id, c.record_path, c.field, c.role, c.record_rev, c.sha256,
|
|
914
|
+
b.http_path, b.file_uri, b.media_type, b.bytes,
|
|
915
|
+
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
|
|
916
|
+
FROM work_citations c JOIN blobs b ON b.sha256=c.sha256 LEFT JOIN job_artifacts x ON x.artifact_id=c.artifact_id;
|
|
917
|
+
|
|
918
|
+
-- Dòng thời gian: event + log + mốc lần thử + check + quyết định (sắp theo at, rồi seq).
|
|
919
|
+
CREATE VIEW IF NOT EXISTS v_timeline AS
|
|
920
|
+
SELECT workflow_id, created_at AS at, seq, 'event' AS source, kind, entity_type, entity_id, attempt_id, payload_json AS detail
|
|
921
|
+
FROM events
|
|
922
|
+
UNION ALL
|
|
923
|
+
SELECT workflow_id, at, seq, 'log', level||':'||kind, actor, node_id, attempt_id, msg FROM logs
|
|
924
|
+
UNION ALL
|
|
925
|
+
SELECT workflow_id, COALESCE(settled_at, reported_at, started_at, dispatched_at, routed_at), attempt_id, 'attempt',
|
|
926
|
+
COALESCE(verdict, report_outcome, 'active'), 'attempt', CAST(attempt_id AS TEXT), attempt_id,
|
|
927
|
+
json_object('op',op_id,'try',try_no,'seq',dispatch_seq,'agent',agent,'model',model,'verdict',verdict)
|
|
928
|
+
FROM op_attempts WHERE COALESCE(settled_at, reported_at, started_at, dispatched_at, routed_at) IS NOT NULL
|
|
929
|
+
UNION ALL
|
|
930
|
+
SELECT workflow_id, COALESCE(started_at, created_at), check_id, 'check', name||' '||status, 'check', CAST(check_id AS TEXT),
|
|
931
|
+
attempt_id, command FROM check_runs
|
|
932
|
+
UNION ALL
|
|
933
|
+
SELECT workflow_id, decided_at, 0, 'decision', choice, subject_type, subject_id, NULL, rationale FROM decisions;
|
|
934
|
+
|
|
935
|
+
-- Tiến độ theo unit.
|
|
936
|
+
CREATE VIEW IF NOT EXISTS v_workflow_progress AS
|
|
937
|
+
SELECT w.workflow_id, w.display_name, w.phase, w.trace_id, w.allowed_parallel,
|
|
938
|
+
count(u.unit_id) AS units_total,
|
|
939
|
+
coalesce(sum(u.state='done'),0) AS units_done,
|
|
940
|
+
coalesce(sum(u.state IN ('running','reported','deciding')),0) AS units_active,
|
|
941
|
+
coalesce(sum(u.state IN ('planned','queued')),0) AS units_waiting,
|
|
942
|
+
coalesce(sum(u.state='failed'),0) AS units_failed,
|
|
943
|
+
coalesce(sum(u.state='dropped'),0) AS units_dropped,
|
|
944
|
+
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,
|
|
945
|
+
coalesce(sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000),0) AS done_last_6h,
|
|
946
|
+
max(u.done_at) AS last_unit_at,
|
|
947
|
+
CASE WHEN coalesce(sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000),0)=0 THEN NULL
|
|
948
|
+
ELSE CAST(unixepoch('subsec')*1000 AS INTEGER) + CAST(21600000.0 * (count(u.unit_id) - sum(u.state IN ('done','dropped')))
|
|
949
|
+
/ sum(u.state='done' AND u.done_at > CAST(unixepoch('subsec')*1000 AS INTEGER) - 21600000) AS INTEGER) END AS eta_at
|
|
950
|
+
FROM workflows w LEFT JOIN work_units u ON u.workflow_id=w.workflow_id
|
|
951
|
+
GROUP BY w.workflow_id;
|
|
952
|
+
|
|
953
|
+
-- Hiệu năng model × op.
|
|
954
|
+
CREATE VIEW IF NOT EXISTS v_model_scorecard AS
|
|
955
|
+
SELECT agent, model, op_id, count(*) AS attempts,
|
|
956
|
+
sum(verdict='pass') AS pass, sum(verdict='fail') AS fail, sum(verdict='blocked') AS blocked,
|
|
957
|
+
sum(end_state='worker-dead') AS worker_dead,
|
|
958
|
+
round(1.0*sum(verdict='pass')/NULLIF(sum(verdict IS NOT NULL),0),3) AS pass_rate,
|
|
959
|
+
avg(COALESCE(wall_ms, settled_at - dispatched_at)) AS avg_cycle_ms,
|
|
960
|
+
sum(tokens_in) AS tokens_in, sum(tokens_out) AS tokens_out, sum(cost_usd) AS cost_usd
|
|
961
|
+
FROM op_attempts GROUP BY agent, model, op_id;
|
|
962
|
+
|
|
963
|
+
-- "Vì sao X kẹt": mọi lý do chặn đang mở, mỗi lý do một dòng, có ai phải gỡ (UI blockedBy).
|
|
964
|
+
CREATE VIEW IF NOT EXISTS v_blocking AS
|
|
965
|
+
SELECT e.workflow_id, 'unit' AS entity_type, e.to_unit AS entity_id, 'unit' AS blocker_type, e.from_unit AS blocker_id,
|
|
966
|
+
'UpstreamNotDone' AS reason_code, p.state AS detail, p.updated_at AS since, 'kernel' AS who
|
|
967
|
+
FROM unit_edges e JOIN work_units t ON t.workflow_id=e.workflow_id AND t.unit_id=e.to_unit
|
|
968
|
+
JOIN work_units p ON p.workflow_id=e.workflow_id AND p.unit_id=e.from_unit
|
|
969
|
+
WHERE t.state IN ('planned','queued') AND p.state NOT IN ('done','dropped')
|
|
970
|
+
UNION ALL
|
|
971
|
+
SELECT workflow_id, COALESCE(entity_type,'workflow'), COALESCE(entity_id,workflow_id), 'decision', di_id,
|
|
972
|
+
'DecisionOpen:'||kind, summary, opened_at, decider
|
|
973
|
+
FROM decision_items WHERE status IN ('open','claimed','escalated')
|
|
974
|
+
UNION ALL
|
|
975
|
+
SELECT workflow_id, entity_type, entity_id, 'condition', type, reason, message, last_transition_at, COALESCE(owner,'controller')
|
|
976
|
+
FROM conditions WHERE status<>'True'
|
|
977
|
+
UNION ALL
|
|
978
|
+
SELECT workflow_id, 'job', COALESCE(job_id,workflow_id), 'incident', incident_id, 'IncidentOpen:'||kind, COALESCE(detail,last_progress),
|
|
979
|
+
created_at, owner
|
|
980
|
+
FROM incidents WHERE status='open'
|
|
981
|
+
UNION ALL
|
|
982
|
+
SELECT workflow_id, 'attempt', CAST(attempt_id AS TEXT), 'question', CAST(inbox_id AS TEXT), 'WorkerQuestionPending',
|
|
983
|
+
json_extract(payload_json,'$.question'), created_at, 'kernel'
|
|
984
|
+
FROM inbox WHERE kind='worker-question' AND status IN ('pending','claimed')
|
|
985
|
+
UNION ALL
|
|
986
|
+
SELECT workflow_id, 'attempt', CAST(attempt_id AS TEXT), 'settle-tail', CAST(attempt_id AS TEXT), 'SettleTailFailed', last_error,
|
|
987
|
+
COALESCE(started_at,queued_at), 'controller'
|
|
988
|
+
FROM settle_tails WHERE state='failed'
|
|
989
|
+
UNION ALL
|
|
990
|
+
SELECT l.workflow_id, 'workflow', l.workflow_id, 'product-land', CAST(l.land_id AS TEXT), 'ProductLand:'||l.result, l.reason,
|
|
991
|
+
l.started_at, 'kernel'
|
|
992
|
+
FROM product_lands l
|
|
993
|
+
WHERE l.result IN ('failed','conflict','red')
|
|
994
|
+
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')
|
|
995
|
+
UNION ALL
|
|
996
|
+
-- H1: a filed report nobody settled within its SLA (the same rows as v_settle_overdue) blocks its job.
|
|
997
|
+
SELECT a.workflow_id, 'attempt', CAST(a.attempt_id AS TEXT), 'settle', CAST(a.attempt_id AS TEXT),
|
|
998
|
+
'SettleOverdue:'||COALESCE(a.report_outcome,'?'), a.job_id, a.reported_at,
|
|
999
|
+
CASE WHEN a.report_outcome='done' THEN 'settler' ELSE 'kernel' END
|
|
1000
|
+
FROM op_attempts a
|
|
1001
|
+
WHERE a.reported_at IS NOT NULL AND a.settled_at IS NULL AND a.end_state IS NULL
|
|
1002
|
+
AND (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) > CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END;
|
|
1003
|
+
|
|
1004
|
+
-- Việc đang mở (DI + job giữ lease/đang chạy).
|
|
1005
|
+
CREATE VIEW IF NOT EXISTS v_open_work AS
|
|
1006
|
+
SELECT 'decision' AS kind, workflow_id, di_id AS ref, summary AS what, opened_at AS since, due_at FROM decision_items
|
|
1007
|
+
WHERE status IN ('open','claimed','escalated')
|
|
1008
|
+
UNION ALL
|
|
1009
|
+
SELECT 'job-'||status, workflow_id, job_id, op_id, updated_at, deadline FROM jobs
|
|
1010
|
+
WHERE status IN ('leased','running','answering','effect_unknown');
|
|
1011
|
+
|
|
1012
|
+
-- H1: report đã nộp mà chưa settle quá SLA (xanh ≤ 3 phút cho settler; không xanh ≤ 15 phút cho Kernel, rồi DI leo thang).
|
|
1013
|
+
-- Job ở 'reported'/'deciding' quá SLA KHÔNG được tính là giữ slot fleet (census đọc view này để trừ ra).
|
|
1014
|
+
CREATE VIEW IF NOT EXISTS v_settle_overdue AS
|
|
1015
|
+
SELECT a.workflow_id, a.attempt_id, a.job_id, a.unit_id, a.op_id, a.report_outcome, j.status AS job_status,
|
|
1016
|
+
a.reported_at, (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) AS waiting_ms,
|
|
1017
|
+
CASE WHEN a.report_outcome='done' THEN 'settler' ELSE 'kernel' END AS who,
|
|
1018
|
+
CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END AS sla_ms,
|
|
1019
|
+
(SELECT di_id FROM decision_items d WHERE d.attempt_id=a.attempt_id AND d.status IN ('open','claimed','escalated')) AS open_di
|
|
1020
|
+
FROM op_attempts a JOIN jobs j ON j.job_id=a.job_id
|
|
1021
|
+
WHERE a.reported_at IS NOT NULL AND a.settled_at IS NULL AND a.end_state IS NULL
|
|
1022
|
+
AND (CAST(unixepoch('subsec')*1000 AS INTEGER) - a.reported_at) > CASE WHEN a.report_outcome='done' THEN 180000 ELSE 900000 END;
|
|
1023
|
+
|
|
1024
|
+
-- Rò: lease quá hạn (kèm chủ) và sự cố mở quá hạn — GC/Resource ctrl quét view này (H12).
|
|
1025
|
+
CREATE VIEW IF NOT EXISTS v_ledger_leaks AS
|
|
1026
|
+
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
|
|
1027
|
+
FROM leases l WHERE l.expires_at < CAST(unixepoch('subsec')*1000 AS INTEGER)
|
|
1028
|
+
UNION ALL
|
|
1029
|
+
SELECT 'incident', i.incident_id, i.workflow_id, i.job_id, i.attempt_id, i.owner, i.due_at
|
|
1030
|
+
FROM incidents i WHERE i.status='open' AND i.due_at < CAST(unixepoch('subsec')*1000 AS INTEGER)
|
|
1031
|
+
UNION ALL
|
|
1032
|
+
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
|
|
1033
|
+
FROM op_attempts a WHERE a.terminal_closed_at IS NOT NULL AND a.transcript_sha IS NULL
|
|
1034
|
+
AND a.terminal_closed_at < CAST(unixepoch('subsec')*1000 AS INTEGER) - 120000; -- SLA TRANSCRIPT_MISSING
|
|
1035
|
+
|
|
1036
|
+
-- Tìm theo id trên mọi thực thể (UI /api/search, phần khoá; phần chữ dùng logs_fts).
|
|
1037
|
+
CREATE VIEW IF NOT EXISTS v_search_ids AS
|
|
1038
|
+
SELECT 'workflow' AS kind, workflow_id AS id, workflow_id, COALESCE(display_name,title) AS title FROM workflows
|
|
1039
|
+
UNION ALL SELECT 'trace', trace_id, workflow_id, COALESCE(display_name,title) FROM workflows
|
|
1040
|
+
UNION ALL SELECT 'unit', unit_id, workflow_id, COALESCE(title,op_id) FROM work_units
|
|
1041
|
+
UNION ALL SELECT 'job', job_id, workflow_id, op_id FROM jobs
|
|
1042
|
+
UNION ALL SELECT 'attempt', CAST(attempt_id AS TEXT), workflow_id, op_id||' #'||try_no||'.'||dispatch_seq FROM op_attempts
|
|
1043
|
+
UNION ALL SELECT 'dispatch', dispatch_id, workflow_id, op_id FROM op_attempts
|
|
1044
|
+
UNION ALL SELECT 'span', span_id, workflow_id, op_id FROM op_attempts
|
|
1045
|
+
UNION ALL SELECT 'terminal', terminal_handle, workflow_id, op_id FROM op_attempts WHERE terminal_handle IS NOT NULL
|
|
1046
|
+
UNION ALL SELECT 'commit', head_sha, workflow_id, op_id FROM op_attempts WHERE head_sha IS NOT NULL
|
|
1047
|
+
UNION ALL SELECT 'decision', di_id, workflow_id, summary FROM decision_items
|
|
1048
|
+
UNION ALL SELECT 'incident', incident_id, workflow_id, kind FROM incidents
|
|
1049
|
+
UNION ALL SELECT 'artifact', CAST(artifact_id AS TEXT), workflow_id, name FROM job_artifacts
|
|
1050
|
+
UNION ALL SELECT 'blob', sha256, NULL, media_type FROM blobs;
|
|
1051
|
+
|
|
1052
|
+
-- Mốc invalidation cho SSE /api/live (server so với lần trước; ETag dùng thêm PRAGMA data_version).
|
|
1053
|
+
CREATE VIEW IF NOT EXISTS v_live_marks AS
|
|
1054
|
+
SELECT 'events' AS topic, COALESCE(max(seq),0) AS mark FROM events
|
|
1055
|
+
UNION ALL SELECT 'logs', COALESCE(max(seq),0) FROM logs
|
|
1056
|
+
UNION ALL SELECT 'attempts', COALESCE(max(max(attempt_id), max(COALESCE(settled_at,reported_at,dispatched_at,0))),0) FROM op_attempts
|
|
1057
|
+
UNION ALL SELECT 'decisions', COALESCE(max(max(opened_at), max(COALESCE(resolved_at,0))),0) FROM decision_items;
|
|
1058
|
+
|
|
1059
|
+
-- Tham chiếu blob trong ledger này (tiện tra cứu; GC dùng blob_ref_columns, không dùng view này).
|
|
1060
|
+
CREATE VIEW IF NOT EXISTS v_blob_refs AS
|
|
1061
|
+
SELECT b.sha256, b.bytes, b.pinned, b.archived_at, b.created_at,
|
|
1062
|
+
(SELECT count(*) FROM job_artifacts x WHERE x.sha256=b.sha256)
|
|
1063
|
+
+ (SELECT count(*) FROM check_runs c WHERE b.sha256 IN (c.stdout_sha,c.stderr_sha,c.output_sha))
|
|
1064
|
+
+ (SELECT count(*) FROM op_attempts a WHERE b.sha256 IN (a.prompt_sha,a.transcript_sha,a.session_sha))
|
|
1065
|
+
+ (SELECT count(*) FROM attempt_transcript_snapshots t WHERE t.sha256=b.sha256)
|
|
1066
|
+
+ (SELECT count(*) FROM work_citations w WHERE w.sha256=b.sha256)
|
|
1067
|
+
+ (SELECT count(*) FROM goal_inputs g WHERE g.sha256=b.sha256)
|
|
1068
|
+
+ (SELECT count(*) FROM product_lands l WHERE l.output_sha=b.sha256)
|
|
1069
|
+
+ (SELECT count(*) FROM events e WHERE e.payload_sha=b.sha256) AS refs
|
|
1070
|
+
FROM blobs b;
|
|
1071
|
+
|
|
1072
|
+
|