fix: keep team decisions scoped to their project - #613
Conversation
Signed-off-by: cdeust <cdeust@icloud.com>
…3.10 CI on Python 3.10 failed at collection of the two reclassification test modules: typing.Self exists from 3.11. The context manager now returns its concrete class under postponed annotations, with no new dependency. The validation record gains the owner-approved mapping of memory 4353879 and the targeted acceptance result on the restored snapshot. The mappings file itself names rows of one private store and stays outside the repository. Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
Four families of CI failures on this branch, none of them a product defect: - the frozen remember oracle (fixtures/w3_4) did not pass team_decision, so bulk and scalar rows differed by one key; - the agent briefing receipts tests seeded rows with no directory_context, which the project predicate now excludes by design; they seed the event's project, the team row is an is_team_decision row, and a row of another project is asserted absent (#611); - I2 allow-list line numbers in sqlite_store.py shifted by one; - scripts/reclassify_team_scope.py reaches core and now wires the composition root (#560). Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
…method-size gate Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
|
ZETETIC-REVIEW: REQUEST_CHANGES Scope reviewed: full diff (git diff origin/main...HEAD, 32 files, ~1235 added lines), ADR-1083, the reclassification scripts and their tests, both hook read paths, both storage backends, schema migration ordering, and the validation doc. Ran the full team-scope-related suite locally against real PostgreSQL (CORTEX_MEMORY_STORE_BACKEND=postgresql, CORTEX_TEST_DATABASE_URL=postgresql:///cortex_611_test): 59 passed. CI on the PR is fully green (all jobs pass, including Craftsmanship Gate, Type Check, Fuzz, Docker Smoke). Blocking
Verified correct (not blocking)
Move 0 (ledger reconciliation and seen-defect check)The PR body and validation doc enumerate every remaining gap explicitly (unresolved production rows, the 60-second SessionStart launcher timeout, no production reclassification yet) and tie each to a named follow-up rather than dismissing it as "pre-existing" or "unrelated." No un-issued seen-defect rationalization found. This is an honest draft PR that says outright it is not claiming #611 closed. What would unblock mergeClose finding 1: bound classify()'s default behavior to the actual defect signature, or invert the default so an unrecognized legacy-global row is left global and reported for owner review rather than cleared, and add a test for the no-agent-context / non-decision / currently-global case. The rest of the change (schema, write path, both hook readers, backfill, migration ordering) is correct and well tested. |
…al row Review finding on #613: classify() cleared is_global on any resolved row the detector did not confirm, so a row made global by an explicit act was lost. A row now loses global scope only when it carries the defect signature, a decision written under an agent context. Other global rows are kept, listed under unexplained_global_ids, and the owner can clear one by id through clear_global_ids. An id cannot be both kept and cleared. Rerun on a fresh restore of the same archive: 93 changes, 0 on the second pass, 67 globals retained (46 unresolved, 6 left to the owner), row count unchanged. The validation record carries the new figures and no longer mentions unrelated private plans. Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
Refs #611 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NLvPnrr8Ab6cKPK1Kt3K7f Signed-off-by: cdeust <cdeust@icloud.com>
|
ZETETIC-REVIEW: APPROVE Re-review of commit f48f17a on branch fix/611-team-scope, same worktree. Verified independently, not just read the diff. Blocking finding from the first review: closedscripts/reclassify_team_scope.py:88-109 (classify()). The rule is now: a currently-global row loses is_global only when it carries the actual defect signature, bool(row.agent_context) and is_decision_content(row.content). Any other global row is kept, tagged reason="global_origin_not_the_defect", and surfaced in make_report()["unexplained_global_ids"]; the owner can clear one explicitly through the new clear_global_ids mapping list, and load_mappings rejects an id present in both keep_global_ids and clear_global_ids. I reproduced my original counterexample against the new code and it is now protected: I then tried to construct two more counterexamples, independently of the new test file:
The new regression test (tests_py/scripts/test_reclassify_team_scope.py::test_a_global_row_the_defect_could_not_have_produced_stays_global) encodes exactly my original repro plus the promoted/keep_global_ids interaction; test_the_owner_can_clear_a_global_the_script_would_keep and test_an_id_cannot_be_both_kept_and_cleared cover the new escape hatch and its guard rail. 109 tests pass locally (tests_py/hooks/test_team_project_scope.py, test_hook_receipts.py, test_agent_briefing.py; tests_py/scripts/test_reclassify_team_scope.py and _pg.py; tests_py/core/test_global_scope_resolution.py, test_memory_ingest_scope_persistence.py, test_memory_ingest_capture_origin.py; tests_py/handlers/test_remember_team_scope.py; tests_py/infrastructure/test_team_scope_backfill.py; tests_py/invariants/test_I2_canonical_writer.py), run against real PostgreSQL (CORTEX_MEMORY_STORE_BACKEND=postgresql, CORTEX_TEST_DATABASE_URL=postgresql:///cortex_611_test). Minor, non-blocking observation on the new logicclassify() cannot clear a row the content detector currently confirms as cross-project, even through clear_global_ids: kept = id in keep_global_ids or detected short-circuits before cleared is checked, so a detector false positive has no override path in this script (the operator would have to fix the detector or edit the row directly). This matches the ADR wording precisely ("the content detector also preserves a row") so it is a deliberate, documented choice, and it biases toward the safer direction for an operation the PR's own procedure gates behind backup and dry-run review. Not blocking. The reason field is overwritten to "global_origin_not_the_defect" when a row is kept-but-unexplained, discarding whatever directory-resolution reason (recorded_project / owner_memory_mapping / verified_domain_mapping) it held before. Cosmetic only, does not affect which rows get resolved or how, and does not appear in any assertion. Not blocking. Hygiene items from the first review: closeddocs/validation/issue-611-team-scope.md no longer mentions the LinkedIn draft plan or a third party by name (grepped both terms, no matches). A new "Rerun after review" section documents the fresh figures on a second restore of the same archive: 93 changes, 0 on the second pass, 67 retained globals (46 unresolved, 6 named and kept as not produced by the defect), 47,041 rows unchanged before and after. Documentation state not yet pushed (report, not blocking)The ADR wording update that is supposed to accompany f48f17a (narrowing "otherwise evaluate the existing content detector and clear is_global..." to describe the new defect-signature-only rule) is present only as a staged, uncommitted change in this worktree; git show origin/fix/611-team-scope:docs/adr/ADR-1083-....md still carries the old wording that describes the behavior my first review flagged, not the behavior actually implemented by f48f17a. Recommend committing and pushing that wording change (docs/adr and its wiki mirror) before merge so the ADR matches the code the PR actually ships. Two items intentionally left unchanged, my view(a) SQLite --apply has no --backup requirement (reclassify_team_scope_db.py, verify_backup only gated on args.database_url). Non-blocking for this PR: the operator procedure this PR documents targets PostgreSQL production, SQLite's own backup (copying the file) is far lower-friction than pg_dump verification, and the correctness of classify() does not depend on which backend applies it. Fine to track as a follow-up issue rather than block here. (b) SQLite session_start has no Team Decisions reader at all (is_team_decision is write-only on that path). Non-blocking: it is not a leak, since memory_matches_project already gates every SQLite banner row by directory_context/is_global regardless of the team flag; it is a feature gap, not a correctness gap, and #611's actual defect (cross-project leak) does not depend on it. Fine as a follow-up issue. CIgh pr checks 613 -R cdeust/Cortex, snapshot at review time: Lint, Changed paths, CodeQL, the three Analyze jobs, HOL plugin scan, plugin-scanner and the identity-contract check have passed. Build Package, Craftsmanship Gate, Type Check, Docker Smoke, Validate MCP host configurations, the two Fuzz PR-batch jobs, and all five Test jobs (Python 3.10-3.13, SQLite backend, Windows SQLite) are still pending on the fresh run triggered by f48f17a; none has failed so far. I ran the directly affected suite locally against real PostgreSQL instead of waiting on the full matrix (109 passed, see above). VerdictAPPROVE on the code. The blocking finding from the first review is closed with a verified fix and a regression test that encodes my exact reproduction; the two hygiene items are closed; the two consciously-deferred asymmetries are reasonable follow-up-issue material, not blockers. Please push the staged ADR wording commit so the merged ADR text matches the shipped behavior, and confirm the still-pending CI jobs land green before merging. |
|
ZETETIC-REVIEW: APPROVE Final confirmation on head commit 334e15d, branch fix/611-team-scope. Verified independently, not from the coordinator's description alone:
This closes the last open item from my prior round (round 2, comment #613 (comment)): the ADR wording now matches the shipped behavior, and the full CI matrix that was pending at that time has since gone green with nothing red. My verdict from round 2 stands: the blocking finding from round 1 (classify() in scripts/reclassify_team_scope.py losing an explicit global whenever the content detector did not confirm it) was fixed in f48f17a and independently verified with my own counterexamples, not just the added test. Nothing in 334e15d touches code, tests, or the validation doc, so that verification still holds at this head. Verdict: APPROVE at 334e15d. |
Agent decisions currently set
is_global, so they bypass the project predicate introduced in #606. This change storesis_team_decisionseparately, scopes both team-decision readers by project, preserves ingestion scope through database reopening, and stops initialization from promoting decisions to global scope. Explicit global writes and the content detector retain their behavior.Refs #611. ADR-1083 is proposed for owner review. Task B is outside this PR.
Validation
pg_dump -Fcfully restored into an isolated database. Reclassification changes 98 of 160 global candidates; second pass changes zero. All 47,490 rows and every field outside the three scope fields have identical before/after fingerprints.write_class; all eight pass on both backends afterward.Owner decisions and rollout
Production has not been reclassified. All 47 rows without
directory_contextalso have nodomain, so domain mapping cannot resolve them. Their IDs and the operator procedure are in the validation report. In particular, 4353879 needs an explicitly approved mapping to japonais-2027; 4356519 also remains in the parent's Team Decisions query pending review.Without that mapping, the real snapshot acceptance condition still fails: the parent auto-recall includes the Score audit decision. The full SessionStart launcher validation also timed out at 60 seconds; direct query tests passed but installed-plugin acceptance remains outstanding. This PR is a draft for review, not a release-ready claim that #611 is resolved.
Deploy the reviewed code before applying the data operation: an old installation's initialization backfill can reverse a data-only repair. Re-run the installed-plugin acceptance after release and update, as required by the owner plan.
Completion ledger
The validation report maps schema, write, read, migration, rollback, unresolved-data, and archive-check paths to their tests and snapshot evidence. The unresolved production acceptance is called out above and remains required before closing #611.