Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .commitrail/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ for current product capability.
|---|---|---|
| [WS-DB-002](initiatives/WS-DB-002/OVERVIEW.md) | Complete | Shared UUIDv7 record generation, native-UUID relationships and fresh v0.1 baseline; natural-owner retry custody and aligned CI/local setup |
| [WS-MCP-002](initiatives/WS-MCP-002/OVERVIEW.md) | Planned | Nine tools through WS-MCP-002-03: self-service and administrative reads; 18 tools remain and WS-MCP-002-04 administrative grant mutations are next |
| [WS-CLI-001](initiatives/WS-CLI-001/OVERVIEW.md) | Planned | Two public Go CLI reads delivered through WS-CLI-001-01; profile editing and later public workflows remain |
| [WS-CLI-001](initiatives/WS-CLI-001/OVERVIEW.md) | Planned | Public Go CLI reads and human profile editing delivered through WS-CLI-001-02; role-specific public journeys and binary distribution remain |
| [WS-ARCH-001](initiatives/WS-ARCH-001/OVERVIEW.md) | Planned | Source storage, inert AUTH contracts, TASK request reservation and hidden exact AUTH preparation are delivered; CON-07/shared acceptance prerequisites are next for the selected automated path, followed by hidden handlers and atomic routing activation. True admission does not depend on CON/shared acceptance. |
| [WS-ART-001](initiatives/WS-ART-001/OVERVIEW.md) | Planned | Exact checker input/output custody and packet foundations are delivered; routing integration, remediation and public intake remain. |
| [WS-AUTH-001](initiatives/WS-AUTH-001/OVERVIEW.md) | Planned | AUTH-19A commitments, TASK request reservation and ARCH-04E2-A hidden strict PREP matching are delivered; the action remains unavailable, with CON-07/shared acceptance, atomic receipt custody and scoped activation still required for the automated path. |
Expand Down
18 changes: 11 additions & 7 deletions .commitrail/initiatives/WS-CLI-001/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
Workstream operations without creating another identity, authority, or product
lifecycle implementation.
- Delivered boundary: [WS-CLI-001-01](WS-CLI-001-01.md), caller-owned profile and
exact-project authorization context through the public REST API.
exact-project authorization context; [WS-CLI-001-02](WS-CLI-001-02.md), human
self-profile editing through the public REST API.

## Current boundary

Expand All @@ -16,8 +17,11 @@ The backend exposes `GET /api/v1/actors/me` and
those operations, but is not a certification of every public or hidden route.
The independent [MCP adapter](../../../mcp_server/README.md) forwards caller
bearers to the same API; it is not a CLI client library. The independent Go
package under `cli/` implements `whoami` and `project access PROJECT_ID`, with
text/JSON output and built-binary integration proof. Further public workflows
package under `cli/` implements `whoami`, `project access PROJECT_ID`, and
`profile update` for caller-owned human display fields. All have text/JSON
output and built-binary integration proof. Mutations preserve omitted/null
semantics and explicitly report uncertain outcomes without automatic retries.
Further public workflows
and binary distribution remain proposed below.

## Design
Expand All @@ -41,16 +45,16 @@ completion. Bubble Tea is a candidate for a later, bounded TUI change, not a
dependency of the foundation. Do not add Python, TypeScript, or Rust duplicate
CLIs. Keep the package independent of backend and MCP runtime dependencies.

## Proposed PR boundaries
## Delivered and later boundaries

1. **WS-CLI-001-01:** Independent Go package, safe caller-token transport, exact
self-profile and project-authorization reads, human/JSON output, built-binary
integration proof, package CI, and documentation. `GET /actors/me` can cause server-owned first
admission and last-seen updates; the CLI must not describe it as side-effect
free.
2. **Later self-service writes:** Profile editing and any further public
self-service operation, with explicit omission/null and uncertain-mutation
behavior. Define its own bounded record when started.
2. **WS-CLI-001-02:** Human profile editing through the existing public PATCH,
with explicit omission/null and uncertain-mutation behavior. It changes no
identity, authority or backend policy.
3. **Later governed-work commands:** Add project setup, task, submission,
review, revision, and contribution reads/writes only as their actual public
contracts and authority boundaries become available. Split by user journey,
Expand Down
135 changes: 135 additions & 0 deletions .commitrail/initiatives/WS-CLI-001/WS-CLI-001-02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# WS-CLI-001-02 — Public caller profile editing

- Initiative: `WS-CLI-001`
- Durable disposition: `Complete`
- Intended merge outcome: The Go CLI edits or clears only caller-owned profile
display fields through the existing public PATCH contract.

## Intent

Complete human self-profile editing for interactive and agent-driven terminals,
without copying Workstream identity or authorization into the client. The user authorized this
slice after the two public reads in PR #471 merged.

## Current behavior

`cli/internal/command/command.go` provides profile and project reads.
`backend/app/api/routes/auth.py:update_current_actor_profile` already publishes
`PATCH /api/v1/actors/me`. `ActorProfileUpdateRequest` in
`backend/app/modules/actors/schemas.py` accepts only `display_name` and
`contact_email`: omitted fields are unchanged, null clears, text is validated
and normalized by the server. `ActorService.update_self` persists only supplied
fields. There is no public optimistic-version or replay-key contract for this
write; the CLI must not invent one.

## Bounded change

### Allowed

- `cli/internal/api/client.go`: one fixed PATCH operation using the existing
transport/strict profile response validation; bounded update document.
- `cli/internal/command/command.go`: `profile update`, set/clear flags and shared
profile output. Built-process tests under `cli/tests/integration/`.
- CLI/root README, affected roadmap statements, this record, CLI overview and
the existing index row.

### Not allowed

- No backend/MCP/product/permission/schema change or private API access.
- No new dependency, endpoint dispatcher, local JWT verifier, credentials in
flags/files, role inference, authority edits, actor selector or background job.
- No automatic retry, invented idempotency/version header, extra preflight GET,
prompt/TUI, release packaging, unit suite or coverage quota.
- No workflow changes or weakening/skipping existing tests.

## Design and decisions

`workstream profile update` offers `--display-name`, `--contact-email`,
`--clear-display-name`, and `--clear-contact-email`. A set and clear for the
same field, or no selected field, fails as invalid arguments without making a
request. A typed update request preserves omission versus explicit null. Server-owned validation is
not duplicated: invalid text receives the actual bounded API failure.
The encoded request is capped at 8 KiB. Service-actor profile editing is not
supported by the existing human-only public contract.

Reuse the existing transport and profile decoder, and return the validated API
profile through the same text/JSON renderer. Only caller token forwarding is
performed. No second authentication or actor lookup is introduced.

A PATCH transport failure, truncated/unreadable response, malformed success,
redirect, or server failure cannot prove whether the write committed. Such
failures retain a nonzero exit and report `outcome_unknown: true` in JSON (a
safe explanation in text). A complete 4xx with a parseable API error envelope
(nonempty string `error.code`) is a known denial/validation failure, independently
of that code's spelling or redaction. Gateway 4xx replies without this envelope
remain uncertain. Do not retry; use `whoami` to inspect current
state, acknowledging that concurrent later writes remain possible. Successful
local output is not a global write-order guarantee. GET failures are unchanged.

Alternatives: a JSON-file editor adds another input path for only two fields;
an interactive editor blocks agent automation. Neither is needed here.

## Acceptance criteria

- [x] One exact public PATCH, caller bearer unchanged, JSON Content-Type and
only selected display fields; omission preserves, null clears.
- [x] Invalid/conflicting flags cause no network write; no actor or
authority-field flags are available.
- [x] Strict profile response decoding, terminal escaping and exact successful
JSON are reused for reads and writes; malformed success cannot report success.
- [x] A dropped post-send response is nonzero, explicitly uncertain and never
retried. Complete denials retain safe metadata; redirect cannot forward body
or bearer. Existing read proofs remain intact.
- [x] Real public API/PostgreSQL proof verifies stored edits, normalization,
omitted/null semantics, field limits, caller separation and suspended denial.
- [x] Documentation reconciles three operations, write uncertainty and public
source availability without claiming distribution or lifecycle completion.

## Risk and review routing

- Risk class: `L1` (credential-bearing public mutation and uncertain execution).
- Required reviewers: `security`, `architecture`, `qa`, `test_delta`,
`documentation`; CI integrity only if workflow behavior changes.
- Human review focus: omission/null, one-request/no-retry semantics, truthful
unknown outcome, public-only client and retained backend authority.
- No additional human design decision or permission is required in scope.

## Evidence

| Claim | Command or proof | Result | Remaining uncertainty |
|---|---|---|---|
| Plan feasibility | Inspect actual public PATCH, request schema, update owner and existing subprocess harness; follow omitted/null and lost-response cases | Reviewed; human-only wording clarified | Plan inspection alone is not execution |
| HTTP process contract | `test_http_boundary.py`: PATCH payload/flag/redirect/uncertain-outcome assertions | Passed in local process execution | Controlled server does not prove real authorization |
| Real write behavior | `test_installed_cli_uses_only_public_profile_and_project_context`: stored profile edits, validation, isolation and suspended denial | Passed with isolated PostgreSQL and verified cleanup | Local Flow-compatible issuer does not certify deployment |
| Package and proof | Go verify/tidy/vet/build; Ruff; `run_isolated_tests.py --timeout-seconds 240 -- python -m pytest -q ../cli/tests/integration`; existing required hosted Backend proof | Local checks pass; exact-head hosted evidence lives in the PR | Binary release remains separate |

## Review findings

Record material findings and repaired behavior here; current review/CI freshness
belongs to the PR rather than this durable record.

