Skip to content

fix(meetings)!: confirming a meeting project keeps the owner's reason; a stale fingerprint is conflict - #2913

Merged
leemour merged 2 commits into
mainfrom
fix/review-compact-meeting-project-reason
Nov 13, 2026
Merged

leemour merged 2 commits into
mainfrom
fix/review-compact-meeting-project-reason

Conversation

@leemour

@leemour leemour commented Nov 13, 2026

Copy link
Copy Markdown
Member

Task

cli-core bugs that memo's new test matrix found (memo #829).

Why

  • The owner's reason was lost. linkMeetingProject with expectedFingerprint confirmed an agent's proposal, but kept the agent's provenance and dropped the owner's --reason.
  • A stale fingerprint had the wrong code. It answered configuration_error, but the meeting's project had changed meanwhile, which is conflict.
  • --json on store recover run printed text in memo. memo's parent store recover also takes --json, and commander gave the flag to the parent.

What

  • Reason: when the owner confirms with a reason, it replaces the agent's.
  • Stale fingerprint: meeting-project.stale is conflict. No downstream CLI checks the old code (memo checked).
  • --json: cli-core reads output flags with optsWithGlobals, so its own commands answer JSON. A probe of the shared command tree found no parent/child --json clash. One test pins the case.
  • Not here: --compact on a single item, such as review show. The flag promises "one line per item", but compactText draws a single item as key: value lines on purpose. Making it one line changes every CLI's show output, so it needs a decision first.

Testing

  • meeting-projects.test.ts: the confirmed link carries the owner's reason, and a stale fingerprint is conflict. Both failed first.
  • envelope.test.ts: a runnable parent and its child, both with --json, answer JSON.
  • src/store, src/cli, src/errors and src/services pass (6757 tests). Typecheck, biome and docs:check pass.

🤖 Generated with Claude Code

leemour and others added 2 commits November 13, 2026 04:37
…; a stale fingerprint is conflict

memo's test matrix (memo #829) found that linkMeetingProject with expectedFingerprint dropped the owner's --reason
and kept the agent's provenance, and that a stale fingerprint answered configuration_error. The owner's reason now
replaces the agent's when given, and meeting-project.stale is a conflict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…JSON

memo's own store recover read --json from the child's opts and printed text (memo #829). cli-core reads the output
flags with optsWithGlobals, so its commands answer JSON; this pins it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@leemour
leemour merged commit 43ba116 into main Nov 13, 2026
43 checks passed
@leemour
leemour deleted the fix/review-compact-meeting-project-reason branch November 13, 2026 03:48
leemour added a commit that referenced this pull request Nov 13, 2026
…rows; IPv6 model addresses; calls link without memo; cli-base 0.99.0 (#2915)

## Task

The cli-core round of 2026-11-13: one PR, so every WireCat CLI moves to
one release that carries every fix. Source: the
review plan of 2026-11-12 (`reports/2026-11-12-cli-family-review.md` in
cli-private) and whatsapp-cli #386. The
release also carries #2906, #2910, #2912, #2913 and #2914, already on
main.

## Why

- **SEC, data loss (WC-SECR-24).** In a run with only
`<PREFIX>_CONFIG_DIR` moved, the state folder is the owner's.
0.650.0 kept the files cli-core deletes itself, but `account remove`
still called the tool's own `signOut`, which
deletes the session where the run's state folder says: the owner's. wa
lost its device this way and guards with
`device.owners`. A moved config folder that is a link to the owner's own
also let cli-core rewrite the owner's
  `config.json`.
- **`--compact` on one item** printed `key: value` lines, though its
help promises one line. The owner chose option C:
  an opt-in row.
- **cli-base 0.99.0** adds `SHARED_HOSTS` to `cli-dev manifest`, beside
cli-core's `CORE_HOSTS`.
- **An IPv6 model address** such as `https://[fd00::1]` was refused:
`hostFetch` takes no IPv6 literal.
- **The issue link** came only from the app definition's `issues`; max
sets none.
- **Calls reached CRM contacts only through memo's `people scan`.**

## What

- `account remove`: a run whose state folder is the owner's never calls
`signOut`; the sign-in is listed under `kept`
with the moved-run reason. The owner's-folder check follows links.
Keyring: `Credentials` with `isolated` deletes
only the run's temporary entry (cli-base `credentials.js` `remove`), so
that part was already safe.
- `RowShape.oneLine`: an item answer prints as the row a list of it
prints (with the list's header and day line), and
its `sections` follow as rows. Nothing changes for a row without it.
memo's `review show|confirm|reject` adopt it in
  memo.
- cli-base 0.99.0 (dev; the peer range stays `>=0.98.0`). `CORE_HOSTS`
is typed out as the two model providers plus
`SHARED_HOSTS`, and a test keeps the two equal. Importing `SHARED_HOSTS`
instead would stop every CLI still on
cli-base 0.98 from loading; cli-base cannot read cli-core's list. No CLI
reads `CORE_HOSTS` today.
- Lint baseline: no row went stale under 0.99.0 (`cli-dev lint`: 69
violations, all in the baseline, 0 new);
`services/archive.ts` and `services/project-gathering.ts` are still
flagged. **One row added, an exception for the
owner:** `src/models/endpoint.ts fetch`. The loopback `[::1]` path
always used the global `fetch`; a ternary hid it
from the rule, and the new IPv6 path uses it too. It goes once
cli-base's `hostFetch` takes bracketed IPv6 literals.
- IPv6: a non-loopback IPv6 model address goes through a small fetch
with `hostFetch`'s rules: only that host, the
same deadline, no redirect followed. `network.host-undeclared` is now
registered in cli-core's reasons.
- `doctor reports show|create`: without `issues`, the GitHub repository
in the installed package's `bugs` or
`repository` gives `<repo>/issues/new`. Read only when a report command
runs.
- A meetings sync (`pull`) that saved callers with a number runs the
scan's phone rule for those callers only
(`linkPhones`, through the optional `MeetingStore.linkCallers`). No
other rule, no scan cursor moved, so memo's next
  scan still sees them. A failure is a sync warning.

## Testing

- Failing first: `account-remove.test.ts` (sign-out with only config
moved; the linked config folder), `endpoint.test.ts`
(IPv6), `doctor-reports-command.test.ts` (package.json fallback),
`compact.test.ts` (oneLine equals the list row,
also with a ref prefix and a time column), `call-phone-link.test.ts` (a
sync links the caller without a scan, leaves
  same-name contacts apart, and leaves the scan cursor unset).
- Missing direct tests added: a call in `canonicalPersonContext`'s
`meetings`; the issue link in the note a person reads.
- Measured: `linkCallers` on a store of 20,000 CRM contacts takes 55–70
ms (a store with many messenger identities that
carry phones reads more rows). Only a sync that saved a caller with a
number pays it: today the unpublished beeline.
- Locally: typecheck, lint, `docs:check`, `docs:lint`, `schema:check`,
`check:dist`, `check:agent-process`,
`help:examples`, `standards:check`, `ci:quick`, and the full suite with
coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant