13 KiB
P5 — Curated FK annotations in Git — Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to apply this plan task-by-task.
Goal: Make the curated FK annotation file the canonical, revision-pinned human review input. The
registry validates <workspace-id>/schema/annotations.yaml as a regular Git blob at the same commit
as the descriptor, synchronizes it to an immutable revision-qualified runtime root on activation, and
replaces the P2 host-file FK review with an explicit operator command
workspace schema accept --run <id> --yes. A pinned historical runtime keeps reading its own
revision's annotations; a newer active revision writes a different directory.
Source of truth: PRD D5 (docs/prd/2026-08-09-workspace-preprocessing-prd.md) and design §7
(docs/superpowers/specs/2026-08-10-p2-p6-workspace-preprocessing-design.md).
Architecture: The backend registry and the compiled operator entrypoint share the sync logic. Git
reads use fixed plumbing (rev-parse, cat-file -t, show) at an exact 40-hex commit — never a
mobile checkout and never author files. The harness keeps owning the annotation parser (Pydantic
Annotations) and the physical-schema orphan check.
Tech Stack: TypeScript (backend registry/preprocessing/runtime rendering), Go (thothctl),
Python (tht schema), YAML. TDD throughout.
Current-state findings recorded by this plan
- P3 implemented revision-scoped Qdrant schema/Evidence records and a binding-keyed DWH cache
(
.tht-dwhatpaths.artifacts.parent), but it did not repurposepaths.artifacts/indexesinto a revision root (they remain workspace-global under/data/sessions/<id>/). - The harness resolves curated annotations at
paths.artifacts/mschema/annotations.yamland already parses them withAnnotations.from_yaml;tht schema checkperforms the physical orphan check. - P2 already records FK candidates and an
FkReviewRecord({ reviewedCandidatesDigest, annotationsDigest, workspaceRevision }) and writes host-file reviews fromschema check --annotations --reviewed-candidates.
Explicit decisions frozen by this plan
- Canonical path is fixed
<workspace-id>/schema/annotations.yaml(not descriptor-configurable). Absence is compatible and yields an empty canonical annotation set plus a warning. Symlinks, submodules/trees at the file path, cross-namespace paths, oversized, non-UTF-8, and malformed annotations are rejected before activation. - Revision-qualified annotations root is rendered as a new explicit path
paths.annotations_root = /data/sessions/<id>/revisions/<commit>/artifacts; the immutable synced file is<annotations_root>/mschema/annotations.yaml.paths.artifacts/indexes/memory/sessionsremain exactly as accepted by P3 because the binding-keyed DWH cache lives atartifacts.parentand must stay shared across content-only revisions. This is a deliberate, surgical refinement of design §7's literal "artifacts and indexes select the revision root": the revision-pinned annotations requirement is satisfied without destabilizing the accepted P3 cache contract. The harness resolves annotations frompaths.annotations_rootwhen present and falls back to the legacyartifacts/mschema/annotations.yamlfor unmigrated workspaces. - Bounds: the annotation blob is ≤ 16 MiB, UTF-8, and structurally parsed (Pydantic
Annotations) before synchronization; the full physical orphan check still runs at review time. - Sync trigger: on registry activation (pull/validate) and before session admission or
preprocessing, each active revision's annotation blob is read with fixed Git argv, validated, and
atomically written no-follow to its revision root with restrictive mode, alongside an ownership
manifest
{ workspace, commit, blobId, contentDigest, destination }. Re-sync is idempotent and re-verifies the manifest. - Human review primitive is
workspace schema accept --run <id> --yes. After commit/push/pull, the operator reviews the current Git blob against the recorded candidate, then runs the accept command. It parses the current blob, validates it against the physical schema via the harness parser, and records{ reviewedCandidatesDigest, annotationsDigest, workspaceRevision }plus the current Git blob id and the new revision.--yesis required. An empty file orschema checkalone is not evidence of human review. - Continuation gate:
preprocess runFK review now requires an accepted review whoseannotationsDigestequals the current revision's synced blob digest and a compatible reusable DWH binding; otherwise the run starts a new review. The P2 host-file review path (schema check --annotations --reviewed-candidateswriting anFkReviewRecord) is superseded:schema checkremains available as read-only validation but no longer records a review. - Error/output: reuse
annotation_invalid,manual_review_required, andpreprocessing_resume_mismatch; JSON results gain the acceptedblobId/annotationsDigestartifact identities. No new public error code is introduced unless a gap is proven by a test. - No push/curation: the preprocessing CLI never stages, commits, or pushes curated content. Curators work in an ordinary author clone.
Completion contract
The phase is complete when:
- A workspace whose Git tree contains a valid
<id>/schema/annotations.yamlblob activates and syncs it to exactly/data/sessions/<id>/revisions/<commit>/artifacts/mschema/annotations.yamlwith a verified ownership manifest; a workspace without the file activates with a warning and an empty canonical set. - Symlink/tree-at-path, cross-namespace, oversized (>16 MiB), non-UTF-8, and malformed annotation objects are refused without mutating the snapshot or runtime roots.
- The harness resolves annotations from
paths.annotations_root(legacy fallback preserved); a session pinned to an older revision reads that revision's synced annotations, and a newer active revision writes/reads a different directory. thothctl ... workspace schema accept --run <id> --yesrecords the accepted candidate/current-blob digests and the new revision;--yesmissing, an unknown run, an empty file, a malformed blob, or a blob not matching the recorded candidate fails closed without recording a review.preprocess runcontinuation succeeds only with the exact accepted blob digest and compatible DWH binding; the superseded host-fileschema checkpath no longer records a review.docs/contracts/workspace-preprocessing-cli.mddocumentsschema acceptand the annotations lifecycle; the P5 manual walkthrough section is runnable; PROJECT_STATE.md records the result.- The clean-state automated process goal passes 1/1 (no retry), and backend/Go/harness focused suites plus the existing P2–P4 gates do not regress.
Task 1: Registry reads and validates the annotation blob at the exact commit
Files: modify backend/src/workspaces/git-repository.ts, backend/src/workspaces/registry.ts; add
tests backend/test/registry-annotations.test.ts.
- Failing tests:
gitObjectType-style read of<id>/schema/annotations.yamlat an exact commit returnsblobor absent; atree/submodule/other type is refused; the blob id (rev-parse) and bytes (show) match; UTF-8 and 16 MiB bounds are enforced; path grammar rejectsworkspace-docs/...and cross-namespace paths. - Implement
GitWorkspaceRepository.annotationsObject(revision, id)returning{ blobId, type, contents } | undefinedwith fixed Git argv and bounded sanitized errors. - In
WorkspaceRegistry.activate, validate every active revision's annotation object; a present-but- invalid object fails activation closed (workspace_invalid), absence is a safe warning. - Commit:
feat: read and validate curated FK annotations at the pinned commit (P5).
Task 2: Atomic revision-qualified annotations sync + ownership manifest
Files: add backend/src/workspaces/annotations-sync.ts; wire into activation and
renderActiveWorkspaceRuntime; tests backend/test/annotations-sync.test.ts.
- Failing tests: sync writes
<dataRoot>/sessions/<id>/revisions/<commit>/artifacts/mschema/ annotations.yaml(mode restrictive, no-follow, exclusive staging + atomic rename + fsync) and an adjacent ownership manifest{ workspace, commit, blobId, contentDigest, destination }; re-sync is idempotent and re-verifies the manifest; a tampered destination or wrong manifest fails closed; a different revision writes a different directory. - Implement the sync (shared by registry activation and the operator/session runtime render).
- Commit:
feat: atomic revision-qualified annotations sync with ownership manifest (P5).
Task 3: Render paths.annotations_root and make the harness resolve it
Files: modify backend/src/workspaces/runtime-config-lease.ts, harness/tht/config.py,
harness/tht/cli/schema_cmd.py; tests both layers.
- Failing tests: rendered config includes
paths.annotations_root = /data/sessions/<id>/revisions/<commit>/artifactswhilepaths.artifacts/indexes/memory/sessionsstay unchanged;tht schemaannotations_pathpreferspaths.annotations_rootand falls back to the legacyartifacts/mschema/annotations.yamlwhen absent; a missing annotations file yields an empty canonical set (not a crash). - Implement the render field and harness resolution with the legacy fallback.
- Commit:
feat: revision-qualified annotations root for pinned runtimes (P5).
Task 4: Operator workspace schema accept --run <id> --yes
Files: modify backend/src/workspaces/preprocessing-service.ts,
backend/src/workspace-maintenance.ts, tools/thothctl/internal/workspaceops/operations.go,
tools/thothctl/cmd/thothctl/main.go; tests workspace-preprocessing-service.test.ts and
operations_test.go.
- Failing tests: the accept command reads the current synced Git blob, stages it, validates it with
the harness parser (structural + orphan check against the recorded candidate), and records
{ reviewedCandidatesDigest, annotationsDigest, workspaceRevision }plusblobId; missing--yes, unknown run, empty/malformed blob, and non-matching candidate fail closed with no review; the recorded review is keyed by the run id. - Implement
WorkspacePreprocessingService.acceptSchema,workspace-maintenancedispatch (schema-accept), and thethothctlgrammar/validation/execute path. - Commit:
feat: operator schema accept command for curated FK review (P5).
Task 5: Continuation gate on the accepted blob; supersede host-file review
Files: modify backend/src/workspaces/preprocessing-service.ts (+ tests).
- Failing tests:
preprocess runFK review requires an accepted review whoseannotationsDigestequals the current revision's synced blob digest and a compatible DWH binding; a digest mismatch starts a new review (manual_review_required); the host-fileschema check --annotations --reviewed-candidatespath validates but does not record a review. - Implement the gate and the supersession.
- Commit:
feat: gate FK review on the accepted revision blob (P5).
Task 6: Contract, manual walkthrough, and clean-state acceptance
Files: modify docs/contracts/workspace-preprocessing-cli.md,
docs/testing/p2-p6-manual-verification.md (P5 section), PROJECT_STATE.md; add
scripts/p5-acceptance.sh, scripts/test-p5-acceptance.sh, backend/scripts/p5-acceptance.mjs,
backend/scripts/p5-acceptance.test.mjs (pattern: P4 acceptance, owned root
.artifacts/p5-integration/p5-<run-id>/).
- Update the CLI contract (new command, annotations lifecycle, exit codes, JSON fields).
- Implement the clean-state scenario: fixture P1.1 registry + curated annotations + REST DWH +
pre-provisioned Qdrant; run
thothctlproduct commands; prove activation sync + ownership manifest, revision isolation, accept happy path,--yes/empty/malformed/mismatch negatives, the continuation gate, no push of curated content, secret scan, exact cleanup. - Finalize the P5 manual walkthrough section and record the phase in PROJECT_STATE.md.
- Commit:
feat: P5 curated FK annotations in Git (acceptance + docs).
Owner checkpoint
After Task 6 the implementation stops for recap. The owner records the P5 manual acceptance (automated PASS is never recorded as manual PASS), then authorizes P6.
Non-goals
- No GUI/backend preprocessing endpoint, no push/stage/commit of curated content.
- No P6 filesystem Evidence materialization (the
evidence_materialization_requiredstop remains). - No real PSD migration, no SSH runtime transport, no policy-driven GC.
- No change to the accepted P1/P1.1/P2/P3/P4 contracts or retained evidence beyond the documented P5 supersession of the host-file FK review.