- `PLAN-SEC-001`: Narrowed human/agent wording to human self-profile editing
usable by noninteractive clients; no service-actor support is introduced.
- `CLI-SEC-003`: Removed server error-code text from write-certainty decisions.
Transport/read/success-decoding failures mark uncertainty at their actual
failure sites; complete API 4xx replies remain known even when their public code
equals `invalid_api_response`. The process regression distinguishes this
defect from the repaired implementation.
- `CLI-EXT-004`: A plain gateway 4xx did not establish an API denial but was
marked known. The existing error decoder now distinguishes a received API
error envelope from an unrecognized reply without coupling certainty to
metadata safety or code spelling. Process proof covers gateway 408/429,
retained code-collision denial and credential-reflection suppression.
- `QA-CLI-004`: Reused the existing strict JSON decoder for error envelopes,
removing case-folded and duplicate-member recognition. Independent outer/
inner-case and duplicate-key process cases preserve uncertainty without a
new parsing abstraction. The pre-fix binary fails the case-folding regression.

## Reconciliation

- Current-source reconciliation: main `bcd0bd49` includes the two-read CLI and
the existing public self-profile PATCH; no new product endpoint is needed.
The shared client ledger retains the merged nine-tool MCP delivery.
- Next usable boundary: later role-specific public journeys, not hidden routes.
- Remaining risks: no server optimistic-update/replay contract is claimed;
production Flow and binary distribution remain separate release proof.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,9 @@ implementation history until their owning migrations replace the runtime.
## Terminal Client

The independent [Go CLI](cli/README.md) provides `workstream whoami` and
`workstream project access PROJECT_ID` through the currently public REST API.
Both support human-readable and JSON output, using the caller's Flow token.
`workstream project access PROJECT_ID`, plus `workstream profile update` for
caller-owned human display fields, through the currently public REST API.
All support human-readable and JSON output, using the caller's Flow token.
The first source package is buildable; further workflow commands and published
binaries remain planned.

Expand Down
39 changes: 38 additions & 1 deletion cli/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# Workstream CLI

An independent Go client for Workstream's public REST API, for humans and
agents using the terminal. The first slice provides two self-service reads:
agents using the terminal. It provides human self-profile reads and editing,
plus one exact-project authority read:

| Command | Public API |
|---|---|
| `workstream whoami` | `GET /api/v1/actors/me` |
| `workstream profile update` | `PATCH /api/v1/actors/me` |
| `workstream project access PROJECT_ID` | `GET /api/v1/actors/me/authorization-context?project_id=PROJECT_ID` |

Workstream verifies the caller's Flow bearer and owns identity resolution,
Expand Down Expand Up @@ -45,6 +47,8 @@ terminal control characters in API text. `--output json` (or `-o json`) writes
the successful API object to stdout without a wrapper. Failures leave stdout
empty and write bounded error metadata to stderr; JSON errors use an `error`
object with `code`, optional HTTP `status`, and optional `correlation_id`.
For machine-readable argument errors, place `--output json` before the command;
flag parsing can stop at an invalid argument before reading later flags.
Raw error bodies and transport exceptions are not printed.
Server error codes and correlation headers containing the caller's bearer
are suppressed, including case-only reflections. Success responses require
Expand All @@ -58,13 +62,46 @@ responses are bounded to 64 KiB and requests are not automatically retried.
Use `--help`, `--version` and `completion bash|zsh|fish|powershell` without a
credential or network connection.

## Edit your profile

```sh
workstream profile update --display-name 'Ada' --contact-email 'ada@example.test'
workstream profile update --clear-contact-email --output json
```

Only human caller-owned `display_name` and `contact_email` are writable.
An omitted flag leaves its field unchanged; a clear flag sends explicit JSON
null. You can also use `--clear-display-name`. Select at least one field; setting
and clearing the same field is invalid. Text must be valid UTF-8 and the JSON
request is capped at 8 KiB. Workstream validates and normalizes the text:
display name has a 200-character limit, contact text 320, and blank or NUL text
is rejected. Contact text does not change your Flow login or identity.
Service-actor editing and authority/lifecycle changes are not CLI operations.

A successful update prints the validated API profile, using the same text/JSON
output as `whoami`. No preflight read or automatic retry is performed, and no
idempotency/version mechanism is invented. If the server might have received
the update but no trustworthy result arrives (including lost connection,
malformed success, redirect or server error), exit status is nonzero and JSON
includes `error.outcome_unknown: true`; text explains the uncertainty. Do not
assume rollback or blindly retry: use `workstream whoami` to inspect the current
profile. That observation cannot establish global order against concurrent
later edits. Complete 4xx replies with a parseable Workstream error envelope
(a nonempty string `error.code`) remain known denials or validation failures,
even when sensitive metadata is suppressed. A gateway 4xx without that envelope
is uncertain too; HTTP status alone does not establish a Workstream denial.

## Verification

Behavior tests invoke the built executable from outside the repository, with
no import of Go internals. One suite uses a controlled HTTP server to exercise
credential/destination safety, output and failure boundaries. The other uses
the current FastAPI app with isolated real PostgreSQL to prove first admission,
profile fields, authorized exact-project context and foreign-project denial.
It also proves persisted profile edits, normalization, omission/null semantics,
field limits, caller isolation and suspended denial. The HTTP fixture proves
the exact PATCH body, invalid local input, redirect refusal and no-retry behavior
when a response is lost after body receipt.
Local Flow-compatible tokens are test fixtures, not deployed-provider proof.
No coverage percentage or test-count target is used.

Expand Down
Loading
Loading