docs: make schema v3 the only workspace contract
This commit is contained in:
+15
-10
@@ -35,8 +35,12 @@
|
||||
cache in `embedding-models`, and `embedding-model-init` blocks `core` until
|
||||
`qwen3-embedding:0.6b` is present.
|
||||
- **Semantic contract.** Internal semantic indexing is fixed to `qwen3-embedding:0.6b`,
|
||||
`1024` dimensions, and cosine distance. Schema-v3 descriptors are operational; schema-v1/v2 descriptors remain `migration_required` until an explicit reviewed migration writes schema version 3. One workspace owns one Qdrant collection, and schema, Evidence, and Memory records
|
||||
coexist inside that collection with payload `kind` separation.
|
||||
`1024` dimensions, and cosine distance. Schema v3 is the only accepted workspace descriptor
|
||||
format. Schema v1 and v2 descriptors are rejected while a candidate snapshot is validated, so
|
||||
activation or a pull fails atomically and leaves the prior valid snapshot active; there is no
|
||||
in-product migrator or automatic conversion. One workspace owns one Qdrant collection, and
|
||||
schema, Evidence, and Memory records coexist inside that collection with payload `kind`
|
||||
separation.
|
||||
- **Final review runtime barriers.** Operational routes, retained session pins, and runtime
|
||||
rendering now require schema version 3 before resolving bindings, readiness, diagnostics, or
|
||||
Pi. Session admission verifies the exact internal Qdrant collection (dimensions, cosine
|
||||
@@ -53,11 +57,13 @@
|
||||
archives exactly one labeled `<project>_qdrant-data` volume and preserves the prior `qdrant`
|
||||
running state. `./scripts/vector-restore.sh --project-name <name> --input <file>
|
||||
--confirm-project <name>` requires the exact repeated project confirmation, validates manifest
|
||||
and archive safety before stopping `qdrant`, stages rollback content, restores in place, and
|
||||
restarts `qdrant` only if it was previously running. Restore does not migrate legacy workspace
|
||||
descriptors, rename collections, or repair a semantic-index incompatibility. Backup and restore
|
||||
share one atomic Docker-daemon lock per Compose project/Qdrant volume; contenders fail before
|
||||
volume resolution, and cleanup removes the lock only when its ownership labels still match.
|
||||
and archive safety before stopping `qdrant`, stages rollback content, restores semantic storage
|
||||
in place, and restarts `qdrant` only if it was previously running. Recovery requires the registry
|
||||
to already hold a reviewed v3 descriptor revision compatible with the restored collection; the
|
||||
helper does not restore descriptors, rename collections, or repair a semantic-index
|
||||
incompatibility. Backup and restore share one atomic Docker-daemon lock per Compose
|
||||
project/Qdrant volume; contenders fail before volume resolution, and cleanup removes the lock
|
||||
only when its ownership labels still match.
|
||||
- **Verification recorded for Task 13 final audit.** On Apple M4 Pro
|
||||
(`Darwin 25.5.0`, Docker Server `29.6.2 linux/arm64`), harness pytest passed
|
||||
**827 passed / 4 deselected**; backend Vitest passed **477/477** plus TypeScript and build;
|
||||
@@ -85,9 +91,8 @@
|
||||
Windows Docker Desktop startup were not manually executed in this run.
|
||||
- **Task 13 known limitations.** Broad harness Ruff remains existing unrelated debt
|
||||
(**220 errors**); touched harness files were verified Ruff-clean. The final active-reference
|
||||
audit remains non-empty only in categorized legacy parser/migration compatibility, legacy
|
||||
descriptor/config fixtures, deterministic negative guards, retained off-repository migration
|
||||
SQL, L2 legacy fixtures, gitignored task notes, and historical reference notes. No active
|
||||
audit remains non-empty only in deterministic negative guards, retained off-repository migration
|
||||
SQL, L2 compatibility fixtures, gitignored task notes, and historical reference notes. No active
|
||||
schema-v3 operator manual or supported runtime deployment path retains external vector or
|
||||
embedding endpoint coupling.
|
||||
- **Final review fix verification.** Backend Vitest passed **477/477** plus TypeScript and build;
|
||||
|
||||
@@ -58,7 +58,7 @@ diagnostics are exposed by `tht doctor` and do not prevent the UI from starting.
|
||||
Workspace descriptors are shared through a validated Git repository while endpoint bindings and
|
||||
secret files remain installation-local. Use the [local Mac/PC installation manual](docs/install/local-workspace-registry.md)
|
||||
for Docker Desktop or a local engine, and the [server installation manual](docs/install/server-workspace-registry.md)
|
||||
for the Gitea, reverse-proxy, backup, migration, and recovery workflow. The isolated deployment
|
||||
for the Gitea, reverse-proxy, backup, upgrade, and recovery workflow. The isolated deployment
|
||||
exercise is `./scripts/workspace-registry-smoke.sh`; both manuals are checked with
|
||||
`./scripts/verify-workspace-install-docs.sh --profile local` or `--profile server`.
|
||||
|
||||
@@ -70,10 +70,12 @@ every revision referenced by an open, closed, or failed unarchived session. It r
|
||||
single local installation list or from a server administrator's complete session list, never from
|
||||
a remote user's partial list.
|
||||
|
||||
Schema-v3 is the operational descriptor contract. Schema-v1/v2 descriptors remain
|
||||
`migration_required` until an explicit reviewed migration writes schema version 3. One workspace
|
||||
owns one Qdrant collection; schema, Evidence, and Memory records share that collection and stay
|
||||
separated by indexed payload `kind`.
|
||||
Schema v3 is the only accepted workspace descriptor format. Schema v1 and v2 descriptors are
|
||||
rejected while a candidate snapshot is validated, so activation or a pull fails atomically and the
|
||||
prior valid snapshot remains active. There is no in-product migrator or automatic conversion. A
|
||||
repository must already contain reviewed v3 descriptors. One workspace owns one Qdrant collection;
|
||||
schema, Evidence, and Memory records share that collection and stay separated by indexed payload
|
||||
`kind`.
|
||||
|
||||
Connector `ssh_tunnel` bindings are diagnostic-only in this release: their bounded probe always
|
||||
cleans up the loopback forward and returns `workspace_not_activatable`; session creation is rejected
|
||||
@@ -228,10 +230,11 @@ Compose project name by passing `--confirm-project`:
|
||||
|
||||
The restore script stops `qdrant`, validates the exact labeled target, stages the current volume
|
||||
contents for rollback, extracts the requested archive into the volume, and then returns the
|
||||
service to its prior running state. After restore, run the backend health checks and a known
|
||||
retrieval query before reopening write traffic. Restore does not migrate schema-v1/v2 workspace
|
||||
descriptors, does not rename collections, and does not reconcile an incompatible collection
|
||||
contract; those remain explicit reviewed recovery steps outside the helper.
|
||||
service to its prior running state. It restores semantic storage only. Before reopening write
|
||||
traffic, the workspace registry must already be at a reviewed v3 descriptor revision compatible
|
||||
with the restored collection; then run backend health checks and a known retrieval query. The
|
||||
helper does not restore descriptors, rename collections, or reconcile an incompatible collection
|
||||
contract.
|
||||
|
||||
## Production trust boundary and secrets
|
||||
|
||||
|
||||
@@ -213,8 +213,11 @@ Use `POST /workspace-registry/pull` to fetch later revisions. Run workspace diag
|
||||
required DWH bindings are mounted. Schema-v3 diagnostics probe the internal Qdrant/Ollama
|
||||
services through backend config; ordinary diagnostics are read-only.
|
||||
|
||||
Schema-v3 is the only operational descriptor format. Schema-v1/v2 descriptors remain
|
||||
`migration_required` until an explicit reviewed migration writes schema version 3. One workspace owns one Qdrant collection; schema, Evidence, and Memory records share that collection and remain
|
||||
Schema v3 is the only accepted workspace descriptor format. Schema v1 and v2 descriptors are
|
||||
rejected while the candidate snapshot is validated, so bootstrap activation or a pull fails
|
||||
atomically and leaves the prior active snapshot unchanged. There is no in-product migrator or
|
||||
automatic conversion. The repository must already contain reviewed v3 descriptors. One workspace
|
||||
owns one Qdrant collection; schema, Evidence, and Memory records share that collection and remain
|
||||
isolated by payload `kind`.
|
||||
|
||||
## Semantic index ownership contract
|
||||
@@ -223,16 +226,9 @@ isolated by payload `kind`.
|
||||
| --- | --- | --- |
|
||||
| Workspace semantic index | Each workspace reserves a single Qdrant collection. | Schema, Evidence, and Memory stay in that one collection and remain isolated by payload `kind`. |
|
||||
|
||||
To migrate an existing legacy descriptor, create/clone an empty private remote, set the absolute
|
||||
`THT_SOURCE_ROOT`, transform with absolute paths, review the schema-v1 result, explicitly produce
|
||||
the reviewed schema-v3 contract, then commit/push. The transformer never imports `${ENV}` values
|
||||
or secrets.
|
||||
|
||||
```sh
|
||||
THT_SOURCE_ROOT=/absolute/path/to/ThothII
|
||||
npm --prefix "$THT_SOURCE_ROOT/backend" run build
|
||||
node "$THT_SOURCE_ROOT/backend/dist/workspaces/migrate-legacy.js" --input /absolute/path/legacy.yaml --output /absolute/path/thoth-workspaces
|
||||
```
|
||||
If source material needs conversion, perform it outside ThothII in a separate reviewed process.
|
||||
Commit only the resulting reviewed v3 descriptors. That external process must not import `${ENV}`
|
||||
values, secret values, certificates, keys, or secret files into the repository.
|
||||
|
||||
## Publish, update, backup, outage recovery, and rollback
|
||||
|
||||
|
||||
@@ -60,9 +60,9 @@ use `ssh://git@git.example.invalid/platform/thoth-workspaces.git`. For HTTPS, cr
|
||||
machine credential in the secret manager and mount the Gitea/private CA separately. Never use a
|
||||
Gitea admin credential in the application.
|
||||
|
||||
Bootstrap an empty remote from a temporary review clone: migrate legacy descriptors, review their
|
||||
schema-v3 identity and generated artifacts, commit, and push `main`. The running server is not an
|
||||
authoring environment for migration.
|
||||
Bootstrap an empty remote from a temporary review clone only after its canonical v3 descriptors
|
||||
and generated public artifacts have been reviewed; commit and push `main`. The running server is
|
||||
not a descriptor authoring or conversion environment.
|
||||
|
||||
## Curator flow for shared-registry Evidence
|
||||
|
||||
@@ -258,14 +258,16 @@ For upgrades, record active status/head, finish active work, use the documented
|
||||
--check-only`, deploy the compatible image through `thothctl`, verify health/status, then resume
|
||||
proxy traffic.
|
||||
|
||||
For legacy descriptor migration, use a temporary review clone and the legacy transformer with absolute paths.
|
||||
Its schema-v1 output is `migration_required`; explicitly supply collection identity, diagnostics,
|
||||
and the reviewed v3 contract before commit. Never import `${ENV}` values or
|
||||
copy secret files.
|
||||
Schema v3 is the only accepted workspace descriptor format. Schema v1 and v2 descriptors are
|
||||
rejected while the candidate snapshot is validated, so initial activation or a pull fails
|
||||
atomically and leaves the prior active snapshot unchanged. There is no in-product migrator or
|
||||
automatic conversion. The repository must already contain reviewed v3 descriptors. One workspace
|
||||
owns one Qdrant collection; schema, Evidence, and Memory records share it and stay separated by
|
||||
payload `kind`.
|
||||
|
||||
Schema-v3 is the only operational descriptor contract. Schema-v1/v2 descriptors remain
|
||||
`migration_required` until an explicit reviewed migration writes version 3. One workspace owns one Qdrant collection; schema, Evidence, and Memory records share it and stay separated by payload
|
||||
`kind`.
|
||||
If source material needs conversion, perform it outside ThothII in a separate reviewed process.
|
||||
Commit only the resulting reviewed v3 descriptors. That external process must not import `${ENV}`
|
||||
values, secret values, certificates, keys, or secret files into the repository.
|
||||
|
||||
## Semantic index ownership contract
|
||||
|
||||
@@ -312,9 +314,10 @@ Use the repository helpers for Qdrant backup/restore:
|
||||
|
||||
Qdrant backup/restore targets exactly one labeled `qdrant-data` volume for the named Compose
|
||||
project. Restore requires the exact repeated project confirmation, validates the archive before
|
||||
stopping `qdrant`, stages rollback content, and restores in place only for that project-scoped
|
||||
volume. It does not migrate schema-v1/v2 workspaces, rename collections, or resolve semantic-index
|
||||
incompatibilities.
|
||||
stopping `qdrant`, stages rollback content, and restores semantic storage in place only for that
|
||||
project-scoped volume. Before recovery, the registry must already contain a reviewed v3 descriptor
|
||||
revision compatible with the restored collection. The helper does not restore descriptors, rename
|
||||
collections, or resolve semantic-index incompatibilities.
|
||||
|
||||
The Ollama model cache is a recoverable local cache, not the canonical semantic source of truth.
|
||||
You may back up `embedding-models` for faster offline recovery, but a cache loss is recoverable by
|
||||
|
||||
@@ -7,9 +7,10 @@ response body belongs in the descriptor, generated `.env.example` files, or diag
|
||||
|
||||
## Scope and safety rules
|
||||
|
||||
- The operational descriptor is schema version 3.
|
||||
- Schema-v1/v2 descriptors are readable only and remain `migration_required` until an explicit
|
||||
reviewed migration writes schema version 3.
|
||||
- Schema v3 is the only accepted workspace descriptor format.
|
||||
- Schema v1 and v2 descriptors are rejected before diagnostics run. There is no in-product
|
||||
migrator or automatic conversion; the Git repository must already contain reviewed v3
|
||||
descriptors.
|
||||
- One workspace owns one Qdrant collection.
|
||||
- Qdrant and Ollama are internal services. Operators do not bind external vector or embedding
|
||||
transports for active manuals or supported diagnostics.
|
||||
|
||||
@@ -214,7 +214,7 @@ cat >"$project_state_positive" <<'EOF'
|
||||
|
||||
## Internal Qdrant + Ollama semantic infrastructure — LIVE 2026-08-08
|
||||
|
||||
- Schema-v3 descriptors are operational and v1/v2 remain `migration_required`.
|
||||
- Schema v3 is the only accepted workspace descriptor format. Schema v1 and v2 descriptors are rejected before activation or diagnostics.
|
||||
- One workspace owns one Qdrant collection.
|
||||
- Only DWH and LLM remain external runtime application endpoints.
|
||||
- The internal stack includes `qdrant`, `embedding`, and `embedding-model-init`.
|
||||
@@ -223,10 +223,146 @@ cat >"$project_state_positive" <<'EOF'
|
||||
|
||||
### Historical snapshot — previous deployment
|
||||
|
||||
- Older notes intentionally live only here.
|
||||
- Schema-v2 descriptors were operational and returned `migration_required`.
|
||||
- Operators used `migrate-legacy` in this superseded workflow.
|
||||
EOF
|
||||
verify_project_state_current_contract "$project_state_positive" positive-project-state >/dev/null
|
||||
|
||||
project_state_unrelated_migration="$negative_root/project-state-unrelated-migration.md"
|
||||
python3 - "$project_state_positive" "$project_state_unrelated_migration" <<'PY'
|
||||
import pathlib, sys
|
||||
source = pathlib.Path(sys.argv[1]).read_text()
|
||||
old = "rejected before activation or diagnostics.\n- One workspace"
|
||||
new = (
|
||||
"rejected before activation or diagnostics. The unrelated session database\n"
|
||||
" upgrade may report `migration_required`.\n- One workspace"
|
||||
)
|
||||
if source.count(old) != 1:
|
||||
raise SystemExit("PROJECT_STATE positive fixture insertion point not found")
|
||||
pathlib.Path(sys.argv[2]).write_text(source.replace(old, new, 1))
|
||||
PY
|
||||
verify_project_state_current_contract "$project_state_unrelated_migration" \
|
||||
unrelated-current-project-state >/dev/null
|
||||
|
||||
generic_unrelated_migration="$negative_root/generic-unrelated-migration.md"
|
||||
python3 - "$root/docs/install/local-workspace-registry.md" "$generic_unrelated_migration" <<'PY'
|
||||
import pathlib, sys
|
||||
source = pathlib.Path(sys.argv[1]).read_text()
|
||||
old = "isolated by payload `kind`.\n\n## Semantic index ownership contract"
|
||||
new = (
|
||||
"isolated by payload `kind`. The unrelated session database\n"
|
||||
"upgrade may report `migration_required`.\n\n## Semantic index ownership contract"
|
||||
)
|
||||
if source.count(old) != 1:
|
||||
raise SystemExit("manual positive fixture insertion point not found")
|
||||
pathlib.Path(sys.argv[2]).write_text(source.replace(old, new, 1))
|
||||
PY
|
||||
verify_workspace_descriptor_doc_contract "$generic_unrelated_migration" \
|
||||
unrelated-current-manual >/dev/null
|
||||
|
||||
assert_schema_contract_rejected() {
|
||||
local source="$1" label="$2" expected="$3" output="$negative_root/schema-contract-output"
|
||||
set +e
|
||||
verify_workspace_descriptor_doc_contract "$source" "$label" >"$output" 2>&1
|
||||
local status=$?
|
||||
set -e
|
||||
if [[ $status -eq 0 ]] || ! grep -Fq "$expected" "$output"; then
|
||||
echo "$label fixture was not rejected correctly" >&2
|
||||
cat "$output" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
project_state_migration_required="$negative_root/project-state-migration-required.md"
|
||||
python3 - "$project_state_positive" "$project_state_migration_required" <<'PY'
|
||||
import pathlib, sys
|
||||
source = pathlib.Path(sys.argv[1]).read_text()
|
||||
marker = source.index("## Historical snapshots")
|
||||
pathlib.Path(sys.argv[2]).write_text(
|
||||
source[:marker]
|
||||
+ "- Schema v1 and v2 descriptors remain\n"
|
||||
+ " `migration_required`.\n\n"
|
||||
+ source[marker:]
|
||||
)
|
||||
PY
|
||||
set +e
|
||||
verify_project_state_current_contract "$project_state_migration_required" current-migration-required >"$project_state_output" 2>&1
|
||||
project_state_status=$?
|
||||
set -e
|
||||
if [[ $project_state_status -eq 0 ]] || ! grep -Fq "migration_required" "$project_state_output"; then
|
||||
echo "current PROJECT_STATE migration_required fixture was not rejected correctly" >&2
|
||||
cat "$project_state_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
project_state_readable="$negative_root/project-state-readable.md"
|
||||
python3 - "$project_state_positive" "$project_state_readable" <<'PY'
|
||||
import pathlib, sys
|
||||
source = pathlib.Path(sys.argv[1]).read_text()
|
||||
source = source.replace(
|
||||
"Schema v1 and v2 descriptors are rejected before activation or diagnostics.",
|
||||
"Schema v1 and v2 descriptors remain readable for operational use.",
|
||||
1,
|
||||
)
|
||||
pathlib.Path(sys.argv[2]).write_text(source)
|
||||
PY
|
||||
set +e
|
||||
verify_project_state_current_contract "$project_state_readable" current-readable-v1-v2 >"$project_state_output" 2>&1
|
||||
project_state_status=$?
|
||||
set -e
|
||||
if [[ $project_state_status -eq 0 ]] || ! grep -Eq "rejected|readable|operational" "$project_state_output"; then
|
||||
echo "current PROJECT_STATE v1/v2 readability fixture was not rejected correctly" >&2
|
||||
cat "$project_state_output" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
manual_migration_required="$negative_root/manual-migration-required.md"
|
||||
cp "$root/docs/install/local-workspace-registry.md" "$manual_migration_required"
|
||||
printf '\nSchema v1 and v2 descriptors remain\n`migration_required`.\n' \
|
||||
>>"$manual_migration_required"
|
||||
assert_schema_contract_rejected "$manual_migration_required" manual-migration-required "migration_required"
|
||||
|
||||
manual_migrate_legacy="$negative_root/manual-migrate-legacy.md"
|
||||
cp "$root/docs/install/local-workspace-registry.md" "$manual_migrate_legacy"
|
||||
printf '\nRun `node backend/dist/workspaces/migrate-legacy.js` before activation.\n' >>"$manual_migrate_legacy"
|
||||
assert_schema_contract_rejected "$manual_migrate_legacy" manual-migrate-legacy "migrate-legacy"
|
||||
|
||||
manual_legacy_procedure="$negative_root/manual-legacy-procedure.md"
|
||||
cp "$root/docs/install/server-workspace-registry.md" "$manual_legacy_procedure"
|
||||
printf '\nMigrate legacy descriptors in a temporary review clone before activation.\n' >>"$manual_legacy_procedure"
|
||||
assert_schema_contract_rejected "$manual_legacy_procedure" manual-legacy-procedure "legacy descriptor procedure"
|
||||
|
||||
manual_missing_v3_only="$negative_root/manual-missing-v3-only.md"
|
||||
python3 - "$root/docs/install/local-workspace-registry.md" "$manual_missing_v3_only" <<'PY'
|
||||
import pathlib, re, sys
|
||||
text = pathlib.Path(sys.argv[1]).read_text()
|
||||
text = re.sub(
|
||||
r"Schema v3 is the only accepted workspace descriptor format\.",
|
||||
"Schema v3 is accepted as a workspace descriptor format.",
|
||||
text,
|
||||
count=1,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
pathlib.Path(sys.argv[2]).write_text(text)
|
||||
PY
|
||||
assert_schema_contract_rejected "$manual_missing_v3_only" manual-missing-v3-only "v3-only acceptance"
|
||||
|
||||
diagnostics_readable="$negative_root/diagnostics-readable-v1-v2.md"
|
||||
python3 - "$root/docs/workspace-diagnostic-protocol.md" "$diagnostics_readable" <<'PY'
|
||||
import pathlib, re, sys
|
||||
text = pathlib.Path(sys.argv[1]).read_text()
|
||||
text, count = re.subn(
|
||||
r"Schema v1 and v2 descriptors are\s+rejected before diagnostics run\.",
|
||||
"Schema v1 and v2 descriptors remain readable for operational diagnostics.",
|
||||
text,
|
||||
count=1,
|
||||
)
|
||||
if count != 1:
|
||||
raise SystemExit("diagnostic rejection sentence not found")
|
||||
pathlib.Path(sys.argv[2]).write_text(text)
|
||||
PY
|
||||
assert_schema_contract_rejected "$diagnostics_readable" diagnostics-readable-v1-v2 "v1/v2 rejection"
|
||||
|
||||
local_manual_paraphrase="$negative_root/local-manual-paraphrase.md"
|
||||
cp "$root/docs/install/local-workspace-registry.md" "$local_manual_paraphrase"
|
||||
python3 - "$local_manual_paraphrase" <<'PY'
|
||||
|
||||
@@ -105,6 +105,109 @@ for token in tokens:
|
||||
PY
|
||||
}
|
||||
|
||||
verify_descriptor_migration_required_context() {
|
||||
local source="$1" label="$2" scope="${3:-all}"
|
||||
python3 - "$source" "$label" "$scope" <<'PY'
|
||||
import pathlib, re, sys
|
||||
|
||||
text = pathlib.Path(sys.argv[1]).read_text()
|
||||
label = sys.argv[2]
|
||||
scope = sys.argv[3]
|
||||
if scope == "current":
|
||||
marker = re.search(r"^## Historical snapshots\b", text, re.MULTILINE)
|
||||
if not marker:
|
||||
raise SystemExit(f"{label}: missing Historical snapshots boundary")
|
||||
text = text[:marker.start()]
|
||||
elif scope != "all":
|
||||
raise SystemExit(f"{label}: invalid migration_required verifier scope: {scope}")
|
||||
|
||||
blocks = []
|
||||
current = []
|
||||
|
||||
def flush():
|
||||
if current:
|
||||
blocks.append(" ".join(current))
|
||||
current.clear()
|
||||
|
||||
boundary = re.compile(r"^(?:#{1,6}\s+|[-*+]\s+|\d+[.)]\s+|>\s+|```|\|)")
|
||||
for raw_line in text.splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line:
|
||||
flush()
|
||||
continue
|
||||
if boundary.match(line):
|
||||
flush()
|
||||
line = re.sub(r"^(?:#{1,6}\s+|[-*+]\s+|\d+[.)]\s+|>\s+)", "", line)
|
||||
current.append(line)
|
||||
flush()
|
||||
|
||||
migration_context = re.compile(
|
||||
r"(?:\bworkspace(?:\s+[a-z0-9_-]+){0,3}\s+descriptors?\b|"
|
||||
r"\bschema(?:[- ]?v?|\s+version\s*)[12]\b|"
|
||||
r"\bv1\s*(?:/|and|or)\s*v2\b)",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
for block in blocks:
|
||||
normalized = re.sub(r"\s+", " ", block).strip()
|
||||
clauses = (part.strip() for part in re.split(r"(?<=[.!?;])\s+", normalized))
|
||||
for clause in clauses:
|
||||
if "migration_required" in clause.lower() and migration_context.search(clause):
|
||||
raise SystemExit(f"{label}: contains forbidden descriptor migration_required support")
|
||||
PY
|
||||
}
|
||||
|
||||
verify_workspace_descriptor_doc_contract() {
|
||||
local source="$1" label="$2"
|
||||
verify_descriptor_migration_required_context "$source" "$label" || return 1
|
||||
python3 - "$source" "$label" <<'PY'
|
||||
import pathlib, re, sys
|
||||
|
||||
text = pathlib.Path(sys.argv[1]).read_text()
|
||||
label = sys.argv[2]
|
||||
|
||||
if not re.search(
|
||||
r"schema[- ]v?3\s+is\s+the\s+only\s+accepted\s+workspace\s+descriptor",
|
||||
text,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
raise SystemExit(f"{label}: missing explicit v3-only acceptance contract")
|
||||
|
||||
legacy_subject = (
|
||||
r"(?:schema[- ]v?1\s*(?:/|and|or)\s*(?:schema[- ]?)?v?2|"
|
||||
r"schema[- ]v?1/v2|v1/v2)\s+descriptors?"
|
||||
)
|
||||
if not re.search(
|
||||
legacy_subject + r".{0,100}\brejected\b",
|
||||
text,
|
||||
re.IGNORECASE | re.DOTALL,
|
||||
):
|
||||
raise SystemExit(f"{label}: missing explicit v1/v2 rejection contract")
|
||||
|
||||
forbidden_literals = {
|
||||
"migrate-legacy": "migrate-legacy",
|
||||
"legacy transformer": "deleted legacy transformer instruction",
|
||||
"backend/dist/workspaces/migrate-legacy.js": "deleted transformer path",
|
||||
}
|
||||
for token, description in forbidden_literals.items():
|
||||
if token in text.lower():
|
||||
raise SystemExit(f"{label}: contains forbidden {description}")
|
||||
if re.search(
|
||||
r"(?:\bmigrat(?:e|ing)\s+legacy\s+descriptors?\b|\blegacy\s+descriptor\s+migration\b)",
|
||||
text,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
raise SystemExit(f"{label}: contains forbidden legacy descriptor procedure")
|
||||
|
||||
legacy_support = [
|
||||
legacy_subject + r".{0,100}\b(?:remain|are|stay)\b.{0,40}\b(?:readable|operational|supported|accepted)\b",
|
||||
r"schema[- ]v?[12]\s+descriptors?.{0,100}\b(?:remain|are|stay)\b.{0,40}\b(?:readable|operational|supported|accepted)\b",
|
||||
]
|
||||
for pattern in legacy_support:
|
||||
if re.search(pattern, text, re.IGNORECASE | re.DOTALL):
|
||||
raise SystemExit(f"{label}: v1/v2 rejection contradicted by readable or operational support")
|
||||
PY
|
||||
}
|
||||
|
||||
verify_markdown_table_relationships() {
|
||||
local source="$1" label="$2" heading="$3" spec_json="$4"
|
||||
python3 - "$source" "$label" "$heading" "$spec_json" <<'PY'
|
||||
@@ -588,6 +691,7 @@ verify_vector_helper_interfaces() {
|
||||
verify_project_state_current_contract() {
|
||||
local source="${1:-$root/PROJECT_STATE.md}"
|
||||
local label="${2:-PROJECT_STATE.md}"
|
||||
verify_descriptor_migration_required_context "$source" "$label current section" current || return 1
|
||||
python3 - "$source" "$label" <<'PY'
|
||||
import pathlib, re, sys
|
||||
text = pathlib.Path(sys.argv[1]).read_text()
|
||||
@@ -599,10 +703,30 @@ current = text[:marker.start()]
|
||||
historical = text[marker.start():]
|
||||
if not re.search(r"Internal Qdrant \+ Ollama semantic infrastructure", current, re.MULTILINE):
|
||||
raise SystemExit(f"{label}: current section missing internal semantic snapshot heading")
|
||||
if not re.search(r"Schema-v3 descriptors are operational", current, re.MULTILINE):
|
||||
raise SystemExit(f"{label}: current section must say schema-v3 is operational")
|
||||
if "migration_required" not in current:
|
||||
raise SystemExit(f"{label}: current section must mention migration_required")
|
||||
if "migrate-legacy" in current.lower():
|
||||
raise SystemExit(f"{label}: current section contains forbidden migrate-legacy instruction")
|
||||
if not re.search(
|
||||
r"schema[- ]v?3\s+is\s+the\s+only\s+accepted\s+workspace\s+descriptor",
|
||||
current,
|
||||
re.IGNORECASE,
|
||||
):
|
||||
raise SystemExit(f"{label}: current section lacks explicit v3-only acceptance contract")
|
||||
legacy_subject = (
|
||||
r"(?:schema[- ]v?1\s*(?:/|and|or)\s*(?:schema[- ]?)?v?2|"
|
||||
r"schema[- ]v?1/v2|v1/v2)\s+descriptors?"
|
||||
)
|
||||
if not re.search(
|
||||
legacy_subject + r".{0,100}\brejected\b",
|
||||
current,
|
||||
re.IGNORECASE | re.DOTALL,
|
||||
):
|
||||
raise SystemExit(f"{label}: current section lacks explicit v1/v2 rejection contract")
|
||||
for pattern in [
|
||||
legacy_subject + r".{0,100}\b(?:remain|are|stay)\b.{0,40}\b(?:readable|operational|supported|accepted)\b",
|
||||
r"schema[- ]v?[12]\s+descriptors?.{0,100}\b(?:remain|are|stay)\b.{0,40}\b(?:readable|operational|supported|accepted)\b",
|
||||
]:
|
||||
if re.search(pattern, current, re.IGNORECASE | re.DOTALL):
|
||||
raise SystemExit(f"{label}: current section contains contradictory active text: v1/v2 readable or operational support")
|
||||
if not re.search(r"\b(one|single)\b.*\bworkspace\b.*\b(one|single)\b.*\bQdrant\b.*\bcollection\b", current, re.IGNORECASE | re.DOTALL):
|
||||
raise SystemExit(f"{label}: current section must describe one-workspace/one-collection ownership")
|
||||
if not re.search(r"\bDWH\b", current) or not re.search(r"\bLLM\b", current):
|
||||
@@ -640,6 +764,10 @@ verify_internal_semantic_infrastructure_docs() {
|
||||
verify_workspace_descriptor_semantic_contract "$root/deploy/workspaces/psd.yaml.example" "psd workspace example" || return 1
|
||||
verify_vector_helper_interfaces || return 1
|
||||
verify_project_state_current_contract "$root/PROJECT_STATE.md" "PROJECT_STATE.md" || return 1
|
||||
verify_workspace_descriptor_doc_contract "$readme" "README" || return 1
|
||||
verify_workspace_descriptor_doc_contract "$local_manual" "local workspace manual" || return 1
|
||||
verify_workspace_descriptor_doc_contract "$server_manual" "server workspace manual" || return 1
|
||||
verify_workspace_descriptor_doc_contract "$diagnostics" "workspace diagnostic protocol" || return 1
|
||||
|
||||
local ownership_spec semantic_index_spec compact_spec
|
||||
ownership_spec='{"rows":[
|
||||
@@ -670,13 +798,12 @@ verify_internal_semantic_infrastructure_docs() {
|
||||
require_pattern "$agents" "AGENTS.md" 'DWH and LLM remain external configuration endpoints' || return 1
|
||||
for manual in "$local_manual" "$server_manual"; do
|
||||
require_pattern "$manual" "$(basename "$manual")" 'qwen3-embedding:0\.6b' || return 1
|
||||
require_pattern "$manual" "$(basename "$manual")" 'migration_required' || return 1
|
||||
done
|
||||
require_pattern "$local_manual" "local workspace manual" 'CPU-first' || return 1
|
||||
require_pattern "$local_manual" "local workspace manual" 'THOTH_ENABLE_EMBEDDING_GPU=1' || return 1
|
||||
require_pattern "$server_manual" "server workspace manual" 'Qdrant backup/restore' || return 1
|
||||
require_pattern "$compact_manual" "four-context install note" '1024 dimensioni' || return 1
|
||||
require_pattern "$diagnostics" "workspace diagnostic protocol" 'schema version 3' || return 1
|
||||
require_pattern "$diagnostics" "workspace diagnostic protocol" 'Schema v1 and v2 descriptors.+rejected before diagnostics' || return 1
|
||||
require_pattern "$diagnostics" "workspace diagnostic protocol" 'semantic_index_incompatible' || return 1
|
||||
require_absent "$diagnostics" "workspace diagnostic protocol" \
|
||||
'engine: pgvector' \
|
||||
|
||||
Reference in New Issue
Block a user