VERA Object × CRUD Reliability Matrix
⚠️ STATUS — 2026-06-06 pivot. This matrix tracked making operations deterministic in Slack, role by role. That arc is complete at the
userrow — theuserCRUD cells below are the live Slack record and remain the canonical Slack surface (everyone, all roles, uses Slack foruser-level actions). Everything above theuserrow — every manager/owner/admin admin verb (project config, financials, deadlines, PM assignment, task toggles, allocations, analytical reads) — has moved to the web admin console (docs/decisions/web-admin-console.md). The manager/owner cells below marked ✅ (M-ENV-*, M-PROJ-R/U, M-TASK-R, deadline) reflect Slack gates that still function but are now legacy/frozen (the console is canonical; do not extend the Slack admin path). New manager/owner/admin work is tracked as web-console chunks (C0–C9, Arc 8), not as new Slack-determinism cells here.
Purpose. A single map of every VERA object and the create/read/update/delete operations on it, scoped by the role that can perform each, marking which path is deterministic (no model — extractor→resolver→tool) vs probabilistic (Claude chooses the tool). This is the reference for working reliability up from the ground: every future issue, PR, and manual test should cite a cell ID (e.g. U-LOG-C) so we can pinpoint scope and track a progress bar across the whole surface.
Why this exists. The one paying client is not yet using VERA because basic actions (a user adding time) aren't yet trustworthy enough. We build confidence bottom-up: get the user role's everyday deterministic operations rock-solid first, then manager, then owner, then admin (admin == owner-level access, excluded only from team stats — see lib/roles.js).
Reads are deterministic targets. Retrieval reads ("show my time", "my hours this week", "show my envelopes", "list projects", "time for CityCore") have a fixed query shape and self-scoped or name-resolvable params — they are function calls wearing a sentence and must resolve→query→format→show without the model and without a confirm gate (a wrong read writes nothing and is self-correcting — strictly lower risk than a write). Only analytical reads that require synthesis/judgement ("how's CityCore tracking?", "am I overallocated?", "LER trend") stay probabilistic (box 2). Rule: retrieval reads = DET; analytical reads = PROB. Deterministic reads need a server-side Slack-text formatter per read verb (new lib/read-formatters.js; no existing reusable one — dashboard formatters are HTML).
How to read it.
- Cell ID =
<ROLE>-<OBJECT>-<OP>. ROLE ∈ {U=user, M=manager, O=owner}. OP ∈ {C,R,U,D}. - Path:
DET= on the deterministic execution path today (model never invoked when fully resolved);DET*= deterministic but multi-turn wizard;DET→= retrieval read, target for the deterministic read path (not built yet);PROB= goes through Claude (writes that need it, or analytical reads);—= not applicable / not allowed for that role. - Status: ✅ verified-solid (live-smoked) · 🟡 works but unproven/rough · 🔴 known-broken/issue open · ⬜ not yet assessed · ❄️ frozen (not a green target).
- ⚠️ Role-scoped smoke rule (step 0, non-negotiable): before smoking any role-scoped cell, VERIFY the test account's actual stored role with
SELECT name, role FROM users WHERE tenant_id=… AND name IN ('OTH','MTH','UTH'). Test tenants do not all daily-reseed, so personas carry leftover roles from prior tests (e.g. UTH left asadminfrom an MT-199 run made U-PROJ-R appear to leak period children to a "user" — it was correct admin behaviour, not a bug; cost three rounds of false diagnosis). Expected baseline: OTH=owner, MTH=manager, UTH=user. A role-scoped smoke against the wrong role proves nothing. - Verified against code 2026-06-03: endpoints in
mcp-server.js, permission mapcheckPermission(mcp-server.js:379) + resolverVERB_PERMISSIONS(lib/entity-resolver.js), deterministic set inindex.jsisEligibleForDeterministicExecution(log_time, create_client, create_task) +create-project-wizard.
Scope: the
userrole section is fully enumerated and at the green floor; themanagersection is now fully enumerated (verified against code) and is the current build-up frontier — its cells are mostly ⬜ pending their tier (see Manager arc — build sequence). Theownersection lists objects and op locations; full enumeration comes after manager. We concentrate on deterministic rows first.❄️marks frozen-type cells (Retainer/Agile, #962) that are explicitly not green targets.
Deterministic path — what's actually model-free today (the verified floor)
Confirmed in index.js: log_time, create_client, create_task (single-turn tryDeterministicExecution), and create_project (multi-turn create-project-wizard). Everything else goes through Claude (PROB). Reads are all PROB today (no deterministic read path exists yet).
ROLE: USER (the priority — everyday, must be bulletproof)
A user can: log their own time, read their own time/projects, edit/delete their own entries, request time off. They cannot create/rename/delete projects, clients, tasks, users, or allocate.
| # | Object | C | R | U | D |
|---|---|---|---|---|---|
| Time entry (own) | time_entries | U-TIME-C DET ✅ (live-smoked MT-215+MT-217: number-words, on/for/to reconciliation, reconciled→confirm, ambiguous→menu; wrong-target write closed in every branch — #1043/#1055/#1059) | U-TIME-R DET ✅ (live-smoked, scoped to requesting user, 30-day window) | U-TIME-U DET ✅ (live-smoked MT-219: recency path, confirm gate, ambiguous→menu, ownership enforced — U-TIME-U+D) | U-TIME-D DET ✅ (live-smoked MT-220: always-confirm, recency path, ambiguous→menu, ownership enforced — U-TIME-U+D) |
| Project | projects | — (owner only) | U-PROJ-R DET ✅ (live-smoked, parent rows only — child filter uses parent_project_id) | — | — |
| Client | clients | — (owner only) | U-CLNT-R DET ✅ (live-smoked, model-free) | — | — |
| Task | tasks | — (owner only) | U-TASK-R DET ✅ (live-smoked, model-free) | — | — |
| Envelope/allocation (own) | budgets | — (mgr/owner) | U-ENV-R DET ✅ (live-smoked, model-free) | — | — |
| Time off (own) | pending_pto | U-OOO-C PROB ⬜ | U-OOO-R DET ✅ (live-smoked, model-free) | — | — |
User cell definitions
- U-TIME-C — log own time. DET via
log_timefast path (index.jsisEligible +tryDeterministicExecution; extractorlib/entity-reference-extractor.js; resolverlib/entity-resolver.js). ✅ #1055: three corruption paths fixed (RC1 number-words, RC2 "for <project>" rebind, RC3 menu-digit guard). #1059: ambiguous project → deterministic disambiguation menu (model never picks) — "log 2h on design to retainer" shows a numbered candidate list and waits for a digit, then executes to the chosen project. RC3 guard also applies viamissing_slots: ['project_name']on the disambiguation store entry (belt-and-suspenders). ✅ live-smoked MT-217: 4-retainer menu → "2" → logged 2h to Harbor (entry 1390, hours=2 not 1), model never invoked. MT-215, MT-217. - U-TIME-R — read own time. DET. ✅
show my time/my hours this week/time for <project>route throughtryDeterministicRead(index.js) → MCP endpoints/get_time_entries,/hours_this_week→lib/read-formatters.js. Model never invoked. Self-scoped reads (show my time, no project) passslack_user_idof the requesting user +start_date= 30 days ago so results are own-only and bounded. Unresolved project name (e.g. "BB08x") falls through to model with pre-resolved annotation. MT-209. Live-smoked (#1049). - U-TIME-U — edit own entry. DET. ✅
/edit_entry(mcp-server.js; per-entry ownership check, no role gate). Recency path: "change that to 4h" / "I meant 3h" / "my last entry" → resolves to highest-id entry in 14-day window (explicit recency phrase OR no attribute filters). Explicit-attribute match with 2+ results → numbered menu (digit never consumed as hours). Resolved + new_hours → before→after confirm gate. Server enforces ownership; non-owner silently sees only own entries (resolver's effectiveUserId = requestingUserId). MT-219. - U-TIME-D — delete own entry. DET. ✅
/delete_entry(mcp-server.js; ownership-checked). ALWAYS confirm (destructive, irreversible). "delete that last one" / "delete that entry" → recency path → confirm. Ambiguous → numbered menu → confirm. Server enforces ownership. MT-220. - U-PROJ-R — read projects. DET. ✅
list projects/show all projectsroute throughtryDeterministicRead→/list_projects. Role-aware: endpoint strips all rows withparent_project_idset (the actual child signal —typefield is unreliable) and deadlines foruserrole; formatter renders only what the endpoint returns. MT-211. Live-smoked (#1049). - U-CLNT-R / U-TASK-R — read clients/tasks. DET. ✅ live-smoked (UTH, model-free):
list clients/list tasksroute deterministically viatryDeterministicRead→/list_clients,/list_tasks. MT-213, MT-212. - U-ENV-R — read own envelopes. DET. ✅ live-smoked (UTH, model-free):
show my envelopes/my envelopes/what am I working onroute throughtryDeterministicRead→/get_my_allocations,/get_my_assignments. Envelope table matches morning-briefing format. MT-210. - U-OOO-C / U-OOO-R — request/view own time off. U-OOO-R DET ✅ live-smoked (UTH, model-free):
show my time off/my pto→tryDeterministicRead→/get_my_time_off. MT-214. U-OOO-C remains PROB. Users create a pending item (DMs managers); never log_time to OOO directly (business rule 14).
ROLE: MANAGER (the current build-up frontier — fully enumerated)
Manager = user's abilities plus: allocate hours (any project), update projects, set deadline/manage tasks for their PM'd projects, approve PTO and log time-off for others, percentage-only financial visibility (never dollar amounts), and — unlike a user — can see deadlines. Cannot create/rename/delete projects/clients/tasks/users, assign PM, set rates, set project budgets, or create liabilities (owner only).
Permission scope is not uniform across manager writes — verified against code:
- Any-manager (no PM gate):
allocate_hours/update_allocation/cancel_allocation/reactivate_allocation(lib/entity-resolver.js:219-222),update_project(mcp-server.js:482),manage_time_off=log_sick_day/log_leavefor others (mcp-server.js:487),approve_pto/reject_pto(mcp-server.js:492). - PM-of-project gated:
set_project_deadline,enable_task_for_project,disable_task_for_project— all share thepmOrOwnerGatefn inlib/entity-resolver.js(VERB_PERMISSIONS). Server-side:enable_disable_task(mcp-server.js:463). - ⚠️ Owner-only, NOT a manager write:
set_project_budget. Layer conflict —checkPermissionwould allow a manager-PM (mcp-server.js:444), but the resolver verb-gate is['owner'](lib/entity-resolver.js:225) and fires first (#694), so it is effectively owner-only. Lives on the ownerO-BUDG-Urow, not here.
| # | Object | C | R | U | D |
|---|---|---|---|---|---|
| Envelope/allocation | budgets | M-ENV-C DET ✅ (live-smoked MT-218 as MTH: 3-way name ambiguity → deterministic menu → digit → correct envelope, model never picks; #1053/#1059; any-manager, no PM gate) | M-ENV-R DET ✅ (live-smoked MT-223 as MTH: get_project_assignments PM-gated, get_project_allocation_summary, get_person_allocation_summary — all model-free; #1076) | M-ENV-U DET ✅ (live-smoked MT-227 as MTH: resolved→before→after confirm diff, two-envelope ambiguity→menu→digit, model never picks; any-manager; #1083) | M-ENV-D DET ✅ (live-smoked MT-228 as MTH: always-confirm terminal, ambiguity→menu, reactivate_allocation instant non-destructive undo; any-manager; #1083) |
| Project | projects | — (owner only) | M-PROJ-R DET ✅ retrieval ("list projects", "show project info for X") — live-smoked MT-224 as MTH, no contract_value/billing_rate in manager payload; analytical ("how's X tracking", LER) stays PROB; financial gate at get_project_info endpoint; #1076 | M-PROJ-U PROB ⬜ (#1084: set_project_deadline DET confirm-gate ✅ — PM-of-project fn-gate; natural-language date → confirm with ISO date → fire; non-PM denial deterministic; update_project financial-write hole closed: contract_value/billing_rate/hours_budget owner-only on write; general edits stay PROB) | — (archive = owner only) |
| Task (per project) | project_tasks | — (owner only) | M-TASK-R DET ✅ (list_tasks already DET; get_project_tasks_read per-project tasks — live-smoked MT-225 as MTH, model-free; #1076) | M-TASK-U DET ⬜ (#1090: enable_task_for_project instant-fire DET; disable_task_for_project confirm-then-fire DET; pmOrOwnerGate in resolver; "remove the X task from Y" no longer hijacked by delete_entry; pending live smoke MT-231) | — (archive = owner only) |
| Time off (others) | pending_pto | M-OOO-C PROB ⬜ (approve_time_off/log_sick_day/log_leave; manage_time_off any-manager mcp-server.js:487) | M-OOO-R DET ✅ (list_pending_pto@4808 — manager-scoped; get_team_time_off@5237 owner-only so not wired; live-smoked MT-226 as MTH; #1076) | M-OOO-U PROB ⬜ (approve_pto/reject_pto; any-manager mcp-server.js:492) | — |
| Sprint (frozen #962 — not a green target) | projects (agile child) | M-SPR-C ❄️ frozen (create_sprint — creation blocked #962) | M-SPR-R ❄️ read-only (functions; not a confidence target while frozen) | M-SPR-U ❄️ (update_sprint_status on existing sprints; not a target) | — |
M-SPR-*is excluded from "everything green." Agile creation is frozen (#962); per the frozen-types shelve policy, frozen-type-specific behaviour is not a confidence target. Reads/update_sprint_statuson pre-existing sprints still function and report correctly.
Manager cell definitions
- M-ENV-C — manager allocates hours. DET via
allocate_hoursfast path (any-manager, no PM gate —lib/entity-resolver.js:219). Mirror ofU-TIME-C: type-reconciliation (#958),on<project>to<user>boundary fixed (#1053), ambiguous project → deterministic disambiguation menu (#1059, model never guesses). ✅ live-smoked MT-218 as MTH: "allocate 10h to Sarah Chen on Test for design" → 3-way menu (Test – T&M / Test – Blue Website / Test – Pro Bono) → "1" →[allocate-disambiguate] executing allocate_hours → candidate[1]→ envelope written to Test – T&M Project, both turns deterministic (noanthropic.message). The model never picked the candidate. - M-ENV-R — read allocations (team/project/person). Built by #1076. "who's on Nike?" →
get_project_assignments(PM-gated for managers); "show allocations for <project>" →get_project_allocation_summary; "what is <person> allocated?" →get_person_allocation_summary. RoutestryDeterministicRead→ endpoint →lib/read-formatters.js, model never invoked. Verbs inlib/verb-context-table.js. ✅ live-smoked MT-223 as MTH: all three verbs deterministic (100–220ms); the PM-gate fired correctly ("who's on CityCore" → scoped denial, MTH doesn't PM it); per-envelope rows faithful (no fanout — endpoint doesenvelopes.map, no grouping). - M-ENV-U — update allocation. DET via the
update_allocationgate + confirm handler (index.js), mirroring U-TIME-U. The resolver's syntheticallocation_id(resolveEnvelopeReference) pins the envelope:resolved+ new hours → before→after confirm gate ("Change … (40h) to 50h? Reply 1"); two envelopes on one project → numbered menu, model never picks;not_found/missing-hours fall through to the model. Audit shows the diff ("40h → 50h"); target user is DM'd. ✅ live-smoked MT-227 as MTH (#1083): confirm diff + 2-envelope menu, every turn model-free. - M-ENV-D — cancel allocation. DET via the
cancel_allocationgate + confirm handler, mirroring U-TIME-D. Terminal (no lineage tracked) and always confirms ("Cancel … This is terminal — reply 1"); ambiguity → menu.reactivate_allocationis the non-destructive undo — fires instantly on a unique resolve (no confirm), menu on ambiguity. ✅ live-smoked MT-228 as MTH (#1083): always-confirm cancel, menu, instant reactivate, all model-free. - M-PROJ-R — manager project view. Split cell: the retrieval form ("list projects", "show project info for <project>") is built by #1076 —
list_projects(already DET) +get_project_info_readverb →get_project_infoendpoint. Financial fields (contract_value,billing_rate) are gated at the endpoint for non-owner roles (fix in #1076). The analytical form ("how's <project> tracking", LER trend) stays PROB (box-2 rule). ✅ live-smoked MT-224 as MTH:show project info for CityCorereturned Type/Status/PM/Deadline/Hours-budget but no contract_value, no billing_rate — confirmed at the data layer (the keys are absent from the manager'sget_project_infopayload, not merely hidden in the formatter). Deterministic (146ms). Bonus: the analytical "how's CityCore tracking" correctly routed to the model and still respected the PM-gate (get_project_statusdenied) — no dollar leak on the PROB path either. - M-PROJ-U — update project. Split disposition (#1084):
set_project_deadlineis DET (PM-of-project fn-gate atlib/entity-resolver.js:208; natural-language date → confirm gate showing the ISO date → fire; non-PM manager denial is deterministic; ISO dates fall through to model).update_projectfinancial-write hole closed:contract_value/billing_rate/hours_budgetare owner-only on write — a manager attempting to set them gets a 403 from the per-field gate added atmcp-server.js(after the role check). General manager edits (new_name,status,notes,deadline,is_billable,alert_threshold) stay PROB (multi-field heterogeneous, low frequency).set_project_budgetis NOT here — owner-only (see scope note). ⬜ (pending live smoke MT-229/230). - M-TASK-R — read tasks.
list_tasks(already DET for all roles) +get_project_tasks_readverb for per-project task lists ("list tasks for <project>"). Built by #1076. ✅ live-smoked MT-225 as MTH: "list tasks for CityCore" →get_project_tasks_read, project resolved, 7 task types scoped to the project (146ms); "list tasks" → plainlist_tasks, all 7 (82ms) — no collision between the two verbs (!debug lastconfirmed the routing). Both model-free. - M-TASK-U — enable/disable task for project.
enable_task_for_project/disable_task_for_project, PM-of-project gated (pmOrOwnerGateinlib/entity-resolver.js). Now DET (#1090). Wired in the extractor (object_nouns: ['task'];strip_keyword_case_insensitive: trueon task_name so "Remove the task Design" works); resolver (pmOrOwnerGate);index.jsgates (enable = instant fire, disable = confirm→fire)."remove the X task from Y"no longer hijacked bydelete_entry— thetaskobject_noun's finite position beatsdelete_entry's null/Infinity.update_projectentry added to extractor to prevent field phrasings from routing toedit_entry/set_rate(routing only — model execution in Phase 1). Pending live smoke MT-231/232. ⬜. - M-OOO-C — log/approve time off for others.
approve_time_off(resolves a pending request and logs the entry),log_sick_day,log_leave— gated bymanage_time_off, any-manager (mcp-server.js:487). PROB (multi-step approval judgement). ⬜. - M-OOO-R — team time off / pending approvals.
list_pending_pto(mcp-server.js:4808) — approve_pto permission = manager+owner. Built by #1076. Note:get_team_time_off(mcp-server.js:5245) is owner-only (isOwnerLevelgate) and is NOT wired here. ✅ live-smoked MT-226 as MTH: "show pending time off" →list_pending_pto, 16 pending (154ms); synonym "pending approvals" routes to the same verb. Model-free. - M-OOO-U — approve/reject PTO.
approve_pto/reject_pto, any-manager (mcp-server.js:492). PROB (decision turn). ⬜.
Manager arc — build sequence (mirrors the user arc, tier by tier)
Each tier becomes its own prescriptive issue when the prior tier lands (the proven user-arc cadence — don't open them all at once). "Green" = live-smoked-solid, whether the path is DET or PROB; retrieval reads go DET, analytical reads stay solid-PROB.
- Tier 1 — retrieval reads + close M-ENV-C (✅ DONE: #1076, live-smoked MT-218/223/224/225/226 as MTH). M-ENV-R, M-PROJ-R (retrieval), M-TASK-R, M-OOO-R deterministic; M-ENV-C closed ✅.
- Tier 2 — allocation writes deterministic (✅ DONE: #1083, live-smoked MT-227/MT-228 as MTH). M-ENV-U (
update_allocation) and M-ENV-D (cancel_allocation, must-confirm;reactivate_allocationinstant undo) on the fast path, mirroring M-ENV-C. The manager envelope row is now C/R/U/D-complete and model-free. - Tier 3 — project/task updates (#1084 + #1090). M-PROJ-U:
set_project_deadlineDET (confirm gate, PM-gated, model-free);update_projectfinancial-write hole closed (owner-only gate); field routing fixed —update_projectextractor entry preventsedit_entry/set_ratehijacks (#1090 Part B); general edits stay PROB (Phase 2). M-TASK-U now DET (#1090):enable_task_for_projectinstant-fire,disable_task_for_projectconfirm→fire, both PM-of-project gated,pmOrOwnerGateshared fn in resolver. Pending live smoke MT-229/230/231/232 → ⬜. - Tier 4 — OOO approvals. M-OOO-C and M-OOO-U — approval-decision turns, smoke the permission boundaries (MTH approves/rejects; UTH denied).
Sprint (M-SPR-*) excluded — frozen (#962).
ROLE: OWNER (and ADMIN ≡ owner-level) — objects + locations, status ⬜ pending
Owner = everything. Admin = owner-level access (isOwnerLevel true) but excluded from team-delivery stats (isDeliveryResource false — lib/roles.js); no material CRUD difference from owner — admin rows = owner rows.
| # | Object | C | R | U | D |
|---|---|---|---|---|---|
| Project | projects | O-PROJ-C DET* ✅ (create-project-wizard; live-smoked as OTH #1035, name over-capture fixed #1042) | O-PROJ-R PROB ⬜ | O-PROJ-U PROB ⬜ (update_project,rename_project) | O-PROJ-D PROB ⬜ (cancel_project/close_project; archive, no hard delete) |
| Client | clients | O-CLNT-C DET ✅ (create_client; live-smoked as OTH #1036) | O-CLNT-R PROB ⬜ (list_clients,get_client_summary) | O-CLNT-U PROB ⬜ (rename_client) | O-CLNT-D PROB ⬜ (archive_client/unarchive_client) |
| Task (global) | tasks | O-TASK-C DET ✅ (create_task; live-smoked as OTH #1036) | O-TASK-R PROB ⬜ (list_tasks) | O-TASK-U PROB ⬜ (rename_task,update_task) | O-TASK-D PROB ⬜ (archive_task/unarchive_task) |
| User | users | O-USER-C PROB ⬜ (add_user) | O-USER-R PROB ⬜ (list_users) | O-USER-U PROB ⬜ (update_user_role,rename_user,assign_pm) | O-USER-D PROB ⬜ (deactivate_user/reactivate_user) |
| Discipline | disciplines | O-DISC-C PROB ⬜ (add_discipline) | O-DISC-R PROB ⬜ (list_disciplines) | O-DISC-U PROB ⬜ (rename_discipline) | O-DISC-D PROB ⬜ (archive_discipline) |
| Rate | rate_history | O-RATE-C PROB ⬜ (set_rate, owner-only, audit-suppressed) | O-RATE-R PROB ⬜ (get_rates) | — | — |
| Liability | liabilities | O-LIAB-C PROB ⬜ (create_liability, vendor-only) | O-LIAB-R PROB ⬜ (list_liabilities) | — | O-LIAB-D PROB ⬜ (cancel_liability) |
| Company holiday | company_calendar | O-HOL-C PROB ⬜ (add_company_holiday) | O-HOL-R PROB ⬜ | — | O-HOL-D PROB ⬜ (remove_company_holiday) |
| Budget (project total) | projects.hours_budget | — | (see project) | O-BUDG-U PROB ⬜ (set_project_budget) | — |
Frozen for creation (#962), reads only: Retainer/Agile projects, retainer periods, sprints. Not confidence targets.
Progress (deterministic rows only — the build-up target)
| Cell | What | Path | Status |
|---|---|---|---|
| U-TIME-C | user logs own time | DET | ✅ — wrong-target write closed: clean fires, reconciled confirms, ambiguous menus (#1043/#1055/#1059) |
| U-TIME-R | user reads own time | DET | ✅ — scoped to requesting user, 30-day window (#1049) |
| U-TIME-U | user edits own time entry | DET | ✅ — live-smoked MT-219 (recency, confirm, ambiguous→menu, ownership) |
| U-TIME-D | user deletes own time entry | DET | ✅ — live-smoked MT-220 (always-confirm, recency, ambiguous→menu, ownership) |
| U-ENV-R | user reads own envelopes | DET | ✅ — live-smoked (#1045/#1046) |
| U-PROJ-R | user lists projects | DET | ✅ — child filter uses parent_project_id (#1049) |
| U-TASK-R | user lists tasks | DET | ✅ — live-smoked (#1045/#1047) |
| U-CLNT-R | user lists clients | DET | ✅ — live-smoked (#1045/#1047) |
| U-OOO-R | user views own time off | DET | ✅ — live-smoked (#1045/#1047) |
| M-ENV-C | mgr allocates hours | DET | ✅ — live-smoked MT-218 as MTH (3-way name ambiguity → menu → digit → correct envelope, model never picks; #1053/#1059) |
| M-ENV-R | mgr reads allocations (team/project/person) | DET | ✅ — live-smoked MT-223 as MTH (#1076; PM-gate fires, no fanout) |
| M-PROJ-R | mgr project view (retrieval only; analytical stays PROB) | DET | ✅ — live-smoked MT-224 as MTH (#1076; no contract_value/billing_rate in manager payload) |
| M-TASK-R | mgr reads tasks | DET | ✅ — live-smoked MT-225 as MTH (#1076; per-project + global, no verb collision) |
| M-OOO-R | mgr reads team time off / pending approvals | DET | ✅ — live-smoked MT-226 as MTH (#1076; list_pending_pto only, get_team_time_off is owner-only) |
| M-ENV-U | mgr updates allocation | DET | ✅ — live-smoked MT-227 as MTH (confirm diff, 2-envelope menu, model-free; #1083) |
| M-ENV-D | mgr cancels allocation (must-confirm) + reactivate undo | DET | ✅ — live-smoked MT-228 as MTH (always-confirm, menu, instant reactivate; #1083) |
| O-CLNT-C | create client | DET | ✅ — live-smoked as OTH (#1036) |
| O-TASK-C | create task | DET | ✅ — live-smoked as OTH (#1036) |
| O-PROJ-C | create project | DET* | ✅ — live-smoked as OTH (#1035); name over-capture fixed (#1042) |
The user's everyday loop = U-TIME-C (log) + U-TIME-R (read back) + U-ENV-R (what am I on). All three must be ✅ and model-free before the role is "trustworthy." This is the client-confidence bedrock.
Definition of ✅ for a cell: deterministic path engages on the natural phrasings (verified raw-text→real-resolver), correct write/read, receipt shown (no scroll/spinner artifact), live-smoked, and an MT exists citing the cell ID.
Conventions for issues / PRs / MTs
- Title or body must cite the cell ID(s) in scope (e.g. "U-TIME-C: confirm-gate for ambiguous time logs").
- An MT that proves a cell must reference the cell ID and update its Status here in the same PR.
- Flip a cell to ✅ only after a live smoke (the standing rule: green tests are necessary, not sufficient).
- Keep this file current — it is the single source of truth for the reliability progress bar.