Compare commits
1 Commits
codex/open
...
akc-wiki-h
| Author | SHA1 | Date | |
|---|---|---|---|
| fbf3e9c806 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -14,8 +14,3 @@ venv/
|
|||||||
.claude/*
|
.claude/*
|
||||||
!.claude/commands/
|
!.claude/commands/
|
||||||
!.claude/commands/**
|
!.claude/commands/**
|
||||||
|
|
||||||
# Editor / IDE state — user-specific, not project config
|
|
||||||
.obsidian/
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|||||||
@@ -6,26 +6,23 @@
|
|||||||
|
|
||||||
## Orientation
|
## Orientation
|
||||||
|
|
||||||
- **live_sha** (Dalidou `/health` build_sha): `2b86543` (verified 2026-04-23T15:20:53Z post-R14 deploy; status=ok)
|
- **live_sha** (Dalidou `/health` build_sha): `775960c` (verified 2026-04-16 via /health, build_time 2026-04-16T17:59:30Z)
|
||||||
- **last_updated**: 2026-04-23 by Claude (R14 squash-merged + deployed; Orientation refreshed)
|
- **last_updated**: 2026-04-18 by Claude (Phase 7A — Memory Consolidation "sleep cycle" V1 on branch, not yet deployed)
|
||||||
- **main_tip**: `2b86543`
|
- **main_tip**: `999788b`
|
||||||
- **test_count**: 548 (547 + 1 R14 regression test)
|
- **test_count**: 533 (prior 521 + 12 new wikilink/redlink tests)
|
||||||
- **harness**: `17/18 PASS` on live Dalidou (p04-constraints expects "Zerodur" — known content gap, not regression; consistent since 2026-04-19)
|
- **harness**: `17/18 PASS` on live Dalidou (p04-constraints expects "Zerodur" — retrieval content gap, not regression)
|
||||||
- **vectors**: 33,253
|
- **vectors**: 33,253
|
||||||
- **active_memories**: 784 (up from 84 pre-density-batch — density gate CRUSHED vs V1-A's 100-target)
|
- **active_memories**: 84 (31 project, 23 knowledge, 10 episodic, 8 adaptation, 7 preference, 5 identity)
|
||||||
- **candidate_memories**: 2 (triage queue drained)
|
- **candidate_memories**: 2
|
||||||
- **interactions**: 500+ (limit=2000 query returned 500 — density batch has been running; actual may be higher, confirm via /stats next update)
|
- **interactions**: 234 total (192 claude-code, 38 openclaw, 4 test)
|
||||||
- **registered_projects**: atocore, p04-gigabit, p05-interferometer, p06-polisher, atomizer-v2, abb-space (aliased p08)
|
- **registered_projects**: atocore, p04-gigabit, p05-interferometer, p06-polisher, atomizer-v2, abb-space (aliased p08)
|
||||||
- **project_state_entries**: 63 (atocore alone; full cross-project count not re-sampled this update)
|
- **project_state_entries**: 110 total (atocore=47, p06=19, p05=18, p04=15, abb=6, atomizer=5)
|
||||||
- **entities**: 66 (up from 35 — V1-0 backfill + ongoing work; 0 open conflicts)
|
- **entities**: 35 (engineering knowledge graph, Layer 2)
|
||||||
- **off_host_backup**: `papa@192.168.86.39:/home/papa/atocore-backups/` via cron, verified
|
- **off_host_backup**: `papa@192.168.86.39:/home/papa/atocore-backups/` via cron, verified
|
||||||
- **nightly_pipeline**: backup → cleanup → rsync → OpenClaw import → vault refresh → extract → auto-triage → **auto-promote/expire (NEW)** → weekly synth/lint Sundays → **retrieval harness (NEW)** → **pipeline summary (NEW)**
|
- **nightly_pipeline**: backup → cleanup → rsync → OpenClaw import → vault refresh → extract → auto-triage → **auto-promote/expire (NEW)** → weekly synth/lint Sundays → **retrieval harness (NEW)** → **pipeline summary (NEW)**
|
||||||
- **capture_clients**: claude-code (Stop hook + cwd project inference), openclaw (before_agent_start + llm_output plugin, verified live)
|
- **capture_clients**: claude-code (Stop hook + cwd project inference), openclaw (before_agent_start + llm_output plugin, verified live)
|
||||||
- **wiki**: http://dalidou:8100/wiki (browse), /wiki/projects/{id}, /wiki/entities/{id}, /wiki/search
|
- **wiki**: http://dalidou:8100/wiki (browse), /wiki/projects/{id}, /wiki/entities/{id}, /wiki/search
|
||||||
- **dashboard**: http://dalidou:8100/admin/dashboard (now shows pipeline health, interaction totals by client, all registered projects)
|
- **dashboard**: http://dalidou:8100/admin/dashboard (now shows pipeline health, interaction totals by client, all registered projects)
|
||||||
- **active_track**: Engineering V1 Completion (started 2026-04-22). V1-0 landed (`2712c5d`). V1-A density gate CLEARED (784 active ≫ 100 target as of 2026-04-23). V1-A soak gate at day 5/~7 (F4 first run 2026-04-19; nightly clean 2026-04-19 through 2026-04-23; failures confined to the known p04-constraints content gap). Plan: `docs/plans/engineering-v1-completion-plan.md`. Resume map: `docs/plans/v1-resume-state.md`.
|
|
||||||
- **last_nightly_pipeline**: `2026-04-23T03:00:20Z` — harness 17/18, triage promoted=3 rejected=7 human=0, dedup 7 clusters (1 tier1 + 6 tier2 auto-merged), graduation 30-skipped 0-graduated 0-errors, auto-triage drained the queue (0 new candidates 2026-04-22T00:52Z run)
|
|
||||||
- **open_branches**: none — R14 squash-merged as `0989fed` and deployed 2026-04-23T15:20:53Z. V1-A is the next scheduled work
|
|
||||||
|
|
||||||
## Active Plan
|
## Active Plan
|
||||||
|
|
||||||
@@ -146,16 +143,9 @@ One branch `codex/extractor-eval-loop` for Day 1-5, a second `codex/retrieval-ha
|
|||||||
| R11 | Codex | P2 | src/atocore/api/routes.py:773-845 | `POST /admin/extract-batch` still accepts `mode="llm"` inside the container and returns a successful 0-candidate result instead of surfacing that host-only LLM extraction is unavailable from this runtime. That is a misleading API contract for operators. | fixed | Claude | 2026-04-12 | (pending) |
|
| R11 | Codex | P2 | src/atocore/api/routes.py:773-845 | `POST /admin/extract-batch` still accepts `mode="llm"` inside the container and returns a successful 0-candidate result instead of surfacing that host-only LLM extraction is unavailable from this runtime. That is a misleading API contract for operators. | fixed | Claude | 2026-04-12 | (pending) |
|
||||||
| R12 | Codex | P2 | scripts/batch_llm_extract_live.py:39-190 | The host-side extractor duplicates the LLM system prompt and JSON parsing logic from `src/atocore/memory/extractor_llm.py`. It works today, but this is now a prompt/parser drift risk across the container and host implementations. | fixed | Claude | 2026-04-12 | (pending) |
|
| R12 | Codex | P2 | scripts/batch_llm_extract_live.py:39-190 | The host-side extractor duplicates the LLM system prompt and JSON parsing logic from `src/atocore/memory/extractor_llm.py`. It works today, but this is now a prompt/parser drift risk across the container and host implementations. | fixed | Claude | 2026-04-12 | (pending) |
|
||||||
| R13 | Codex | P2 | DEV-LEDGER.md:12 | The new `286 passing` test-count claim is not reproducibly auditable from the current audit environments: neither Dalidou nor the clean worktree has `pytest` available. The claim may be true in Claude's dev shell, but it remains unverified in this audit. | fixed | Claude | 2026-04-12 | (pending) |
|
| R13 | Codex | P2 | DEV-LEDGER.md:12 | The new `286 passing` test-count claim is not reproducibly auditable from the current audit environments: neither Dalidou nor the clean worktree has `pytest` available. The claim may be true in Claude's dev shell, but it remains unverified in this audit. | fixed | Claude | 2026-04-12 | (pending) |
|
||||||
| R14 | Codex | P2 | src/atocore/api/routes.py (POST /entities/{id}/promote) | The HTTP `POST /entities/{id}/promote` route does not translate the new service-layer `ValueError("source_refs required: cannot promote a candidate with no provenance...")` into a 400. A legacy no-provenance candidate promoted through the API currently surfaces as a 500. Does not block V1-0 acceptance; tidy in a follow-up. | fixed | Claude | 2026-04-22 | 0989fed |
|
|
||||||
|
|
||||||
## Recent Decisions
|
## Recent Decisions
|
||||||
|
|
||||||
- **2026-04-22** **Wiki reorg reframed as read-only operator orientation.** The earlier `docs/plans/wiki-reorg-plan.md` (5 additions W-1…W-5 adding `/wiki/interactions`, `/wiki/memories`, project-page restructure, recent feed, topnav exposure) is **superseded** and marked as such in-tree. Successor is `docs/plans/operator-orientation-plan.md` in the `ATOCore-clean` workspace: no `/wiki` surface, no Human Mirror implementation, no new API routes, no schema changes, no new source of truth. First deliverables are docs-only: `docs/where-things-live.md` (operator map of source/staged/machine/registry/trusted-state/memories/interactions/logs/backups with explicit edit-vs-don't-edit boundaries) and `docs/operator-home.md` (short daily starting page indexing those docs + a 4-command read-only orientation sequence). README and operations.md link both. Optional future work (`project-overview` and `memory-candidates` CLI helpers over existing endpoints) is gated on the docs proving useful in practice. *Proposed by:* Antoine. *Drafted by:* Claude. *Pending Codex review.*
|
|
||||||
- **2026-04-22** **V1-0 done: approved, merged, deployed, prod backfilled.** Codex pulled `f16cd52`, re-ran the two original probes (both pass), re-ran the three targeted regression suites (all pass). Squash-merged to main as `2712c5d`. Dalidou deployed via canonical deploy script; `/health` reports build_sha=`2712c5d2d03cb2a6af38b559664afd1c4cd0e050`, status=ok. Validated backup snapshot taken at `/srv/storage/atocore/backups/snapshots/20260422T190624Z` before backfill. Prod backfill: `--dry-run` found 31 active/superseded entities with no provenance; list reviewed and sane; live run updated 31 rows via the default `hand_authored=1` flag path; follow-up dry-run returned 0 rows remaining. Residual logged as R14 (P2): `POST /entities/{id}/promote` HTTP route doesn't translate the new service-layer `ValueError` into a 400 — legacy bad candidate promotes via the API return 500 instead. Does not block V1-0 acceptance. V1-0 closed. Next: V1-A (Q-001 subsystem-scoped variant + Q-6 integration). V1-A holds until the soak window ends ~2026-04-26 and the 100-memory density target is hit. *Approved + landed by:* Codex. *Ratified by:* Antoine.
|
|
||||||
- **2026-04-22** **Engineering V1 Completion Plan — Codex sign-off (third round)**. Codex's third-round audit closed the remaining five open questions with concrete resolutions, patched inline in `docs/plans/engineering-v1-completion-plan.md`: (1) F-7 row rewritten with ground truth — schema + preserve-original + test coverage already exist (`graduated_to_entity_id` at `database.py:143-146`, `graduated` status in memory service, promote hook at `service.py:354-356,389-451`, tests at `test_engineering_v1_phase5.py:67-90`); **real gaps** are missing direct `POST /memory/{id}/graduate` route and spec's `knowledge→Fact` mismatch (no `fact` entity type exists; reconcile to `parameter` or similar); V1-E 2 → **3–4 days**; (2) Q-5 determinism reframed — don't stabilize the call to `datetime.now()`, inject regenerated timestamp + checksum as renderer inputs, remove DB iteration ordering dependencies; V1-D scope updated; (3) `project` vs `project_id` — doc note only, no rename, resolved; (4) total estimate 16.5–17.5 → **17.5–19.5 focused days** with calendar buffer on top; (5) "Minions" must not be canonized in D-3 release notes — neutral wording ("queued background processing / async workers") only. **Agreement reached**: Claude + Codex + Antoine aligned. V1-0 is ready to start once the current pipeline soak window ends (~2026-04-26) and the 100-memory density target is hit. *Patched by:* Codex. *Signed off by:* Codex ("with those edits, I'd sign off on the five questions"). *Accepted by:* Antoine. *Executor (V1-0 onwards):* Claude.
|
|
||||||
- **2026-04-22** **Engineering V1 Completion Plan revised per Codex second-round file-level audit** — three findings folded in, all with exact file:line refs from Codex: (1) F-1 downgraded from ✅ to 🟡 — `extractor_version` and `canonical_home` missing from `Entity` dataclass and `entities` table per `engineering-v1-acceptance.md:45`; V1-0 scope now adds both fields via additive migration + doc note that `project` IS `project_id` per "fields equivalent to" spec wording; (2) F-2 replaced with ground-truth per-query status: 9 of 20 v1-required queries done (Q-004/Q-005/Q-006/Q-008/Q-009/Q-011/Q-013/Q-016/Q-017), 1 partial (Q-001 needs subsystem-scoped variant), 10 missing (Q-002/003/007/010/012/014/018/019/020); V1-A scope shrank to Q-001 shape fix + Q-6 integration (pillar queries already implemented); V1-C closes the 8 remaining new queries + Q-020 deferred to V1-D; (3) F-5 reframed — generic `conflicts` + `conflict_members` schema already present at `database.py:190`, no migration needed; divergence is detector body (per-type dispatch needs generalization) + routes (`/admin/conflicts/*` needs `/conflicts/*` alias). Total revised to 16.5–17.5 days, ~60 tests. Plan: `docs/plans/engineering-v1-completion-plan.md` at commit `ce3a878` (Codex pulled clean). Three of Codex's eight open questions now answered; remaining: F-7 graduation depth, mirror determinism, `project` rename question, velocity calibration, minions naming. *Proposed by:* Claude. *Reviewed by:* Codex (two rounds).
|
|
||||||
- **2026-04-22** **Engineering V1 Completion Plan revised per Codex first-round review** — original six-phase order (queries → ingest → mirror → graduation → provenance → ops) rejected by Codex as backward: provenance-at-write (F-8) and conflict-detection hooks (F-5 minimal) must precede any phase that writes active entities. Revised to seven phases: V1-0 write-time invariants (F-8 + F-5 hooks + F-1 audit) as hard prerequisite, V1-A minimum query slice proving the model, V1-B ingest, V1-C full query catalog, V1-D mirror, V1-E graduation, V1-F full F-5 spec + ops + docs. Also softened "parallel with Now list" — real collision points listed explicitly; schedule shifted ~4 weeks to reflect that V1-0 cannot start during pipeline soak. Withdrew the "50–70% built" global framing in favor of the per-criterion gap table. Workspace sync note added: Codex's Playground workspace can't see the plan file; canonical dev tree is Windows `C:\Users\antoi\ATOCore`. Plan: `docs/plans/engineering-v1-completion-plan.md`. Awaiting Codex file-level audit once workspace syncs. *Proposed by:* Claude. *First-round review by:* Codex.
|
|
||||||
- **2026-04-22** gbrain-inspired "Phase 8 Minions + typed edges" plan **rejected as packaged** — wrong sequencing (leapfrogged `master-plan-status.md` Now list), wrong predicate set (6 vs V1's 17), wrong canonical boundary (edges-on-wikilinks instead of typed entities+relationships per `memory-vs-entities.md`). Mechanic (durable jobs + typed graph) deferred to V1 home. Record: `docs/decisions/2026-04-22-gbrain-plan-rejection.md`. *Proposed by:* Claude. *Reviewed/rejected by:* Codex. *Ratified by:* Antoine.
|
|
||||||
- **2026-04-12** Day 4 gate cleared: LLM-assisted extraction via `claude -p` (OAuth, no API key) is the path forward. Rule extractor stays as default for structural cues. *Proposed by:* Claude. *Ratified by:* Antoine.
|
- **2026-04-12** Day 4 gate cleared: LLM-assisted extraction via `claude -p` (OAuth, no API key) is the path forward. Rule extractor stays as default for structural cues. *Proposed by:* Claude. *Ratified by:* Antoine.
|
||||||
- **2026-04-12** First live triage: 16 promoted, 35 rejected from 51 LLM-extracted candidates. 31% accept rate. Active memory count 20->36. *Executed by:* Claude. *Ratified by:* Antoine.
|
- **2026-04-12** First live triage: 16 promoted, 35 rejected from 51 LLM-extracted candidates. 31% accept rate. Active memory count 20->36. *Executed by:* Claude. *Ratified by:* Antoine.
|
||||||
- **2026-04-12** No API keys allowed in AtoCore — LLM-assisted features use OAuth via `claude -p` or equivalent CLI-authenticated paths. *Proposed by:* Antoine.
|
- **2026-04-12** No API keys allowed in AtoCore — LLM-assisted features use OAuth via `claude -p` or equivalent CLI-authenticated paths. *Proposed by:* Antoine.
|
||||||
@@ -170,24 +160,6 @@ One branch `codex/extractor-eval-loop` for Day 1-5, a second `codex/retrieval-ha
|
|||||||
|
|
||||||
## Session Log
|
## Session Log
|
||||||
|
|
||||||
- **2026-04-23 Codex + Claude (R14 closed)** Codex reviewed `claude/r14-promote-400` at `3888db9`, no findings: "The route change is narrowly scoped: `promote_entity()` still returns False for not-found/not-candidate cases, so the existing 404 behavior remains intact, while caller-fixable validation failures now surface as 400." Ran `pytest tests/test_v1_0_write_invariants.py -q` from an isolated worktree: 15 passed in 1.91s. Claude squash-merged to main as `0989fed`, followed by ledger close-out `2b86543`, then deployed via canonical script. Dalidou `/health` reports build_sha=`2b86543e6ad26011b39a44509cc8df3809725171`, build_time `2026-04-23T15:20:53Z`, status=ok. R14 closed. Orientation refreshed earlier this session also reflected the V1-A gate status: **density gate CLEARED** (784 active memories vs 100 target — density batch-extract ran between 2026-04-22 and 2026-04-23 and more than crushed the gate), **soak gate at day 5 of ~7** (F4 first run 2026-04-19; nightly clean 2026-04-19 through 2026-04-23; only chronic failure is the known p04-constraints "Zerodur" content gap). V1-A branches from a clean V1-0 baseline as soon as the soak is called done.
|
|
||||||
|
|
||||||
- **2026-04-22 Codex + Antoine (V1-0 closed)** Codex approved `f16cd52` after re-running both original probes (legacy-candidate promote + supersede hook — both correct) and the three targeted regression suites (`test_v1_0_write_invariants.py`, `test_engineering_v1_phase5.py`, `test_inbox_crossproject.py` — all pass). Squash-merged to main as `2712c5d` ("feat(engineering): enforce V1-0 write invariants"). Deployed to Dalidou via the canonical deploy script; `/health` build_sha=`2712c5d2d03cb2a6af38b559664afd1c4cd0e050` status=ok. Validated backup snapshot at `/srv/storage/atocore/backups/snapshots/20260422T190624Z` taken BEFORE prod backfill. Prod backfill of `scripts/v1_0_backfill_provenance.py` against live DB: dry-run found 31 active/superseded entities with no provenance, list reviewed and looked sane; live run with default `hand_authored=1` flag path updated 31 rows; follow-up dry-run returned 0 rows remaining → no lingering F-8 violations in prod. Codex logged one residual P2 (R14): HTTP `POST /entities/{id}/promote` route doesn't translate the new service-layer `ValueError` into 400 — legacy bad candidate promoted through the API surfaces as 500. Not blocking. V1-0 closed. **Gates for V1-A**: soak window ends ~2026-04-26; 100-active-memory density target (currently 84 active + the ~31 newly flagged ones — need to check how those count in density math). V1-A holds until both gates clear.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (V1-0 patches per Codex review)** Codex audit of commit `cbf9e03` surfaced two P1 gaps + one P2 scope concern, all verified with code-level probes. **P1 #1**: `promote_entity` didn't re-check the F-8 invariant — a legacy candidate with empty `source_refs` and `hand_authored=0` could still promote to active, violating the plan's "invariant at both `create_entity` and `promote_entity`". Fixed: `promote_entity` at `service.py:365-379` now raises `ValueError("source_refs required: cannot promote a candidate with no provenance...")` before flipping status. Stays symmetric with the create-side error. **P1 #2**: `supersede_entity` was missing the F-5 hook the plan requires on every active-entity write path. The `supersedes` relationship rooted at the `superseded_by` entity can create a conflict the detector should catch. Fixed at `service.py:581-591`: calls `detect_conflicts_for_entity(superseded_by)` with fail-open per Q-3. **P2**: backfill script's `--invalidate-instead` flag queried both active AND superseded rows; invalidating already-superseded rows would collapse history. Fixed at `scripts/v1_0_backfill_provenance.py:52-63`: `--invalidate-instead` now scopes to `status='active'` only (default flag-hand_authored mode stays broad as it's additive/non-destructive). Help text tightened to make the destructive posture explicit. **Four new regression tests** in `test_v1_0_write_invariants.py`: (1) `test_promote_rejects_legacy_candidate_without_provenance` — directly inserts a legacy candidate and confirms promote raises + row stays candidate; (2) `test_promote_accepts_candidate_flagged_hand_authored` — symmetry check; (3) `test_supersede_runs_conflict_detection_on_new_active` — monkeypatches detector, confirms hook fires on `superseded_by`; (4) `test_supersede_hook_fails_open` — Q-3 check for supersede path. **Test count**: 543 → 547 (+4 regression). Full suite `547 passed in 81.07s`. Next: commit patches on branch, push, Codex re-review.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (V1-0 landed on branch)** First V1 completion phase done on branch `claude/v1-0-write-invariants`. **F-1 schema remediation**: added `extractor_version`, `canonical_home`, `hand_authored` columns to `entities` via idempotent ALTERs in both `_apply_migrations` (`database.py:148-170`) and `init_engineering_schema` (`service.py:95-139`). CREATE TABLE also updated so fresh DBs get the columns natively. New `_table_exists` helper at `database.py:378`. `Entity` dataclass gains the three fields with sensible defaults. `EXTRACTOR_VERSION = "v1.0.0"` module constant at top of `service.py`. `_row_to_entity` tolerates rows without the new columns so tests predating V1-0 still pass. **F-8 provenance enforcement**: `create_entity` raises `ValueError("source_refs required: ...")` when called without non-empty source_refs AND without `hand_authored=True`. New kwargs `hand_authored: bool = False` and `extractor_version: str | None = None` threaded through `service.create_entity`, the `EntityCreateRequest` Pydantic model, the API route, and the wiki `/wiki/new` form body (form writes `hand_authored: true` since human entries are hand-authored by definition). **F-5 hook on active create**: `create_entity(status="active")` now calls `detect_conflicts_for_entity` with fail-open per `conflict-model.md:256` (errors log warning, write still succeeds). The promote path's existing hook at `service.py:400-404` was kept as-is. **Doc note** added to `engineering-ontology-v1.md` recording that `project` IS the `project_id` per "fields equivalent to" wording. **Backfill script** at `scripts/v1_0_backfill_provenance.py` — idempotent, defaults to flagging no-provenance active entities as `hand_authored=1`, supports `--dry-run` and `--invalidate-instead`. **Tests**: 10 new in `tests/test_v1_0_write_invariants.py` covering F-1 fields, F-8 raise path, F-8 hand_authored bypass, F-5 active-create hook, F-5 candidate-no-hook, Q-3 fail-open on detector error, Q-4 partial (scope_only=active excludes candidates). **Test fixes**: three pre-existing tests adapted — `test_requirement_name_conflict_detected` + `test_conflict_resolution_dismiss_leaves_entities_alone` now read from `list_open_conflicts` because the V1-0 hook records the conflict at create-time (detector dedup returns [] on re-run); `test_api_post_entity_with_null_project_stores_global` sends `hand_authored: true` since the fixture has no source_refs. **conftest.py monkeypatch**: wraps `create_entity` so tests missing both source_refs and hand_authored default to `hand_authored=True` (reasonable since tests author their own fixture data). Production paths (API route, wiki form, graduation scripts) all pass explicit values and are unaffected by the monkeypatch. **Test count**: 533 → 543 (+10), full suite `543 passed in 77.86s`. **Not yet**: commit + push + Codex review + deploy. **Branch**: `claude/v1-0-write-invariants`.
|
|
||||||
|
|
||||||
- **2026-04-22 Codex (late night)** Third-round audit closed the remaining five open questions. Patched `docs/plans/engineering-v1-completion-plan.md` inline (no commit by Codex). **F-7 finding (P1):** graduation stack is partially built — `_graduation_prompt.py`, `scripts/graduate_memories.py`, `database.py:143-146` (`graduated_to_entity_id`), memory `graduated` status, promote-preserves-original at `service.py:354-356,389-451`, tests at `test_engineering_v1_phase5.py:67-90` all exist. Real gaps: no direct `POST /memory/{id}/graduate` route at `routes.py:756`; spec's `knowledge→Fact` doesn't match ontology (`service.py:16` has no `fact` type — reconcile to `parameter` or similar). V1-E estimate 2 → 3–4 days. **Q-5 finding (P2):** "stabilize timestamp" insufficient — renderer reads wall-clock in `_footer()` at `mirror.py:320`; fix is inject regenerated timestamp + checksum as renderer inputs + sort DB iteration + remove dict ordering deps. V1-D scope patched. **Remaining three (P3):** `project` stays as doc-note equivalence (no rename); total estimate 17.5–19.5 focused days; release notes must NOT canonize "Minions" — neutral "queued background processing / async workers" only. **Sign-off:** "with those edits, I'd sign off on the five questions. The only non-architectural uncertainty left in the plan is scheduling discipline against the current Now list; that does not block V1-0 once the soak window and memory-density gate clear." **Status:** Claude + Codex agreed. Plan frozen pending Antoine final accept and gate clearance. Claude to commit Codex's patches + push.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (late night)** Codex second-round review did the full file-level audit and came back with three P1/P2 findings, all with exact file:line refs. Verified each against current code before revising. (1) **F-1 not clean**: `Entity` dataclass at `service.py:67` and `entities` table schema are missing the `extractor_version` and `canonical_home` shared-header fields required by `engineering-v1-acceptance.md:45`; `project` field is the project identifier but not named `project_id` as spec writes (spec wording "fields equivalent to" allows the naming, but needs explicit doc note). V1-0 scope now includes adding both missing fields via additive `_apply_migrations` pattern. (2) **F-2 needed exact statuses, not guesses**: per-function audit gave ground truth — 9 of 20 v1-required queries done, 1 partial (Q-001 returns project-wide tree not subsystem-scoped expand=contains per `engineering-query-catalog.md:71`), 10 missing. V1-A scope shrank to Q-001 shape fix + Q-6 integration (most pillar queries already implemented); V1-C closes the 8 net-new queries + Q-020 to V1-D. (3) **F-5 misframed**: the generic `conflicts` + `conflict_members` schema is ALREADY spec-compliant at `database.py:190`; divergence is detector body at `conflicts.py:36` (per-type dispatch needs generalization) + route path (`/admin/conflicts/*` needs `/conflicts/*` alias). V1-F no longer includes a schema migration; detector generalization + route alignment only. Totals revised to 16.5–17.5 days, ~60 tests (down from 12–17 / 65 because V1-A and V1-F scopes both shrank after audit). Three of the eight open questions resolved. Remaining open: F-7 graduation depth, mirror determinism, `project` naming, velocity calibration, minions-as-V2 naming. No code changes this session — plan + ledger only. Next: commit + push revised plan, then await Antoine+Codex joint sign-off before V1-0 starts.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (night)** Codex first-round review of the V1 Completion Plan summary came back with four findings. Three substantive, one workspace-sync: (1) "50–70% built" too loose — replaced with per-criterion table, global framing withdrawn; (2) phase order backward — provenance-at-write (F-8) and conflict hooks (F-5 minimal) depend-upon by every later phase but were in V1-E; new V1-0 prerequisite phase inserted to establish write-time invariants, and V1-A shrunk to a minimum query slice (four pillars Q-001/Q-005/Q-006/Q-017 + Q-6 integration) rather than full catalog closure; (3) "parallel with Now list / disjoint surfaces" too strong — real collisions listed explicitly (V1-0 provenance + memory extractor write path, V1-E graduation + memory module, V1-F conflicts migration + memory promote); schedule shifted ~4 weeks, V1-0 cannot start during pipeline soak; (4) Codex's Playground workspace can't see the plan file or the `src/atocore/engineering/` code — added a Workspace note to the plan directing per-file audit at the Windows canonical dev tree (`C:\Users\antoi\ATOCore`) and noting the three visible file paths (`docs/plans/engineering-v1-completion-plan.md`, `docs/decisions/2026-04-22-gbrain-plan-rejection.md`, `DEV-LEDGER.md`). Revised plan estimate: 12–17 days across 7 phases (up from 11–14 / 6), ~65 tests added (up from ~50). V1-0 is a hard prerequisite; no later phase starts until it lands. Pending Antoine decision on workspace sync (commit+push vs paste-to-Codex) so Codex can do the file-level audit. No code changes this session.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (late eve)** After the rejection, read the four core V1 architecture docs end-to-end (`engineering-ontology-v1.md`, `engineering-query-catalog.md`, `memory-vs-entities.md`, `engineering-v1-acceptance.md`) plus the four supporting docs (`promotion-rules.md`, `conflict-model.md`, `human-mirror-rules.md`, `tool-handoff-boundaries.md`). Cross-referenced against current code in `src/atocore/engineering/`. **Key finding:** V1 is already 50–70% built — entity types (16, superset of V1's 12), all 18 V1 relationship types, 4-state lifecycle, CRUD + supersede + invalidate + PATCH, queries module with most killer-correctness queries (orphan_requirements, risky_decisions, unsupported_claims, impact_analysis, evidence_chain), conflicts module scaffolded, mirror scaffolded, graduation endpoint scaffolded. Recent commits e147ab2/b94f9df/081c058/069d155/b1a3dd0 are all V1 entity-layer work. Drafted `docs/plans/engineering-v1-completion-plan.md` reframing the work as **V1 completion, not V1 start**. Six sequential phases V1-A through V1-F, estimated 11–14 days, ~50 new tests (533 → ~580). Phases run in parallel with the Now list (pipeline soak + density + multi-model triage + p04-constraints) because surfaces are disjoint. Plan explicitly defers the minions/queue mechanic per acceptance-doc negative list. Pending Codex audit of the plan itself — especially the F-2 query gap list (Claude didn't read each query function end-to-end), F-5 conflicts schema divergence (per-type detectors vs spec's generic slot-keyed shape), and F-7 graduation depth. No code changes this session.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (eve)** gbrain review session. Antoine surfaced https://github.com/garrytan/gbrain for compare/contrast. Claude drafted a "Phase 8 Minions + typed edges" plan pairing a durable job queue with a 6-predicate edge upgrade over wikilinks. Codex reviewed and rejected as packaged: (1) sequencing leapfrogged the `master-plan-status.md` Now list (pipeline soak → 100+ memories → multi-model triage → p04-constraints fix); (2) 6 predicates vs V1's 17 across Structural/Intent/Validation/Provenance families — would have been schema debt on day one per `engineering-ontology-v1.md:112-137`; (3) "edges over wikilinks" bypassed the V1 canonical entity + promotion contract in `memory-vs-entities.md`. Claude verified each high finding against the cited files and concurred. The underlying mechanic (durable background jobs + typed relationship graph) is still a valid future direction, but its correct home is the Engineering V1 sprint under **Next** in `master-plan-status.md:179`, not a leapfrog phase. Decision record: `docs/decisions/2026-04-22-gbrain-plan-rejection.md`. No code changes this session. Next (pending Antoine ack): read the four V1 architecture docs end-to-end, then draft an Engineering V1 foundation plan that follows the existing contract, not a new external reference. Phase 8 (OpenClaw) name remains untouched — Claude's misuse of "Phase 8" in the rejected plan was a naming collision, not a renaming.
|
|
||||||
|
|
||||||
- **2026-04-22 Claude (pm)** Issue B (wiki redlinks) landed — last remaining P2 from Antoine's sprint plan. `_wikilink_transform(text, current_project)` in `src/atocore/engineering/wiki.py` replaces `[[Name]]` / `[[Name|Display]]` tokens (pre-markdown) with HTML anchors. Resolution order: same-project exact-name match → live `wikilink`; other-project match → live link with `(in project X)` scope indicator (`wikilink-cross`); no match → `redlink` pointing at `/wiki/new?name=<quoted>&project=<current>`. New route `GET /wiki/new` renders a pre-filled "create this entity" form that POSTs to `/v1/entities` via a minimal inline fetch() and redirects to the new entity's wiki page on success. Transform applied in `render_project` (over the mirror markdown) and `render_entity` (over the description body). CSS: dashed-underline accent for live wikilinks, red italic + dashed for redlinks. 12 new tests including the regression from the spec (entity A references `[[EntityB]]` → initial render has `class="redlink"`; after EntityB is created, re-render no longer has redlink and includes `/wiki/entities/{b.id}`). Tests 521 → 533. All 6 acceptance criteria from the sprint plan ("daily-usable") now green: retract/supersede, edit without cloning, cross-project has a home, visual evidence, wiki readable, AKC can capture reliably.
|
- **2026-04-22 Claude (pm)** Issue B (wiki redlinks) landed — last remaining P2 from Antoine's sprint plan. `_wikilink_transform(text, current_project)` in `src/atocore/engineering/wiki.py` replaces `[[Name]]` / `[[Name|Display]]` tokens (pre-markdown) with HTML anchors. Resolution order: same-project exact-name match → live `wikilink`; other-project match → live link with `(in project X)` scope indicator (`wikilink-cross`); no match → `redlink` pointing at `/wiki/new?name=<quoted>&project=<current>`. New route `GET /wiki/new` renders a pre-filled "create this entity" form that POSTs to `/v1/entities` via a minimal inline fetch() and redirects to the new entity's wiki page on success. Transform applied in `render_project` (over the mirror markdown) and `render_entity` (over the description body). CSS: dashed-underline accent for live wikilinks, red italic + dashed for redlinks. 12 new tests including the regression from the spec (entity A references `[[EntityB]]` → initial render has `class="redlink"`; after EntityB is created, re-render no longer has redlink and includes `/wiki/entities/{b.id}`). Tests 521 → 533. All 6 acceptance criteria from the sprint plan ("daily-usable") now green: retract/supersede, edit without cloning, cross-project has a home, visual evidence, wiki readable, AKC can capture reliably.
|
||||||
|
|
||||||
- **2026-04-22 Claude** PATCH `/entities/{id}` + Issue D (/v1/engineering/* aliases) landed. New `update_entity()` in `src/atocore/engineering/service.py` supports partial updates to description (replace), properties (shallow merge — `null` value deletes a key), confidence (0..1, 400 on bounds violation), source_refs (append + dedup). Writes an `updated` audit row with full before/after snapshots. Forbidden via this path: entity_type / project / name / status — those require supersede+create or the dedicated status endpoints, by design. New route `PATCH /entities/{id}` aliased under `/v1`. Issue D: all 10 `/engineering/*` query paths (decisions, systems, components/{id}/requirements, changes, gaps + sub-paths, impact, evidence) added to the `/v1` allowlist. 12 new PATCH tests (merge, null-delete, confidence bounds, source_refs dedup, 404, audit row, v1 alias). Tests 509 → 521. Next: commit + deploy, then Issue B (wiki redlinks) as the last remaining P2 per Antoine's sprint order.
|
- **2026-04-22 Claude** PATCH `/entities/{id}` + Issue D (/v1/engineering/* aliases) landed. New `update_entity()` in `src/atocore/engineering/service.py` supports partial updates to description (replace), properties (shallow merge — `null` value deletes a key), confidence (0..1, 400 on bounds violation), source_refs (append + dedup). Writes an `updated` audit row with full before/after snapshots. Forbidden via this path: entity_type / project / name / status — those require supersede+create or the dedicated status endpoints, by design. New route `PATCH /entities/{id}` aliased under `/v1`. Issue D: all 10 `/engineering/*` query paths (decisions, systems, components/{id}/requirements, changes, gaps + sub-paths, impact, evidence) added to the `/v1` allowlist. 12 new PATCH tests (merge, null-delete, confidence bounds, source_refs dedup, 404, audit row, v1 alias). Tests 509 → 521. Next: commit + deploy, then Issue B (wiki redlinks) as the last remaining P2 per Antoine's sprint order.
|
||||||
|
|||||||
@@ -159,17 +159,6 @@ Every major object should support fields equivalent to:
|
|||||||
- `created_at`
|
- `created_at`
|
||||||
- `updated_at`
|
- `updated_at`
|
||||||
- `notes` (optional)
|
- `notes` (optional)
|
||||||
- `extractor_version` (V1-0)
|
|
||||||
- `canonical_home` (V1-0)
|
|
||||||
|
|
||||||
**Naming note (V1-0, 2026-04-22).** The AtoCore `entities` table and
|
|
||||||
`Entity` dataclass name the project-identifier field `project`, not
|
|
||||||
`project_id`. This doc's "fields equivalent to" wording allows that
|
|
||||||
naming flexibility — the `project` field on entity rows IS the
|
|
||||||
`project_id` per spec. No storage rename is planned; downstream readers
|
|
||||||
should treat `entity.project` as the project identifier. This was
|
|
||||||
resolved in Codex's third-round audit of the V1 Completion Plan (see
|
|
||||||
`docs/plans/engineering-v1-completion-plan.md`).
|
|
||||||
|
|
||||||
## Suggested Status Lifecycle
|
## Suggested Status Lifecycle
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,6 @@
|
|||||||
# AtoCore — Current State (2026-04-22)
|
# AtoCore — Current State (2026-04-19)
|
||||||
|
|
||||||
Live deploy: `2712c5d` · Dalidou health: ok · Harness: 17/18 · Tests: 547 passing.
|
Live deploy: `877b97e` · Dalidou health: ok · Harness: 17/18.
|
||||||
|
|
||||||
## V1-0 landed 2026-04-22
|
|
||||||
|
|
||||||
Engineering V1 completion track has started. **V1-0 write-time invariants**
|
|
||||||
merged and deployed: F-1 shared-header fields (`extractor_version`,
|
|
||||||
`canonical_home`, `hand_authored`) added to `entities`, F-8 provenance
|
|
||||||
enforcement at both `create_entity` and `promote_entity`, F-5 synchronous
|
|
||||||
conflict-detection hook on every active-entity write path (create, promote,
|
|
||||||
supersede) with Q-3 fail-open. Prod backfill ran cleanly — 31 legacy
|
|
||||||
active/superseded entities flagged `hand_authored=1`, follow-up dry-run
|
|
||||||
returned 0 remaining rows. Test count 533 → 547 (+14).
|
|
||||||
|
|
||||||
R14 (P2, non-blocking): `POST /entities/{id}/promote` route fix translates
|
|
||||||
the new `ValueError` into 400. Branch `claude/r14-promote-400` pending
|
|
||||||
Codex review + squash-merge.
|
|
||||||
|
|
||||||
**Next in the V1 track:** V1-A (minimal query slice + Q-6 killer-correctness
|
|
||||||
integration). Gated on pipeline soak (~2026-04-26) + 100+ active memory
|
|
||||||
density target. See `docs/plans/engineering-v1-completion-plan.md` for
|
|
||||||
the full 7-phase roadmap and `docs/plans/v1-resume-state.md` for the
|
|
||||||
"you are here" map.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Snapshot from previous update (2026-04-19)
|
|
||||||
|
|
||||||
## The numbers
|
## The numbers
|
||||||
|
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
# Decision record: gbrain-inspired "Phase 8 Minions + typed edges" plan rejected
|
|
||||||
|
|
||||||
**Date:** 2026-04-22
|
|
||||||
**Author of plan:** Claude
|
|
||||||
**Reviewer:** Codex
|
|
||||||
**Ratified by:** Antoine
|
|
||||||
**Status:** Rejected as packaged. Underlying mechanic (durable background jobs + typed relationships) deferred to its correct home.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Antoine surfaced https://github.com/garrytan/gbrain and asked for a compare/contrast and a
|
|
||||||
plan to improve AtoCore. Claude proposed a "Phase 8" plan pairing:
|
|
||||||
|
|
||||||
1. A Minion-style durable job queue replacing the nightly cron pipeline
|
|
||||||
2. A typed-edge upgrade over existing wikilinks, with a six-predicate set
|
|
||||||
(`mentions`, `decided_by`, `supersedes`, `evidences`, `part_of`, `blocks`)
|
|
||||||
|
|
||||||
Codex reviewed and rejected the plan as packaged. This record captures what went wrong,
|
|
||||||
what was right, and where the ideas should actually land.
|
|
||||||
|
|
||||||
## What Codex flagged (verified against repo)
|
|
||||||
|
|
||||||
### High — wrong sequencing
|
|
||||||
|
|
||||||
`docs/master-plan-status.md` defines the **Now** list:
|
|
||||||
|
|
||||||
1. Observe the enhanced pipeline for a week
|
|
||||||
2. Knowledge density — batch-extract over all 234 interactions, target 100+ memories
|
|
||||||
3. Multi-model triage (Phase 11 entry)
|
|
||||||
4. Fix p04-constraints harness failure
|
|
||||||
|
|
||||||
Engineering V1 appears under **Next** (line 179) as
|
|
||||||
"Engineering V1 implementation sprint — once knowledge density is sufficient and the
|
|
||||||
pipeline feels boring and dependable."
|
|
||||||
|
|
||||||
Claude's plan jumped over all four **Now** items. That was the primary sequencing error.
|
|
||||||
|
|
||||||
### High — wrong predicate set
|
|
||||||
|
|
||||||
`docs/architecture/engineering-ontology-v1.md` already defines a 17-predicate V1
|
|
||||||
ontology across four families:
|
|
||||||
|
|
||||||
- **Structural:** `CONTAINS`, `PART_OF`, `INTERFACES_WITH`
|
|
||||||
- **Intent / logic:** `SATISFIES`, `CONSTRAINED_BY`, `BASED_ON_ASSUMPTION`,
|
|
||||||
`AFFECTED_BY_DECISION`, `SUPERSEDES`
|
|
||||||
- **Validation:** `ANALYZED_BY`, `VALIDATED_BY`, `SUPPORTS`, `CONFLICTS_WITH`,
|
|
||||||
`DEPENDS_ON`
|
|
||||||
- **Artifact / provenance:** `DESCRIBED_BY`, `UPDATED_BY_SESSION`, `EVIDENCED_BY`,
|
|
||||||
`SUMMARIZED_IN`
|
|
||||||
|
|
||||||
Claude's six-predicate set was a gbrain-shaped subset that could not express the V1
|
|
||||||
example statements at lines 141–147 of that doc. Shipping it first would have been
|
|
||||||
schema debt on day one.
|
|
||||||
|
|
||||||
### High — wrong canonical boundary
|
|
||||||
|
|
||||||
`docs/architecture/memory-vs-entities.md` and
|
|
||||||
`docs/architecture/engineering-v1-acceptance.md` establish that V1 is **typed
|
|
||||||
entities plus typed relationships**, with one canonical home per concept, a shared
|
|
||||||
candidate-review / promotion flow, provenance, conflict handling, and mirror
|
|
||||||
generation. Claude's "typed edges on top of wikilinks" framing bypassed the canonical
|
|
||||||
entity contract — it would have produced labelled links over notes without the
|
|
||||||
promotion / canonicalization machinery V1 actually requires.
|
|
||||||
|
|
||||||
### Medium — overstated problem
|
|
||||||
|
|
||||||
Claude described the nightly pipeline as a "monolithic bash script" that needed to be
|
|
||||||
replaced. The actual runtime is API-driven (`src/atocore/api/routes.py:516`,
|
|
||||||
`src/atocore/interactions/service.py:55`), SQLite is already in WAL with a busy
|
|
||||||
timeout (`src/atocore/models/database.py:151`), and the reflection loop is explicit
|
|
||||||
capture / reinforce / extract. The queue argument overstated the current shape.
|
|
||||||
|
|
||||||
## What was right
|
|
||||||
|
|
||||||
- gbrain is genuine validation of the general pattern: **durable background jobs +
|
|
||||||
typed relationship graph compound value**. The gbrain v0.12.0 graph release and
|
|
||||||
Minions benchmark (both 2026-04-18) are evidence, not just inspiration.
|
|
||||||
- Async-ification of extraction with retries, per-job visibility, and SLOs remains a
|
|
||||||
real future win for AtoCore — but **additively, behind flags, after V1**, not as a
|
|
||||||
replacement for the current explicit endpoints.
|
|
||||||
|
|
||||||
## What we will do instead
|
|
||||||
|
|
||||||
1. **Keep to the `master-plan-status.md` Now list.** No leapfrog. Observe the
|
|
||||||
pipeline (including the confidence-decay Step F4 first real run), land knowledge
|
|
||||||
density via full-backlog batch extract, progress multi-model triage, fix
|
|
||||||
p04-constraints.
|
|
||||||
2. **When Engineering V1 is ready to start** (criterion: pipeline feels boring and
|
|
||||||
dependable, knowledge density ≥ 100 active memories), write a V1 foundation plan
|
|
||||||
that follows `engineering-ontology-v1.md`, `engineering-query-catalog.md`,
|
|
||||||
`memory-vs-entities.md`, and `engineering-v1-acceptance.md` — entities +
|
|
||||||
relationships + memory-to-entity bridge + mirror / query surfaces, in that order.
|
|
||||||
3. **Async workerization is optional and later.** Only after V1 is working, and only
|
|
||||||
if observed contention or latency warrants it. Jobs stay in the primary SQLite
|
|
||||||
(WAL already in place). No separate DB unless contention is measured.
|
|
||||||
|
|
||||||
## Lesson for future plans
|
|
||||||
|
|
||||||
A plan built from a **new external reference** (gbrain) without reading the
|
|
||||||
repository's own architecture docs will mis-specify predicates, boundaries, and
|
|
||||||
sequencing — even when the underlying mechanic is valid. Read the four V1
|
|
||||||
architecture docs end-to-end before proposing schema work.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- https://github.com/garrytan/gbrain
|
|
||||||
- `docs/master-plan-status.md` (Now / Next / Later)
|
|
||||||
- `docs/architecture/engineering-ontology-v1.md`
|
|
||||||
- `docs/architecture/engineering-query-catalog.md`
|
|
||||||
- `docs/architecture/memory-vs-entities.md`
|
|
||||||
- `docs/architecture/engineering-v1-acceptance.md`
|
|
||||||
- `docs/architecture/llm-client-integration.md`
|
|
||||||
- `docs/architecture/human-mirror-rules.md`
|
|
||||||
@@ -168,40 +168,16 @@ These are the current practical priorities.
|
|||||||
"Zerodur" for p04 constraint queries. Investigate if it's a missing
|
"Zerodur" for p04 constraint queries. Investigate if it's a missing
|
||||||
memory or retrieval ranking issue.
|
memory or retrieval ranking issue.
|
||||||
|
|
||||||
## Active — Engineering V1 Completion Track (started 2026-04-22)
|
|
||||||
|
|
||||||
The Engineering V1 sprint moved from **Next** to **Active** on 2026-04-22.
|
|
||||||
The discovery from the gbrain review was that V1 entity infrastructure
|
|
||||||
had been built incrementally already; the sprint is a **completion** plan
|
|
||||||
against `engineering-v1-acceptance.md`, not a greenfield build. Full plan:
|
|
||||||
`docs/plans/engineering-v1-completion-plan.md`. "You are here" single-page
|
|
||||||
map: `docs/plans/v1-resume-state.md`.
|
|
||||||
|
|
||||||
Seven phases, ~17.5–19.5 focused days, runs in parallel with the Now list
|
|
||||||
where surfaces are disjoint, pauses when they collide.
|
|
||||||
|
|
||||||
| Phase | Scope | Status |
|
|
||||||
|---|---|---|
|
|
||||||
| V1-0 | Write-time invariants: F-1 header fields + F-8 provenance enforcement + F-5 hook on every active-entity write + Q-3 flag-never-block | ✅ done 2026-04-22 (`2712c5d`) |
|
|
||||||
| V1-A | Minimum query slice: Q-001 subsystem-scoped variant + Q-6 killer-correctness integration test on p05-interferometer | 🟡 gated — starts when soak (~2026-04-26) + density (100+ active memories) gates clear |
|
|
||||||
| V1-B | KB-CAD + KB-FEM ingest (`POST /ingest/kb-cad/export`, `POST /ingest/kb-fem/export`) + D-2 schema docs | pending V1-A |
|
|
||||||
| V1-C | Close the remaining 8 queries (Q-002/003/007/010/012/014/018/019; Q-020 to V1-D) | pending V1-B |
|
|
||||||
| V1-D | Full mirror surface (3 spec routes + regenerate + determinism + disputed + curated markers) + Q-5 golden file | pending V1-C |
|
|
||||||
| V1-E | Memory→entity graduation end-to-end + remaining Q-4 trust tests | pending V1-D (note: collides with memory extractor; pauses for multi-model triage work) |
|
|
||||||
| V1-F | F-5 detector generalization + route alias + O-1/O-2/O-3 operational + D-1/D-3/D-4 docs | finish line |
|
|
||||||
|
|
||||||
R14 (P2, non-blocking): `POST /entities/{id}/promote` route returns 500
|
|
||||||
on the new V1-0 `ValueError` instead of 400. Fix on branch
|
|
||||||
`claude/r14-promote-400`, pending Codex review.
|
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
These are the next major layers after V1 and the current stabilization pass.
|
These are the next major layers after the current stabilization pass.
|
||||||
|
|
||||||
1. Phase 6 AtoDrive — clarify Google Drive as a trusted operational
|
1. Phase 6 AtoDrive — clarify Google Drive as a trusted operational
|
||||||
source and ingest from it
|
source and ingest from it
|
||||||
2. Phase 13 Hardening — Chroma backup policy, monitoring, alerting,
|
2. Phase 13 Hardening — Chroma backup policy, monitoring, alerting,
|
||||||
failure visibility beyond log files
|
failure visibility beyond log files
|
||||||
|
3. Engineering V1 implementation sprint — once knowledge density is
|
||||||
|
sufficient and the pipeline feels boring and dependable
|
||||||
|
|
||||||
## Later
|
## Later
|
||||||
|
|
||||||
|
|||||||
@@ -1,317 +0,0 @@
|
|||||||
# OpenClaw x AtoCore V1 Audit Note
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This note is the Phase 1 audit for a safe OpenClaw x AtoCore operating model.
|
|
||||||
It covers only what was directly verified in `/home/papa/ATOCore` and `/home/papa/clawd` on 2026-04-23, plus explicit assumptions called out as assumptions.
|
|
||||||
|
|
||||||
This phase does not change code, runtime behavior, skills, helpers, or automation.
|
|
||||||
|
|
||||||
## Files requested and verified
|
|
||||||
|
|
||||||
The following requested AtoCore files were present and reviewed:
|
|
||||||
|
|
||||||
- `docs/openclaw-integration-contract.md`
|
|
||||||
- `docs/architecture/llm-client-integration.md`
|
|
||||||
- `docs/architecture/representation-authority.md`
|
|
||||||
- `docs/operating-model.md`
|
|
||||||
- `docs/current-state.md`
|
|
||||||
- `docs/master-plan-status.md`
|
|
||||||
- `docs/operations.md`
|
|
||||||
- `AGENTS.md`
|
|
||||||
- `CLAUDE.md`
|
|
||||||
- `DEV-LEDGER.md`
|
|
||||||
|
|
||||||
No requested files were missing.
|
|
||||||
|
|
||||||
## What was directly verified
|
|
||||||
|
|
||||||
### 1. OpenClaw instruction surface
|
|
||||||
|
|
||||||
In `/home/papa/clawd/AGENTS.md`, OpenClaw is currently instructed to:
|
|
||||||
|
|
||||||
- use the `atocore-context` skill for project-dependent work
|
|
||||||
- treat AtoCore as additive and fail-open
|
|
||||||
- prefer `auto-context` for project knowledge questions
|
|
||||||
- prefer `project-state` for trusted current truth
|
|
||||||
- use `refresh-project` if the human explicitly asked to refresh or ingest project changes
|
|
||||||
- use `discrawl` automatically when Antoine asks about prior Discord discussions
|
|
||||||
|
|
||||||
This is already close to the intended additive read path, but it also exposes mutating project operations in a general operator workflow.
|
|
||||||
|
|
||||||
### 2. OpenClaw helper skill surface
|
|
||||||
|
|
||||||
The current helper skill is:
|
|
||||||
|
|
||||||
- `/home/papa/clawd/skills/atocore-context/SKILL.md`
|
|
||||||
- `/home/papa/clawd/skills/atocore-context/scripts/atocore.sh`
|
|
||||||
|
|
||||||
The skill describes AtoCore as a read-only additive context service, but the helper script currently exposes the following commands:
|
|
||||||
|
|
||||||
- `health`
|
|
||||||
- `sources`
|
|
||||||
- `stats`
|
|
||||||
- `projects`
|
|
||||||
- `project-template`
|
|
||||||
- `detect-project`
|
|
||||||
- `auto-context`
|
|
||||||
- `debug-context`
|
|
||||||
- `propose-project`
|
|
||||||
- `register-project`
|
|
||||||
- `update-project`
|
|
||||||
- `refresh-project`
|
|
||||||
- `project-state`
|
|
||||||
- `query`
|
|
||||||
- `context-build`
|
|
||||||
- `ingest-sources`
|
|
||||||
|
|
||||||
That means the helper is not actually read-only. It can drive registry mutation and ingestion-related operations.
|
|
||||||
|
|
||||||
### 3. AtoCore shared operator client surface
|
|
||||||
|
|
||||||
The shared operator client in `/home/papa/ATOCore/scripts/atocore_client.py` exposes a broader surface than the OpenClaw helper, including:
|
|
||||||
|
|
||||||
- all of the project and context operations above
|
|
||||||
- `project-state-set`
|
|
||||||
- `project-state-invalidate`
|
|
||||||
- `capture`
|
|
||||||
- `extract`
|
|
||||||
- `reinforce-interaction`
|
|
||||||
- `list-interactions`
|
|
||||||
- `get-interaction`
|
|
||||||
- `queue`
|
|
||||||
- `promote`
|
|
||||||
- `reject`
|
|
||||||
- `batch-extract`
|
|
||||||
- `triage`
|
|
||||||
|
|
||||||
This matches the architectural intent in `docs/architecture/llm-client-integration.md`: a shared operator client should be the canonical reusable surface for multiple frontends.
|
|
||||||
|
|
||||||
### 4. Actual layering status today
|
|
||||||
|
|
||||||
The intended layering is documented in `docs/architecture/llm-client-integration.md` as:
|
|
||||||
|
|
||||||
- AtoCore HTTP API
|
|
||||||
- shared operator client
|
|
||||||
- thin per-agent frontends
|
|
||||||
|
|
||||||
But the current OpenClaw helper is still its own Bash implementation. It does not shell out to the shared operator client today.
|
|
||||||
|
|
||||||
So the shared-client pattern is documented, but not yet applied to OpenClaw.
|
|
||||||
|
|
||||||
### 5. AtoCore availability and fail-open behavior
|
|
||||||
|
|
||||||
The OpenClaw helper successfully reached the live AtoCore instance during this audit.
|
|
||||||
|
|
||||||
Verified live behavior:
|
|
||||||
|
|
||||||
- `health` worked
|
|
||||||
- `projects` worked
|
|
||||||
- the helper still has fail-open logic when network access fails
|
|
||||||
|
|
||||||
This part is consistent with the stated additive and fail-open stance.
|
|
||||||
|
|
||||||
### 6. Discrawl availability
|
|
||||||
|
|
||||||
The `discrawl` CLI is installed locally and available.
|
|
||||||
|
|
||||||
Verified during audit:
|
|
||||||
|
|
||||||
- binary present
|
|
||||||
- version `0.3.0`
|
|
||||||
- OpenClaw workspace instructions explicitly route project-history recall through `discrawl`
|
|
||||||
|
|
||||||
This supports the desired framing of Discord and Discrawl as an evidence stream.
|
|
||||||
|
|
||||||
### 7. Screenpipe status
|
|
||||||
|
|
||||||
`screenpipe` was not present as a local command in this environment during the audit.
|
|
||||||
|
|
||||||
For V1, Screenpipe is deferred and out of scope. No active Screenpipe input lane was verified or adopted in the final V1 policy.
|
|
||||||
|
|
||||||
## Current implementation shape
|
|
||||||
|
|
||||||
### What OpenClaw can do safely right now
|
|
||||||
|
|
||||||
The current safe, directly verified OpenClaw -> AtoCore path is:
|
|
||||||
|
|
||||||
- project detection
|
|
||||||
- context build
|
|
||||||
- query and retrieval
|
|
||||||
- project-state read
|
|
||||||
- service inspection
|
|
||||||
- fail-open fallback
|
|
||||||
|
|
||||||
That is the mature part of the integration.
|
|
||||||
|
|
||||||
### What OpenClaw can also do today, but should be treated as controlled operator actions
|
|
||||||
|
|
||||||
The current helper also exposes:
|
|
||||||
|
|
||||||
- project proposal preview
|
|
||||||
- project registration
|
|
||||||
- project update
|
|
||||||
- project refresh
|
|
||||||
- ingest-sources
|
|
||||||
|
|
||||||
These should not be treated as background or conversational automation. They are operator actions and need explicit approval policy.
|
|
||||||
|
|
||||||
### What exists in AtoCore but is not exposed through the OpenClaw helper
|
|
||||||
|
|
||||||
The shared operator client already supports:
|
|
||||||
|
|
||||||
- interaction capture
|
|
||||||
- candidate extraction
|
|
||||||
- queue review
|
|
||||||
- promote or reject
|
|
||||||
- trusted project-state write and invalidate
|
|
||||||
|
|
||||||
The current OpenClaw helper does not expose that surface.
|
|
||||||
|
|
||||||
This is important for V1 design: the write-capable lanes already exist in AtoCore, but they are not yet safely shaped for Discord-originated automation.
|
|
||||||
|
|
||||||
## Conflicts with the target V1 stance
|
|
||||||
|
|
||||||
The following conflicts are real and should be named explicitly.
|
|
||||||
|
|
||||||
### Conflict 1 - the OpenClaw helper is described as read-only, but it is not read-only
|
|
||||||
|
|
||||||
`SKILL.md` frames the integration as read-only additive context.
|
|
||||||
`atocore.sh` exposes mutating operations:
|
|
||||||
|
|
||||||
- `register-project`
|
|
||||||
- `update-project`
|
|
||||||
- `refresh-project`
|
|
||||||
- `ingest-sources`
|
|
||||||
|
|
||||||
That mismatch needs a policy fix in Phase 2. For Phase 1 it must be documented as a conflict.
|
|
||||||
|
|
||||||
### Conflict 2 - OpenClaw duplicates client logic instead of using the shared operator client
|
|
||||||
|
|
||||||
The architecture docs prefer a shared operator client reused across frontends.
|
|
||||||
The OpenClaw helper currently reimplements request logic and project detection in Bash.
|
|
||||||
|
|
||||||
That is a direct conflict with the preferred shared-client pattern.
|
|
||||||
|
|
||||||
### Conflict 3 - mutating project operations are too close to the conversational surface
|
|
||||||
|
|
||||||
The helper makes registry and ingestion operations reachable from the OpenClaw side without a dedicated Discord-specific approval gate.
|
|
||||||
|
|
||||||
Even if the human explicitly asks for a refresh, the current shape does not yet distinguish between:
|
|
||||||
|
|
||||||
- a direct trusted operator action in a controlled session
|
|
||||||
- a Discord-originated conversational path that should require an explicit human approval step before mutation
|
|
||||||
|
|
||||||
The Phase 2 V1 policy needs that distinction.
|
|
||||||
|
|
||||||
### Conflict 4 - current docs overstate or blur write capabilities
|
|
||||||
|
|
||||||
`docs/current-state.md` says OpenClaw can seed AtoCore through project-scoped memory entries and staged document ingestion.
|
|
||||||
That was not directly verified through the current OpenClaw helper surface in `/home/papa/clawd`.
|
|
||||||
|
|
||||||
The helper script does not expose:
|
|
||||||
|
|
||||||
- `capture`
|
|
||||||
- `extract`
|
|
||||||
- `promote`
|
|
||||||
- `reject`
|
|
||||||
- `project-state-set`
|
|
||||||
|
|
||||||
So there is at least a documentation and runtime-surface mismatch.
|
|
||||||
|
|
||||||
### Conflict 5 - there was no single OpenClaw-facing evidence lane description before this doc set
|
|
||||||
|
|
||||||
The target architecture needs a clean distinction between:
|
|
||||||
|
|
||||||
- raw evidence
|
|
||||||
- reviewable candidates
|
|
||||||
- active memories and entities
|
|
||||||
- trusted project_state
|
|
||||||
|
|
||||||
Today that distinction exists conceptually across several AtoCore docs, but before this Phase 1 doc set there was no single OpenClaw-facing operating model that told an operator exactly where Discord and Discrawl signals are allowed to land.
|
|
||||||
|
|
||||||
That is the main gap this doc set closes.
|
|
||||||
|
|
||||||
## What is already aligned with the target V1 stance
|
|
||||||
|
|
||||||
Several important pieces are already aligned.
|
|
||||||
|
|
||||||
### Aligned 1 - additive plus fail-open
|
|
||||||
|
|
||||||
Both AtoCore and OpenClaw docs consistently say AtoCore should be additive and fail-open from the OpenClaw side.
|
|
||||||
That is the right baseline and was verified live.
|
|
||||||
|
|
||||||
### Aligned 2 - project_state is already treated as special and curated
|
|
||||||
|
|
||||||
AtoCore architecture docs already treat `project_state` as the highest-trust curated layer.
|
|
||||||
This supports the rule that raw signals must not directly auto-write trusted project state.
|
|
||||||
|
|
||||||
### Aligned 3 - canonical-home thinking already exists
|
|
||||||
|
|
||||||
`docs/architecture/representation-authority.md` already establishes that each fact type needs one canonical home.
|
|
||||||
That is exactly the right foundation for the Discord and Discrawl design.
|
|
||||||
|
|
||||||
### Aligned 4 - reflection and candidate lifecycle already exists in AtoCore
|
|
||||||
|
|
||||||
The shared operator client and AtoCore docs already have a candidate workflow:
|
|
||||||
|
|
||||||
- capture
|
|
||||||
- extract
|
|
||||||
- queue
|
|
||||||
- promote or reject
|
|
||||||
|
|
||||||
That means V1 does not need to invent a new trust model. It needs to apply the existing one correctly to Discord and Discrawl signals.
|
|
||||||
|
|
||||||
## Recommended V1 operating interpretation
|
|
||||||
|
|
||||||
Until implementation work begins, the safest V1 operating interpretation is:
|
|
||||||
|
|
||||||
1. Discord and Discrawl are evidence sources, not truth sources.
|
|
||||||
2. OpenClaw is the orchestrator and operator, not canonical storage.
|
|
||||||
3. AtoCore memories may hold reviewed episodic, personal, and loose project signal.
|
|
||||||
4. Future AtoCore entities should hold reviewed structured decisions, requirements, and constraints.
|
|
||||||
5. `project_state` remains manual or tightly gated only.
|
|
||||||
6. Registry mutation, refresh, ingestion, and candidate promotion or rejection require explicit human approval on Discord-originated paths.
|
|
||||||
7. The shared operator client should become the only write-capable operator surface reused by OpenClaw and other frontends.
|
|
||||||
8. Screenpipe remains deferred and out of V1 scope.
|
|
||||||
|
|
||||||
## Assumption log
|
|
||||||
|
|
||||||
The following points were not directly verified and must stay labeled as assumptions.
|
|
||||||
|
|
||||||
1. Screenpipe integration shape is unverified and deferred.
|
|
||||||
- The `screenpipe` command was not present locally.
|
|
||||||
- No verified Screenpipe pipeline files were found in the inspected workspaces.
|
|
||||||
- V1 therefore excludes Screenpipe from active policy and runtime scope.
|
|
||||||
|
|
||||||
2. No direct Discord -> AtoCore auto-mutation path was verified in code.
|
|
||||||
- The OpenClaw workspace clearly contains read and query context behavior and a Discrawl retrieval rule.
|
|
||||||
- It does not clearly expose a verified Discord-triggered path that auto-calls `project-state-set`, `promote`, `reject`, or `register-project`.
|
|
||||||
- The risk is therefore policy and proximity of commands, not a proven live mutation bug.
|
|
||||||
|
|
||||||
3. OpenClaw runtime use of the shared operator client was not verified because it is not implemented yet.
|
|
||||||
- The shared client exists in the AtoCore repo.
|
|
||||||
- The OpenClaw helper is still its own Bash implementation.
|
|
||||||
|
|
||||||
4. A dedicated evidence store was not verified as a first-class AtoCore schema layer.
|
|
||||||
- Existing AtoCore surfaces clearly support interactions and candidate memories.
|
|
||||||
- This V1 model therefore uses evidence artifacts, interactions, and archive bundles as an architectural lane, without claiming a new implemented table already exists.
|
|
||||||
|
|
||||||
5. Future entities remain future.
|
|
||||||
- The entity layer is architected in AtoCore docs.
|
|
||||||
- This audit did not verify a production entity promotion flow being used by OpenClaw.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The good news is that the trust foundations already exist.
|
|
||||||
|
|
||||||
The main conclusion is that the current system is closest to a safe V1 when interpreted this way:
|
|
||||||
|
|
||||||
- keep AtoCore additive and fail-open
|
|
||||||
- treat Discord and Discrawl as evidence only
|
|
||||||
- route reviewed signal into memory candidates first
|
|
||||||
- reserve `project_state` for explicit curation only
|
|
||||||
- move OpenClaw toward the shared operator client instead of maintaining a separate write-capable helper surface
|
|
||||||
- keep Screenpipe out of V1
|
|
||||||
|
|
||||||
That gives a coherent path to Phase 2 without pretending the current implementation is already there.
|
|
||||||
@@ -1,224 +0,0 @@
|
|||||||
commit 80bd99aaea1bcab2ea5ea732df2f749e84d84318
|
|
||||||
Author: Anto01 <antoine.letarte@gmail.com>
|
|
||||||
Date: Thu Apr 23 15:59:59 2026 +0000
|
|
||||||
|
|
||||||
Tighten OpenClaw AtoCore governance policy
|
|
||||||
|
|
||||||
diff --git a/AGENTS.md b/AGENTS.md
|
|
||||||
index 1da3385..ea4d103 100644
|
|
||||||
--- a/AGENTS.md
|
|
||||||
+++ b/AGENTS.md
|
|
||||||
@@ -105,7 +105,7 @@ Reactions are lightweight social signals. Humans use them constantly — they sa
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
-When a task is contextual and project-dependent, use the `atocore-context` skill to query Dalidou-hosted AtoCore for trusted project state, retrieval, context-building, registered project refresh, or project registration discovery when that will improve accuracy. Treat AtoCore as additive and fail-open; do not replace OpenClaw's own memory with it. Prefer `projects` and `refresh-project <id>` when a known project needs a clean source refresh, and use `project-template` when proposing a new project registration, and `propose-project ...` when you want a normalized preview before editing the registry manually.
|
|
||||||
+When a task is contextual and project-dependent, use the `atocore-context` skill to query Dalidou-hosted AtoCore for trusted project-state reads, retrieval, and context-building when that will improve accuracy. Treat AtoCore as additive and fail-open; do not replace OpenClaw's own memory with it.
|
|
||||||
|
|
||||||
### Organic AtoCore Routing
|
|
||||||
|
|
||||||
@@ -116,14 +116,60 @@ Use AtoCore first when the prompt:
|
|
||||||
- asks about architecture, constraints, status, requirements, vendors, planning, prior decisions, or current project truth
|
|
||||||
- would benefit from cross-source context instead of only the local repo
|
|
||||||
|
|
||||||
-Preferred flow:
|
|
||||||
+Preferred read path:
|
|
||||||
1. `auto-context "<prompt>" 3000` for most project knowledge questions
|
|
||||||
2. `project-state <project>` when the user is clearly asking for trusted current truth
|
|
||||||
-3. `refresh-project <id>` before answering if the user explicitly asked to refresh or ingest project changes
|
|
||||||
+3. fall back to normal OpenClaw tools and memory if AtoCore returns `no_project_match` or is unavailable
|
|
||||||
|
|
||||||
Do not force AtoCore for purely local coding actions like fixing a function, editing one file, or running tests, unless broader project context is likely to matter.
|
|
||||||
|
|
||||||
-If `auto-context` returns `no_project_match` or AtoCore is unavailable, continue normally with OpenClaw's own tools and memory.
|
|
||||||
+### AtoCore Governance
|
|
||||||
+
|
|
||||||
+Default Discord posture for AtoCore is read-only and additive.
|
|
||||||
+
|
|
||||||
+Discord-originated or Discrawl-originated context may inform:
|
|
||||||
+- evidence collection
|
|
||||||
+- retrieval
|
|
||||||
+- context building
|
|
||||||
+- candidate review preparation
|
|
||||||
+
|
|
||||||
+It must not directly perform AtoCore mutating actions.
|
|
||||||
+
|
|
||||||
+Mutating AtoCore actions include:
|
|
||||||
+- `register-project`
|
|
||||||
+- `update-project`
|
|
||||||
+- `refresh-project`
|
|
||||||
+- `ingest-sources`
|
|
||||||
+- `project-state-set`
|
|
||||||
+- `project-state-invalidate`
|
|
||||||
+- `promote`
|
|
||||||
+- `reject`
|
|
||||||
+- any future trusted-state or review mutation
|
|
||||||
+
|
|
||||||
+These actions require explicit human approval for the specific action in the current thread or session.
|
|
||||||
+Do not infer approval from:
|
|
||||||
+- prior Discord discussion
|
|
||||||
+- Discrawl archive recall
|
|
||||||
+- screener output
|
|
||||||
+- vague intent like "we should probably refresh this"
|
|
||||||
+
|
|
||||||
+Hard rules:
|
|
||||||
+- no direct Discord -> `project_state`
|
|
||||||
+- no direct Discord -> register / update / refresh / ingest / promote / reject
|
|
||||||
+- no hidden mutation inside screening or review-prep flows
|
|
||||||
+- PKM notes are not the main operator instruction surface for AtoCore behavior
|
|
||||||
+
|
|
||||||
+### Discord Archive Retrieval (discrawl)
|
|
||||||
+
|
|
||||||
+When Antoine asks in natural language about prior project discussions, decisions, thread history, answers, or whether something was already discussed in Discord, use the local `discrawl` archive automatically.
|
|
||||||
+
|
|
||||||
+Rules:
|
|
||||||
+- Antoine should not need to remember or type `discrawl` commands.
|
|
||||||
+- Treat Discord history as a normal background retrieval source, like memory or project docs.
|
|
||||||
+- Use `discrawl` silently when it will materially improve recall or confidence.
|
|
||||||
+- Prefer this for prompts like "what did we decide", "did we discuss", "summarize the thread", "what were the open questions", or anything clearly anchored in prior Discord conversation.
|
|
||||||
+- If both AtoCore and Discord history are relevant, use both and synthesize.
|
|
||||||
+- If `discrawl` is stale or unavailable, say so briefly and continue with the best available context.
|
|
||||||
|
|
||||||
Skills provide your tools. When you need one, check its `SKILL.md`. Keep local notes (camera names, SSH details, voice preferences) in `TOOLS.md`.
|
|
||||||
|
|
||||||
diff --git a/skills/atocore-context/SKILL.md b/skills/atocore-context/SKILL.md
|
|
||||||
index e42a7b7..fa23207 100644
|
|
||||||
--- a/skills/atocore-context/SKILL.md
|
|
||||||
+++ b/skills/atocore-context/SKILL.md
|
|
||||||
@@ -1,12 +1,11 @@
|
|
||||||
---
|
|
||||||
name: atocore-context
|
|
||||||
-description: Use Dalidou-hosted AtoCore as a read-only external context service for project state, retrieval, and context-building without touching OpenClaw's own memory.
|
|
||||||
+description: Use Dalidou-hosted AtoCore as an additive external context service for project-state reads, retrieval, and context-building without replacing OpenClaw's own memory.
|
|
||||||
---
|
|
||||||
|
|
||||||
# AtoCore Context
|
|
||||||
|
|
||||||
-Use this skill when you need trusted project context, retrieval help, or AtoCore
|
|
||||||
-health/status from the canonical Dalidou instance.
|
|
||||||
+Use this skill when you need trusted project context, retrieval help, or AtoCore health and status from the canonical Dalidou instance.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ AtoCore is an additive external context service.
|
|
||||||
|
|
||||||
- It does not replace OpenClaw's own memory.
|
|
||||||
- It should be used for contextual work, not trivial prompts.
|
|
||||||
-- It is read-only in this first integration batch.
|
|
||||||
+- The default posture is read-only and fail-open.
|
|
||||||
- If AtoCore is unavailable, continue normally.
|
|
||||||
|
|
||||||
## Canonical Endpoint
|
|
||||||
@@ -31,27 +30,22 @@ Override with:
|
|
||||||
ATOCORE_BASE_URL=http://host:port
|
|
||||||
```
|
|
||||||
|
|
||||||
-## Safe Usage
|
|
||||||
+## V1 scope
|
|
||||||
|
|
||||||
-Use AtoCore for:
|
|
||||||
-- project-state checks
|
|
||||||
+Use this skill in V1 for:
|
|
||||||
+
|
|
||||||
+- project-state reads
|
|
||||||
- automatic project detection for normal project questions
|
|
||||||
-- retrieval over ingested project/ecosystem docs
|
|
||||||
+- retrieval over ingested project and ecosystem docs
|
|
||||||
- context-building for complex project prompts
|
|
||||||
- verifying current AtoCore hosting and architecture state
|
|
||||||
-- listing registered projects and refreshing a known project source set
|
|
||||||
-- inspecting the project registration template before proposing a new project entry
|
|
||||||
-- generating a proposal preview for a new project registration without writing it
|
|
||||||
-- registering an approved project entry when explicitly requested
|
|
||||||
-- updating an existing registered project when aliases or description need refinement
|
|
||||||
+- inspecting project registrations and proposal previews when operator review is needed
|
|
||||||
|
|
||||||
-Do not use AtoCore for:
|
|
||||||
-- automatic memory write-back
|
|
||||||
-- replacing OpenClaw memory
|
|
||||||
-- silent ingestion of broad new corpora without approval
|
|
||||||
-- mutating the registry automatically without human approval
|
|
||||||
+Screenpipe is out of V1 scope. Do not treat it as an active input lane or dependency for this skill.
|
|
||||||
+
|
|
||||||
+## Read path commands
|
|
||||||
|
|
||||||
-## Commands
|
|
||||||
+These are the normal additive commands:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh health
|
|
||||||
@@ -62,15 +56,56 @@ Do not use AtoCore for:
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh detect-project "what's the interferometer error budget?"
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh auto-context "what's the interferometer error budget?" 3000
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh debug-context
|
|
||||||
-~/clawd/skills/atocore-context/scripts/atocore.sh propose-project p07-example "p07,example-project" vault incoming/projects/p07-example "Example project" "Primary staged project docs"
|
|
||||||
-~/clawd/skills/atocore-context/scripts/atocore.sh register-project p07-example "p07,example-project" vault incoming/projects/p07-example "Example project" "Primary staged project docs"
|
|
||||||
-~/clawd/skills/atocore-context/scripts/atocore.sh update-project p05 "Curated staged docs for the P05 interferometer architecture, vendors, and error-budget project."
|
|
||||||
-~/clawd/skills/atocore-context/scripts/atocore.sh refresh-project p05
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh project-state atocore
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh query "What is AtoDrive?"
|
|
||||||
~/clawd/skills/atocore-context/scripts/atocore.sh context-build "Need current AtoCore architecture" atocore 3000
|
|
||||||
```
|
|
||||||
|
|
||||||
+## Approved operator actions only
|
|
||||||
+
|
|
||||||
+The helper currently exposes some mutating commands, but they are not normal background behavior.
|
|
||||||
+Treat them as approved operator actions only:
|
|
||||||
+
|
|
||||||
+```bash
|
|
||||||
+~/clawd/skills/atocore-context/scripts/atocore.sh propose-project ...
|
|
||||||
+~/clawd/skills/atocore-context/scripts/atocore.sh register-project ...
|
|
||||||
+~/clawd/skills/atocore-context/scripts/atocore.sh update-project ...
|
|
||||||
+~/clawd/skills/atocore-context/scripts/atocore.sh refresh-project ...
|
|
||||||
+~/clawd/skills/atocore-context/scripts/atocore.sh ingest-sources
|
|
||||||
+```
|
|
||||||
+
|
|
||||||
+Do not use these from a Discord-originated path unless the human explicitly approves the specific action in the current thread or session.
|
|
||||||
+
|
|
||||||
+## Explicit approval rule
|
|
||||||
+
|
|
||||||
+Explicit approval means all of the following:
|
|
||||||
+
|
|
||||||
+- the human directly instructs the specific mutating action
|
|
||||||
+- the instruction is in the current thread or current session
|
|
||||||
+- the approval is for that specific action
|
|
||||||
+- the approval is not inferred from Discord evidence, Discrawl recall, screener output, or vague intent
|
|
||||||
+
|
|
||||||
+Examples of explicit approval:
|
|
||||||
+
|
|
||||||
+- "refresh p05 now"
|
|
||||||
+- "register this project"
|
|
||||||
+- "update the aliases"
|
|
||||||
+
|
|
||||||
+Non-examples:
|
|
||||||
+
|
|
||||||
+- "we should probably refresh this"
|
|
||||||
+- archived discussion suggesting a refresh
|
|
||||||
+- a screener note recommending promotion or ingestion
|
|
||||||
+
|
|
||||||
+## Do not use AtoCore for
|
|
||||||
+
|
|
||||||
+- automatic memory write-back
|
|
||||||
+- replacing OpenClaw memory
|
|
||||||
+- silent ingestion of broad new corpora without approval
|
|
||||||
+- automatic registry mutation
|
|
||||||
+- direct Discord-originated mutation of trusted or operator state
|
|
||||||
+- direct Discord-originated promote or reject actions
|
|
||||||
+
|
|
||||||
## Contract
|
|
||||||
|
|
||||||
- prefer AtoCore only when additional context is genuinely useful
|
|
||||||
@@ -79,10 +114,6 @@ Do not use AtoCore for:
|
|
||||||
- cite when information came from AtoCore rather than local OpenClaw memory
|
|
||||||
- for normal project knowledge questions, prefer `auto-context "<prompt>" 3000` before answering
|
|
||||||
- use `detect-project "<prompt>"` when you want to inspect project inference explicitly
|
|
||||||
-- use `debug-context` right after `auto-context` or `context-build` when you want
|
|
||||||
- to inspect the exact last AtoCore context pack
|
|
||||||
-- prefer `projects` plus `refresh-project <id>` over long ad hoc ingest instructions when the project is already registered
|
|
||||||
-- use `project-template` when preparing a new project registration proposal
|
|
||||||
-- use `propose-project ...` to draft a normalized entry and review collisions first
|
|
||||||
-- use `register-project ...` only after the proposal has been reviewed and approved
|
|
||||||
-- use `update-project ...` when a registered project's description or aliases need refinement before refresh
|
|
||||||
+- use `debug-context` right after `auto-context` or `context-build` when you want to inspect the exact last AtoCore context pack
|
|
||||||
+- use `project-template` and `propose-project ...` when preparing a reviewed registration proposal
|
|
||||||
+- use `register-project ...`, `update-project ...`, `refresh-project ...`, and `ingest-sources` only after explicit approval
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
# OpenClaw x AtoCore Nightly Screener Runbook
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
The nightly screener is the V1 bridge between broad evidence capture and narrow trusted state.
|
|
||||||
|
|
||||||
Its job is to:
|
|
||||||
|
|
||||||
- gather raw evidence from approved V1 sources
|
|
||||||
- reduce noise
|
|
||||||
- produce reviewable candidate material
|
|
||||||
- prepare operator review work
|
|
||||||
- never silently create trusted truth
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
The nightly screener is a screening and preparation job.
|
|
||||||
It is not a trusted-state writer.
|
|
||||||
It is not a registry operator.
|
|
||||||
It is not a hidden reviewer.
|
|
||||||
|
|
||||||
V1 active inputs are:
|
|
||||||
|
|
||||||
- Discord and Discrawl evidence
|
|
||||||
- OpenClaw interaction evidence
|
|
||||||
- PKM, repos, and KB references
|
|
||||||
- read-only AtoCore context for comparison and deduplication
|
|
||||||
|
|
||||||
## Explicit approval rule
|
|
||||||
|
|
||||||
If the screener output points at a mutating operator action, that action still requires:
|
|
||||||
|
|
||||||
- direct human instruction
|
|
||||||
- in the current thread or current session
|
|
||||||
- for that specific action
|
|
||||||
- with no inference from evidence or screener output alone
|
|
||||||
|
|
||||||
The screener may recommend review. It may not manufacture approval.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
The screener may consume the following inputs when available.
|
|
||||||
|
|
||||||
### 1. Discord and Discrawl evidence
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- recent archived Discord messages
|
|
||||||
- thread excerpts relevant to known projects
|
|
||||||
- conversation clusters around decisions, requirements, constraints, or repeated questions
|
|
||||||
|
|
||||||
### 2. OpenClaw interaction evidence
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- captured interactions
|
|
||||||
- recent operator conversations relevant to projects
|
|
||||||
- already-logged evidence bundles
|
|
||||||
|
|
||||||
### 3. Read-only AtoCore context inputs
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- project registry lookup for project matching
|
|
||||||
- project_state read for comparison only
|
|
||||||
- memory or entity lookups for deduplication only
|
|
||||||
|
|
||||||
These reads may help the screener rank or classify candidates, but they must not be used as a write side effect.
|
|
||||||
|
|
||||||
### 4. Optional canonical-source references
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- PKM notes
|
|
||||||
- repo docs
|
|
||||||
- KB-export summaries
|
|
||||||
|
|
||||||
These may be consulted to decide whether a signal appears to duplicate or contradict already-canonical truth.
|
|
||||||
|
|
||||||
## Outputs
|
|
||||||
|
|
||||||
The screener should produce output in four buckets.
|
|
||||||
|
|
||||||
### 1. Nightly screener report
|
|
||||||
|
|
||||||
A compact report describing:
|
|
||||||
|
|
||||||
- inputs seen
|
|
||||||
- items skipped
|
|
||||||
- candidate counts
|
|
||||||
- project match confidence distribution
|
|
||||||
- failures or unavailable sources
|
|
||||||
- items requiring human review
|
|
||||||
|
|
||||||
### 2. Evidence bundle or manifest
|
|
||||||
|
|
||||||
A structured bundle of the source snippets that justified each candidate or unresolved item.
|
|
||||||
This is the reviewer's provenance package.
|
|
||||||
|
|
||||||
### 3. Candidate manifests
|
|
||||||
|
|
||||||
Separate candidate manifests for:
|
|
||||||
|
|
||||||
- memory candidates
|
|
||||||
- entity candidates later
|
|
||||||
- unresolved "needs canonical-source update first" items
|
|
||||||
|
|
||||||
### 4. Operator action queue
|
|
||||||
|
|
||||||
A short list of items needing explicit human action, such as:
|
|
||||||
|
|
||||||
- review these candidates
|
|
||||||
- decide whether to refresh project X
|
|
||||||
- decide whether to curate project_state
|
|
||||||
- decide whether a Discord-originated claim should first be reflected in PKM, repo, or KB
|
|
||||||
|
|
||||||
## Required non-output
|
|
||||||
|
|
||||||
The screener must not directly produce any of the following:
|
|
||||||
|
|
||||||
- active memories without review
|
|
||||||
- active entities without review
|
|
||||||
- project_state writes
|
|
||||||
- registry mutation
|
|
||||||
- refresh operations
|
|
||||||
- ingestion operations
|
|
||||||
- promote or reject decisions
|
|
||||||
|
|
||||||
## Nightly procedure
|
|
||||||
|
|
||||||
### Step 1 - load last-run checkpoint
|
|
||||||
|
|
||||||
Read the last successful screener checkpoint so the run knows:
|
|
||||||
|
|
||||||
- what time range to inspect
|
|
||||||
- what evidence was already processed
|
|
||||||
- which items were already dropped or bundled
|
|
||||||
|
|
||||||
If no checkpoint exists, use a conservative bounded time window and mark the run as bootstrap mode.
|
|
||||||
|
|
||||||
### Step 2 - gather evidence
|
|
||||||
|
|
||||||
Collect available evidence from each configured source.
|
|
||||||
|
|
||||||
Per-source rule:
|
|
||||||
|
|
||||||
- source unavailable -> note it, continue
|
|
||||||
- source empty -> note it, continue
|
|
||||||
- source noisy -> keep raw capture bounded and deduplicated
|
|
||||||
|
|
||||||
### Step 3 - normalize and deduplicate
|
|
||||||
|
|
||||||
For each collected item:
|
|
||||||
|
|
||||||
- normalize timestamps, source ids, and project hints
|
|
||||||
- remove exact duplicates
|
|
||||||
- group repeated or near-identical evidence when practical
|
|
||||||
- keep provenance pointers intact
|
|
||||||
|
|
||||||
The goal is to avoid flooding review with repeated copies of the same conversation.
|
|
||||||
|
|
||||||
### Step 4 - attempt project association
|
|
||||||
|
|
||||||
For each evidence item, try to associate it with:
|
|
||||||
|
|
||||||
- a registered project id, or
|
|
||||||
- `unassigned` if confidence is low
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
- high confidence match -> attach project id
|
|
||||||
- low confidence match -> mark as uncertain
|
|
||||||
- no good match -> leave unassigned
|
|
||||||
|
|
||||||
Do not force a project assignment just to make the output tidier.
|
|
||||||
|
|
||||||
### Step 5 - classify signal type
|
|
||||||
|
|
||||||
Classify each normalized item into one of these buckets:
|
|
||||||
|
|
||||||
- noise / ignore
|
|
||||||
- evidence only
|
|
||||||
- memory candidate
|
|
||||||
- entity candidate
|
|
||||||
- needs canonical-source update first
|
|
||||||
- needs explicit operator decision
|
|
||||||
|
|
||||||
If the classification is uncertain, choose the lower-trust bucket.
|
|
||||||
|
|
||||||
### Step 6 - compare against higher-trust layers
|
|
||||||
|
|
||||||
For non-noise items, compare against the current higher-trust landscape.
|
|
||||||
|
|
||||||
Check for:
|
|
||||||
|
|
||||||
- already-active equivalent memory
|
|
||||||
- already-active equivalent entity later
|
|
||||||
- existing project_state answer
|
|
||||||
- obvious duplication of canonical source truth
|
|
||||||
- obvious contradiction with canonical source truth
|
|
||||||
|
|
||||||
This comparison is read-only.
|
|
||||||
It is used only to rank and annotate output.
|
|
||||||
|
|
||||||
### Step 7 - build candidate bundles
|
|
||||||
|
|
||||||
For each candidate:
|
|
||||||
|
|
||||||
- include the candidate text or shape
|
|
||||||
- include provenance snippets
|
|
||||||
- include source type
|
|
||||||
- include project association confidence
|
|
||||||
- include reason for candidate classification
|
|
||||||
- include conflict or duplicate notes if found
|
|
||||||
|
|
||||||
### Step 8 - build unresolved operator queue
|
|
||||||
|
|
||||||
Some items should not become candidates yet.
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "This looks like current truth but should first be updated in PKM, repo, or KB."
|
|
||||||
- "This Discord-originated request asks for refresh or ingest."
|
|
||||||
- "This might be a decision, but confidence is too low."
|
|
||||||
|
|
||||||
These belong in a small operator queue, not in trusted state.
|
|
||||||
|
|
||||||
### Step 9 - persist report artifacts only
|
|
||||||
|
|
||||||
Persist only:
|
|
||||||
|
|
||||||
- screener report
|
|
||||||
- evidence manifests
|
|
||||||
- candidate manifests
|
|
||||||
- checkpoint metadata
|
|
||||||
|
|
||||||
If candidate persistence into AtoCore is enabled later, it still remains a candidate-only path and must not skip review.
|
|
||||||
|
|
||||||
### Step 10 - exit fail-open
|
|
||||||
|
|
||||||
If the screener could not reach AtoCore or some source system:
|
|
||||||
|
|
||||||
- write the failure or skip into the report
|
|
||||||
- keep the checkpoint conservative
|
|
||||||
- do not fake success
|
|
||||||
- do not silently mutate anything elsewhere
|
|
||||||
|
|
||||||
## Failure modes
|
|
||||||
|
|
||||||
### Failure mode 1 - AtoCore unavailable
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- continue in fail-open mode if possible
|
|
||||||
- write a report that the run was evidence-only or degraded
|
|
||||||
- do not attempt write-side recovery actions
|
|
||||||
|
|
||||||
### Failure mode 2 - Discrawl unavailable or stale
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- note Discord archive input unavailable or stale
|
|
||||||
- continue with other sources
|
|
||||||
- do not invent Discord evidence summaries
|
|
||||||
|
|
||||||
### Failure mode 3 - candidate explosion
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- rank candidates
|
|
||||||
- keep only a bounded top set for review
|
|
||||||
- put the remainder into a dropped or deferred manifest
|
|
||||||
- do not overwhelm the reviewer queue
|
|
||||||
|
|
||||||
### Failure mode 4 - low-confidence project mapping
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- leave items unassigned or uncertain
|
|
||||||
- do not force them into a project-specific truth lane
|
|
||||||
|
|
||||||
### Failure mode 5 - contradiction with trusted truth
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- flag the contradiction in the report
|
|
||||||
- keep the evidence or candidate for review if useful
|
|
||||||
- do not overwrite project_state
|
|
||||||
|
|
||||||
### Failure mode 6 - direct operator-action request found in evidence
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "register this project"
|
|
||||||
- "refresh this source"
|
|
||||||
- "promote this memory"
|
|
||||||
|
|
||||||
Behavior:
|
|
||||||
|
|
||||||
- place the item into the operator action queue
|
|
||||||
- require explicit human approval
|
|
||||||
- do not perform the mutation as part of the screener
|
|
||||||
|
|
||||||
## Review handoff format
|
|
||||||
|
|
||||||
Each screener run should hand off a compact review package containing:
|
|
||||||
|
|
||||||
1. a run summary
|
|
||||||
2. candidate counts by type and project
|
|
||||||
3. top candidates with provenance
|
|
||||||
4. unresolved items needing explicit operator choice
|
|
||||||
5. unavailable-source notes
|
|
||||||
6. checkpoint status
|
|
||||||
|
|
||||||
The handoff should be short enough for a human to review without reading the entire raw archive.
|
|
||||||
|
|
||||||
## Safety rules
|
|
||||||
|
|
||||||
The screener must obey these rules every night.
|
|
||||||
|
|
||||||
1. No direct project_state writes.
|
|
||||||
2. No direct registry mutation.
|
|
||||||
3. No direct refresh or ingest.
|
|
||||||
4. No direct promote or reject.
|
|
||||||
5. No treating Discord or Discrawl as trusted truth.
|
|
||||||
6. No hiding source uncertainty.
|
|
||||||
7. No inventing missing integrations.
|
|
||||||
8. No bringing deferred sources into V1 through policy drift or hidden dependency.
|
|
||||||
|
|
||||||
## Minimum useful run
|
|
||||||
|
|
||||||
A useful screener run can still succeed even if it only does this:
|
|
||||||
|
|
||||||
- gathers available Discord and OpenClaw evidence
|
|
||||||
- filters obvious noise
|
|
||||||
- produces a small candidate manifest
|
|
||||||
- notes unavailable archive inputs if any
|
|
||||||
- leaves trusted state untouched
|
|
||||||
|
|
||||||
That is still a correct V1 run.
|
|
||||||
|
|
||||||
## Deferred from V1
|
|
||||||
|
|
||||||
Screenpipe is deferred from V1. It is not an active input, not a required dependency, and not part of the runtime behavior of this V1 screener.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The nightly screener is not the brain of the system.
|
|
||||||
It is the filter.
|
|
||||||
|
|
||||||
Its purpose is to make human review easier while preserving the trust hierarchy:
|
|
||||||
|
|
||||||
- broad capture in
|
|
||||||
- narrow reviewed truth out
|
|
||||||
- no hidden mutations in the middle
|
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
# OpenClaw x AtoCore V1 Promotion Pipeline
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
This document defines the V1 promotion pipeline for signals coming from Discord, Discrawl, OpenClaw, PKM, and repos.
|
|
||||||
|
|
||||||
The rule is simple:
|
|
||||||
|
|
||||||
- raw capture is evidence
|
|
||||||
- screening turns evidence into candidate material
|
|
||||||
- review promotes candidates into canonical homes
|
|
||||||
- trusted state is curated explicitly, not inferred automatically
|
|
||||||
|
|
||||||
## V1 scope
|
|
||||||
|
|
||||||
V1 active inputs are:
|
|
||||||
|
|
||||||
- Discord live conversation
|
|
||||||
- Discrawl archive retrieval
|
|
||||||
- OpenClaw interaction logs and evidence bundles
|
|
||||||
- PKM notes
|
|
||||||
- repos, KB exports, and repo docs
|
|
||||||
|
|
||||||
Read-only AtoCore context may be consulted for comparison and deduplication.
|
|
||||||
|
|
||||||
## Explicit approval rule
|
|
||||||
|
|
||||||
When this pipeline refers to approval or review for a mutating action, it means:
|
|
||||||
|
|
||||||
- the human directly instructs the specific action
|
|
||||||
- the instruction is in the current thread or current session
|
|
||||||
- the approval is for that specific action
|
|
||||||
- the approval is not inferred from evidence, archives, or screener output
|
|
||||||
|
|
||||||
## Pipeline summary
|
|
||||||
|
|
||||||
```text
|
|
||||||
raw capture
|
|
||||||
-> evidence bundle
|
|
||||||
-> nightly screening
|
|
||||||
-> candidate queue
|
|
||||||
-> human review
|
|
||||||
-> canonical home
|
|
||||||
-> optional trusted-state curation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Stage 0 - raw capture
|
|
||||||
|
|
||||||
### Inputs
|
|
||||||
|
|
||||||
Raw capture may come from:
|
|
||||||
|
|
||||||
- Discord live conversation
|
|
||||||
- Discrawl archive retrieval
|
|
||||||
- OpenClaw interaction logs
|
|
||||||
- PKM notes
|
|
||||||
- repos / KB exports / repo docs
|
|
||||||
|
|
||||||
### Rule at this stage
|
|
||||||
|
|
||||||
Nothing captured here is trusted truth yet.
|
|
||||||
Everything is either:
|
|
||||||
|
|
||||||
- raw evidence, or
|
|
||||||
- a pointer to an already-canonical source
|
|
||||||
|
|
||||||
## Stage 1 - evidence bundle
|
|
||||||
|
|
||||||
The first durable V1 destination for raw signals is the evidence lane.
|
|
||||||
|
|
||||||
Examples of evidence bundle forms:
|
|
||||||
|
|
||||||
- AtoCore interaction records
|
|
||||||
- Discrawl retrieval result sets
|
|
||||||
- nightly screener input bundles
|
|
||||||
- local archived artifacts or manifests
|
|
||||||
- optional source snapshots used only for review preparation
|
|
||||||
|
|
||||||
### What evidence is for
|
|
||||||
|
|
||||||
Evidence exists so the operator can later answer:
|
|
||||||
|
|
||||||
- what did we actually see?
|
|
||||||
- where did this claim come from?
|
|
||||||
- what context supported the candidate?
|
|
||||||
- what should the reviewer inspect before promoting anything?
|
|
||||||
|
|
||||||
### What evidence is not for
|
|
||||||
|
|
||||||
Evidence is not:
|
|
||||||
|
|
||||||
- active memory
|
|
||||||
- active entity
|
|
||||||
- trusted project_state
|
|
||||||
- registry truth
|
|
||||||
|
|
||||||
## Stage 2 - screening
|
|
||||||
|
|
||||||
The nightly screener or an explicit review flow reads evidence and classifies it.
|
|
||||||
|
|
||||||
### Screening outputs
|
|
||||||
|
|
||||||
Each observed signal should be classified into one of these lanes:
|
|
||||||
|
|
||||||
1. Ignore / noise
|
|
||||||
- chatter
|
|
||||||
- duplicate archive material
|
|
||||||
- ambiguous fragments
|
|
||||||
- low-signal scraps
|
|
||||||
|
|
||||||
2. Keep as evidence only
|
|
||||||
- useful context, but too ambiguous or too raw to promote
|
|
||||||
|
|
||||||
3. Memory candidate
|
|
||||||
- stable enough to review as episodic, personal, or loose project signal
|
|
||||||
|
|
||||||
4. Entity candidate
|
|
||||||
- structured enough to review as a future decision, requirement, constraint, or validation fact
|
|
||||||
|
|
||||||
5. Needs canonical-source update first
|
|
||||||
- appears to assert current trusted truth but should first be reflected in the real canonical home, such as PKM, repo, or KB tool
|
|
||||||
|
|
||||||
### Key screening rule
|
|
||||||
|
|
||||||
If the screener cannot confidently tell whether a signal is:
|
|
||||||
|
|
||||||
- raw evidence,
|
|
||||||
- a loose durable memory,
|
|
||||||
- or a structured project truth,
|
|
||||||
|
|
||||||
then it must pick the lower-trust lane.
|
|
||||||
|
|
||||||
In V1, uncertainty resolves downward.
|
|
||||||
|
|
||||||
## Stage 3 - candidate queue
|
|
||||||
|
|
||||||
Only screened outputs may enter the candidate queue.
|
|
||||||
|
|
||||||
### Memory-candidate lane
|
|
||||||
|
|
||||||
Use this lane for reviewed-signal candidates such as:
|
|
||||||
|
|
||||||
- preferences
|
|
||||||
- episodic facts
|
|
||||||
- identity facts
|
|
||||||
- loose stable project signal that is useful to remember but not yet a formal structured entity
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "Antoine prefers operator summaries without extra ceremony."
|
|
||||||
- "The team discussed moving OpenClaw toward a shared operator client."
|
|
||||||
- "Discord history is useful as evidence but not as direct truth."
|
|
||||||
|
|
||||||
### Entity-candidate lane
|
|
||||||
|
|
||||||
Use this lane for future structured facts such as:
|
|
||||||
|
|
||||||
- decisions
|
|
||||||
- requirements
|
|
||||||
- constraints
|
|
||||||
- validation claims
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "Decision: use the shared operator client instead of duplicated frontend logic."
|
|
||||||
- "Constraint: Discord-originated paths must not directly mutate project_state."
|
|
||||||
|
|
||||||
### What cannot enter directly from raw capture
|
|
||||||
|
|
||||||
The following must not be created directly from raw Discord or Discrawl evidence without a screening step:
|
|
||||||
|
|
||||||
- active memories
|
|
||||||
- active entities
|
|
||||||
- project_state entries
|
|
||||||
- registry mutations
|
|
||||||
- promote or reject decisions
|
|
||||||
|
|
||||||
## Stage 4 - human review
|
|
||||||
|
|
||||||
This is the load-bearing stage.
|
|
||||||
|
|
||||||
A human reviewer, mediated by OpenClaw and eventually using the shared operator client, decides whether the candidate:
|
|
||||||
|
|
||||||
- should be promoted
|
|
||||||
- should be rejected
|
|
||||||
- should stay pending
|
|
||||||
- should first be rewritten into the actual canonical source
|
|
||||||
- should become project_state only after stronger curation
|
|
||||||
|
|
||||||
### Review questions
|
|
||||||
|
|
||||||
For every candidate, the reviewer should ask:
|
|
||||||
|
|
||||||
1. Is this actually stable enough to preserve?
|
|
||||||
2. Is this fact ambiguous, historical, or current?
|
|
||||||
3. What is the one canonical home for this fact type?
|
|
||||||
4. Is memory the right home, or should this be an entity later?
|
|
||||||
5. Is project_state justified, or is this still only evidence or candidate material?
|
|
||||||
6. Does the source prove current truth, or only past conversation?
|
|
||||||
|
|
||||||
## Stage 5 - canonical promotion
|
|
||||||
|
|
||||||
After review, the signal can move into exactly one canonical home.
|
|
||||||
|
|
||||||
## Promotion rules by fact shape
|
|
||||||
|
|
||||||
### A. Personal, episodic, or loose project signal
|
|
||||||
|
|
||||||
Promotion destination:
|
|
||||||
|
|
||||||
- AtoCore memory
|
|
||||||
|
|
||||||
Use when the fact is durable and useful, but not a formal structured engineering record.
|
|
||||||
|
|
||||||
### B. Structured engineering fact
|
|
||||||
|
|
||||||
Promotion destination:
|
|
||||||
|
|
||||||
- future AtoCore entity
|
|
||||||
|
|
||||||
Use when the fact is really a:
|
|
||||||
|
|
||||||
- decision
|
|
||||||
- requirement
|
|
||||||
- constraint
|
|
||||||
- validation claim
|
|
||||||
|
|
||||||
### C. Current trusted project answer
|
|
||||||
|
|
||||||
Promotion destination:
|
|
||||||
|
|
||||||
- AtoCore project_state
|
|
||||||
|
|
||||||
But only after explicit curation.
|
|
||||||
|
|
||||||
A candidate does not become project_state just because it looks important.
|
|
||||||
The reviewer must decide that it now represents the trusted current answer.
|
|
||||||
|
|
||||||
### D. Human or tool source truth
|
|
||||||
|
|
||||||
Promotion destination:
|
|
||||||
|
|
||||||
- PKM / repo / KB tool of origin
|
|
||||||
|
|
||||||
If a Discord-originated signal claims current truth but the canonical home is not AtoCore memory or entity, the right move may be:
|
|
||||||
|
|
||||||
1. update the canonical source first
|
|
||||||
2. then optionally refresh or ingest, with explicit approval if the action is mutating
|
|
||||||
3. then optionally curate a project_state answer
|
|
||||||
|
|
||||||
This prevents Discord from becoming the hidden source of truth.
|
|
||||||
|
|
||||||
## Stage 6 - optional trusted-state curation
|
|
||||||
|
|
||||||
`project_state` is not the general destination for important facts.
|
|
||||||
It is the curated destination for current trusted project answers.
|
|
||||||
|
|
||||||
Examples that may justify explicit project_state curation:
|
|
||||||
|
|
||||||
- current selected architecture
|
|
||||||
- current next milestone
|
|
||||||
- current status summary
|
|
||||||
- current trusted decision outcome
|
|
||||||
|
|
||||||
Examples that usually do not justify immediate project_state curation:
|
|
||||||
|
|
||||||
- a raw Discord debate
|
|
||||||
- a speculative suggestion
|
|
||||||
- a historical conversation retrieved through Discrawl
|
|
||||||
|
|
||||||
## Discord-originated pipeline examples
|
|
||||||
|
|
||||||
### Example 1 - raw discussion about operator-client refactor
|
|
||||||
|
|
||||||
1. Discord message enters the evidence lane.
|
|
||||||
2. Nightly screener marks it as either evidence-only or decision candidate.
|
|
||||||
3. Human review checks whether it is an actual decision or just discussion.
|
|
||||||
4. If stable and approved, it becomes a memory or future entity.
|
|
||||||
5. It reaches project_state only if explicitly curated as the trusted current answer.
|
|
||||||
|
|
||||||
### Example 2 - Discord thread says "refresh this project now"
|
|
||||||
|
|
||||||
1. Discord message is evidence of operator intent.
|
|
||||||
2. It does not auto-trigger refresh.
|
|
||||||
3. OpenClaw asks for or recognizes explicit human approval.
|
|
||||||
4. Approved operator action invokes the shared operator client.
|
|
||||||
5. Refresh result may later influence candidates or trusted state, but the raw Discord message never performed the mutation by itself.
|
|
||||||
|
|
||||||
### Example 3 - archived thread says a requirement might be current
|
|
||||||
|
|
||||||
1. Discrawl retrieval enters the evidence lane.
|
|
||||||
2. Screener marks it as evidence-only or a requirement candidate.
|
|
||||||
3. Human review checks the canonical source alignment.
|
|
||||||
4. If accepted later, it becomes an entity candidate or active entity.
|
|
||||||
5. project_state remains a separate explicit curation step.
|
|
||||||
|
|
||||||
## Promotion invariants
|
|
||||||
|
|
||||||
The pipeline must preserve these invariants.
|
|
||||||
|
|
||||||
### Invariant 1 - raw evidence is not trusted truth
|
|
||||||
|
|
||||||
No raw Discord or Discrawl signal can directly become trusted project_state.
|
|
||||||
|
|
||||||
### Invariant 2 - unreviewed signals can at most become candidates
|
|
||||||
|
|
||||||
Automatic processing stops at evidence or candidate creation.
|
|
||||||
|
|
||||||
### Invariant 3 - each fact has one canonical home
|
|
||||||
|
|
||||||
A fact may be supported by many evidence items, but after review it belongs in one canonical place.
|
|
||||||
|
|
||||||
### Invariant 4 - operator mutations require explicit approval
|
|
||||||
|
|
||||||
Registry mutation, refresh, ingest, promote, reject, and project_state writes are operator actions.
|
|
||||||
|
|
||||||
### Invariant 5 - OpenClaw orchestrates; it does not become storage
|
|
||||||
|
|
||||||
OpenClaw should coordinate the pipeline, not silently become the canonical data layer.
|
|
||||||
|
|
||||||
## Decision table
|
|
||||||
|
|
||||||
| Observed signal type | Default pipeline outcome | Canonical destination if accepted |
|
|
||||||
|---|---|---|
|
|
||||||
| Ambiguous or raw conversation | Evidence only | none |
|
|
||||||
| Historical archive context | Evidence only or candidate | memory or entity only after review |
|
|
||||||
| Personal preference | Memory candidate | AtoCore memory |
|
|
||||||
| Episodic fact | Memory candidate | AtoCore memory |
|
|
||||||
| Loose stable project signal | Memory candidate | AtoCore memory |
|
|
||||||
| Structured decision / requirement / constraint | Entity candidate | future AtoCore entity |
|
|
||||||
| Claimed current trusted answer | Needs explicit curation | project_state, but only after review |
|
|
||||||
| Tool-origin engineering fact | Canonical source update first | repo / KB / PKM tool of origin |
|
|
||||||
|
|
||||||
## What the pipeline deliberately prevents
|
|
||||||
|
|
||||||
This V1 pipeline deliberately prevents these bad paths:
|
|
||||||
|
|
||||||
- Discord -> project_state directly
|
|
||||||
- Discrawl archive -> project_state directly
|
|
||||||
- Discord -> registry mutation directly
|
|
||||||
- Discord -> refresh or ingest directly without explicit approval
|
|
||||||
- raw chat -> promote or reject directly
|
|
||||||
- OpenClaw turning evidence into truth without a review gate
|
|
||||||
|
|
||||||
## Deferred from V1
|
|
||||||
|
|
||||||
Screenpipe is deferred from V1. It is not an active input lane in this pipeline and it is not a runtime dependency of this pipeline. If it is revisited later, it should be handled in a separate future design and not treated as an implicit part of this pipeline.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The promotion pipeline is intentionally conservative.
|
|
||||||
|
|
||||||
Its job is not to maximize writes.
|
|
||||||
Its job is to preserve trust while still letting Discord, Discrawl, OpenClaw, PKM, and repos contribute useful signal.
|
|
||||||
|
|
||||||
That means the safe default path is:
|
|
||||||
|
|
||||||
- capture broadly
|
|
||||||
- trust narrowly
|
|
||||||
- promote deliberately
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
# OpenClaw x AtoCore Shared-Client Consolidation Preview
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Proposal only. Not applied.
|
|
||||||
|
|
||||||
## Why this exists
|
|
||||||
|
|
||||||
The current OpenClaw helper script duplicates AtoCore-calling logic that already exists in the shared operator client:
|
|
||||||
|
|
||||||
- request handling
|
|
||||||
- fail-open behavior
|
|
||||||
- project detection
|
|
||||||
- project lifecycle command surface
|
|
||||||
|
|
||||||
The preferred direction is to consolidate OpenClaw toward the shared operator client pattern documented in `docs/architecture/llm-client-integration.md`.
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Keep the OpenClaw skill and operator policy in OpenClaw, but stop maintaining a separate Bash implementation of the AtoCore client surface when the shared client already exists in `/home/papa/ATOCore/scripts/atocore_client.py`.
|
|
||||||
|
|
||||||
## Non-goals for this preview
|
|
||||||
|
|
||||||
- no implementation in this phase
|
|
||||||
- no runtime change in this phase
|
|
||||||
- no new helper command in this phase
|
|
||||||
- no change to approval policy in this preview
|
|
||||||
|
|
||||||
## Preview diff
|
|
||||||
|
|
||||||
This is a conceptual diff preview only.
|
|
||||||
It is not applied.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- a/skills/atocore-context/scripts/atocore.sh
|
|
||||||
+++ b/skills/atocore-context/scripts/atocore.sh
|
|
||||||
@@
|
|
||||||
-#!/usr/bin/env bash
|
|
||||||
-set -euo pipefail
|
|
||||||
-
|
|
||||||
-BASE_URL="${ATOCORE_BASE_URL:-http://dalidou:8100}"
|
|
||||||
-TIMEOUT="${ATOCORE_TIMEOUT_SECONDS:-30}"
|
|
||||||
-REFRESH_TIMEOUT="${ATOCORE_REFRESH_TIMEOUT_SECONDS:-1800}"
|
|
||||||
-FAIL_OPEN="${ATOCORE_FAIL_OPEN:-true}"
|
|
||||||
-
|
|
||||||
-request() {
|
|
||||||
- # local curl-based request logic
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
-detect_project() {
|
|
||||||
- # local project detection logic
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
-case "$cmd" in
|
|
||||||
- health) request GET /health ;;
|
|
||||||
- projects) request GET /projects ;;
|
|
||||||
- auto-context) ... ;;
|
|
||||||
- register-project) ... ;;
|
|
||||||
- refresh-project) ... ;;
|
|
||||||
- ingest-sources) ... ;;
|
|
||||||
-esac
|
|
||||||
+#!/usr/bin/env bash
|
|
||||||
+set -euo pipefail
|
|
||||||
+
|
|
||||||
+CLIENT="${ATOCORE_SHARED_CLIENT:-/home/papa/ATOCore/scripts/atocore_client.py}"
|
|
||||||
+
|
|
||||||
+if [[ ! -f "$CLIENT" ]]; then
|
|
||||||
+ echo "Shared AtoCore client not found: $CLIENT" >&2
|
|
||||||
+ exit 1
|
|
||||||
+fi
|
|
||||||
+
|
|
||||||
+exec python3 "$CLIENT" "$@"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recommended implementation shape later
|
|
||||||
|
|
||||||
If and when this is implemented, the safer shape is:
|
|
||||||
|
|
||||||
1. keep policy and approval guidance in OpenClaw instructions and skill text
|
|
||||||
2. delegate actual AtoCore client behavior to the shared operator client
|
|
||||||
3. avoid adding any new helper command unless explicitly approved
|
|
||||||
4. keep read-path and approved-operator-path distinctions in the OpenClaw guidance layer
|
|
||||||
|
|
||||||
## Risk notes
|
|
||||||
|
|
||||||
Potential follow-up concerns to handle before applying:
|
|
||||||
|
|
||||||
- path dependency on `/home/papa/ATOCore/scripts/atocore_client.py`
|
|
||||||
- what should happen if the AtoCore repo is unavailable from the OpenClaw machine
|
|
||||||
- whether a thin compatibility wrapper is needed for help text or argument normalization
|
|
||||||
- ensuring OpenClaw policy still blocks unapproved Discord-originated mutations even if the shared client exposes them
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The duplication is real and consolidation is still the right direction.
|
|
||||||
But in this phase it remains a proposal only.
|
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
# OpenClaw x AtoCore V1 Architecture
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
This document defines the safe V1 operating model for how Discord, Discrawl, OpenClaw, PKM, repos, and AtoCore work together.
|
|
||||||
|
|
||||||
The goal is to let these systems contribute useful signal into AtoCore without turning AtoCore into a raw dump and without blurring trust boundaries.
|
|
||||||
|
|
||||||
## V1 scope
|
|
||||||
|
|
||||||
V1 active inputs are:
|
|
||||||
|
|
||||||
- Discord and Discrawl evidence
|
|
||||||
- OpenClaw interaction evidence
|
|
||||||
- PKM, repos, and KB sources
|
|
||||||
- read-only AtoCore context for comparison and deduplication
|
|
||||||
|
|
||||||
## Core stance
|
|
||||||
|
|
||||||
The V1 stance is simple:
|
|
||||||
|
|
||||||
- Discord and Discrawl are evidence streams.
|
|
||||||
- OpenClaw is the operator and orchestrator.
|
|
||||||
- PKM, repos, and KB tools remain the canonical human and tool truth.
|
|
||||||
- AtoCore memories hold reviewed episodic, personal, and loose project signal.
|
|
||||||
- AtoCore project_state holds the current trusted project answer, manually or tightly gated only.
|
|
||||||
- Future AtoCore entities hold reviewed structured decisions, requirements, constraints, and related facts.
|
|
||||||
|
|
||||||
## Architectural principles
|
|
||||||
|
|
||||||
1. AtoCore remains additive and fail-open from the OpenClaw side.
|
|
||||||
2. Every fact type has exactly one canonical home.
|
|
||||||
3. Raw evidence is not trusted truth.
|
|
||||||
4. Unreviewed signals become evidence or candidates, not active truth.
|
|
||||||
5. Discord-originated paths never directly mutate project_state, registry state, refresh state, ingestion state, or review decisions without explicit human approval.
|
|
||||||
6. OpenClaw is not canonical storage. It retrieves, compares, summarizes, requests approval, and performs approved operator actions.
|
|
||||||
7. The shared operator client is the canonical mutating operator surface. Frontends should reuse it instead of reimplementing AtoCore-calling logic.
|
|
||||||
|
|
||||||
## Explicit approval rule
|
|
||||||
|
|
||||||
In this V1 policy, explicit approval means all of the following:
|
|
||||||
|
|
||||||
- the human directly instructs the specific mutating action
|
|
||||||
- the instruction appears in the current thread or current session
|
|
||||||
- the approval is for that specific action, not vague intent
|
|
||||||
- the approval is not inferred from Discord evidence, Discrawl recall, screener output, or general discussion
|
|
||||||
|
|
||||||
Examples of explicit approval:
|
|
||||||
|
|
||||||
- "refresh p05 now"
|
|
||||||
- "register this project"
|
|
||||||
- "promote that candidate"
|
|
||||||
- "write this to project_state"
|
|
||||||
|
|
||||||
Examples that are not explicit approval:
|
|
||||||
|
|
||||||
- "we should probably refresh this sometime"
|
|
||||||
- "I think this is the current answer"
|
|
||||||
- archived discussion saying a mutation might be useful
|
|
||||||
- a screener report recommending a mutation
|
|
||||||
|
|
||||||
## System roles
|
|
||||||
|
|
||||||
### Discord
|
|
||||||
|
|
||||||
Discord is a live conversational source.
|
|
||||||
It contains fresh context, discussion, uncertainty, and project language grounded in real work.
|
|
||||||
It is not authoritative by itself.
|
|
||||||
|
|
||||||
Discord-originated material should be treated as:
|
|
||||||
|
|
||||||
- raw evidence
|
|
||||||
- candidate material after screening
|
|
||||||
- possible justification for a later human-reviewed promotion into a canonical home
|
|
||||||
|
|
||||||
Discord should never be treated as direct trusted project truth just because someone said it in chat.
|
|
||||||
|
|
||||||
### Discrawl
|
|
||||||
|
|
||||||
Discrawl is a retrieval and archive layer over Discord history.
|
|
||||||
It turns prior conversation into searchable evidence.
|
|
||||||
That is useful for recall, context building, and finding prior decisions or open questions.
|
|
||||||
|
|
||||||
Discrawl is still evidence, not authority.
|
|
||||||
A retrieved Discord thread may show what people thought or said. It does not by itself become trusted project_state.
|
|
||||||
|
|
||||||
### OpenClaw
|
|
||||||
|
|
||||||
OpenClaw is the orchestrator and operator.
|
|
||||||
It is where the human interacts, where approvals happen, and where cross-source reasoning happens.
|
|
||||||
|
|
||||||
OpenClaw's job is to:
|
|
||||||
|
|
||||||
- retrieve
|
|
||||||
- compare
|
|
||||||
- summarize
|
|
||||||
- ask for approval when mutation is requested
|
|
||||||
- call the shared operator client for approved writes
|
|
||||||
- fail open when AtoCore is unavailable
|
|
||||||
|
|
||||||
OpenClaw is not the canonical place where project facts live long-term.
|
|
||||||
|
|
||||||
### PKM
|
|
||||||
|
|
||||||
PKM is a canonical human-authored prose source.
|
|
||||||
It is where notes, thinking, and ongoing project writing live.
|
|
||||||
|
|
||||||
PKM is the canonical home for:
|
|
||||||
|
|
||||||
- project prose notes
|
|
||||||
- working notes
|
|
||||||
- long-form summaries
|
|
||||||
- journal-style project history
|
|
||||||
|
|
||||||
PKM is not the place where OpenClaw should be taught how to operate AtoCore. Operator instructions belong in repo docs and OpenClaw instructions and skills.
|
|
||||||
|
|
||||||
### Repos and KB tools
|
|
||||||
|
|
||||||
Repos and KB tools are canonical human and tool truth for code and structured engineering artifacts.
|
|
||||||
|
|
||||||
They are the canonical home for:
|
|
||||||
|
|
||||||
- source code
|
|
||||||
- repo design docs
|
|
||||||
- structured tool outputs
|
|
||||||
- KB-CAD and KB-FEM facts where those systems are the tool of origin
|
|
||||||
|
|
||||||
### AtoCore memories
|
|
||||||
|
|
||||||
AtoCore memories are for reviewed, durable machine-usable signal that is still loose enough to belong in memory rather than in a stricter structured layer.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- episodic facts
|
|
||||||
- preferences
|
|
||||||
- identity facts
|
|
||||||
- reviewed loose project facts
|
|
||||||
|
|
||||||
AtoCore memories are not a place to dump raw Discord capture.
|
|
||||||
|
|
||||||
### AtoCore project_state
|
|
||||||
|
|
||||||
AtoCore project_state is the trusted current answer layer.
|
|
||||||
It is the place for questions like:
|
|
||||||
|
|
||||||
- what is the current selected architecture?
|
|
||||||
- what is the current next focus?
|
|
||||||
- what is the trusted status answer right now?
|
|
||||||
|
|
||||||
Because this layer answers current-truth questions, it must remain manually curated or tightly gated.
|
|
||||||
|
|
||||||
### Future AtoCore entities
|
|
||||||
|
|
||||||
Future entities are the canonical home for structured engineering facts that deserve stronger representation than freeform memory.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- decisions
|
|
||||||
- requirements
|
|
||||||
- constraints
|
|
||||||
- validation claims
|
|
||||||
- structured relationships later
|
|
||||||
|
|
||||||
These should be promoted from evidence or candidates only after review.
|
|
||||||
|
|
||||||
## Logical flow
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discord live chat --.
|
|
||||||
Discrawl archive ----+--> evidence bundle / interactions / screener input
|
|
||||||
OpenClaw evidence ---'
|
|
||||||
|
|
|
||||||
v
|
|
||||||
nightly screener
|
|
||||||
|
|
|
||||||
.--------+--------.
|
|
||||||
v v
|
|
||||||
memory candidates entity candidates (later)
|
|
||||||
| |
|
|
||||||
'--------+--------'
|
|
||||||
v
|
|
||||||
human review in OpenClaw
|
|
||||||
|
|
|
||||||
.-----------------+-----------------.
|
|
||||||
v v v
|
|
||||||
active memory active entity explicit curation
|
|
||||||
|
|
|
||||||
v
|
|
||||||
project_state
|
|
||||||
```
|
|
||||||
|
|
||||||
The load-bearing rule is that review happens before trust.
|
|
||||||
|
|
||||||
## Canonical-home table
|
|
||||||
|
|
||||||
Every named fact type below has exactly one canonical home.
|
|
||||||
|
|
||||||
| Fact type | Canonical home | Why |
|
|
||||||
|---|---|---|
|
|
||||||
| Raw Discord message | Discord / Discrawl archive | It is conversational evidence, not normalized truth |
|
|
||||||
| Archived Discord thread history | Discrawl archive | It is the retrieval form of Discord evidence |
|
|
||||||
| OpenClaw operator instructions | OpenClaw repo docs / skills / instructions | Operating behavior should live in code-adjacent instructions, not PKM |
|
|
||||||
| Project prose notes | PKM | Human-authored project prose belongs in PKM |
|
|
||||||
| Source code | Repo | Code truth lives in version control |
|
|
||||||
| Repo design or architecture doc | Repo | The documentation belongs with the code or system it describes |
|
|
||||||
| Structured KB-CAD / KB-FEM fact | KB tool of origin | Tool-managed structured engineering facts belong in their tool of origin |
|
|
||||||
| Personal identity fact | AtoCore memory (`identity`) | AtoCore memory is the durable machine-usable home |
|
|
||||||
| Preference fact | AtoCore memory (`preference`) | Same reason |
|
|
||||||
| Episodic fact | AtoCore memory (`episodic`) | It is durable recall, not project_state |
|
|
||||||
| Loose reviewed project signal | AtoCore memory (`project`) | Good fit for reviewed but not fully structured project signal |
|
|
||||||
| Engineering decision | Future AtoCore entity (`Decision`) | Decisions need structured lifecycle and supersession |
|
|
||||||
| Requirement | Future AtoCore entity (`Requirement`) | Requirements need structured management |
|
|
||||||
| Constraint | Future AtoCore entity (`Constraint`) | Constraints need structured management |
|
|
||||||
| Current trusted project answer | AtoCore `project_state` | This layer is explicitly for current trusted truth |
|
|
||||||
| Project registration metadata | AtoCore project registry | Registry state is its own canonical operator layer |
|
|
||||||
| Review action (promote / reject / invalidate) | AtoCore audit trail / operator action log | Review decisions are operator events, not source facts |
|
|
||||||
|
|
||||||
## What this means for Discord-originated facts
|
|
||||||
|
|
||||||
A Discord-originated signal can end in more than one place, but not directly.
|
|
||||||
|
|
||||||
### If the signal is conversational, ambiguous, or historical
|
|
||||||
|
|
||||||
It stays in the evidence lane:
|
|
||||||
|
|
||||||
- Discord
|
|
||||||
- Discrawl archive
|
|
||||||
- optional screener artifact
|
|
||||||
- optional candidate queue
|
|
||||||
|
|
||||||
It does not become trusted project_state.
|
|
||||||
|
|
||||||
### If the signal is a stable personal or episodic fact
|
|
||||||
|
|
||||||
It may be promoted to AtoCore memory after review.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "Antoine prefers concise operator summaries."
|
|
||||||
- "We decided in discussion to keep AtoCore additive."
|
|
||||||
|
|
||||||
These belong in reviewed memory, not in project_state.
|
|
||||||
|
|
||||||
### If the signal expresses a structured engineering fact
|
|
||||||
|
|
||||||
It may become an entity candidate and later an active entity.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- a requirement
|
|
||||||
- a decision
|
|
||||||
- a constraint
|
|
||||||
|
|
||||||
Again, not directly from raw chat. The chat is evidence for the candidate.
|
|
||||||
|
|
||||||
### If the signal is the current trusted answer
|
|
||||||
|
|
||||||
It still should not jump directly from Discord into project_state.
|
|
||||||
Instead, a human should explicitly curate it into project_state after checking it against the right canonical home.
|
|
||||||
|
|
||||||
That canonical home may be:
|
|
||||||
|
|
||||||
- PKM for prose and project notes
|
|
||||||
- repo for code and design docs
|
|
||||||
- KB tools for structured engineering facts
|
|
||||||
- active entity if the engineering layer is the canonical home
|
|
||||||
|
|
||||||
## Approval boundaries
|
|
||||||
|
|
||||||
### Reads
|
|
||||||
|
|
||||||
The following may be invoked automatically when useful:
|
|
||||||
|
|
||||||
- `health`
|
|
||||||
- `projects`
|
|
||||||
- `detect-project`
|
|
||||||
- `auto-context`
|
|
||||||
- `query`
|
|
||||||
- `project-state` read
|
|
||||||
- Discrawl retrieval
|
|
||||||
|
|
||||||
These are additive and fail-open.
|
|
||||||
|
|
||||||
### Mutations requiring explicit human approval
|
|
||||||
|
|
||||||
The following are operator actions, not conversational automation:
|
|
||||||
|
|
||||||
- `register-project`
|
|
||||||
- `update-project`
|
|
||||||
- `refresh-project`
|
|
||||||
- `ingest-sources`
|
|
||||||
- `project-state-set`
|
|
||||||
- `project-state-invalidate`
|
|
||||||
- `capture` when used as a durable write outside conservative logging policy
|
|
||||||
- `extract` with persistence
|
|
||||||
- `promote`
|
|
||||||
- `reject`
|
|
||||||
- future entity promotion or rejection
|
|
||||||
|
|
||||||
For Discord-originated paths, approval must satisfy the explicit approval rule above.
|
|
||||||
|
|
||||||
## Shared operator client rule
|
|
||||||
|
|
||||||
The preferred V1 architecture is:
|
|
||||||
|
|
||||||
- AtoCore HTTP API as system interface
|
|
||||||
- shared operator client as reusable mutating surface
|
|
||||||
- OpenClaw as a thin frontend and operator around that client
|
|
||||||
|
|
||||||
That avoids duplicating:
|
|
||||||
|
|
||||||
- project detection logic
|
|
||||||
- request logic
|
|
||||||
- failure handling
|
|
||||||
- mutation surface behavior
|
|
||||||
- approval wrappers
|
|
||||||
|
|
||||||
OpenClaw should keep its own high-level operating instructions, but it should not keep growing a parallel AtoCore mutation implementation.
|
|
||||||
|
|
||||||
## V1 boundary summary
|
|
||||||
|
|
||||||
### Allowed automatic behavior
|
|
||||||
|
|
||||||
- read-only retrieval
|
|
||||||
- context build
|
|
||||||
- Discrawl recall
|
|
||||||
- evidence collection
|
|
||||||
- nightly screening into reviewable output
|
|
||||||
- fail-open fallback when AtoCore is unavailable
|
|
||||||
|
|
||||||
### Allowed only after explicit human review or approval
|
|
||||||
|
|
||||||
- candidate persistence from evidence
|
|
||||||
- candidate promotion or rejection
|
|
||||||
- project refresh or ingestion
|
|
||||||
- registry mutation
|
|
||||||
- trusted project_state writes
|
|
||||||
|
|
||||||
### Not allowed as automatic behavior
|
|
||||||
|
|
||||||
- direct Discord -> project_state writes
|
|
||||||
- direct Discord -> register / update / refresh / ingest / promote / reject
|
|
||||||
- hidden mutation inside the screener
|
|
||||||
- treating PKM as the main operator-instruction layer for AtoCore behavior
|
|
||||||
|
|
||||||
## Deferred from V1
|
|
||||||
|
|
||||||
Screenpipe is deferred.
|
|
||||||
It is not an active input lane in V1 and it must not become a runtime, skill, or policy dependency in V1.
|
|
||||||
If it is revisited later, it must be treated as a separate future design decision, not as an implicit V1 extension.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The safe V1 architecture is not "everything can write into AtoCore."
|
|
||||||
It is a layered system where:
|
|
||||||
|
|
||||||
- evidence comes in broadly
|
|
||||||
- trust rises slowly
|
|
||||||
- canonical homes stay singular
|
|
||||||
- OpenClaw remains the operator
|
|
||||||
- AtoCore remains the additive machine-memory and trusted-state layer
|
|
||||||
- the shared operator client becomes the one reusable write-capable surface
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
# OpenClaw x AtoCore V1 Proof Runbook
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
This is the concise proof and operator runbook for the final V1 policy.
|
|
||||||
It shows, in concrete paths, that:
|
|
||||||
|
|
||||||
- a Discord-originated signal cannot reach `project_state` without candidate or review gating
|
|
||||||
- Discord cannot directly execute `register-project`, `update-project`, `refresh-project`, `ingest-sources`, `promote`, or `reject` without explicit approval
|
|
||||||
|
|
||||||
## Explicit approval definition
|
|
||||||
|
|
||||||
For V1, explicit approval means:
|
|
||||||
|
|
||||||
- the human directly instructs the specific mutating action
|
|
||||||
- the instruction is in the current thread or current session
|
|
||||||
- the approval is for that exact action
|
|
||||||
- the approval is not inferred from evidence, archives, or screener output
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
- "refresh p05 now"
|
|
||||||
- "register this project"
|
|
||||||
- "promote that candidate"
|
|
||||||
- "write this to project_state"
|
|
||||||
|
|
||||||
Non-examples:
|
|
||||||
|
|
||||||
- "this looks like the current answer"
|
|
||||||
- "we should probably refresh this"
|
|
||||||
- an old Discord thread saying a refresh might help
|
|
||||||
- a screener report recommending a mutation
|
|
||||||
|
|
||||||
## Proof 1 - Discord cannot directly reach project_state
|
|
||||||
|
|
||||||
Blocked path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discord message
|
|
||||||
-> evidence
|
|
||||||
-> optional candidate
|
|
||||||
-> review
|
|
||||||
-> optional explicit curation
|
|
||||||
-> project_state
|
|
||||||
```
|
|
||||||
|
|
||||||
What is blocked:
|
|
||||||
|
|
||||||
- Discord -> project_state directly
|
|
||||||
- Discrawl archive -> project_state directly
|
|
||||||
- screener output -> project_state directly
|
|
||||||
|
|
||||||
What is allowed:
|
|
||||||
|
|
||||||
1. Discord message enters the evidence lane.
|
|
||||||
2. It may become a memory or entity candidate after screening.
|
|
||||||
3. A human reviews the candidate.
|
|
||||||
4. If the fact is truly the current trusted answer, the human may explicitly curate it into `project_state`.
|
|
||||||
|
|
||||||
Conclusion:
|
|
||||||
|
|
||||||
`project_state` is reachable only after review and explicit curation. There is no direct Discord-originated write path.
|
|
||||||
|
|
||||||
## Proof 2 - Discord cannot directly execute mutating operator actions
|
|
||||||
|
|
||||||
Blocked direct actions:
|
|
||||||
|
|
||||||
- `register-project`
|
|
||||||
- `update-project`
|
|
||||||
- `refresh-project`
|
|
||||||
- `ingest-sources`
|
|
||||||
- `promote`
|
|
||||||
- `reject`
|
|
||||||
- `project-state-set`
|
|
||||||
- `project-state-invalidate`
|
|
||||||
|
|
||||||
Blocked path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discord message
|
|
||||||
-> evidence or operator request context
|
|
||||||
-X-> direct mutation
|
|
||||||
```
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discord message
|
|
||||||
-> OpenClaw recognizes requested operator action
|
|
||||||
-> explicit approval check
|
|
||||||
-> approved operator action
|
|
||||||
-> shared operator client or helper call
|
|
||||||
```
|
|
||||||
|
|
||||||
Conclusion:
|
|
||||||
|
|
||||||
Discord can request or justify a mutation, but it cannot perform it on its own.
|
|
||||||
|
|
||||||
## Proof 3 - Discrawl does not create approval
|
|
||||||
|
|
||||||
Discrawl is evidence retrieval.
|
|
||||||
It may surface:
|
|
||||||
|
|
||||||
- prior discussions
|
|
||||||
- earlier decisions
|
|
||||||
- unresolved questions
|
|
||||||
- prior suggestions to mutate state
|
|
||||||
|
|
||||||
It does not create approval for mutation.
|
|
||||||
|
|
||||||
Blocked path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discrawl recall
|
|
||||||
-X-> refresh-project
|
|
||||||
-X-> promote
|
|
||||||
-X-> project_state write
|
|
||||||
```
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Discrawl recall
|
|
||||||
-> evidence for human review
|
|
||||||
-> explicit approval in current thread/session if mutation is desired
|
|
||||||
-> approved operator action
|
|
||||||
```
|
|
||||||
|
|
||||||
Conclusion:
|
|
||||||
|
|
||||||
Archive recall informs review. It does not authorize writes.
|
|
||||||
|
|
||||||
## Proof 4 - Screener has no hidden mutation lane
|
|
||||||
|
|
||||||
The screener may:
|
|
||||||
|
|
||||||
- gather evidence
|
|
||||||
- classify evidence
|
|
||||||
- prepare candidates
|
|
||||||
- prepare operator queues
|
|
||||||
- report contradictions or missing context
|
|
||||||
|
|
||||||
The screener may not:
|
|
||||||
|
|
||||||
- write `project_state`
|
|
||||||
- mutate registry state
|
|
||||||
- refresh or ingest directly
|
|
||||||
- promote or reject directly
|
|
||||||
|
|
||||||
Blocked path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
screener output
|
|
||||||
-X-> hidden mutation
|
|
||||||
```
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
```text
|
|
||||||
screener output
|
|
||||||
-> review queue or operator queue
|
|
||||||
-> explicit approval if mutation is wanted
|
|
||||||
-> approved operator action
|
|
||||||
```
|
|
||||||
|
|
||||||
Conclusion:
|
|
||||||
|
|
||||||
The screener is a filter, not a hidden writer.
|
|
||||||
|
|
||||||
## Minimal operator decision table
|
|
||||||
|
|
||||||
| Situation | Allowed next step | Blocked next step |
|
|
||||||
|---|---|---|
|
|
||||||
| Discord says "this is the current answer" | evidence, then review, then possible explicit curation | direct `project_state` write |
|
|
||||||
| Discord says "refresh p05" without direct instruction | ask for explicit approval | direct `refresh-project` |
|
|
||||||
| Discord says "refresh p05 now" | approved operator action may run | none, if approval is explicit |
|
|
||||||
| Discrawl finds an old thread asking for registration | use as review context only | direct `register-project` |
|
|
||||||
| Screener recommends promotion | ask for explicit review decision | direct `promote` |
|
|
||||||
|
|
||||||
## Practical runbook
|
|
||||||
|
|
||||||
### Case A - current-truth claim from Discord
|
|
||||||
|
|
||||||
1. Treat the message as evidence.
|
|
||||||
2. Check the canonical home.
|
|
||||||
3. If needed, prepare a candidate or review note.
|
|
||||||
4. Do not write `project_state` unless the human explicitly approves that curation step.
|
|
||||||
|
|
||||||
### Case B - requested refresh from Discord
|
|
||||||
|
|
||||||
1. Determine whether the message is a direct instruction or only discussion.
|
|
||||||
2. If not explicit, ask for approval.
|
|
||||||
3. Only perform `refresh-project` after explicit approval in the current thread or session.
|
|
||||||
|
|
||||||
### Case C - candidate promotion request
|
|
||||||
|
|
||||||
1. Candidate exists or is proposed.
|
|
||||||
2. Review the evidence and the candidate text.
|
|
||||||
3. Only perform `promote` or `reject` after explicit review decision.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
The V1 rule is easy to test:
|
|
||||||
|
|
||||||
If the path starts from Discord or Discrawl and ends in trusted or operator state, there must be a visible approval or review step in the middle.
|
|
||||||
|
|
||||||
If that visible step is missing, the action is not allowed.
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
# OpenClaw x AtoCore V1 Write-Policy Matrix
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
This matrix defines what each source is allowed to write to each target in V1.
|
|
||||||
|
|
||||||
Policy meanings:
|
|
||||||
|
|
||||||
- `auto-write` = allowed automatically without a human approval gate
|
|
||||||
- `candidate-only` = may create reviewable candidate material, but not active truth
|
|
||||||
- `human-review` = allowed only after explicit human review or explicit human approval
|
|
||||||
- `never-auto-write` = never allowed as an automatic write path
|
|
||||||
|
|
||||||
## Explicit approval rule
|
|
||||||
|
|
||||||
In this matrix, `human-review` is concrete, not vague.
|
|
||||||
For Discord-originated or Discrawl-originated paths it means:
|
|
||||||
|
|
||||||
- the human directly instructs the specific mutating action
|
|
||||||
- the instruction is in the current thread or current session
|
|
||||||
- the approval is for that specific action
|
|
||||||
- the approval is not inferred from evidence, archives, screener output, or general discussion
|
|
||||||
|
|
||||||
Examples of explicit approval:
|
|
||||||
|
|
||||||
- "refresh p05 now"
|
|
||||||
- "register this project"
|
|
||||||
- "promote this candidate"
|
|
||||||
- "write this to project_state"
|
|
||||||
|
|
||||||
Non-examples:
|
|
||||||
|
|
||||||
- "this looks important"
|
|
||||||
- "we should probably refresh this"
|
|
||||||
- archived discussion that once mentioned a similar mutation
|
|
||||||
- a screener note recommending promotion
|
|
||||||
|
|
||||||
## V1 scope note
|
|
||||||
|
|
||||||
V1 active inputs are:
|
|
||||||
|
|
||||||
- Discord and Discrawl
|
|
||||||
- OpenClaw interaction evidence
|
|
||||||
- PKM, repos, and KB sources
|
|
||||||
- read-only AtoCore context for comparison and deduplication
|
|
||||||
|
|
||||||
## Targets
|
|
||||||
|
|
||||||
The targets below are the only ones that matter for this policy.
|
|
||||||
|
|
||||||
- Evidence artifacts
|
|
||||||
- Memory candidates
|
|
||||||
- Active memories
|
|
||||||
- Entity candidates
|
|
||||||
- Active entities
|
|
||||||
- Trusted project_state
|
|
||||||
- Registry / refresh / ingest mutations
|
|
||||||
- Review actions
|
|
||||||
|
|
||||||
## Matrix
|
|
||||||
|
|
||||||
| Source | Target | Policy | Notes / gate |
|
|
||||||
|---|---|---|---|
|
|
||||||
| Discord live message | Evidence artifacts | auto-write | Safe evidence capture or archive only |
|
|
||||||
| Discord live message | Memory candidates | candidate-only | Only after screening or extraction; never direct active write |
|
|
||||||
| Discord live message | Active memories | human-review | Promote only after review of the candidate and evidence |
|
|
||||||
| Discord live message | Entity candidates | candidate-only | Only when structured signal is extracted from evidence |
|
|
||||||
| Discord live message | Active entities | human-review | Review required before promotion |
|
|
||||||
| Discord live message | Trusted project_state | human-review | Only via explicit curation; never directly from raw chat |
|
|
||||||
| Discord live message | Registry / refresh / ingest mutations | human-review | Requires explicit approval in the current thread or session |
|
|
||||||
| Discord live message | Review actions | human-review | Discord cannot silently promote or reject on its own |
|
|
||||||
| Discrawl archive result | Evidence artifacts | auto-write | Archive or search result is evidence by design |
|
|
||||||
| Discrawl archive result | Memory candidates | candidate-only | Extract reviewed signal from archived conversation |
|
|
||||||
| Discrawl archive result | Active memories | human-review | Promotion required |
|
|
||||||
| Discrawl archive result | Entity candidates | candidate-only | Archived discussion may justify candidate creation |
|
|
||||||
| Discrawl archive result | Active entities | human-review | Promotion required |
|
|
||||||
| Discrawl archive result | Trusted project_state | human-review | Must be explicitly curated; never inferred directly from archive |
|
|
||||||
| Discrawl archive result | Registry / refresh / ingest mutations | human-review | Archive recall cannot directly mutate operator state |
|
|
||||||
| Discrawl archive result | Review actions | human-review | Archive evidence informs review; it does not perform review |
|
|
||||||
| OpenClaw read/query flow | Evidence artifacts | auto-write | Conservative interaction or evidence logging is acceptable |
|
|
||||||
| OpenClaw read/query flow | Memory candidates | candidate-only | Only through explicit extraction path |
|
|
||||||
| OpenClaw read/query flow | Active memories | human-review | Requires operator review |
|
|
||||||
| OpenClaw read/query flow | Entity candidates | candidate-only | Future extraction path |
|
|
||||||
| OpenClaw read/query flow | Active entities | human-review | Requires operator review |
|
|
||||||
| OpenClaw read/query flow | Trusted project_state | never-auto-write | Read/query flow must stay additive |
|
|
||||||
| OpenClaw read/query flow | Registry / refresh / ingest mutations | never-auto-write | Read/query automation must not mutate operator state |
|
|
||||||
| OpenClaw read/query flow | Review actions | never-auto-write | Read automation cannot silently promote or reject |
|
|
||||||
| OpenClaw approved operator action | Evidence artifacts | auto-write | May create operator or audit artifacts |
|
|
||||||
| OpenClaw approved operator action | Memory candidates | human-review | Candidate persistence is itself an approved operator action |
|
|
||||||
| OpenClaw approved operator action | Active memories | human-review | Promotion allowed only through reviewed operator action |
|
|
||||||
| OpenClaw approved operator action | Entity candidates | human-review | Same rule for future entities |
|
|
||||||
| OpenClaw approved operator action | Active entities | human-review | Promotion allowed only through reviewed operator action |
|
|
||||||
| OpenClaw approved operator action | Trusted project_state | human-review | Allowed only as explicit curation |
|
|
||||||
| OpenClaw approved operator action | Registry / refresh / ingest mutations | human-review | Explicit approval required |
|
|
||||||
| OpenClaw approved operator action | Review actions | human-review | Explicit review required |
|
|
||||||
| PKM note | Evidence artifacts | human-review | Snapshotting into evidence is optional, not the primary path |
|
|
||||||
| PKM note | Memory candidates | candidate-only | Extraction from PKM is allowed into the candidate lane |
|
|
||||||
| PKM note | Active memories | human-review | Promotion required |
|
|
||||||
| PKM note | Entity candidates | candidate-only | Extract structured signal into the candidate lane |
|
|
||||||
| PKM note | Active entities | human-review | Promotion required |
|
|
||||||
| PKM note | Trusted project_state | human-review | Only via explicit curation of current truth |
|
|
||||||
| PKM note | Registry / refresh / ingest mutations | human-review | A human may choose to refresh based on PKM changes |
|
|
||||||
| PKM note | Review actions | human-review | PKM may support the decision, but not execute it automatically |
|
|
||||||
| Repo / KB source | Evidence artifacts | human-review | Optional audit or screener snapshot only |
|
|
||||||
| Repo / KB source | Memory candidates | candidate-only | Extract loose durable signal if useful |
|
|
||||||
| Repo / KB source | Active memories | human-review | Promotion required |
|
|
||||||
| Repo / KB source | Entity candidates | candidate-only | Strong future path for structured facts |
|
|
||||||
| Repo / KB source | Active entities | human-review | Promotion required |
|
|
||||||
| Repo / KB source | Trusted project_state | human-review | Explicit curation only |
|
|
||||||
| Repo / KB source | Registry / refresh / ingest mutations | human-review | A human may refresh or ingest based on source changes |
|
|
||||||
| Repo / KB source | Review actions | human-review | Source can justify review; it does not perform review |
|
|
||||||
| AtoCore active memory | Evidence artifacts | never-auto-write | Active memory is already above the evidence layer |
|
|
||||||
| AtoCore active memory | Memory candidates | never-auto-write | Do not recursively re-candidate active memory |
|
|
||||||
| AtoCore active memory | Active memories | never-auto-write | Already active |
|
|
||||||
| AtoCore active memory | Entity candidates | human-review | Graduation proposal only with review |
|
|
||||||
| AtoCore active memory | Active entities | human-review | Requires graduation plus promotion |
|
|
||||||
| AtoCore active memory | Trusted project_state | human-review | A human may explicitly curate current truth from memory |
|
|
||||||
| AtoCore active memory | Registry / refresh / ingest mutations | never-auto-write | Memory must not mutate registry or ingestion state |
|
|
||||||
| AtoCore active memory | Review actions | human-review | Human reviewer decides |
|
|
||||||
| AtoCore active entity | Evidence artifacts | never-auto-write | Already above the evidence layer |
|
|
||||||
| AtoCore active entity | Memory candidates | never-auto-write | Do not backflow structured truth into memory candidates automatically |
|
|
||||||
| AtoCore active entity | Active memories | never-auto-write | Canonical home is the entity, not a new memory |
|
|
||||||
| AtoCore active entity | Entity candidates | never-auto-write | Already active |
|
|
||||||
| AtoCore active entity | Active entities | never-auto-write | Already active |
|
|
||||||
| AtoCore active entity | Trusted project_state | human-review | Explicit curation may publish the current trusted answer |
|
|
||||||
| AtoCore active entity | Registry / refresh / ingest mutations | never-auto-write | Entities do not operate the registry |
|
|
||||||
| AtoCore active entity | Review actions | human-review | Human reviewer decides |
|
|
||||||
|
|
||||||
## Discord-originated trace examples
|
|
||||||
|
|
||||||
### Example 1 - conversational decision in Discord
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
1. Discord live message -> Evidence artifacts (`auto-write`)
|
|
||||||
2. Evidence artifacts -> Memory candidates or Entity candidates (`candidate-only`)
|
|
||||||
3. Candidate -> Active memory or Active entity (`human-review`)
|
|
||||||
4. If it becomes the current trusted answer, a human may explicitly curate it into Trusted project_state (`human-review`)
|
|
||||||
|
|
||||||
There is no direct Discord -> project_state automatic path.
|
|
||||||
|
|
||||||
### Example 2 - archived Discord thread via Discrawl
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
1. Discrawl result -> Evidence artifacts (`auto-write`)
|
|
||||||
2. Discrawl result -> Memory candidates or Entity candidates (`candidate-only`)
|
|
||||||
3. Human review decides promotion
|
|
||||||
4. Optional explicit curation into project_state later
|
|
||||||
|
|
||||||
Again, there is no direct archive -> trusted truth path.
|
|
||||||
|
|
||||||
### Example 3 - Discord request to refresh a project
|
|
||||||
|
|
||||||
Allowed path:
|
|
||||||
|
|
||||||
1. Discord message is evidence of requested operator intent
|
|
||||||
2. No mutation happens automatically
|
|
||||||
3. OpenClaw requires explicit approval in the current thread or session for `refresh-project`
|
|
||||||
4. Only then may OpenClaw perform the approved operator action
|
|
||||||
|
|
||||||
There is no direct Discord -> refresh path without explicit approval.
|
|
||||||
|
|
||||||
## V1 interpretation rules
|
|
||||||
|
|
||||||
1. Evidence can flow in broadly.
|
|
||||||
2. Truth can only rise through review.
|
|
||||||
3. project_state is the narrowest lane.
|
|
||||||
4. Registry and ingestion operations are operator actions, not evidence effects.
|
|
||||||
5. Discord-originated paths can inform operator actions, but they cannot silently execute them.
|
|
||||||
6. Deferred sources that are out of V1 scope have no automatic or manual role in this V1 matrix.
|
|
||||||
|
|
||||||
## Deferred from V1
|
|
||||||
|
|
||||||
Screenpipe is deferred and intentionally omitted from this V1 matrix.
|
|
||||||
|
|
||||||
## Bottom line
|
|
||||||
|
|
||||||
If a source is noisy, conversational, or archived, its maximum automatic privilege in V1 is:
|
|
||||||
|
|
||||||
- evidence capture, or
|
|
||||||
- candidate creation
|
|
||||||
|
|
||||||
Everything above that requires explicit human review or explicit human approval.
|
|
||||||
@@ -1,617 +0,0 @@
|
|||||||
# Engineering V1 Completion Plan
|
|
||||||
|
|
||||||
**Date:** 2026-04-22
|
|
||||||
**Author:** Claude (after reading the four V1 architecture docs + promotion-rules,
|
|
||||||
conflict-model, human-mirror-rules, tool-handoff-boundaries end-to-end)
|
|
||||||
**Status:** Draft, pending Codex review
|
|
||||||
**Replaces:** the rejected "Phase 8 Minions + typed edges" plan (see
|
|
||||||
`docs/decisions/2026-04-22-gbrain-plan-rejection.md`)
|
|
||||||
|
|
||||||
## Position
|
|
||||||
|
|
||||||
This is **not** a plan to start Engineering V1. It is a plan to **finish** V1.
|
|
||||||
|
|
||||||
**Against what criterion?** Each F/Q/O/D item in `engineering-v1-acceptance.md`
|
|
||||||
gets scored individually in the Gap audit table below with exact code/test/doc
|
|
||||||
references. No global percentage. The headline framing from the first draft
|
|
||||||
("50–70% built") is withdrawn — it's either done per-criterion or it isn't.
|
|
||||||
|
|
||||||
The relevant observation is narrower: the entity schema, the full
|
|
||||||
relationship type set, the 4-state lifecycle, basic CRUD and most of the
|
|
||||||
killer-correctness query functions are already implemented in
|
|
||||||
`src/atocore/engineering/*.py` in the Windows working tree at
|
|
||||||
`C:\Users\antoi\ATOCore` (the canonical dev workspace, per
|
|
||||||
`CLAUDE.md`). The recent commits e147ab2, b94f9df, 081c058, 069d155, b1a3dd0
|
|
||||||
are V1 entity-layer work. **Codex auditors working in a different
|
|
||||||
workspace / branch should sync from the canonical dev tree before
|
|
||||||
per-file review** — see the "Workspace note" at the end of this doc.
|
|
||||||
|
|
||||||
The question this plan answers: given the current code state, in what
|
|
||||||
order should the remaining V1 acceptance criteria be closed so that
|
|
||||||
every phase builds on invariants the earlier phases already enforced?
|
|
||||||
|
|
||||||
## Corrected sequencing principle (post-Codex review 2026-04-22)
|
|
||||||
|
|
||||||
The first draft ordered phases F-1 → F-2 → F-3 → F-4 → F-5 → F-6 → F-7 → F-8
|
|
||||||
following the acceptance doc's suggested reading order. Codex rejected
|
|
||||||
that ordering. The correct dependency order, which this revision adopts, is:
|
|
||||||
|
|
||||||
1. **Write-time invariants come first.** Every later phase creates active
|
|
||||||
entities. Provenance-at-write (F-8) and synchronous conflict-detection
|
|
||||||
hooks (F-5 minimal) must be enforced **before** any phase that writes
|
|
||||||
entities at scale (ingest, graduation, or broad query coverage that
|
|
||||||
depends on the model being populated).
|
|
||||||
2. **Query closure sits on top of the schema + invariants**, not ahead of
|
|
||||||
them. A minimum query slice that proves the model is fine early. The
|
|
||||||
full catalog closure waits until after the write paths are invariant-safe.
|
|
||||||
3. **Mirror is a derived consumer** of the entity layer, not a midstream
|
|
||||||
milestone. It comes after the entity layer produces enforced, correct data.
|
|
||||||
4. **Graduation and full conflict-spec compliance** are finishing work that
|
|
||||||
depend on everything above being stable.
|
|
||||||
|
|
||||||
The acceptance criteria are unchanged. Only the order of closing them changes.
|
|
||||||
|
|
||||||
## How this plan respects the rejected-plan lessons
|
|
||||||
|
|
||||||
- **No new predicates.** The V1 ontology in `engineering-ontology-v1.md:112-137`
|
|
||||||
already defines 18 relationship types; `service.py:38-62` already implements
|
|
||||||
them. Nothing added, nothing reshaped.
|
|
||||||
- **No new canonical boundary.** Typed entities + typed relationships with
|
|
||||||
promotion-based candidate flow per `memory-vs-entities.md`. Not
|
|
||||||
edges-over-wikilinks.
|
|
||||||
- **No leapfrog of `master-plan-status.md` Now list.** This plan is **in
|
|
||||||
parallel** with (not ahead of) the Now items because V1 entity work is
|
|
||||||
already happening alongside them. The sequencing section below is explicit.
|
|
||||||
- **Queue/worker infrastructure is explicitly out of scope.** The "flag it for
|
|
||||||
later" note at the end of this doc is the only mention, per
|
|
||||||
`engineering-v1-acceptance.md:378` negative list.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Gap audit against `engineering-v1-acceptance.md`
|
|
||||||
|
|
||||||
Each criterion marked: ✅ done / 🟡 partial / ❌ missing. "Partial" means the
|
|
||||||
capability exists but does not yet match spec shape or coverage.
|
|
||||||
|
|
||||||
### Functional (F-1 through F-8)
|
|
||||||
|
|
||||||
| ID | Criterion | Status | Evidence |
|
|
||||||
|----|-----------|--------|----------|
|
|
||||||
| F-1 | 12 V1 entity types, 4 relationship families, shared header fields, 4-state lifecycle | 🟡 partial (per Codex 2026-04-22 audit) | `service.py:16-36` has 16 types (superset of V1's 12), `service.py:38-62` has 18 relationship types, `service.py:64` statuses, `Entity` dataclass at line 67. **Gaps vs `engineering-v1-acceptance.md:45`**: `extractor_version` missing from dataclass and `entities` table; `canonical_home` missing from dataclass and table; `project` field is the project identifier but not named `project_id` as spec uses — spec says "fields equivalent to" so naming flexibility is allowed but needs an explicit doc note. Remediation lands in V1-0 |
|
|
||||||
| F-2 | All v1-required Q-001 through Q-020 implemented, with provenance where required | 🟡 partial (per Codex 2026-04-22 per-function audit) | **Ground truth from per-function read of `queries.py` + `routes.py:2092+`:** Q-001 partial (`system_map()` returns project-wide tree, not the catalog's subsystem-scoped `GET /entities/Subsystem/<id>?expand=contains` shape per `engineering-query-catalog.md:71`); Q-002 missing; Q-003 missing; Q-004 done (covered by `system_map()`); Q-005 done (`requirements_for()`); Q-006 done (`orphan_requirements()`); Q-007 missing; Q-008 done (`decisions_affecting()`); Q-009 done (`risky_decisions()`); Q-010 missing; Q-011 done (`unsupported_claims()`); Q-012 missing; Q-013 done (`recent_changes()`); Q-014 missing; Q-016 done (`impact_analysis()`); Q-017 done (`evidence_chain()`); Q-018 missing; Q-019 missing; Q-020 missing (mirror route in spec shape). **Net: 9 of 20 v1-required queries done, 1 partial (Q-001), 10 missing.** Q-015 is v1-stretch, out of scope |
|
|
||||||
| F-3 | `POST /ingest/kb-cad/export` and `POST /ingest/kb-fem/export` | ❌ missing | No `/ingest/kb-cad` or `/ingest/kb-fem` route in `api/routes.py`. No schema doc under `docs/architecture/` |
|
|
||||||
| F-4 | Candidate review queue end-to-end (list/promote/reject/edit) | 🟡 partial for entities | Memory side shipped in Phase 9 Commit C. Entity side has `promote_entity`, `supersede_entity`, `invalidate_active_entity` but reject path and editable-before-promote may not match spec shape. Need to verify `GET /entities?status=candidate` returns spec shape |
|
|
||||||
| F-5 | Conflict detector fires synchronously; `POST /conflicts/{id}/resolve` + dismiss | 🟡 partial (per Codex 2026-04-22 audit — schema present, detector+routes divergent) | **Schema is already spec-shaped**: `database.py:190` defines the generic `conflicts` + `conflict_members` tables per `conflict-model.md`; `conflicts.py:154` persists through them. **Divergences are in detection and API, not schema**: (1) `conflicts.py:36` dispatches per-type detectors only (`_check_component_conflicts`, `_check_requirement_conflicts`) — needs generalization to slot-key-driven detection; (2) routes live at `/admin/conflicts/*`, spec says `/conflicts/*` — needs alias + deprecation. **No schema migration needed** |
|
|
||||||
| F-6 | Mirror: `/mirror/{project}/overview`, `/decisions`, `/subsystems/{id}`, `/regenerate`; files under `/srv/storage/atocore/data/mirror/`; disputed + curated markers; deterministic output | 🟡 partial | `mirror.py` has `generate_project_overview` with header/state/system/decisions/requirements/materials/vendors/memories/footer sections. API at `/projects/{project_name}/mirror` and `.html`. **Gaps**: no separate `/mirror/{project}/decisions` or `/mirror/{project}/subsystems/{id}` routes, no `POST /regenerate` endpoint, no debounced-async-on-write, no daily refresh, no `⚠ disputed` markers wired to conflicts, no `(curated)` override annotations verified, no golden-file test for determinism |
|
|
||||||
| F-7 | Memory→entity graduation: `POST /memory/{id}/graduate` + `graduated` status + forward pointer + original preserved | 🟡 partial (per Codex 2026-04-22 third-round audit) | `_graduation_prompt.py` exists; `scripts/graduate_memories.py` creates entity candidates from active memories; `database.py:143-146` adds `graduated_to_entity_id`; `memory.service` already has a `graduated` status; `service.py:354-356,389-451` preserves the original memory and marks it `graduated` with a forward pointer on entity promote; `tests/test_engineering_v1_phase5.py:67-90` covers that flow. **Gaps vs spec**: no direct `POST /memory/{id}/graduate` route yet (current surface is batch/admin-driven via `/admin/graduation/request`); no explicit acceptance tests yet for `adaptation→decision` and `project→requirement`; spec wording `knowledge→Fact` does not match the current ontology (there is no `fact` entity type in `service.py` / `_graduation_prompt.py`) and should be reconciled to an actual V1 type such as `parameter` or another ontology-defined entity. |
|
|
||||||
| F-8 | Every active entity has `source_refs`; Q-017 returns ≥1 row for every active entity | 🟡 partial | `Entity.source_refs` field exists; Q-017 (`evidence_chain`) exists. **Gap**: is provenance enforced at write time (not NULL), or just encouraged? Per spec it must be mandatory |
|
|
||||||
|
|
||||||
### Quality (Q-1 through Q-6)
|
|
||||||
|
|
||||||
| ID | Criterion | Status | Evidence |
|
|
||||||
|----|-----------|--------|----------|
|
|
||||||
| Q-1 | All pre-V1 tests still pass | ✅ presumed | 533 tests passing per DEV-LEDGER line 12 |
|
|
||||||
| Q-2 | Each F criterion has happy-path + error-path test, <10s each, <30s total | 🟡 partial | 16 + 15 + 15 + 12 = 58 tests in engineering/queries/v1-phase5/patch files. Need to verify coverage of each F criterion one-for-one |
|
|
||||||
| Q-3 | Conflict invariants enforced by tests (contradictory imports produce conflict, can't promote both, flag-never-block) | 🟡 partial | Tests likely exist in `test_engineering_v1_phase5.py` — verify explicit coverage of the three invariants |
|
|
||||||
| Q-4 | Trust hierarchy enforced by tests (candidates never in context, active-only reinforcement, no auto-project-state writes) | 🟡 partial | Phase 9 Commit B covered the memory side; verify entity side has equivalent tests |
|
|
||||||
| Q-5 | Mirror has golden-file test, deterministic output | ❌ missing | No golden file seen; mirror output reads wall-clock time inside `_footer()` (`mirror.py:320-327`). Determinism should come from injecting the regenerated timestamp/checksum as inputs to the renderer and pinning them in the golden-file test, not from calling `datetime.now()` inside render code |
|
|
||||||
| Q-6 | Killer correctness queries pass against seeded real-ish data (5 seed cases per Q-006/Q-009/Q-011) | ❌ likely missing | No fixture file named for this seen. The three queries exist but there's no evidence of the single integration test described in Q-6 |
|
|
||||||
|
|
||||||
### Operational (O-1 through O-5)
|
|
||||||
|
|
||||||
| ID | Criterion | Status | Evidence |
|
|
||||||
|----|-----------|--------|----------|
|
|
||||||
| O-1 | Schema migration additive, idempotent, tested against fresh + prod-copy DB | 🟡 presumed | `_apply_migrations` pattern is in use per CLAUDE.md sessions; tables exist. Need one confirmation run against a Dalidou backup copy |
|
|
||||||
| O-2 | Backup includes new tables; full restore drill passes; post-restore Q-001 works | ❌ not done | No evidence a restore drill has been run on V1 entity state. `docs/backup-restore-procedure.md` exists but drill is an explicit V1 prerequisite |
|
|
||||||
| O-3 | Performance bounds: write <100ms p99, query <500ms p99 at 1000 entities, mirror <5s per project | 🟡 unmeasured | 35 entities in system — bounds unmeasured at scale. Spec says "sanity-checked, not benchmarked", so this is a one-off manual check |
|
|
||||||
| O-4 | No new manual ops burden | 🟡 | Mirror regen auto-triggers not wired yet (see F-6 gap) — they must be wired for O-4 to pass |
|
|
||||||
| O-5 | Phase 9 reflection loop unchanged for identity/preference/episodic | ✅ presumed | `memory-vs-entities.md` says these three types don't interact with engineering layer. No recent change to memory extractor for these types |
|
|
||||||
|
|
||||||
### Documentation (D-1 through D-4)
|
|
||||||
|
|
||||||
| ID | Criterion | Status | Evidence |
|
|
||||||
|----|-----------|--------|----------|
|
|
||||||
| D-1 | 12 per-entity-type spec docs under `docs/architecture/entities/` | ❌ missing | No `docs/architecture/entities/` folder |
|
|
||||||
| D-2 | `kb-cad-export-schema.md` + `kb-fem-export-schema.md` | ❌ missing | No such files in `docs/architecture/` |
|
|
||||||
| D-3 | `docs/v1-release-notes.md` | ❌ missing | Not written yet (appropriately — it's written when V1 is done) |
|
|
||||||
| D-4 | `master-plan-status.md` + `current-state.md` updated with V1 completion | ❌ not yet | `master-plan-status.md:179` still has V1 under **Next** |
|
|
||||||
|
|
||||||
### Summary (revised per Codex 2026-04-22 per-file audit)
|
|
||||||
|
|
||||||
- **Functional:** 0/8 ✅, 7/8 🟡 partial (F-1 downgraded from ✅ — two header fields missing; F-2 through F-7 partial), 1/8 ❌ missing (F-3 ingest endpoints) → the entity layer shape is real but not yet spec-clean; write-time invariants come first, then everything builds on stable invariants
|
|
||||||
- **F-2 detail:** 9 of 20 v1-required queries done, 1 partial (Q-001 needs subsystem-scoped variant), 10 missing
|
|
||||||
- **F-5 detail:** generic `conflicts` + `conflict_members` schema already present (no migration needed); detector body + routes diverge from spec
|
|
||||||
- **Quality:** 1/6 ✅, 3/6 🟡 partial, 2/6 ❌ missing → golden file + killer-correctness integration test are the two clear gaps
|
|
||||||
- **Operational:** 0/5 ✅ (none fully verified), 3/5 🟡, 1/5 ❌ → backup drill is the one hard blocker here
|
|
||||||
- **Documentation:** 0/4 ✅, 4/4 ❌ → all 4 docs need writing
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Proposed completion order (revised post-Codex review)
|
|
||||||
|
|
||||||
Seven phases instead of six. The new V1-0 establishes the write-time
|
|
||||||
invariants (provenance enforcement F-8 + synchronous conflict hooks F-5
|
|
||||||
minimal) that every later phase depends on. V1-A becomes a **minimal query
|
|
||||||
slice** that proves the model on one project, not a full catalog closure.
|
|
||||||
Full query catalog closure moves to V1-C. Full F-5 spec compliance (the
|
|
||||||
generic `conflicts`/`conflict_members` slot-key schema) stays in V1-F
|
|
||||||
because that's the final shape, but the *minimal hooks* that fire
|
|
||||||
synchronously on writes land in V1-0.
|
|
||||||
|
|
||||||
Skipped by construction: F-1 core schema (already implemented) and O-5
|
|
||||||
(identity/preference/episodic don't touch the engineering layer).
|
|
||||||
|
|
||||||
### Phase V1-0: Write-time invariants (F-8 + F-5 minimal + F-1 audit)
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- **F-1 remediation (Codex audit 2026-04-22 already completed).** Add
|
|
||||||
the two missing shared-header fields to the `Entity` dataclass
|
|
||||||
(`service.py:67`) and the `entities` table schema:
|
|
||||||
- `extractor_version TEXT` — semver-ish string carrying the extractor
|
|
||||||
module version per `promotion-rules.md:268`. Backfill existing rows
|
|
||||||
with `"0.0.0"` or `NULL` flagged as unknown. Every future
|
|
||||||
write carries the current `EXTRACTOR_VERSION` constant.
|
|
||||||
- `canonical_home TEXT` — which layer is canonical for this concept.
|
|
||||||
For entities, value is always `"entity"`. For future graduation
|
|
||||||
records it may be `"memory"` (frozen pointer). Backfill active
|
|
||||||
rows with `"entity"`.
|
|
||||||
- Additive migration via the existing `_apply_migrations` pattern,
|
|
||||||
idempotent, safe on replay.
|
|
||||||
- Add doc note in `engineering-ontology-v1.md` clarifying that the
|
|
||||||
`project` field IS the `project_id` per spec — "fields equivalent
|
|
||||||
to" wording in the spec allows this, but make it explicit so
|
|
||||||
future readers don't trip on the naming.
|
|
||||||
- **F-8 provenance enforcement.** Add a NOT-NULL invariant at
|
|
||||||
`create_entity` and `promote_entity` that `source_refs` is non-empty
|
|
||||||
OR an explicit `hand_authored=True` flag is set (per
|
|
||||||
`promotion-rules.md:253`). Backfill any existing active entities that
|
|
||||||
fail the invariant — either attach provenance, flag as hand-authored,
|
|
||||||
or invalidate. Every future active entity has provenance by schema,
|
|
||||||
not by discipline.
|
|
||||||
- **F-5 minimal hooks.** Wire synchronous conflict detection into every
|
|
||||||
active-entity write path (`create_entity` with status=active,
|
|
||||||
`promote_entity`, `supersede_entity`). The *detector* can stay in its
|
|
||||||
current per-type form (`_check_component_conflicts`,
|
|
||||||
`_check_requirement_conflicts`); the *hook* must fire on every write.
|
|
||||||
Full generic slot-keyed schema lands in V1-F; the hook shape must be
|
|
||||||
generic enough that V1-F is a detector-body swap, not an API refactor.
|
|
||||||
- **Q-3 "flag never block" test.** The hook must return conflict-id in
|
|
||||||
the response body but never 4xx-block the write. One test per write
|
|
||||||
path demonstrating this.
|
|
||||||
- **Q-4 trust-hierarchy test for candidates.** One test: entity
|
|
||||||
candidates never appear in `/context/build` output. (Full trust tests
|
|
||||||
land in V1-E; this is the one that V1-0 can cover without graduation
|
|
||||||
being ready.)
|
|
||||||
|
|
||||||
**Acceptance:** F-1 ✅ (after `extractor_version` + `canonical_home`
|
|
||||||
land + doc note on `project` naming), F-8 ✅, F-5 hooks ✅, Q-3 ✅,
|
|
||||||
partial Q-4 ✅.
|
|
||||||
|
|
||||||
**Estimated size:** 3 days (two small schema additions + invariant
|
|
||||||
patches + hook wiring + tests; no audit overhead — Codex already did
|
|
||||||
that part).
|
|
||||||
|
|
||||||
**Tests added:** ~10.
|
|
||||||
|
|
||||||
**Why first:** every later phase writes entities. Without F-8 + F-5
|
|
||||||
hooks, V1-A through V1-F can leak invalid state into the store that
|
|
||||||
must then be cleaned up.
|
|
||||||
|
|
||||||
### Phase V1-A: Minimal query slice that proves the model (partial F-2 + Q-6)
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- Pick the **four pillar queries**: Q-001 (subsystem contents),
|
|
||||||
Q-005 (component satisfies requirements), Q-006 (orphan requirements —
|
|
||||||
killer correctness), Q-017 (evidence chain). These exercise structural +
|
|
||||||
intent + killer-correctness + provenance.
|
|
||||||
- **Q-001 needs a shape fix**: Codex's audit confirms the existing
|
|
||||||
`system_map()` returns a project-wide tree, not the spec's
|
|
||||||
subsystem-scoped `GET /entities/Subsystem/<id>?expand=contains`.
|
|
||||||
Add a subsystem-scoped variant (the existing project-wide route stays
|
|
||||||
for Q-004). This is the only shape fix in V1-A; larger query additions
|
|
||||||
move to V1-C.
|
|
||||||
- Q-005, Q-006, Q-017 are already implemented per Codex audit. V1-A
|
|
||||||
verifies them against seeded data; no code changes expected.
|
|
||||||
- Seed p05-interferometer with Q-6 integration data (one satisfying
|
|
||||||
Component + one orphan Requirement + one Decision on flagged
|
|
||||||
Assumption + one supported ValidationClaim + one unsupported
|
|
||||||
ValidationClaim).
|
|
||||||
- All three killer-correctness queries (Q-006, Q-009, Q-011) are
|
|
||||||
**already implemented** per Codex audit. V1-A runs them as a single
|
|
||||||
integration test against the seed data.
|
|
||||||
|
|
||||||
**Acceptance:** Q-001 subsystem-scoped variant + Q-6 integration test.
|
|
||||||
Partial F-2 (remaining 10 missing + 1 partial queries land in V1-C).
|
|
||||||
|
|
||||||
**Estimated size:** 1.5 days (scope shrunk — most pillar queries already
|
|
||||||
work per Codex audit; only Q-001 shape fix + seed data + integration
|
|
||||||
test required).
|
|
||||||
|
|
||||||
**Tests added:** ~4.
|
|
||||||
|
|
||||||
**Why second:** proves the entity layer shape works end-to-end on real
|
|
||||||
data before we start bolting ingest, graduation, or mirror onto it. If
|
|
||||||
the four pillar queries don't work, stopping here is cheap.
|
|
||||||
|
|
||||||
### Phase V1-B: KB-CAD / KB-FEM ingest (F-3) + D-2 schema docs
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- Write `docs/architecture/kb-cad-export-schema.md` and
|
|
||||||
`kb-fem-export-schema.md` (matches D-2).
|
|
||||||
- Implement `POST /ingest/kb-cad/export` and `POST /ingest/kb-fem/export`
|
|
||||||
per `tool-handoff-boundaries.md` sketches. Validator + entity-candidate
|
|
||||||
producer + provenance population **using the F-8 invariant from V1-0**.
|
|
||||||
- Hand-craft one real KB-CAD export for p05-interferometer and
|
|
||||||
round-trip it: export → candidate queue → reviewer promotes → queryable
|
|
||||||
via V1-A's four pillar queries.
|
|
||||||
- Tests: valid export → candidates created; invalid export → 400;
|
|
||||||
duplicate re-export → no duplicate candidates; re-export with changed
|
|
||||||
value → new candidate + conflict row (exercises V1-0's F-5 hook on a
|
|
||||||
real workload).
|
|
||||||
|
|
||||||
**Acceptance:** F-3 ✅, D-2 ✅.
|
|
||||||
|
|
||||||
**Estimated size:** 2 days.
|
|
||||||
|
|
||||||
**Tests added:** ~8.
|
|
||||||
|
|
||||||
**Why third:** ingest is the first real stress test of the V1-0
|
|
||||||
invariants. A re-import that creates a conflict must trigger the V1-0
|
|
||||||
hook; if it doesn't, V1-0 is incomplete and we catch it before going
|
|
||||||
further.
|
|
||||||
|
|
||||||
### Phase V1-C: Close the rest of the query catalog (remaining F-2)
|
|
||||||
|
|
||||||
**Scope:** close the 10 missing queries per Codex's audit. Already-done
|
|
||||||
queries (Q-004/Q-005/Q-006/Q-008/Q-009/Q-011/Q-013/Q-016/Q-017) are
|
|
||||||
verified but not rewritten.
|
|
||||||
- Q-002 (component → parents, inverse of CONTAINS)
|
|
||||||
- Q-003 (subsystem interfaces, Interface as simple string label)
|
|
||||||
- Q-007 (component → constraints via CONSTRAINED_BY)
|
|
||||||
- Q-010 (ValidationClaim → supporting results + AnalysisModel trace)
|
|
||||||
- Q-012 (conflicting results on same claim — exercises V1-0's F-5 hook)
|
|
||||||
- Q-014 (decision log ordered + superseded chain)
|
|
||||||
- Q-018 (`include=superseded` for supersession chains)
|
|
||||||
- Q-019 (Material → components, derived from Component.material field
|
|
||||||
per `engineering-query-catalog.md:266`, no edge needed)
|
|
||||||
- Q-020 (project overview mirror route) — deferred to V1-D where the
|
|
||||||
mirror lands in full.
|
|
||||||
|
|
||||||
**Acceptance:** F-2 ✅ (all 19 of 20 v1-required queries; Q-020 in V1-D).
|
|
||||||
|
|
||||||
**Estimated size:** 2 days (eight new query functions + routes +
|
|
||||||
per-query happy-path tests).
|
|
||||||
|
|
||||||
**Tests added:** ~12.
|
|
||||||
|
|
||||||
**Why fourth:** with the model proven (V1-A) and ingest exercising the
|
|
||||||
write invariants (V1-B), filling in the remaining queries is mechanical.
|
|
||||||
They all sit on top of the same entity store and V1-0 invariants.
|
|
||||||
|
|
||||||
### Phase V1-D: Full Mirror surface (F-6) + determinism golden file (Q-5) + Q-020
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- Split the single `/projects/{project_name}/mirror` route into the three
|
|
||||||
spec routes: `/mirror/{project}/overview` (= Q-020),
|
|
||||||
`/mirror/{project}/decisions`, `/mirror/{project}/subsystems/{subsystem}`.
|
|
||||||
- Add `POST /mirror/{project}/regenerate`.
|
|
||||||
- Move generated files to `/srv/storage/atocore/data/mirror/{project}/`.
|
|
||||||
- **Deterministic output:** inject regenerated timestamp + checksum as
|
|
||||||
renderer inputs (pinned by golden tests), sort every iteration, and
|
|
||||||
remove `dict` / database ordering dependencies. The renderer should
|
|
||||||
not call wall-clock time directly.
|
|
||||||
- `⚠ disputed` markers inline wherever an open conflict touches a
|
|
||||||
rendered field (uses V1-0's F-5 hook output).
|
|
||||||
- `(curated)` annotations where project_state overrides entity state.
|
|
||||||
- Regeneration triggers: synchronous on regenerate, debounced async on
|
|
||||||
entity write (30s window), daily scheduled refresh via existing
|
|
||||||
nightly cron (one new cron line, not a new queue).
|
|
||||||
- `mirror_regeneration_failures` table.
|
|
||||||
- Golden-file test: fixture project state → render → bytes equal.
|
|
||||||
|
|
||||||
**Acceptance:** F-6 ✅, Q-5 ✅, Q-020 ✅, O-4 moves toward ✅.
|
|
||||||
|
|
||||||
**Estimated size:** 3–4 days.
|
|
||||||
|
|
||||||
**Tests added:** ~15.
|
|
||||||
|
|
||||||
**Why fifth:** mirror is a derived consumer. It cannot be correct
|
|
||||||
before the entity store + queries + conflict hooks are correct. It
|
|
||||||
lands after everything it depends on is stable.
|
|
||||||
|
|
||||||
### Phase V1-E: Memory→entity graduation end-to-end (F-7) + remaining Q-4
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- Verify and close F-7 spec gaps:
|
|
||||||
- Add the missing direct `POST /memory/{id}/graduate` route, reusing the
|
|
||||||
same prompt/parser as the batch graduation path.
|
|
||||||
- Keep `/admin/graduation/request` as the bulk lane; direct route is the
|
|
||||||
per-memory acceptance surface.
|
|
||||||
- Preserve current behavior where promote marks source memories
|
|
||||||
`status="graduated"` and sets `graduated_to_entity_id`.
|
|
||||||
- Flow tested for `adaptation` → Decision and `project` → Requirement.
|
|
||||||
- Reconcile the spec's `knowledge` → Fact wording with the actual V1
|
|
||||||
ontology (no `fact` entity type exists today). Prefer doc alignment to
|
|
||||||
an existing typed entity such as `parameter`, rather than adding a vague
|
|
||||||
catch-all `Fact` type late in V1.
|
|
||||||
- Schema is mostly already in place: `graduated` status exists in memory
|
|
||||||
service, `graduated_to_entity_id` column + index exist, and promote
|
|
||||||
preserves the original memory. Remaining work is route surface,
|
|
||||||
ontology/spec reconciliation, and targeted end-to-end tests.
|
|
||||||
- **Q-4 full trust-hierarchy tests**: no auto-write to project_state
|
|
||||||
from any promote path; active-only reinforcement for entities; etc.
|
|
||||||
(The entity-candidates-excluded-from-context test shipped in V1-0.)
|
|
||||||
|
|
||||||
**Acceptance:** F-7 ✅, Q-4 ✅.
|
|
||||||
|
|
||||||
**Estimated size:** 3–4 days.
|
|
||||||
|
|
||||||
**Tests added:** ~8.
|
|
||||||
|
|
||||||
**Why sixth:** graduation touches memory-layer semantics (adds a
|
|
||||||
`graduated` status, flows memory→entity, requires memory-module changes).
|
|
||||||
Doing it after the entity layer is fully invariant-safe + query-complete
|
|
||||||
+ mirror-derived means the memory side only has to deal with one shape:
|
|
||||||
a stable, tested entity layer.
|
|
||||||
|
|
||||||
### Phase V1-F: Full F-5 spec compliance + O-1/O-2/O-3 + D-1/D-3/D-4
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
- **F-5 full spec compliance** (Codex 2026-04-22 audit already confirmed
|
|
||||||
the gap shape — schema is spec-compliant, divergence is in detector +
|
|
||||||
routes only).
|
|
||||||
- **Detector generalization.** Replace the per-type dispatch at
|
|
||||||
`conflicts.py:36` (`_check_component_conflicts`,
|
|
||||||
`_check_requirement_conflicts`) with a slot-key-driven generic
|
|
||||||
detector that reads the per-entity-type conflict slot from a
|
|
||||||
registry and queries the already-generic `conflicts` +
|
|
||||||
`conflict_members` tables. The V1-0 hook shape was chosen to make
|
|
||||||
this a detector-body swap, not an API change.
|
|
||||||
- **Route alignment.** Add `/conflicts/*` routes as the canonical
|
|
||||||
surface per `conflict-model.md:187`. Keep `/admin/conflicts/*` as
|
|
||||||
aliases for one release, deprecate in D-3 release notes, remove
|
|
||||||
in V1.1.
|
|
||||||
- **No schema migration needed** (the tables at `database.py:190`
|
|
||||||
already match the spec).
|
|
||||||
- **O-1:** Run the full migration against a Dalidou backup copy.
|
|
||||||
Confirm additive, idempotent, safe to run twice.
|
|
||||||
- **O-2:** Run a full restore drill on the test project per
|
|
||||||
`docs/backup-restore-procedure.md`. Post-restore, Q-001 returns
|
|
||||||
correct shape. `POST /admin/backup` snapshot includes the new tables.
|
|
||||||
- **O-3:** Manual sanity-check of the three performance bounds.
|
|
||||||
- **D-1:** Write 12 short spec docs under `docs/architecture/entities/`
|
|
||||||
(one per V1 entity type).
|
|
||||||
- **D-3:** Write `docs/v1-release-notes.md`.
|
|
||||||
- **D-4:** Update `master-plan-status.md` and `current-state.md` —
|
|
||||||
move engineering V1 from **Next** to **What Is Real Today**.
|
|
||||||
|
|
||||||
**Acceptance:** F-5 ✅, O-1 ✅, O-2 ✅, O-3 ✅, D-1 ✅, D-3 ✅, D-4 ✅ →
|
|
||||||
**V1 is done.**
|
|
||||||
|
|
||||||
**Estimated size:** 3 days (F-5 migration if needed is the main unknown;
|
|
||||||
D-1 entity docs at ~30 min each ≈ 6 hours; verification is fast).
|
|
||||||
|
|
||||||
**Tests added:** ~6 (F-5 spec-shape tests; verification adds no automated
|
|
||||||
tests).
|
|
||||||
|
|
||||||
### Total (revised after Codex 2026-04-22 audit)
|
|
||||||
|
|
||||||
- Phase budgets: V1-0 (3) + V1-A (1.5) + V1-B (2) + V1-C (2) + V1-D (3-4)
|
|
||||||
+ V1-E (3-4) + V1-F (3) ≈ **17.5–19.5 days of focused work**. This is a
|
|
||||||
realistic engineering-effort estimate, but a single-operator calendar
|
|
||||||
plan should still carry context-switch / soak / review buffer on top.
|
|
||||||
- Adds roughly **60 tests** (533 → ~593).
|
|
||||||
- Branch strategy: one branch per phase (V1-0 → V1-F), each squash-merged
|
|
||||||
to main after Codex review. Phases sequential because each builds on
|
|
||||||
the previous. **V1-0 is a hard prerequisite for all later phases** —
|
|
||||||
nothing starts until V1-0 lands.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Sequencing with the `master-plan-status.md` Now list
|
|
||||||
|
|
||||||
The **Now** list from master-plan-status.md:159-169 is:
|
|
||||||
|
|
||||||
1. Observe the enhanced pipeline (1 week soak — first F4 confidence decay
|
|
||||||
run was 2026-04-19 per Trusted State, so soak window ends ~2026-04-26)
|
|
||||||
2. Knowledge density — batch extract over 234 interactions, target 100+
|
|
||||||
active memories (currently 84)
|
|
||||||
3. Multi-model triage (Phase 11 entry)
|
|
||||||
4. Fix p04-constraints harness failure
|
|
||||||
|
|
||||||
**Principle (revised per Codex review):** V1 work and the Now list are
|
|
||||||
**less disjoint than the first draft claimed**. Real collision points:
|
|
||||||
|
|
||||||
| V1 phase | Collides with Now list at |
|
|
||||||
|---|---|
|
|
||||||
| V1-0 provenance enforcement | memory extractor write path if it shares helper functions; context assembly for the Q-4 partial trust test |
|
|
||||||
| V1-0 F-5 hooks | any write path that creates active rows (limited collision; entity writes are separate from memory writes) |
|
|
||||||
| V1-B KB-CAD/FEM ingest | none on the Now list, but adds an ingest surface that becomes operational burden (ties to O-4 "no new manual ops") |
|
|
||||||
| V1-D mirror regen triggers | scheduling / ops behavior that intersects with "boring and dependable" gate — mirror regen failures become an observable that the pipeline soak must accommodate |
|
|
||||||
| V1-E graduation | memory module (new `graduated` status, memory→entity flow); direct collision with memory extractor + triage |
|
|
||||||
| V1-F F-5 migration | conflicts.py touches the write path shared with memory promotion |
|
|
||||||
|
|
||||||
**Recommended schedule (revised):**
|
|
||||||
|
|
||||||
- **This week (2026-04-22 to 2026-04-26):** Pipeline soak continues.
|
|
||||||
Density batch-extract continues. V1 work **waits** — V1-0 would start
|
|
||||||
touching write paths, which is explicitly something we should not do
|
|
||||||
during a soak window. Density target (100+ active memories) and the
|
|
||||||
pipeline soak complete first.
|
|
||||||
- **Week of 2026-04-27:** If soak is clean and density reached, V1-0
|
|
||||||
starts. V1-0 is a hard prerequisite and cannot be skipped or parallelized.
|
|
||||||
- **Weeks of 2026-05-04 and 2026-05-11:** V1-A through V1-D in order.
|
|
||||||
Multi-model triage work (Now list item 3) continues in parallel only
|
|
||||||
if its touch-surface is triage-path-only (memory side). Any memory
|
|
||||||
extractor change pauses V1-E.
|
|
||||||
- **Week of 2026-05-18 approx:** V1-E (graduation). **This phase must
|
|
||||||
not run in parallel with memory extractor changes** — it directly
|
|
||||||
modifies memory module semantics. Multi-model triage should be settled
|
|
||||||
before V1-E starts.
|
|
||||||
- **Week of 2026-05-25:** V1-F.
|
|
||||||
- **End date target:** ~2026-06-01, four weeks later than the first
|
|
||||||
draft's 2026-05-18 soft target. The shift is deliberate — the first
|
|
||||||
draft's "parallel / disjoint" claim understated the real collisions.
|
|
||||||
|
|
||||||
**Pause points (explicit):**
|
|
||||||
|
|
||||||
- Any Now-list item that regresses the pipeline → V1 pauses immediately.
|
|
||||||
- Memory extractor changes in flight → V1-E pauses until they land and
|
|
||||||
soak.
|
|
||||||
- p04-constraints fix requires retrieval ranking changes → V1 does not
|
|
||||||
pause (retrieval is genuinely disjoint from entities).
|
|
||||||
- Multi-model triage work touching the entity extractor path (if one
|
|
||||||
gets prototyped) → V1-0 pauses until the triage decision settles.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test project
|
|
||||||
|
|
||||||
Per `engineering-v1-acceptance.md:379`, the recommended test bed is
|
|
||||||
**p05-interferometer** — "the optical/structural domain has the cleanest
|
|
||||||
entity model". I agree. Every F-2, F-3, F-6 criterion asserts against this
|
|
||||||
project.
|
|
||||||
|
|
||||||
p06-polisher is the backup test bed if p05 turns out to have data gaps
|
|
||||||
(polisher suite is actively worked and has more content).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What V1 completion does NOT include
|
|
||||||
|
|
||||||
Per the negative list in `engineering-v1-acceptance.md:351-373`, all of the
|
|
||||||
following are **explicitly out of scope** for this plan:
|
|
||||||
|
|
||||||
- LLM extractor for entities (rule-based is V1)
|
|
||||||
- Auto-promotion of candidates (human-only in V1)
|
|
||||||
- Write-back to KB-CAD / KB-FEM
|
|
||||||
- Multi-user auth
|
|
||||||
- Real-time UI (API + Mirror markdown only)
|
|
||||||
- Cross-project rollups
|
|
||||||
- Time-travel queries (Q-015 stays stretch)
|
|
||||||
- Nightly conflict sweep (synchronous only)
|
|
||||||
- Incremental Chroma snapshots
|
|
||||||
- Retention cleanup script
|
|
||||||
- Backup encryption
|
|
||||||
- Off-Dalidou backup target (already exists at clawdbot per ledger, but
|
|
||||||
not a V1 criterion)
|
|
||||||
- **Async job queue / minions pattern** (the rejected plan's centerpiece —
|
|
||||||
explicitly deferred to post-V1 per the negative list)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Open questions for Codex (post-second-round revision)
|
|
||||||
|
|
||||||
Three of the original eight questions (F-1 field audit, F-2 per-query
|
|
||||||
audit, F-5 schema divergence) were answered by Codex's 2026-04-22 audit
|
|
||||||
and folded into the plan. One open question remains; the rest are now
|
|
||||||
resolved in-plan:
|
|
||||||
|
|
||||||
1. **Parallel schedule vs Now list.** The first-round review correctly
|
|
||||||
softened this from "fully parallel" to "less disjoint than claimed".
|
|
||||||
Is the revised collision table + pause-points section enough, or
|
|
||||||
should specific Now-list items gate specific V1 phases more strictly?
|
|
||||||
|
|
||||||
2. **F-7 graduation gap depth.** Resolved by Codex audit. The schema and
|
|
||||||
preserve-original-memory hook are already in place, so V1-E is not a
|
|
||||||
greenfield build. But the direct `/memory/{id}/graduate` route and the
|
|
||||||
ontology/spec mismatch around `knowledge` → `Fact` are still open, so
|
|
||||||
V1-E is closer to **3–4 days** than 2.
|
|
||||||
|
|
||||||
3. **Mirror determinism — where does `now` go?** Resolved. Keep the
|
|
||||||
regenerated timestamp in the rendered output if desired, but pass it
|
|
||||||
into the renderer as an input value. Golden-file tests pin that input;
|
|
||||||
render code must not read the clock directly.
|
|
||||||
|
|
||||||
4. **`project` field naming.** Resolved. Keep the existing `project`
|
|
||||||
field; add the explicit doc note that it is the project identifier for
|
|
||||||
V1 acceptance purposes. No storage rename needed.
|
|
||||||
|
|
||||||
5. **Velocity calibration.** Resolved. **17.5–19.5 focused days** is a
|
|
||||||
fair engineering-effort estimate after the F-7 audit. For an actual
|
|
||||||
operator schedule, keep additional buffer for context switching, soak,
|
|
||||||
and review rounds.
|
|
||||||
|
|
||||||
6. **Minions/queue as V2 item in D-3.** Resolved. Do not name the
|
|
||||||
rejected "Minions" plan in V1 release notes. If D-3 includes a future
|
|
||||||
work section, refer to it neutrally as "queued background processing /
|
|
||||||
async workers" rather than canonizing a V2 codename before V2 is
|
|
||||||
designed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Risks
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
|---|---|
|
|
||||||
| V1 work slows the Now list | V1 pauses on any Now-list blocker. Codex veto on any V1 PR that touches memory extractor, retrieval ranking, or triage paths |
|
|
||||||
| F-5 detector generalization harder than estimated | Codex audit confirmed schema is already spec-compliant; only detector body + routes need work. If detector generalization still slips, keep per-type detectors and document as a V1.1 cleanup (detection correctness is unaffected, only code organization) |
|
|
||||||
| Mirror determinism regresses existing mirror output | Keep `/projects/{project_name}/mirror` alias returning the current shape; new `/mirror/{project}/overview` is the spec-compliant one. Deprecate old in V1 release notes |
|
|
||||||
| Golden file churn as templates evolve | Standard workflow: updating a golden file is a normal part of template work, documented in V1-C commit message |
|
|
||||||
| Backup drill on Dalidou is disruptive | Run against a clone of the Dalidou DB at a safe hour; no production drill required for V1 acceptance |
|
|
||||||
| p05-interferometer data gaps | Fall back to p06-polisher per this plan's test-project section |
|
|
||||||
| Scope creep during V1-A query audit | Any query that isn't in the v1-required set (Q-021 onward) is out of scope, period |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What this plan is **for**
|
|
||||||
|
|
||||||
1. A checklist Claude can follow to close V1.
|
|
||||||
2. A review target for Codex — every phase has explicit acceptance
|
|
||||||
criteria tied to the acceptance doc.
|
|
||||||
3. A communication artifact for Antoine — "here's what's left, here's why,
|
|
||||||
here's the order, here's the risk."
|
|
||||||
|
|
||||||
## What this plan is **not**
|
|
||||||
|
|
||||||
1. Not a commitment to start tomorrow. Pipeline soak + density density
|
|
||||||
come first in parallel; V1-A can start this week only because it's
|
|
||||||
zero-risk additive work.
|
|
||||||
2. Not a rewrite. Every phase builds on existing code.
|
|
||||||
3. Not an ontology debate. The ontology is fixed in
|
|
||||||
`engineering-ontology-v1.md`. Any desire to change it is V2 material.
|
|
||||||
|
|
||||||
## Workspace note (for Codex audit)
|
|
||||||
|
|
||||||
Codex's first-round review (2026-04-22) flagged that
|
|
||||||
`docs/plans/engineering-v1-completion-plan.md` and `DEV-LEDGER.md` were
|
|
||||||
**not visible** in the Playground workspace they were running against,
|
|
||||||
and that `src/atocore/engineering/` appeared empty there.
|
|
||||||
|
|
||||||
The canonical dev workspace for AtoCore is the Windows path
|
|
||||||
`C:\Users\antoi\ATOCore` (per `CLAUDE.md`). The engineering layer code
|
|
||||||
(`src/atocore/engineering/service.py`, `queries.py`, `conflicts.py`,
|
|
||||||
`mirror.py`, `_graduation_prompt.py`, `wiki.py`, `triage_ui.py`) exists
|
|
||||||
there and is what the recent commits (e147ab2, b94f9df, 081c058, 069d155,
|
|
||||||
b1a3dd0) touched. The Windows working tree is what this plan was written
|
|
||||||
against.
|
|
||||||
|
|
||||||
Before the file-level audit:
|
|
||||||
|
|
||||||
1. Confirm which branch / SHA Codex is reviewing. The Windows working
|
|
||||||
tree has uncommitted changes to this plan + DEV-LEDGER as of
|
|
||||||
2026-04-22; commit will be made only after Antoine approves sync.
|
|
||||||
2. If Codex is reviewing `ATOCore-clean` or a Playground snapshot, that
|
|
||||||
tree may lag the canonical dev tree. Sync or re-clone from the
|
|
||||||
Windows working tree / current `origin/main` before per-file audit.
|
|
||||||
3. The three visible-to-Codex file paths for this plan are:
|
|
||||||
- `docs/plans/engineering-v1-completion-plan.md` (this file)
|
|
||||||
- `docs/decisions/2026-04-22-gbrain-plan-rejection.md` (prior decision)
|
|
||||||
- `DEV-LEDGER.md` (Recent Decisions + Session Log entries 2026-04-22)
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- `docs/architecture/engineering-ontology-v1.md`
|
|
||||||
- `docs/architecture/engineering-query-catalog.md`
|
|
||||||
- `docs/architecture/memory-vs-entities.md`
|
|
||||||
- `docs/architecture/engineering-v1-acceptance.md`
|
|
||||||
- `docs/architecture/promotion-rules.md`
|
|
||||||
- `docs/architecture/conflict-model.md`
|
|
||||||
- `docs/architecture/human-mirror-rules.md`
|
|
||||||
- `docs/architecture/tool-handoff-boundaries.md`
|
|
||||||
- `docs/master-plan-status.md` (Now/Next/Later list)
|
|
||||||
- `docs/decisions/2026-04-22-gbrain-plan-rejection.md` (the rejected plan)
|
|
||||||
- `src/atocore/engineering/service.py` (current V1 entity service)
|
|
||||||
- `src/atocore/engineering/queries.py` (current V1 query implementations)
|
|
||||||
- `src/atocore/engineering/conflicts.py` (current conflicts module)
|
|
||||||
- `src/atocore/engineering/mirror.py` (current mirror module)
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
# V1 Completion — Resume State
|
|
||||||
|
|
||||||
**Last updated:** 2026-04-22 (after V1-0 landed + R14 branch pushed)
|
|
||||||
**Purpose:** single-page "you are here" so any future session can pick up
|
|
||||||
the V1 completion sprint without re-reading the full plan history.
|
|
||||||
|
|
||||||
## State of play
|
|
||||||
|
|
||||||
- **V1-0 is DONE.** Merged to main as `2712c5d`, deployed to Dalidou,
|
|
||||||
prod backfill ran cleanly (31 legacy entities flagged
|
|
||||||
`hand_authored=1`, zero violations remaining).
|
|
||||||
- **R14 is on a branch.** `claude/r14-promote-400` at `3888db9` —
|
|
||||||
HTTP promote route returns 400 instead of 500 on V1-0 `ValueError`.
|
|
||||||
Pending Codex review + squash-merge. Non-blocking for V1-A.
|
|
||||||
- **V1-A is next but GATED.** Doesn't start until both gates clear.
|
|
||||||
|
|
||||||
## Start-gates for V1-A
|
|
||||||
|
|
||||||
| Gate | Condition | Status as of 2026-04-22 |
|
|
||||||
|---|---|---|
|
|
||||||
| Soak | Four clean nightly cycles since F4 confidence-decay first real run 2026-04-19 | Day 3 of 4 — expected clear around **2026-04-26** |
|
|
||||||
| Density | 100+ active memories | 84 active as of last ledger update — need +16. Lever: `scripts/batch_llm_extract_live.py` against 234-interaction backlog |
|
|
||||||
|
|
||||||
**When both are green, start V1-A.** If only one is green, hold.
|
|
||||||
|
|
||||||
## Pre-flight checklist when resuming
|
|
||||||
|
|
||||||
Before opening the V1-A branch, run through this in order:
|
|
||||||
|
|
||||||
1. `git checkout main && git pull` — make sure you're at the tip
|
|
||||||
2. Check `DEV-LEDGER.md` **Orientation** for current `live_sha`, `test_count`, `active_memories`
|
|
||||||
3. Check `/health` on Dalidou returns the same `build_sha` as Orientation
|
|
||||||
4. Check the dashboard for pipeline health: http://dalidou:8100/admin/dashboard
|
|
||||||
5. Confirm R14 branch status — either merged or explicitly deferred
|
|
||||||
6. Re-read the two core plan docs:
|
|
||||||
- `docs/plans/engineering-v1-completion-plan.md` — the full 7-phase plan
|
|
||||||
- `docs/architecture/engineering-v1-acceptance.md` — the acceptance contract
|
|
||||||
7. Skim the relevant spec docs for the phase you're about to start:
|
|
||||||
- V1-A: `engineering-query-catalog.md` (Q-001 + Q-006/Q-009/Q-011 killer queries)
|
|
||||||
- V1-B: `tool-handoff-boundaries.md` (KB-CAD/KB-FEM export shapes)
|
|
||||||
- V1-C: `engineering-query-catalog.md` (all remaining v1-required queries)
|
|
||||||
- V1-D: `human-mirror-rules.md` (mirror spec end-to-end)
|
|
||||||
- V1-E: `memory-vs-entities.md` (graduation flow)
|
|
||||||
- V1-F: `conflict-model.md` (generic slot-key detector)
|
|
||||||
|
|
||||||
## What V1-A looks like when started
|
|
||||||
|
|
||||||
**Branch:** `claude/v1-a-pillar-queries`
|
|
||||||
|
|
||||||
**Scope (~1.5 days):**
|
|
||||||
- **Q-001 shape fix.** Add a subsystem-scoped variant of `system_map()`
|
|
||||||
matching `GET /entities/Subsystem/<id>?expand=contains` per
|
|
||||||
`engineering-query-catalog.md:71`. The project-wide version stays
|
|
||||||
(it serves Q-004).
|
|
||||||
- **Q-6 integration test.** Seed p05-interferometer with five cases:
|
|
||||||
1 satisfying Component, 1 orphan Requirement, 1 Decision on flagged
|
|
||||||
Assumption, 1 supported ValidationClaim, 1 unsupported ValidationClaim.
|
|
||||||
One test asserting Q-006 / Q-009 / Q-011 return exactly the expected
|
|
||||||
members.
|
|
||||||
- The four "pillar" queries (Q-001, Q-005, Q-006, Q-017) already work
|
|
||||||
per Codex's 2026-04-22 audit. V1-A does NOT re-implement them —
|
|
||||||
V1-A verifies them on seeded data.
|
|
||||||
|
|
||||||
**Acceptance:** Q-001 subsystem-scoped variant + Q-6 integration test both
|
|
||||||
green. F-2 moves from 🟡 partial to slightly-less-partial.
|
|
||||||
|
|
||||||
**Estimated tests added:** ~4 (not ~12 — V1-A scope shrank after Codex
|
|
||||||
confirmed most queries already work).
|
|
||||||
|
|
||||||
## Map of the remaining phases
|
|
||||||
|
|
||||||
```
|
|
||||||
V1-0 ✅ write-time invariants landed 2026-04-22 (2712c5d)
|
|
||||||
↓
|
|
||||||
V1-A 🟡 minimum query slice gated on soak + density (~1.5d when started)
|
|
||||||
↓
|
|
||||||
V1-B KB-CAD/KB-FEM ingest + D-2 ~2d
|
|
||||||
↓
|
|
||||||
V1-C close 8 remaining queries ~2d
|
|
||||||
↓
|
|
||||||
V1-D full mirror + determinism ~3-4d (biggest phase)
|
|
||||||
↓
|
|
||||||
V1-E graduation + trust tests ~3-4d (pauses for multi-model triage)
|
|
||||||
↓
|
|
||||||
V1-F F-5 generalization + ops + docs ~3d — V1 done
|
|
||||||
```
|
|
||||||
|
|
||||||
## Parallel work that can run WITHOUT touching V1
|
|
||||||
|
|
||||||
These are genuinely disjoint surfaces; pick any of them during the gate
|
|
||||||
pause or as scheduling allows:
|
|
||||||
|
|
||||||
- **Density batch-extract** — *required* to unblock V1-A. Not optional.
|
|
||||||
- **p04-constraints harness fix** — retrieval-ranking change, fully
|
|
||||||
disjoint from entities. Safe to do anywhere in the V1 track.
|
|
||||||
- **Multi-model triage (Phase 11 entry)** — memory-side work, disjoint
|
|
||||||
from V1-A/B/C/D. **Pause before V1-E starts** because V1-E touches
|
|
||||||
memory module semantics.
|
|
||||||
|
|
||||||
## What NOT to do
|
|
||||||
|
|
||||||
- Don't start V1-A until both gates are green.
|
|
||||||
- Don't touch the memory extractor write path while V1-E is open.
|
|
||||||
- Don't name the rejected "Minions" plan in any doc — neutral wording
|
|
||||||
only ("queued background processing / async workers") per Codex
|
|
||||||
sign-off.
|
|
||||||
- Don't rename the `project` field to `project_id` — Codex + Antoine
|
|
||||||
agreed it stays as `project`, with a doc note in
|
|
||||||
`engineering-ontology-v1.md` that this IS the project_id per spec.
|
|
||||||
|
|
||||||
## Open review findings
|
|
||||||
|
|
||||||
| id | severity | summary | status |
|
|
||||||
|---|---|---|---|
|
|
||||||
| R14 | P2 | `POST /entities/{id}/promote` returns 500 on V1-0 `ValueError` instead of 400 | fixed on branch `claude/r14-promote-400`, pending Codex review |
|
|
||||||
|
|
||||||
Closed V1-0 findings: P1 "promote path allows provenance-less legacy
|
|
||||||
candidates" (service.py:365-379), P1 "supersede path missing F-5 hook"
|
|
||||||
(service.py:581-591), P2 "`--invalidate-instead` backfill too broad"
|
|
||||||
(v1_0_backfill_provenance.py:52-63). All three patched and approved in
|
|
||||||
the squash-merge to `2712c5d`.
|
|
||||||
|
|
||||||
## How agreement between Claude + Codex has worked so far
|
|
||||||
|
|
||||||
Three review rounds before V1-0 started + three during implementation:
|
|
||||||
|
|
||||||
1. **Rejection round.** Claude drafted a gbrain-inspired "Phase 8
|
|
||||||
Minions + typed edges" plan; Codex rejected as wrong-packaging.
|
|
||||||
Record: `docs/decisions/2026-04-22-gbrain-plan-rejection.md`.
|
|
||||||
2. **Completion-plan rewrite.** Claude rewrote against
|
|
||||||
`engineering-v1-acceptance.md`. Codex first-round review fixed the
|
|
||||||
phase order (provenance-first).
|
|
||||||
3. **Per-file audit.** Codex's second-round audit found F-1 / F-2 /
|
|
||||||
F-5 gaps, all folded in.
|
|
||||||
4. **Sign-off round.** Codex's third-round review resolved the five
|
|
||||||
remaining open questions inline and signed off: *"with those edits,
|
|
||||||
I'd sign off on the five questions."*
|
|
||||||
5. **V1-0 review.** Codex found two P1 gaps (promote re-check missing,
|
|
||||||
supersede hook missing) + one P2 (backfill scope too broad). All
|
|
||||||
three patched. Codex re-ran probes + regression suites, approved,
|
|
||||||
squash-merged.
|
|
||||||
6. **V1-0 deploy + prod backfill.** Codex deployed + ran backfill,
|
|
||||||
logged R14 as P2 residual.
|
|
||||||
|
|
||||||
Protocol has been: Claude writes, Codex audits, human Antoine ratifies.
|
|
||||||
Continue this for V1-A onward.
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- `docs/plans/engineering-v1-completion-plan.md` — full 7-phase plan
|
|
||||||
- `docs/decisions/2026-04-22-gbrain-plan-rejection.md` — prior rejection
|
|
||||||
- `docs/architecture/engineering-ontology-v1.md` — V1 ontology (18 predicates)
|
|
||||||
- `docs/architecture/engineering-query-catalog.md` — Q-001 through Q-020 spec
|
|
||||||
- `docs/architecture/engineering-v1-acceptance.md` — F/Q/O/D acceptance table
|
|
||||||
- `docs/architecture/promotion-rules.md` — candidate → active flow
|
|
||||||
- `docs/architecture/conflict-model.md` — F-5 spec
|
|
||||||
- `docs/architecture/human-mirror-rules.md` — V1-D spec
|
|
||||||
- `docs/architecture/memory-vs-entities.md` — V1-E spec
|
|
||||||
- `docs/architecture/tool-handoff-boundaries.md` — V1-B KB-CAD/KB-FEM
|
|
||||||
- `docs/master-plan-status.md` — Now / Active / Next / Later
|
|
||||||
- `DEV-LEDGER.md` — Orientation + Open Review Findings + Session Log
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
# Wiki Reorg Plan — Human-Readable Navigation of AtoCore State
|
|
||||||
|
|
||||||
> **SUPERSEDED — 2026-04-22.** Do not implement this plan. It has been
|
|
||||||
> replaced by the read-only operator orientation work at
|
|
||||||
> `docs/plans/operator-orientation-plan.md` (in the `ATOCore-clean`
|
|
||||||
> workspace). The successor reframes the problem as orientation over
|
|
||||||
> existing APIs and docs rather than a new `/wiki` surface, and drops
|
|
||||||
> interaction browser / memory index / project-page restructure from
|
|
||||||
> scope. Nothing below should be picked up as a work item. Kept in tree
|
|
||||||
> for context only.
|
|
||||||
|
|
||||||
**Date:** 2026-04-22
|
|
||||||
**Author:** Claude (after walking the live wiki at `http://dalidou:8100/wiki`
|
|
||||||
and reading `src/atocore/engineering/wiki.py` + `/wiki/*` routes in
|
|
||||||
`src/atocore/api/routes.py`)
|
|
||||||
**Status:** SUPERSEDED (see banner above). Previously: Draft, pending Codex review
|
|
||||||
**Scope boundary:** This plan does NOT touch Inbox / Global / Emerging.
|
|
||||||
Those three surfaces stay exactly as they are today — the registration
|
|
||||||
flow and scope semantics around them are load-bearing and out of scope
|
|
||||||
for this reorg.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Position
|
|
||||||
|
|
||||||
The wiki is Layer 3 of the engineering architecture (Human Mirror —
|
|
||||||
see `docs/architecture/engineering-knowledge-hybrid-architecture.md`).
|
|
||||||
It is a **derived view** over the same database the LLM reads via
|
|
||||||
`atocore_context` / `atocore_search`. There is no parallel store; if a
|
|
||||||
fact is in the DB it is reachable from the wiki.
|
|
||||||
|
|
||||||
The user-facing problem is not representation, it is **findability and
|
|
||||||
structure**. Content the operator produced (proposals, decisions,
|
|
||||||
constraints, captured conversations) is in the DB but is not reachable
|
|
||||||
along a natural human reading path. Today the only routes to it are:
|
|
||||||
|
|
||||||
- typing the right keyword into `/wiki/search`
|
|
||||||
- remembering which project it lived under and scrolling the auto-
|
|
||||||
generated mirror markdown on `/wiki/projects/{id}`
|
|
||||||
- the homepage "What the brain is doing" strip, which shows counts of
|
|
||||||
audit actions but no content
|
|
||||||
|
|
||||||
There is no "recent conversations" view, no memory index, no way to
|
|
||||||
browse by memory type (decision vs preference vs episodic), and the
|
|
||||||
topnav only exposes Home / Activity / Triage / Dashboard.
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Make the wiki a surface the operator actually uses to answer three
|
|
||||||
recurring questions:
|
|
||||||
|
|
||||||
1. **"Where did I say / decide / propose X?"** — find a memory or
|
|
||||||
interaction by topic, not by exact-string search.
|
|
||||||
2. **"What is the current state of project Y?"** — read decisions,
|
|
||||||
constraints, and open questions as distinct sections, not as one
|
|
||||||
wall of auto-generated markdown.
|
|
||||||
3. **"What happened in the system recently, and what does it mean?"**
|
|
||||||
— a human-meaningful recent feed (captures, promotions, decisions
|
|
||||||
recorded), not a count of audit-action names.
|
|
||||||
|
|
||||||
Success criterion: for each of the three questions above, a first-time
|
|
||||||
visitor reaches a useful answer in **≤ 2 clicks from `/wiki`**.
|
|
||||||
|
|
||||||
## Non-goals
|
|
||||||
|
|
||||||
- No schema changes. All data already exists.
|
|
||||||
- No change to ingestion, extraction, promotion, or the trust rules.
|
|
||||||
- No change to Inbox / Global / Emerging surfaces or semantics.
|
|
||||||
- No change to `/admin/triage` or `/admin/dashboard`.
|
|
||||||
- No change to the `atocore_context` / `atocore_search` LLM-facing APIs.
|
|
||||||
- No new storage. Every new page is a new render function over existing
|
|
||||||
service-layer calls.
|
|
||||||
|
|
||||||
## What it will do
|
|
||||||
|
|
||||||
Five additions, in priority order. Each is independently mergeable and
|
|
||||||
independently revertable.
|
|
||||||
|
|
||||||
### W-1 — Interaction browser: `/wiki/interactions`
|
|
||||||
|
|
||||||
The DB holds 234 captured interactions (per ledger `2026-04-22`). None
|
|
||||||
are readable in the wiki. Add a paginated list view and a detail view.
|
|
||||||
|
|
||||||
- `/wiki/interactions?project=&client=&since=` — list, newest first,
|
|
||||||
showing timestamp, client (claude-code / openclaw / test), inferred
|
|
||||||
project, and the first ~160 chars of the user prompt.
|
|
||||||
- `/wiki/interactions/{id}` — full prompt + response, plus any
|
|
||||||
candidate / active memories extracted from it (back-link from the
|
|
||||||
memory → interaction relation if present, else a "no extractions"
|
|
||||||
note).
|
|
||||||
- Filters: project (multi), client, date range, "has extractions" boolean.
|
|
||||||
|
|
||||||
Answers Q1 ("where did I say X") by giving the user a time-ordered
|
|
||||||
stream of their own conversations, not just extracted summaries.
|
|
||||||
|
|
||||||
### W-2 — Memory index: `/wiki/memories`
|
|
||||||
|
|
||||||
Today `/wiki/memories/{id}` exists but there is no list view. Add one.
|
|
||||||
|
|
||||||
- `/wiki/memories?project=&type=&status=&tag=` — filterable list.
|
|
||||||
Status defaults to `active`. Types: decision, preference, episodic,
|
|
||||||
knowledge, identity, adaptation.
|
|
||||||
- Faceted counts in the sidebar (e.g. "decision: 14 · preference: 7").
|
|
||||||
- Each row links to `/wiki/memories/{id}` and, where present, the
|
|
||||||
originating interaction.
|
|
||||||
|
|
||||||
### W-3 — Project page restructure (tabbed, not flat)
|
|
||||||
|
|
||||||
`/wiki/projects/{id}` today renders the full mirror markdown in one
|
|
||||||
scroll. Restructure to tabs (or anchored sections, same thing
|
|
||||||
semantically) over the data already produced by
|
|
||||||
`generate_project_overview`:
|
|
||||||
|
|
||||||
- **Overview** — stage, client, type, description, headline state.
|
|
||||||
- **Decisions** — memories of type `decision` for this project.
|
|
||||||
- **Constraints** — project_state entries in the `constraints` category.
|
|
||||||
- **Proposals** — memories tagged `proposal` or type `preference` with
|
|
||||||
proposal semantics (exact filter TBD during W-3; see Open Questions).
|
|
||||||
- **Entities** — current list, already rendered inline today.
|
|
||||||
- **Memories** — all memories, reusing the W-2 list component filtered
|
|
||||||
to this project.
|
|
||||||
- **Timeline** — interactions for this project, reusing W-1 filtered
|
|
||||||
to this project.
|
|
||||||
|
|
||||||
No new data is produced; this is purely a slicing change.
|
|
||||||
|
|
||||||
### W-4 — Recent feed on homepage
|
|
||||||
|
|
||||||
Replace the current "What the brain is doing" strip (which shows audit
|
|
||||||
action counts) with a **content** feed: the last N events that a human
|
|
||||||
cares about —
|
|
||||||
|
|
||||||
- new active memory (not candidate)
|
|
||||||
- memory promoted candidate → active
|
|
||||||
- state entry added / changed
|
|
||||||
- new registered project
|
|
||||||
- interaction captured (optional, may be noisy — gate behind a toggle)
|
|
||||||
|
|
||||||
Each feed row links to the thing it describes. The audit-count strip
|
|
||||||
can move to `/wiki/activity` where the full audit timeline already lives.
|
|
||||||
|
|
||||||
### W-5 — Topnav exposure
|
|
||||||
|
|
||||||
Surface the new pages in the topnav so they are discoverable:
|
|
||||||
|
|
||||||
Current: `🏠 Home · 📡 Activity · 🔀 Triage · 📊 Dashboard`
|
|
||||||
Proposed: `🏠 Home · 💬 Interactions · 🧠 Memories · 📡 Activity · 🔀 Triage · 📊 Dashboard`
|
|
||||||
|
|
||||||
Domain pages (`/wiki/domains/{tag}`) stay reachable via tag chips on
|
|
||||||
memory rows, as today — no new topnav entry for them.
|
|
||||||
|
|
||||||
## Outcome
|
|
||||||
|
|
||||||
- Every captured interaction is readable in the wiki, with a clear
|
|
||||||
path from interaction → extracted memories and back.
|
|
||||||
- Memories are browsable without knowing a keyword in advance.
|
|
||||||
- A project page separates decisions, constraints, and proposals
|
|
||||||
instead of interleaving them in auto-generated markdown.
|
|
||||||
- The homepage tells the operator what **content** is new, not which
|
|
||||||
audit action counters incremented.
|
|
||||||
- Nothing in the Inbox / Global / Emerging flow changes.
|
|
||||||
- Nothing the LLM reads changes.
|
|
||||||
|
|
||||||
## Non-outcomes (explicitly)
|
|
||||||
|
|
||||||
- This plan does not improve retrieval quality. It does not touch the
|
|
||||||
extractor, ranking, or harness.
|
|
||||||
- This plan does not change what is captured or when.
|
|
||||||
- This plan does not replace `/admin/triage`. Candidate review stays there.
|
|
||||||
|
|
||||||
## Sequencing
|
|
||||||
|
|
||||||
1. **W-1 first.** Interaction browser is the biggest findability win
|
|
||||||
and has no coupling to memory code.
|
|
||||||
2. **W-2 next.** Memory index reuses list/filter infra from W-1.
|
|
||||||
3. **W-3** depends on W-2 (reuses the memory list component).
|
|
||||||
4. **W-4** is independent; can land any time after W-1.
|
|
||||||
5. **W-5** lands last so topnav only exposes pages that exist.
|
|
||||||
|
|
||||||
Each step ships with at least one render test (HTML contains expected
|
|
||||||
anchors / row counts against a seeded fixture), following the pattern
|
|
||||||
already in `tests/engineering/` for existing wiki renders.
|
|
||||||
|
|
||||||
## Risk & reversibility
|
|
||||||
|
|
||||||
- All additions are new routes / new render functions. Reverting any
|
|
||||||
step is a file delete + a topnav edit.
|
|
||||||
- Project page restructure (W-3) is the only edit to an existing
|
|
||||||
surface. Keep the flat-markdown render behind a query flag
|
|
||||||
(`?layout=flat`) for one release so regressions are observable.
|
|
||||||
- No DB migrations. No service-layer signatures change.
|
|
||||||
|
|
||||||
## Open questions for Codex
|
|
||||||
|
|
||||||
1. **"Proposal" as a first-class filter** (W-3) — is this a memory type,
|
|
||||||
a domain tag, a structural field we should add, or should it stay
|
|
||||||
derived by filter? Current DB has no explicit proposal type; we'd be
|
|
||||||
inferring from tags/content. If that inference is unreliable, W-3's
|
|
||||||
Proposals tab becomes noise.
|
|
||||||
2. **Interaction → memory back-link** (W-1) — does the current schema
|
|
||||||
already record which interaction an extracted memory came from? If
|
|
||||||
not, is exposing that link in the wiki worth a schema addition, or
|
|
||||||
should W-1 ship without it?
|
|
||||||
3. **Recent feed noise floor** (W-4) — should every captured
|
|
||||||
interaction appear in the feed, or only interactions that produced
|
|
||||||
at least one candidate memory? The former is complete but may drown
|
|
||||||
out signal at current capture rates (~10/day).
|
|
||||||
4. **Ordering vs V1 Completion track** — should any of this land before
|
|
||||||
V1-A (currently gated on soak ~2026-04-26 + density 100+), or is it
|
|
||||||
strictly after V1 closes?
|
|
||||||
|
|
||||||
## Workspace note
|
|
||||||
|
|
||||||
Canonical dev workspace is `C:\Users\antoi\ATOCore` (per `CLAUDE.md`).
|
|
||||||
Any Codex audit of this plan should sync from `origin/main` at or after
|
|
||||||
`2712c5d` before reviewing.
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
"""V1-0 one-time backfill: flag existing active entities that have no
|
|
||||||
provenance (empty source_refs) as hand_authored=1 so they stop failing
|
|
||||||
the F-8 invariant.
|
|
||||||
|
|
||||||
Runs against the live AtoCore DB. Idempotent: a second run after the
|
|
||||||
first touches nothing because the flagged rows already have
|
|
||||||
hand_authored=1.
|
|
||||||
|
|
||||||
Per the Engineering V1 Completion Plan (V1-0 scope), the three options
|
|
||||||
for an existing active entity without provenance are:
|
|
||||||
|
|
||||||
1. Attach provenance — impossible without human review, not automatable
|
|
||||||
2. Flag hand-authored — safe, additive, this script's default
|
|
||||||
3. Invalidate — destructive, requires operator sign-off
|
|
||||||
|
|
||||||
This script picks option (2) by default. Add --dry-run to see what
|
|
||||||
would change without writing. Add --invalidate-instead to pick option
|
|
||||||
(3) for all rows (not recommended for first run).
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python scripts/v1_0_backfill_provenance.py --base-url http://dalidou:8100 --dry-run
|
|
||||||
python scripts/v1_0_backfill_provenance.py --base-url http://dalidou:8100
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import sqlite3
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
def run(db_path: Path, dry_run: bool, invalidate_instead: bool) -> int:
|
|
||||||
if not db_path.exists():
|
|
||||||
print(f"ERROR: db not found: {db_path}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
conn = sqlite3.connect(str(db_path))
|
|
||||||
conn.row_factory = sqlite3.Row
|
|
||||||
|
|
||||||
# Verify the V1-0 migration ran: if hand_authored column is missing
|
|
||||||
# the operator hasn't deployed V1-0 yet, and running this script
|
|
||||||
# would crash. Fail loud rather than attempt the ALTER here.
|
|
||||||
cols = {r["name"] for r in conn.execute("PRAGMA table_info(entities)").fetchall()}
|
|
||||||
if "hand_authored" not in cols:
|
|
||||||
print(
|
|
||||||
"ERROR: entities table lacks the hand_authored column. "
|
|
||||||
"Deploy V1-0 migrations first (init_db + init_engineering_schema).",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
# Scope differs by mode:
|
|
||||||
# - Default (flag hand_authored=1): safe/additive, applies to active
|
|
||||||
# AND superseded rows so the historical trail is consistent.
|
|
||||||
# - --invalidate-instead: destructive — scope to ACTIVE rows only.
|
|
||||||
# Invalidating already-superseded history would collapse the audit
|
|
||||||
# trail, which the plan's remediation scope never intended
|
|
||||||
# (V1-0 talks about existing active no-provenance entities).
|
|
||||||
if invalidate_instead:
|
|
||||||
scope_sql = "status = 'active'"
|
|
||||||
else:
|
|
||||||
scope_sql = "status IN ('active', 'superseded')"
|
|
||||||
rows = conn.execute(
|
|
||||||
f"SELECT id, entity_type, name, project, status, source_refs, hand_authored "
|
|
||||||
f"FROM entities WHERE {scope_sql} AND hand_authored = 0"
|
|
||||||
).fetchall()
|
|
||||||
|
|
||||||
needs_fix = []
|
|
||||||
for row in rows:
|
|
||||||
refs_raw = row["source_refs"] or "[]"
|
|
||||||
try:
|
|
||||||
refs = json.loads(refs_raw)
|
|
||||||
except Exception:
|
|
||||||
refs = []
|
|
||||||
if not refs:
|
|
||||||
needs_fix.append(row)
|
|
||||||
|
|
||||||
print(f"found {len(needs_fix)} active/superseded entities with no provenance")
|
|
||||||
for row in needs_fix:
|
|
||||||
print(
|
|
||||||
f" - {row['id'][:8]} [{row['entity_type']}] "
|
|
||||||
f"{row['name']!r} project={row['project']!r} status={row['status']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if dry_run:
|
|
||||||
print("--dry-run: no changes written")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if not needs_fix:
|
|
||||||
print("nothing to do")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
action = "invalidate" if invalidate_instead else "flag hand_authored=1"
|
|
||||||
print(f"applying: {action}")
|
|
||||||
|
|
||||||
cur = conn.cursor()
|
|
||||||
for row in needs_fix:
|
|
||||||
if invalidate_instead:
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE entities SET status = 'invalid', "
|
|
||||||
"updated_at = CURRENT_TIMESTAMP WHERE id = ?",
|
|
||||||
(row["id"],),
|
|
||||||
)
|
|
||||||
cur.execute(
|
|
||||||
"INSERT INTO memory_audit "
|
|
||||||
"(id, memory_id, action, actor, before_json, after_json, note, entity_kind) "
|
|
||||||
"VALUES (?, ?, 'invalidated', 'v1_0_backfill', ?, ?, ?, 'entity')",
|
|
||||||
(
|
|
||||||
f"v10bf-{row['id'][:8]}-inv",
|
|
||||||
row["id"],
|
|
||||||
json.dumps({"status": row["status"]}),
|
|
||||||
json.dumps({"status": "invalid"}),
|
|
||||||
"V1-0 backfill: invalidated, no provenance",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
cur.execute(
|
|
||||||
"UPDATE entities SET hand_authored = 1, "
|
|
||||||
"updated_at = CURRENT_TIMESTAMP WHERE id = ?",
|
|
||||||
(row["id"],),
|
|
||||||
)
|
|
||||||
cur.execute(
|
|
||||||
"INSERT INTO memory_audit "
|
|
||||||
"(id, memory_id, action, actor, before_json, after_json, note, entity_kind) "
|
|
||||||
"VALUES (?, ?, 'hand_authored_flagged', 'v1_0_backfill', ?, ?, ?, 'entity')",
|
|
||||||
(
|
|
||||||
f"v10bf-{row['id'][:8]}-ha",
|
|
||||||
row["id"],
|
|
||||||
json.dumps({"hand_authored": False}),
|
|
||||||
json.dumps({"hand_authored": True}),
|
|
||||||
"V1-0 backfill: flagged hand_authored since source_refs empty",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
conn.commit()
|
|
||||||
print(f"done: updated {len(needs_fix)} entities")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
|
||||||
parser.add_argument(
|
|
||||||
"--db",
|
|
||||||
type=Path,
|
|
||||||
default=Path("data/db/atocore.db"),
|
|
||||||
help="Path to the SQLite database (default: data/db/atocore.db)",
|
|
||||||
)
|
|
||||||
parser.add_argument("--dry-run", action="store_true", help="Report only; no writes")
|
|
||||||
parser.add_argument(
|
|
||||||
"--invalidate-instead",
|
|
||||||
action="store_true",
|
|
||||||
help=(
|
|
||||||
"DESTRUCTIVE. Invalidate active rows with no provenance instead "
|
|
||||||
"of flagging them hand_authored. Scoped to status='active' only "
|
|
||||||
"(superseded rows are left alone to preserve audit history). "
|
|
||||||
"Not recommended for first run — start with --dry-run, then "
|
|
||||||
"the default hand_authored flag path."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
args = parser.parse_args()
|
|
||||||
return run(args.db, args.dry_run, args.invalidate_instead)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
@@ -1457,11 +1457,6 @@ class EntityCreateRequest(BaseModel):
|
|||||||
status: str = "active"
|
status: str = "active"
|
||||||
confidence: float = 1.0
|
confidence: float = 1.0
|
||||||
source_refs: list[str] | None = None
|
source_refs: list[str] | None = None
|
||||||
# V1-0 provenance enforcement (F-8). Clients must either pass
|
|
||||||
# non-empty source_refs or set hand_authored=true. The service layer
|
|
||||||
# raises ValueError otherwise, surfaced here as 400.
|
|
||||||
hand_authored: bool = False
|
|
||||||
extractor_version: str | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class EntityPromoteRequest(BaseModel):
|
class EntityPromoteRequest(BaseModel):
|
||||||
@@ -1491,8 +1486,6 @@ def api_create_entity(req: EntityCreateRequest) -> dict:
|
|||||||
confidence=req.confidence,
|
confidence=req.confidence,
|
||||||
source_refs=req.source_refs,
|
source_refs=req.source_refs,
|
||||||
actor="api-http",
|
actor="api-http",
|
||||||
hand_authored=req.hand_authored,
|
|
||||||
extractor_version=req.extractor_version,
|
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
@@ -2187,17 +2180,12 @@ def api_promote_entity(
|
|||||||
from atocore.engineering.service import promote_entity
|
from atocore.engineering.service import promote_entity
|
||||||
target_project = req.target_project if req is not None else None
|
target_project = req.target_project if req is not None else None
|
||||||
note = req.note if req is not None else ""
|
note = req.note if req is not None else ""
|
||||||
try:
|
|
||||||
success = promote_entity(
|
success = promote_entity(
|
||||||
entity_id,
|
entity_id,
|
||||||
actor="api-http",
|
actor="api-http",
|
||||||
note=note,
|
note=note,
|
||||||
target_project=target_project,
|
target_project=target_project,
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
|
||||||
# V1-0 F-8 re-check raises ValueError for no-provenance candidates
|
|
||||||
# (see service.promote_entity). Surface as 400, not 500.
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
if not success:
|
if not success:
|
||||||
raise HTTPException(status_code=404, detail=f"Entity not found or not a candidate: {entity_id}")
|
raise HTTPException(status_code=404, detail=f"Entity not found or not a candidate: {entity_id}")
|
||||||
result = {"status": "promoted", "id": entity_id}
|
result = {"status": "promoted", "id": entity_id}
|
||||||
|
|||||||
@@ -63,12 +63,6 @@ RELATIONSHIP_TYPES = [
|
|||||||
|
|
||||||
ENTITY_STATUSES = ["candidate", "active", "superseded", "invalid"]
|
ENTITY_STATUSES = ["candidate", "active", "superseded", "invalid"]
|
||||||
|
|
||||||
# V1-0: extractor version this module writes into new entity rows.
|
|
||||||
# Per promotion-rules.md:268, every candidate must record the version of
|
|
||||||
# the extractor that produced it so later re-evaluation is auditable.
|
|
||||||
# Bump this when extraction logic materially changes.
|
|
||||||
EXTRACTOR_VERSION = "v1.0.0"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Entity:
|
class Entity:
|
||||||
@@ -83,10 +77,6 @@ class Entity:
|
|||||||
source_refs: list[str] = field(default_factory=list)
|
source_refs: list[str] = field(default_factory=list)
|
||||||
created_at: str = ""
|
created_at: str = ""
|
||||||
updated_at: str = ""
|
updated_at: str = ""
|
||||||
# V1-0 shared-header fields per engineering-v1-acceptance.md:45.
|
|
||||||
extractor_version: str = ""
|
|
||||||
canonical_home: str = "entity"
|
|
||||||
hand_authored: bool = False
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -113,25 +103,10 @@ def init_engineering_schema() -> None:
|
|||||||
status TEXT NOT NULL DEFAULT 'active',
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
confidence REAL NOT NULL DEFAULT 1.0,
|
confidence REAL NOT NULL DEFAULT 1.0,
|
||||||
source_refs TEXT NOT NULL DEFAULT '[]',
|
source_refs TEXT NOT NULL DEFAULT '[]',
|
||||||
extractor_version TEXT NOT NULL DEFAULT '',
|
|
||||||
canonical_home TEXT NOT NULL DEFAULT 'entity',
|
|
||||||
hand_authored INTEGER NOT NULL DEFAULT 0,
|
|
||||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
# V1-0 (Engineering V1 completion): the three shared-header fields
|
|
||||||
# per engineering-v1-acceptance.md:45. Idempotent ALTERs for
|
|
||||||
# databases created before V1-0 land these columns without a full
|
|
||||||
# migration. Fresh DBs get them via the CREATE TABLE above; the
|
|
||||||
# ALTERs below are a no-op there.
|
|
||||||
from atocore.models.database import _column_exists # late import; avoids cycle
|
|
||||||
if not _column_exists(conn, "entities", "extractor_version"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN extractor_version TEXT DEFAULT ''")
|
|
||||||
if not _column_exists(conn, "entities", "canonical_home"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN canonical_home TEXT DEFAULT 'entity'")
|
|
||||||
if not _column_exists(conn, "entities", "hand_authored"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN hand_authored INTEGER DEFAULT 0")
|
|
||||||
conn.execute("""
|
conn.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS relationships (
|
CREATE TABLE IF NOT EXISTS relationships (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
@@ -174,8 +149,6 @@ def create_entity(
|
|||||||
confidence: float = 1.0,
|
confidence: float = 1.0,
|
||||||
source_refs: list[str] | None = None,
|
source_refs: list[str] | None = None,
|
||||||
actor: str = "api",
|
actor: str = "api",
|
||||||
hand_authored: bool = False,
|
|
||||||
extractor_version: str | None = None,
|
|
||||||
) -> Entity:
|
) -> Entity:
|
||||||
if entity_type not in ENTITY_TYPES:
|
if entity_type not in ENTITY_TYPES:
|
||||||
raise ValueError(f"Invalid entity type: {entity_type}. Must be one of {ENTITY_TYPES}")
|
raise ValueError(f"Invalid entity type: {entity_type}. Must be one of {ENTITY_TYPES}")
|
||||||
@@ -184,21 +157,6 @@ def create_entity(
|
|||||||
if not name or not name.strip():
|
if not name or not name.strip():
|
||||||
raise ValueError("Entity name must be non-empty")
|
raise ValueError("Entity name must be non-empty")
|
||||||
|
|
||||||
refs = list(source_refs) if source_refs else []
|
|
||||||
|
|
||||||
# V1-0 (F-8 provenance enforcement, engineering-v1-acceptance.md:147):
|
|
||||||
# every new entity row must carry non-empty source_refs OR be explicitly
|
|
||||||
# flagged hand_authored. This is the non-negotiable invariant every
|
|
||||||
# later V1 phase depends on — without it, active entities can escape
|
|
||||||
# into the graph with no traceable origin. Raises at the write seam so
|
|
||||||
# the bug is impossible to introduce silently.
|
|
||||||
if not refs and not hand_authored:
|
|
||||||
raise ValueError(
|
|
||||||
"source_refs required: every entity must carry provenance "
|
|
||||||
"(source_chunk_id / source_interaction_id / kb_cad_export_id / ...) "
|
|
||||||
"or set hand_authored=True to explicitly flag a direct human write"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Phase 5: enforce project canonicalization contract at the write seam.
|
# Phase 5: enforce project canonicalization contract at the write seam.
|
||||||
# Aliases like "p04" become "p04-gigabit" so downstream reads stay
|
# Aliases like "p04" become "p04-gigabit" so downstream reads stay
|
||||||
# consistent with the registry.
|
# consistent with the registry.
|
||||||
@@ -207,22 +165,18 @@ def create_entity(
|
|||||||
entity_id = str(uuid.uuid4())
|
entity_id = str(uuid.uuid4())
|
||||||
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
||||||
props = properties or {}
|
props = properties or {}
|
||||||
ev = extractor_version if extractor_version is not None else EXTRACTOR_VERSION
|
refs = source_refs or []
|
||||||
|
|
||||||
with get_connection() as conn:
|
with get_connection() as conn:
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"""INSERT INTO entities
|
"""INSERT INTO entities
|
||||||
(id, entity_type, name, project, description, properties,
|
(id, entity_type, name, project, description, properties,
|
||||||
status, confidence, source_refs,
|
status, confidence, source_refs, created_at, updated_at)
|
||||||
extractor_version, canonical_home, hand_authored,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||||
created_at, updated_at)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
|
||||||
(
|
(
|
||||||
entity_id, entity_type, name.strip(), project,
|
entity_id, entity_type, name.strip(), project,
|
||||||
description, json.dumps(props), status, confidence,
|
description, json.dumps(props), status, confidence,
|
||||||
json.dumps(refs),
|
json.dumps(refs), now, now,
|
||||||
ev, "entity", 1 if hand_authored else 0,
|
|
||||||
now, now,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -240,31 +194,14 @@ def create_entity(
|
|||||||
"project": project,
|
"project": project,
|
||||||
"status": status,
|
"status": status,
|
||||||
"confidence": confidence,
|
"confidence": confidence,
|
||||||
"hand_authored": hand_authored,
|
|
||||||
"extractor_version": ev,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# V1-0 (F-5 hook, engineering-v1-acceptance.md:99): synchronous
|
|
||||||
# conflict detection on any active-entity write. The promote path
|
|
||||||
# already had this hook (see promote_entity below); V1-0 adds it to
|
|
||||||
# direct-active creates so every active row — however it got that
|
|
||||||
# way — is checked. Fail-open per "flag, never block" rule in
|
|
||||||
# conflict-model.md:256: detector errors log but never fail the write.
|
|
||||||
if status == "active":
|
|
||||||
try:
|
|
||||||
from atocore.engineering.conflicts import detect_conflicts_for_entity
|
|
||||||
detect_conflicts_for_entity(entity_id)
|
|
||||||
except Exception as e:
|
|
||||||
log.warning("conflict_detection_failed", entity_id=entity_id, error=str(e))
|
|
||||||
|
|
||||||
return Entity(
|
return Entity(
|
||||||
id=entity_id, entity_type=entity_type, name=name.strip(),
|
id=entity_id, entity_type=entity_type, name=name.strip(),
|
||||||
project=project, description=description, properties=props,
|
project=project, description=description, properties=props,
|
||||||
status=status, confidence=confidence, source_refs=refs,
|
status=status, confidence=confidence, source_refs=refs,
|
||||||
created_at=now, updated_at=now,
|
created_at=now, updated_at=now,
|
||||||
extractor_version=ev, canonical_home="entity",
|
|
||||||
hand_authored=hand_authored,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -424,20 +361,6 @@ def promote_entity(
|
|||||||
if entity is None or entity.status != "candidate":
|
if entity is None or entity.status != "candidate":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# V1-0 (F-8 provenance re-check at promote). The invariant must hold at
|
|
||||||
# BOTH create_entity AND promote_entity per the plan, because candidate
|
|
||||||
# rows can exist in the DB from before V1-0 (no enforcement at their
|
|
||||||
# create time) or can be inserted by code paths that bypass the service
|
|
||||||
# layer. Block any candidate with empty source_refs that is NOT flagged
|
|
||||||
# hand_authored from ever becoming active. Same error shape as the
|
|
||||||
# create-side check for symmetry.
|
|
||||||
if not (entity.source_refs or []) and not entity.hand_authored:
|
|
||||||
raise ValueError(
|
|
||||||
"source_refs required: cannot promote a candidate with no "
|
|
||||||
"provenance. Attach source_refs via PATCH /entities/{id}, "
|
|
||||||
"or flag hand_authored=true before promoting."
|
|
||||||
)
|
|
||||||
|
|
||||||
if target_project is not None:
|
if target_project is not None:
|
||||||
new_project = (
|
new_project = (
|
||||||
resolve_project_name(target_project) if target_project else ""
|
resolve_project_name(target_project) if target_project else ""
|
||||||
@@ -580,22 +503,6 @@ def supersede_entity(
|
|||||||
superseded_by=superseded_by,
|
superseded_by=superseded_by,
|
||||||
error=str(e),
|
error=str(e),
|
||||||
)
|
)
|
||||||
|
|
||||||
# V1-0 (F-5 hook on supersede, per plan's "every active-entity
|
|
||||||
# write path"). Supersede demotes `entity_id` AND adds a
|
|
||||||
# `supersedes` relationship rooted at the already-active
|
|
||||||
# `superseded_by`. That new edge can create a conflict the
|
|
||||||
# detector should catch synchronously. Fail-open per
|
|
||||||
# conflict-model.md:256.
|
|
||||||
try:
|
|
||||||
from atocore.engineering.conflicts import detect_conflicts_for_entity
|
|
||||||
detect_conflicts_for_entity(superseded_by)
|
|
||||||
except Exception as e:
|
|
||||||
log.warning(
|
|
||||||
"conflict_detection_failed",
|
|
||||||
entity_id=superseded_by,
|
|
||||||
error=str(e),
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@@ -867,15 +774,6 @@ def get_entity_with_context(entity_id: str) -> dict | None:
|
|||||||
|
|
||||||
|
|
||||||
def _row_to_entity(row) -> Entity:
|
def _row_to_entity(row) -> Entity:
|
||||||
# V1-0 shared-header fields are optional on read — rows that predate
|
|
||||||
# V1-0 migration have NULL / missing values, so defaults kick in and
|
|
||||||
# older tests that build Entity() without the new fields keep passing.
|
|
||||||
# `row.keys()` lets us tolerate SQLite rows that lack the columns
|
|
||||||
# entirely (pre-migration sqlite3.Row).
|
|
||||||
keys = set(row.keys())
|
|
||||||
extractor_version = (row["extractor_version"] or "") if "extractor_version" in keys else ""
|
|
||||||
canonical_home = (row["canonical_home"] or "entity") if "canonical_home" in keys else "entity"
|
|
||||||
hand_authored = bool(row["hand_authored"]) if "hand_authored" in keys and row["hand_authored"] is not None else False
|
|
||||||
return Entity(
|
return Entity(
|
||||||
id=row["id"],
|
id=row["id"],
|
||||||
entity_type=row["entity_type"],
|
entity_type=row["entity_type"],
|
||||||
@@ -888,9 +786,6 @@ def _row_to_entity(row) -> Entity:
|
|||||||
source_refs=json.loads(row["source_refs"] or "[]"),
|
source_refs=json.loads(row["source_refs"] or "[]"),
|
||||||
created_at=row["created_at"] or "",
|
created_at=row["created_at"] or "",
|
||||||
updated_at=row["updated_at"] or "",
|
updated_at=row["updated_at"] or "",
|
||||||
extractor_version=extractor_version,
|
|
||||||
canonical_home=canonical_home,
|
|
||||||
hand_authored=hand_authored,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -240,10 +240,30 @@ def render_homepage() -> str:
|
|||||||
|
|
||||||
# Quick stats
|
# Quick stats
|
||||||
all_entities = get_entities(limit=500)
|
all_entities = get_entities(limit=500)
|
||||||
all_memories = get_memories(active_only=True, limit=500)
|
all_memories_raw = get_memories(active_only=True, limit=500)
|
||||||
|
# Partition real knowledge from ambient provenance so counts are honest.
|
||||||
|
# Each memory lands in exactly one bucket (low-signal takes priority).
|
||||||
|
all_memories: list = []
|
||||||
|
akc_session_count = 0
|
||||||
|
low_signal_count = 0
|
||||||
|
for _m in all_memories_raw:
|
||||||
|
if _is_low_signal_memory(_m):
|
||||||
|
low_signal_count += 1
|
||||||
|
elif _is_akc_session_memory(_m):
|
||||||
|
akc_session_count += 1
|
||||||
|
else:
|
||||||
|
all_memories.append(_m)
|
||||||
pending = get_memories(status="candidate", limit=500)
|
pending = get_memories(status="candidate", limit=500)
|
||||||
lines.append('<h2>System</h2>')
|
lines.append('<h2>System</h2>')
|
||||||
lines.append(f'<p>{len(all_entities)} entities · {len(all_memories)} active memories · {len(projects)} projects</p>')
|
lines.append(
|
||||||
|
f'<p>{len(all_entities)} entities · {len(all_memories)} memories · '
|
||||||
|
f'{len(projects)} projects'
|
||||||
|
+ (f' · <span style="color:#888;">{akc_session_count} AKC session snapshots'
|
||||||
|
+ (f", {low_signal_count} low-signal hidden" if low_signal_count else "")
|
||||||
|
+ '</span>'
|
||||||
|
if akc_session_count or low_signal_count else '')
|
||||||
|
+ '</p>'
|
||||||
|
)
|
||||||
|
|
||||||
# Triage queue prompt — surfaced prominently if non-empty
|
# Triage queue prompt — surfaced prominently if non-empty
|
||||||
if pending:
|
if pending:
|
||||||
@@ -286,6 +306,44 @@ import re as _re
|
|||||||
_WIKILINK_PATTERN = _re.compile(r"\[\[([^\[\]|]+?)(?:\|([^\[\]]+?))?\]\]")
|
_WIKILINK_PATTERN = _re.compile(r"\[\[([^\[\]|]+?)(?:\|([^\[\]]+?))?\]\]")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------- signal/noise
|
||||||
|
# Memories with these patterns are low-signal ambient artefacts — they
|
||||||
|
# inflate lists on the homepage and domain pages without being informative.
|
||||||
|
# They remain in the DB (for provenance / audit) but are filtered from
|
||||||
|
# default browsing surfaces. Pass `include_low_signal=True` on a page query
|
||||||
|
# param to surface them.
|
||||||
|
_LOW_SIGNAL_CONTENT_PATTERNS = (
|
||||||
|
"(no transcript)", # silent-mic AKC sessions
|
||||||
|
"synthetic AKC integration", # E2E test pollution
|
||||||
|
"AKC-E2E-", # E2E test prefix in content
|
||||||
|
"AKC-IMG-TEST-", # image-upload test prefix
|
||||||
|
"IMG integration test — synthetic", # E2E narrative header
|
||||||
|
)
|
||||||
|
|
||||||
|
# AKC voice-session ambient memories follow this pattern — they're
|
||||||
|
# provenance records, not knowledge. Collapse them behind a link on domain
|
||||||
|
# pages instead of rendering each inline.
|
||||||
|
_AKC_SESSION_HEADER = "AKC voice session "
|
||||||
|
|
||||||
|
|
||||||
|
def _is_low_signal_memory(mem) -> bool:
|
||||||
|
"""True for memories whose content is known ambient/test pollution."""
|
||||||
|
content = (getattr(mem, "content", "") or "")
|
||||||
|
if not content:
|
||||||
|
return True
|
||||||
|
return any(p in content for p in _LOW_SIGNAL_CONTENT_PATTERNS)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_akc_session_memory(mem) -> bool:
|
||||||
|
"""True for AKC voice-session ambient snapshots (have value as provenance,
|
||||||
|
but shouldn't clutter topical listings)."""
|
||||||
|
content = (getattr(mem, "content", "") or "")
|
||||||
|
tags = getattr(mem, "domain_tags", None) or []
|
||||||
|
if any(t in ("session", "akc") for t in tags) and "voice" in tags:
|
||||||
|
return True
|
||||||
|
return content.startswith(_AKC_SESSION_HEADER)
|
||||||
|
|
||||||
|
|
||||||
def _resolve_wikilink(target: str, current_project: str | None) -> tuple[str, str, str]:
|
def _resolve_wikilink(target: str, current_project: str | None) -> tuple[str, str, str]:
|
||||||
"""Resolve a ``[[Name]]`` target to ``(href, css_class, extra_suffix)``.
|
"""Resolve a ``[[Name]]`` target to ``(href, css_class, extra_suffix)``.
|
||||||
|
|
||||||
@@ -391,8 +449,6 @@ def render_new_entity_form(name: str = "", project: str = "") -> str:
|
|||||||
entity_type: fd.get('entity_type'),
|
entity_type: fd.get('entity_type'),
|
||||||
project: fd.get('project') || '',
|
project: fd.get('project') || '',
|
||||||
description: fd.get('description') || '',
|
description: fd.get('description') || '',
|
||||||
// V1-0: human writes via the wiki form are hand_authored by definition.
|
|
||||||
hand_authored: true,
|
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
const r = await fetch('/v1/entities', {
|
const r = await fetch('/v1/entities', {
|
||||||
@@ -876,9 +932,23 @@ def render_domain(tag: str) -> str:
|
|||||||
breadcrumbs=[("Wiki", "/wiki"), ("Domains", "")])
|
breadcrumbs=[("Wiki", "/wiki"), ("Domains", "")])
|
||||||
|
|
||||||
all_mems = get_memories(active_only=True, limit=500)
|
all_mems = get_memories(active_only=True, limit=500)
|
||||||
matching = [m for m in all_mems
|
matching_all = [m for m in all_mems
|
||||||
if any((t or "").lower() == tag for t in (m.domain_tags or []))]
|
if any((t or "").lower() == tag for t in (m.domain_tags or []))]
|
||||||
|
|
||||||
|
# Partition: low-signal test pollution is hidden entirely, ambient AKC
|
||||||
|
# session memories are collapsed (shown as a count + link to
|
||||||
|
# /wiki/activity). Priority: low-signal > session > real.
|
||||||
|
matching: list = []
|
||||||
|
akc_sessions: list = []
|
||||||
|
hidden_low_signal = 0
|
||||||
|
for m in matching_all:
|
||||||
|
if _is_low_signal_memory(m):
|
||||||
|
hidden_low_signal += 1
|
||||||
|
elif _is_akc_session_memory(m):
|
||||||
|
akc_sessions.append(m)
|
||||||
|
else:
|
||||||
|
matching.append(m)
|
||||||
|
|
||||||
# Group by project
|
# Group by project
|
||||||
by_project: dict[str, list] = {}
|
by_project: dict[str, list] = {}
|
||||||
for m in matching:
|
for m in matching:
|
||||||
@@ -886,6 +956,18 @@ def render_domain(tag: str) -> str:
|
|||||||
|
|
||||||
lines = [f'<h1>Domain: <code>{tag}</code></h1>']
|
lines = [f'<h1>Domain: <code>{tag}</code></h1>']
|
||||||
lines.append(f'<p class="meta">{len(matching)} active memories across {len(by_project)} projects</p>')
|
lines.append(f'<p class="meta">{len(matching)} active memories across {len(by_project)} projects</p>')
|
||||||
|
if akc_sessions or hidden_low_signal:
|
||||||
|
noise_bits = []
|
||||||
|
if akc_sessions:
|
||||||
|
noise_bits.append(
|
||||||
|
f'<a href="/wiki/activity">{len(akc_sessions)} AKC voice session snapshots</a>'
|
||||||
|
)
|
||||||
|
if hidden_low_signal:
|
||||||
|
noise_bits.append(f"{hidden_low_signal} low-signal memories hidden")
|
||||||
|
lines.append(
|
||||||
|
f'<p class="meta" style="font-size:0.85rem; color:#888;">'
|
||||||
|
f'Ambient provenance not listed: {" · ".join(noise_bits)}.</p>'
|
||||||
|
)
|
||||||
|
|
||||||
if not matching:
|
if not matching:
|
||||||
lines.append(
|
lines.append(
|
||||||
|
|||||||
@@ -146,28 +146,6 @@ def _apply_migrations(conn: sqlite3.Connection) -> None:
|
|||||||
"CREATE INDEX IF NOT EXISTS idx_memories_graduated ON memories(graduated_to_entity_id)"
|
"CREATE INDEX IF NOT EXISTS idx_memories_graduated ON memories(graduated_to_entity_id)"
|
||||||
)
|
)
|
||||||
|
|
||||||
# V1-0 (Engineering V1 completion): shared header fields per
|
|
||||||
# engineering-v1-acceptance.md:45. Three columns on `entities`:
|
|
||||||
# - extractor_version: which extractor produced this row. Lets old
|
|
||||||
# candidates be re-evaluated with a newer extractor per
|
|
||||||
# promotion-rules.md:268.
|
|
||||||
# - canonical_home: which layer holds the canonical record. Always
|
|
||||||
# "entity" for rows written via create_entity; reserved for future
|
|
||||||
# cross-layer bookkeeping.
|
|
||||||
# - hand_authored: 1 when the row was created directly by a human
|
|
||||||
# without source provenance. Enforced by the write path so every
|
|
||||||
# non-hand-authored row must carry non-empty source_refs (F-8).
|
|
||||||
# The entities table itself is created by init_engineering_schema
|
|
||||||
# (see engineering/service.py); these ALTERs cover existing DBs
|
|
||||||
# where the original CREATE TABLE predates V1-0.
|
|
||||||
if _table_exists(conn, "entities"):
|
|
||||||
if not _column_exists(conn, "entities", "extractor_version"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN extractor_version TEXT DEFAULT ''")
|
|
||||||
if not _column_exists(conn, "entities", "canonical_home"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN canonical_home TEXT DEFAULT 'entity'")
|
|
||||||
if not _column_exists(conn, "entities", "hand_authored"):
|
|
||||||
conn.execute("ALTER TABLE entities ADD COLUMN hand_authored INTEGER DEFAULT 0")
|
|
||||||
|
|
||||||
# Phase 4 (Robustness V1): append-only audit log for memory mutations.
|
# Phase 4 (Robustness V1): append-only audit log for memory mutations.
|
||||||
# Every create/update/promote/reject/supersede/invalidate/reinforce/expire/
|
# Every create/update/promote/reject/supersede/invalidate/reinforce/expire/
|
||||||
# auto_promote writes one row here. before/after are JSON snapshots of the
|
# auto_promote writes one row here. before/after are JSON snapshots of the
|
||||||
@@ -374,14 +352,6 @@ def _column_exists(conn: sqlite3.Connection, table: str, column: str) -> bool:
|
|||||||
return any(row["name"] == column for row in rows)
|
return any(row["name"] == column for row in rows)
|
||||||
|
|
||||||
|
|
||||||
def _table_exists(conn: sqlite3.Connection, table: str) -> bool:
|
|
||||||
row = conn.execute(
|
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name=?",
|
|
||||||
(table,),
|
|
||||||
).fetchone()
|
|
||||||
return row is not None
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def get_connection() -> Generator[sqlite3.Connection, None, None]:
|
def get_connection() -> Generator[sqlite3.Connection, None, None]:
|
||||||
"""Get a database connection with row factory."""
|
"""Get a database connection with row factory."""
|
||||||
|
|||||||
@@ -16,36 +16,6 @@ os.environ["ATOCORE_DATA_DIR"] = _default_test_dir
|
|||||||
os.environ["ATOCORE_DEBUG"] = "true"
|
os.environ["ATOCORE_DEBUG"] = "true"
|
||||||
|
|
||||||
|
|
||||||
# V1-0: every entity created in a test is "hand authored" by the test
|
|
||||||
# author — fixture data, not extracted content. Rather than rewrite 100+
|
|
||||||
# existing test call sites, wrap create_entity so that tests which don't
|
|
||||||
# provide source_refs get hand_authored=True automatically. Tests that
|
|
||||||
# explicitly pass source_refs or hand_authored are unaffected. This keeps
|
|
||||||
# the F-8 invariant enforced in production (the API, the wiki form, and
|
|
||||||
# graduation scripts all go through the unwrapped function) while leaving
|
|
||||||
# the existing test corpus intact.
|
|
||||||
def _patch_create_entity_for_tests():
|
|
||||||
from atocore.engineering import service as _svc
|
|
||||||
|
|
||||||
_original = _svc.create_entity
|
|
||||||
|
|
||||||
def _create_entity_test(*args, **kwargs):
|
|
||||||
# Only auto-flag when hand_authored isn't explicitly specified.
|
|
||||||
# Tests that want to exercise the F-8 raise path pass
|
|
||||||
# hand_authored=False explicitly and should hit the error.
|
|
||||||
if (
|
|
||||||
not kwargs.get("source_refs")
|
|
||||||
and "hand_authored" not in kwargs
|
|
||||||
):
|
|
||||||
kwargs["hand_authored"] = True
|
|
||||||
return _original(*args, **kwargs)
|
|
||||||
|
|
||||||
_svc.create_entity = _create_entity_test
|
|
||||||
|
|
||||||
|
|
||||||
_patch_create_entity_for_tests()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def tmp_data_dir(tmp_path):
|
def tmp_data_dir(tmp_path):
|
||||||
"""Provide a temporary data directory for tests."""
|
"""Provide a temporary data directory for tests."""
|
||||||
|
|||||||
@@ -143,11 +143,8 @@ def test_requirement_name_conflict_detected(tmp_data_dir):
|
|||||||
r2 = create_entity("requirement", "Surface figure < 25nm",
|
r2 = create_entity("requirement", "Surface figure < 25nm",
|
||||||
project="p-test", description="Different interpretation")
|
project="p-test", description="Different interpretation")
|
||||||
|
|
||||||
# V1-0 synchronous hook: the conflict is already detected at r2's
|
detected = detect_conflicts_for_entity(r2.id)
|
||||||
# create-time, so a redundant detect call returns [] due to
|
assert len(detected) == 1
|
||||||
# _record_conflict dedup. Assert on list_open_conflicts instead —
|
|
||||||
# that's what the intent of this test really tests: duplicate
|
|
||||||
# active requirements surface as an open conflict.
|
|
||||||
conflicts = list_open_conflicts(project="p-test")
|
conflicts = list_open_conflicts(project="p-test")
|
||||||
assert any(c["slot_kind"] == "requirement.name" for c in conflicts)
|
assert any(c["slot_kind"] == "requirement.name" for c in conflicts)
|
||||||
|
|
||||||
@@ -194,12 +191,8 @@ def test_conflict_resolution_dismiss_leaves_entities_alone(tmp_data_dir):
|
|||||||
description="first meaning")
|
description="first meaning")
|
||||||
r2 = create_entity("requirement", "Dup req", project="p-test",
|
r2 = create_entity("requirement", "Dup req", project="p-test",
|
||||||
description="second meaning")
|
description="second meaning")
|
||||||
# V1-0 synchronous hook already recorded the conflict at r2's
|
detected = detect_conflicts_for_entity(r2.id)
|
||||||
# create-time. Look it up via list_open_conflicts rather than
|
conflict_id = detected[0]
|
||||||
# calling the detector again (which returns [] due to dedup).
|
|
||||||
open_list = list_open_conflicts(project="p-test")
|
|
||||||
assert open_list, "expected conflict recorded by create-time hook"
|
|
||||||
conflict_id = open_list[0]["id"]
|
|
||||||
|
|
||||||
assert resolve_conflict(conflict_id, "dismiss")
|
assert resolve_conflict(conflict_id, "dismiss")
|
||||||
# Both still active — dismiss just clears the conflict marker
|
# Both still active — dismiss just clears the conflict marker
|
||||||
|
|||||||
@@ -132,7 +132,6 @@ def test_api_post_entity_with_null_project_stores_global(seeded_db):
|
|||||||
"entity_type": "material",
|
"entity_type": "material",
|
||||||
"name": "Titanium",
|
"name": "Titanium",
|
||||||
"project": None,
|
"project": None,
|
||||||
"hand_authored": True, # V1-0 F-8: test fixture, no source_refs
|
|
||||||
})
|
})
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
|
|
||||||
|
|||||||
@@ -1,398 +0,0 @@
|
|||||||
"""V1-0 write-time invariant tests.
|
|
||||||
|
|
||||||
Covers the Engineering V1 completion plan Phase V1-0 acceptance:
|
|
||||||
- F-1 shared-header fields: extractor_version + canonical_home + hand_authored
|
|
||||||
land in the entities table with working defaults
|
|
||||||
- F-8 provenance enforcement: create_entity raises without source_refs
|
|
||||||
unless hand_authored=True
|
|
||||||
- F-5 synchronous conflict-detection hook on any active-entity write
|
|
||||||
(create_entity with status="active" + the pre-existing promote_entity
|
|
||||||
path); fail-open per conflict-model.md:256
|
|
||||||
- Q-3 "flag, never block": a conflict never 4xx-blocks the write
|
|
||||||
- Q-4 partial trust: get_entities scope_only filters candidates out
|
|
||||||
|
|
||||||
Plan: docs/plans/engineering-v1-completion-plan.md
|
|
||||||
Spec: docs/architecture/engineering-v1-acceptance.md
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from atocore.engineering.service import (
|
|
||||||
EXTRACTOR_VERSION,
|
|
||||||
create_entity,
|
|
||||||
create_relationship,
|
|
||||||
get_entities,
|
|
||||||
get_entity,
|
|
||||||
init_engineering_schema,
|
|
||||||
promote_entity,
|
|
||||||
supersede_entity,
|
|
||||||
)
|
|
||||||
from atocore.models.database import get_connection, init_db
|
|
||||||
|
|
||||||
|
|
||||||
# ---------- F-1: shared-header fields ----------
|
|
||||||
|
|
||||||
|
|
||||||
def test_entity_row_has_shared_header_fields(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
with get_connection() as conn:
|
|
||||||
cols = {row["name"] for row in conn.execute("PRAGMA table_info(entities)").fetchall()}
|
|
||||||
assert "extractor_version" in cols
|
|
||||||
assert "canonical_home" in cols
|
|
||||||
assert "hand_authored" in cols
|
|
||||||
|
|
||||||
|
|
||||||
def test_created_entity_has_default_extractor_version_and_canonical_home(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
e = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Pivot Pin",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:fixture"],
|
|
||||||
)
|
|
||||||
assert e.extractor_version == EXTRACTOR_VERSION
|
|
||||||
assert e.canonical_home == "entity"
|
|
||||||
assert e.hand_authored is False
|
|
||||||
|
|
||||||
# round-trip through get_entity to confirm the row mapper returns
|
|
||||||
# the same values (not just the return-by-construct path)
|
|
||||||
got = get_entity(e.id)
|
|
||||||
assert got is not None
|
|
||||||
assert got.extractor_version == EXTRACTOR_VERSION
|
|
||||||
assert got.canonical_home == "entity"
|
|
||||||
assert got.hand_authored is False
|
|
||||||
|
|
||||||
|
|
||||||
def test_explicit_extractor_version_is_persisted(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
e = create_entity(
|
|
||||||
entity_type="decision",
|
|
||||||
name="Pick GF-PTFE pads",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["interaction:abc"],
|
|
||||||
extractor_version="custom-v2.3",
|
|
||||||
)
|
|
||||||
got = get_entity(e.id)
|
|
||||||
assert got.extractor_version == "custom-v2.3"
|
|
||||||
|
|
||||||
|
|
||||||
# ---------- F-8: provenance enforcement ----------
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_entity_without_provenance_raises(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
with pytest.raises(ValueError, match="source_refs required"):
|
|
||||||
create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="No Provenance",
|
|
||||||
project="p04-gigabit",
|
|
||||||
hand_authored=False, # explicit — bypasses the test-conftest auto-flag
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_entity_with_hand_authored_needs_no_source_refs(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
e = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Human Entry",
|
|
||||||
project="p04-gigabit",
|
|
||||||
hand_authored=True,
|
|
||||||
)
|
|
||||||
assert e.hand_authored is True
|
|
||||||
got = get_entity(e.id)
|
|
||||||
assert got.hand_authored is True
|
|
||||||
# source_refs stays empty — the hand_authored flag IS the provenance
|
|
||||||
assert got.source_refs == []
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_entity_with_empty_source_refs_list_is_treated_as_missing(tmp_data_dir):
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
with pytest.raises(ValueError, match="source_refs required"):
|
|
||||||
create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Empty Refs",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=[],
|
|
||||||
hand_authored=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_promote_rejects_legacy_candidate_without_provenance(tmp_data_dir):
|
|
||||||
"""Regression (Codex V1-0 probe): candidate rows can exist in the DB
|
|
||||||
from before V1-0 enforcement (or from paths that bypass create_entity).
|
|
||||||
promote_entity must re-check the invariant and refuse to flip a
|
|
||||||
no-provenance candidate to active. Without this check, the active
|
|
||||||
store can leak F-8 violations in from legacy data."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
# Simulate a pre-V1-0 candidate by inserting directly into the table,
|
|
||||||
# bypassing the service-layer invariant. Real legacy rows look exactly
|
|
||||||
# like this: empty source_refs, hand_authored=0.
|
|
||||||
import uuid as _uuid
|
|
||||||
entity_id = str(_uuid.uuid4())
|
|
||||||
with get_connection() as conn:
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO entities (id, entity_type, name, project, "
|
|
||||||
"description, properties, status, confidence, source_refs, "
|
|
||||||
"extractor_version, canonical_home, hand_authored, "
|
|
||||||
"created_at, updated_at) "
|
|
||||||
"VALUES (?, 'component', 'Legacy Orphan', 'p04-gigabit', "
|
|
||||||
"'', '{}', 'candidate', 1.0, '[]', '', 'entity', 0, "
|
|
||||||
"CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)",
|
|
||||||
(entity_id,),
|
|
||||||
)
|
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="source_refs required"):
|
|
||||||
promote_entity(entity_id)
|
|
||||||
|
|
||||||
# And the row stays a candidate — no half-transition.
|
|
||||||
got = get_entity(entity_id)
|
|
||||||
assert got is not None
|
|
||||||
assert got.status == "candidate"
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_promote_returns_400_on_legacy_no_provenance(tmp_data_dir):
|
|
||||||
"""R14 (Codex, 2026-04-22): the HTTP promote route must translate
|
|
||||||
the V1-0 ValueError for no-provenance candidates into 400, not 500.
|
|
||||||
Previously the route didn't catch ValueError so legacy bad
|
|
||||||
candidates surfaced as a server error."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
import uuid as _uuid
|
|
||||||
from fastapi.testclient import TestClient
|
|
||||||
from atocore.main import app
|
|
||||||
|
|
||||||
entity_id = str(_uuid.uuid4())
|
|
||||||
with get_connection() as conn:
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO entities (id, entity_type, name, project, "
|
|
||||||
"description, properties, status, confidence, source_refs, "
|
|
||||||
"extractor_version, canonical_home, hand_authored, "
|
|
||||||
"created_at, updated_at) "
|
|
||||||
"VALUES (?, 'component', 'Legacy HTTP', 'p04-gigabit', "
|
|
||||||
"'', '{}', 'candidate', 1.0, '[]', '', 'entity', 0, "
|
|
||||||
"CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)",
|
|
||||||
(entity_id,),
|
|
||||||
)
|
|
||||||
|
|
||||||
client = TestClient(app)
|
|
||||||
r = client.post(f"/entities/{entity_id}/promote")
|
|
||||||
assert r.status_code == 400
|
|
||||||
assert "source_refs required" in r.json().get("detail", "")
|
|
||||||
|
|
||||||
# Row still candidate — the 400 didn't half-transition.
|
|
||||||
got = get_entity(entity_id)
|
|
||||||
assert got is not None
|
|
||||||
assert got.status == "candidate"
|
|
||||||
|
|
||||||
|
|
||||||
def test_promote_accepts_candidate_flagged_hand_authored(tmp_data_dir):
|
|
||||||
"""The other side of the promote re-check: hand_authored=1 with
|
|
||||||
empty source_refs still lets promote succeed, matching
|
|
||||||
create_entity's symmetry."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
import uuid as _uuid
|
|
||||||
entity_id = str(_uuid.uuid4())
|
|
||||||
with get_connection() as conn:
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO entities (id, entity_type, name, project, "
|
|
||||||
"description, properties, status, confidence, source_refs, "
|
|
||||||
"extractor_version, canonical_home, hand_authored, "
|
|
||||||
"created_at, updated_at) "
|
|
||||||
"VALUES (?, 'component', 'Hand Authored Candidate', "
|
|
||||||
"'p04-gigabit', '', '{}', 'candidate', 1.0, '[]', '', "
|
|
||||||
"'entity', 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)",
|
|
||||||
(entity_id,),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert promote_entity(entity_id) is True
|
|
||||||
assert get_entity(entity_id).status == "active"
|
|
||||||
|
|
||||||
|
|
||||||
# ---------- F-5: synchronous conflict-detection hook ----------
|
|
||||||
|
|
||||||
|
|
||||||
def test_active_create_runs_conflict_detection_hook(tmp_data_dir, monkeypatch):
|
|
||||||
"""status=active writes trigger detect_conflicts_for_entity."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
called_with: list[str] = []
|
|
||||||
|
|
||||||
def _fake_detect(entity_id: str):
|
|
||||||
called_with.append(entity_id)
|
|
||||||
return []
|
|
||||||
|
|
||||||
import atocore.engineering.conflicts as conflicts_mod
|
|
||||||
monkeypatch.setattr(conflicts_mod, "detect_conflicts_for_entity", _fake_detect)
|
|
||||||
|
|
||||||
e = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Active With Hook",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:hook"],
|
|
||||||
status="active",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert called_with == [e.id]
|
|
||||||
|
|
||||||
|
|
||||||
def test_supersede_runs_conflict_detection_on_new_active(tmp_data_dir, monkeypatch):
|
|
||||||
"""Regression (Codex V1-0 probe): per plan's 'every active-entity
|
|
||||||
write path', supersede_entity must trigger synchronous conflict
|
|
||||||
detection. The subject is the `superseded_by` entity — the one
|
|
||||||
whose graph state just changed because a new `supersedes` edge was
|
|
||||||
rooted at it."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
old = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Old Pad",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:old"],
|
|
||||||
status="active",
|
|
||||||
)
|
|
||||||
new = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="New Pad",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:new"],
|
|
||||||
status="active",
|
|
||||||
)
|
|
||||||
|
|
||||||
called_with: list[str] = []
|
|
||||||
|
|
||||||
def _fake_detect(entity_id: str):
|
|
||||||
called_with.append(entity_id)
|
|
||||||
return []
|
|
||||||
|
|
||||||
import atocore.engineering.conflicts as conflicts_mod
|
|
||||||
monkeypatch.setattr(conflicts_mod, "detect_conflicts_for_entity", _fake_detect)
|
|
||||||
|
|
||||||
assert supersede_entity(old.id, superseded_by=new.id) is True
|
|
||||||
|
|
||||||
# The detector fires on the `superseded_by` entity — the one whose
|
|
||||||
# edges just grew a new `supersedes` relationship.
|
|
||||||
assert new.id in called_with
|
|
||||||
|
|
||||||
|
|
||||||
def test_supersede_hook_fails_open(tmp_data_dir, monkeypatch):
|
|
||||||
"""Supersede must survive a broken detector per Q-3 flag-never-block."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
old = create_entity(
|
|
||||||
entity_type="component", name="Old2", project="p04-gigabit",
|
|
||||||
source_refs=["test:old"], status="active",
|
|
||||||
)
|
|
||||||
new = create_entity(
|
|
||||||
entity_type="component", name="New2", project="p04-gigabit",
|
|
||||||
source_refs=["test:new"], status="active",
|
|
||||||
)
|
|
||||||
|
|
||||||
def _boom(entity_id: str):
|
|
||||||
raise RuntimeError("synthetic detector failure")
|
|
||||||
|
|
||||||
import atocore.engineering.conflicts as conflicts_mod
|
|
||||||
monkeypatch.setattr(conflicts_mod, "detect_conflicts_for_entity", _boom)
|
|
||||||
|
|
||||||
# The supersede still succeeds despite the detector blowing up.
|
|
||||||
assert supersede_entity(old.id, superseded_by=new.id) is True
|
|
||||||
assert get_entity(old.id).status == "superseded"
|
|
||||||
|
|
||||||
|
|
||||||
def test_candidate_create_does_not_run_conflict_hook(tmp_data_dir, monkeypatch):
|
|
||||||
"""status=candidate writes do NOT trigger detection — the hook is
|
|
||||||
for active rows only, per V1-0 scope. Candidates are checked at
|
|
||||||
promote time."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
called: list[str] = []
|
|
||||||
|
|
||||||
def _fake_detect(entity_id: str):
|
|
||||||
called.append(entity_id)
|
|
||||||
return []
|
|
||||||
|
|
||||||
import atocore.engineering.conflicts as conflicts_mod
|
|
||||||
monkeypatch.setattr(conflicts_mod, "detect_conflicts_for_entity", _fake_detect)
|
|
||||||
|
|
||||||
create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Candidate No Hook",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:cand"],
|
|
||||||
status="candidate",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert called == []
|
|
||||||
|
|
||||||
|
|
||||||
# ---------- Q-3: flag, never block ----------
|
|
||||||
|
|
||||||
|
|
||||||
def test_conflict_detector_failure_does_not_block_write(tmp_data_dir, monkeypatch):
|
|
||||||
"""Per conflict-model.md:256: detection errors must not fail the
|
|
||||||
write. The entity is still created; only a warning is logged."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
def _boom(entity_id: str):
|
|
||||||
raise RuntimeError("synthetic detector failure")
|
|
||||||
|
|
||||||
import atocore.engineering.conflicts as conflicts_mod
|
|
||||||
monkeypatch.setattr(conflicts_mod, "detect_conflicts_for_entity", _boom)
|
|
||||||
|
|
||||||
# The write still succeeds — no exception propagates.
|
|
||||||
e = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Hook Fails Open",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:failopen"],
|
|
||||||
status="active",
|
|
||||||
)
|
|
||||||
assert get_entity(e.id) is not None
|
|
||||||
|
|
||||||
|
|
||||||
# ---------- Q-4 (partial): trust-hierarchy — scope_only filters candidates ----------
|
|
||||||
|
|
||||||
|
|
||||||
def test_scope_only_active_does_not_return_candidates(tmp_data_dir):
|
|
||||||
"""V1-0 partial Q-4: active-scoped listing never returns candidates.
|
|
||||||
Full trust-hierarchy coverage (no-auto-project-state, etc.) ships in
|
|
||||||
V1-E per plan."""
|
|
||||||
init_db()
|
|
||||||
init_engineering_schema()
|
|
||||||
|
|
||||||
active = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Active Alpha",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:alpha"],
|
|
||||||
status="active",
|
|
||||||
)
|
|
||||||
candidate = create_entity(
|
|
||||||
entity_type="component",
|
|
||||||
name="Candidate Beta",
|
|
||||||
project="p04-gigabit",
|
|
||||||
source_refs=["test:beta"],
|
|
||||||
status="candidate",
|
|
||||||
)
|
|
||||||
|
|
||||||
listed = get_entities(project="p04-gigabit", status="active", scope_only=True)
|
|
||||||
ids = {e.id for e in listed}
|
|
||||||
assert active.id in ids
|
|
||||||
assert candidate.id not in ids
|
|
||||||
@@ -161,3 +161,103 @@ def test_memory_detail_shows_superseded_sources(tmp_data_dir):
|
|||||||
assert html1 is not None
|
assert html1 is not None
|
||||||
assert "superseded" in html1
|
assert "superseded" in html1
|
||||||
assert "auto-dedup-tier1" in html1 # audit trail shows who merged
|
assert "auto-dedup-tier1" in html1 # audit trail shows who merged
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------- low-signal wiki filters
|
||||||
|
# Ambient AKC session memories and test pollution shouldn't dominate domain
|
||||||
|
# pages / homepage counts. These tests lock the partitioning behaviour.
|
||||||
|
|
||||||
|
def test_domain_page_hides_empty_transcript_sessions(tmp_data_dir):
|
||||||
|
"""Silent-mic AKC sessions (content has '(no transcript)') are ambient
|
||||||
|
noise — they go into the hidden count, not the main list."""
|
||||||
|
_init_all()
|
||||||
|
# One real knowledge memory with tag "optics"
|
||||||
|
create_memory(
|
||||||
|
"knowledge",
|
||||||
|
"CGH null corrector supports F/1.2 asphere testing",
|
||||||
|
project="p05", confidence=0.9, domain_tags=["optics", "cgh"],
|
||||||
|
)
|
||||||
|
# One silent AKC session with the same tag — should NOT appear
|
||||||
|
create_memory(
|
||||||
|
"episodic",
|
||||||
|
"AKC voice session abc (gen-002)\nDuration: 60s, 2 captures\n"
|
||||||
|
"\n## Transcript\n(no transcript)\n",
|
||||||
|
project="p05", confidence=0.7,
|
||||||
|
domain_tags=["optics", "session", "akc", "voice"],
|
||||||
|
)
|
||||||
|
html = render_domain("optics")
|
||||||
|
assert "CGH null corrector" in html
|
||||||
|
# The hidden-count banner should be present
|
||||||
|
assert "low-signal" in html or "Ambient provenance" in html
|
||||||
|
# And the empty-transcript content itself is not rendered inline
|
||||||
|
assert "(no transcript)" not in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_domain_page_collapses_akc_session_snapshots(tmp_data_dir):
|
||||||
|
"""AKC voice-session memories are provenance records — count them as
|
||||||
|
a single collapsed link, don't inline every one."""
|
||||||
|
_init_all()
|
||||||
|
for i in range(5):
|
||||||
|
create_memory(
|
||||||
|
"episodic",
|
||||||
|
f"AKC voice session session-{i} (gen-00{i})\nDuration: 120s, 3 captures\n"
|
||||||
|
f"\n## Transcript\nReal transcript number {i}",
|
||||||
|
project="p05", confidence=0.7,
|
||||||
|
domain_tags=["optics", "session", "akc", "voice"],
|
||||||
|
)
|
||||||
|
html = render_domain("optics")
|
||||||
|
# Inline count should mention AKC session snapshots
|
||||||
|
assert "AKC voice session snapshots" in html
|
||||||
|
# None of the session transcripts should be pasted inline on the domain
|
||||||
|
# page (they're provenance, linked via /wiki/activity)
|
||||||
|
assert "Real transcript number 0" not in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_homepage_stats_exclude_ambient_memory(tmp_data_dir):
|
||||||
|
"""Homepage system-stats line shows real memory count, pushes ambient
|
||||||
|
counts into a dimmed sub-segment."""
|
||||||
|
_init_all()
|
||||||
|
# 2 real memories + 3 ambient sessions + 1 silent junk
|
||||||
|
create_memory("knowledge", "Real fact 1", project="p05", confidence=0.8)
|
||||||
|
create_memory("knowledge", "Real fact 2", project="p05", confidence=0.8)
|
||||||
|
for i in range(3):
|
||||||
|
create_memory(
|
||||||
|
"episodic",
|
||||||
|
f"AKC voice session s{i} (gen-00{i})\nReal transcript x",
|
||||||
|
project="p05", confidence=0.7,
|
||||||
|
domain_tags=["session", "akc", "voice"],
|
||||||
|
)
|
||||||
|
create_memory(
|
||||||
|
"episodic",
|
||||||
|
"AKC voice session silent (gen-099)\nDuration: 30s, 0 captures\n"
|
||||||
|
"\n## Transcript\n(no transcript)\n",
|
||||||
|
project="p05", confidence=0.7,
|
||||||
|
domain_tags=["session", "akc", "voice"],
|
||||||
|
)
|
||||||
|
html = render_homepage()
|
||||||
|
assert "3 AKC session snapshots" in html
|
||||||
|
assert "low-signal hidden" in html
|
||||||
|
# Main count reflects only real knowledge
|
||||||
|
assert "2 memories" in html
|
||||||
|
|
||||||
|
|
||||||
|
def test_low_signal_predicate_catches_known_patterns():
|
||||||
|
from atocore.engineering.wiki import _is_low_signal_memory, _is_akc_session_memory
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class M:
|
||||||
|
content: str = ""
|
||||||
|
domain_tags: list = None
|
||||||
|
|
||||||
|
# Explicit empty-transcript — low signal
|
||||||
|
assert _is_low_signal_memory(M(content="AKC voice session x\n## Transcript\n(no transcript)\n"))
|
||||||
|
# E2E test pollution — low signal
|
||||||
|
assert _is_low_signal_memory(M(content="IMG integration test — synthetic session"))
|
||||||
|
assert _is_low_signal_memory(M(content="synthetic AKC integration session"))
|
||||||
|
# Real knowledge — NOT low signal
|
||||||
|
assert not _is_low_signal_memory(M(content="The CGH is mounted to the fold mirror via…"))
|
||||||
|
# AKC session tag predicate
|
||||||
|
assert _is_akc_session_memory(M(content="anything", domain_tags=["session", "akc", "voice"]))
|
||||||
|
assert _is_akc_session_memory(M(content="AKC voice session abc"))
|
||||||
|
assert not _is_akc_session_memory(M(content="Real fact", domain_tags=["optics"]))
|
||||||
|
|||||||
Reference in New Issue
Block a user