Skip to content
Merged
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 | Public Go CLI self-service, project inspection, manager browsing, contributor discovery, claim/start, governing context/intake requirements and draft project creation delivered through WS-CLI-001-08; hidden submission is not exposed, guide setup, further public journeys and binary distribution remain |
| [WS-CLI-001](initiatives/WS-CLI-001/OVERVIEW.md) | Planned | Public Go CLI self-service, inspection, task browsing/claim/start, governing context, project creation and guide declaration delivered through WS-CLI-001-09; document upload, further public journeys and binary distribution remain; hidden submission is not exposed |
| [WS-ARCH-001](initiatives/WS-ARCH-001/OVERVIEW.md) | Planned | Source storage, inert AUTH contracts, TASK request reservation, hidden exact AUTH preparation and the REV-04C hidden FinalAcceptance/TASK/CON participant are delivered; TASK-before-CHECKERS reservation/current-read custody and ordered admission INSERTs are delivered by 04E1B-B1; 04E1B-B2 adds exact source preparation without publication; 04E1B-B3 retains inspected ZIP metadata and B4 binds Submission text to checked packet custody and B5 validates bounded evaluation content before durable admission; initial Submission/dispatch composition is next under the [first-layer sequence](initiatives/WS-ARCH-001/planning/PLAN.md#first-complete-contributor-milestone), ending in a public contributor drill before live human review/revision. True admission remains independent of shared acceptance; false activation still requires exact AUTH receipt custody, database complete-set enforcement, currentness race proof and atomic routing activation. |
| [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, ARCH-04E2-A hidden strict PREP matching and the REV-04C hidden FinalAcceptance/TASK/CON participant are delivered; the action remains unavailable, with mandatory exact AUTH receipt input, database closure, audit/outbox and scoped activation still required for the automated path. |
Expand Down
14 changes: 11 additions & 3 deletions .commitrail/initiatives/WS-CLI-001/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
[WS-CLI-001-05](WS-CLI-001-05.md), contributor ready-task discovery and instructions;
[WS-CLI-001-06](WS-CLI-001-06.md), public contributor claim/start with explicit retry keys;
[WS-CLI-001-07](WS-CLI-001-07.md), contributor governing context and locked intake requirements;
[WS-CLI-001-08](WS-CLI-001-08.md), draft project shell creation with explicit caller-owned replay.
[WS-CLI-001-08](WS-CLI-001-08.md), draft project shell creation with explicit caller-owned replay;
[WS-CLI-001-09](WS-CLI-001-09.md), guide declaration, illustrative tasks and document upload selectors.

## Current boundary

Expand All @@ -39,6 +40,10 @@ authority; these reads do not expose the hidden submission intake.
`project create --name TEXT --slug TEXT --idempotency-key UUID` creates a draft
shell through the public POST. The API owns system-scoped creation authority
and committed recovery; guide upload, approval and activation are not added.
`project guide create PROJECT_ID --input FILE --idempotency-key UUID` declares
a draft guide, task examples and document targets; setup awaits document upload.
This is a stored creation receipt, not live readiness. The API reauthorizes
guide creation replay, unlike project-shell committed recovery.
All have text/JSON
output and built-binary integration proof. Mutations preserve omitted/null
semantics and explicitly report uncertain outcomes without automatic retries.
Expand Down Expand Up @@ -92,11 +97,14 @@ CLIs. Keep the package independent of backend and MCP runtime dependencies.
8. **WS-CLI-001-08:** Create a draft project shell through the public POST with
explicit caller-supplied keys, exact 201/full-response validation and unknown
outcomes. Reuse project inspection parsing/output; no guide activation.
9. **Later governed-work commands:** Add further project setup, submission,
9. **WS-CLI-001-09:** Declare a guide, required illustrative tasks and source
document targets through public POST using an exact bounded JSON file.
Actual document upload, setup inspection, approval and activation remain.
10. **Later governed-work commands:** Add further project setup, submission,
review, revision, and contribution reads/writes only as their actual public
contracts and authority boundaries become available. Split by user journey,
not one PR per endpoint or one giant catalogue PR.
10. **Optional TUI:** Add a focused public queue/evidence view after its API
11. **Optional TUI:** Add a focused public queue/evidence view after its API
workflow is complete. Never require a TUI for agents or scripts.

## Risks and proof
Expand Down
126 changes: 126 additions & 0 deletions .commitrail/initiatives/WS-CLI-001/WS-CLI-001-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# WS-CLI-001-09 — Declare a guide and its required setup inputs

- Initiative: `WS-CLI-001`
- Durable disposition: `Complete`
- Intended merge outcome: Create guide metadata, illustrative task examples and
declared document targets through the existing public API.

## Intent

CLI08 creates a project shell. Its next setup boundary is the public
`POST /api/v1/projects/{project_id}/guides`, owned by
`GuideMutationService.create_guide`. Add `workstream project guide create
PROJECT_ID --input FILE --idempotency-key UUID`. The UTF-8 JSON file contains
the exact public ProjectGuideCreate body: version, optional change_summary,
required task_examples and required documents. Preserve field omission, null
where supported, text and order; reject malformed/duplicate/unknown fields.
The backend retains semantic validation, authority, immutable source membership,
setup identity, transaction and replay. No source text is extracted by the CLI.

This step intentionally ends at declared upload selectors and awaiting_documents,
not an approved or usable guide. Binary document upload needs a separate bounded
file-stream/transport proof, rather than hiding that risk in JSON creation.
Prefer one fixed typed command over generic HTTP passthrough, shell argument
arrays of large example text, or a second setup engine. No new human decision
or backend authority mechanism is required.

## Bounded change

Allowed: new `cli/internal/api/guide_create.go` and
`cli/internal/command/guide_create.go`; existing client.go and command.go;
new `cli/tests/integration/test_guide_create_http.py` and guide_create_journey.py;
test_public_self_service.py and the existing HTTP fixture's raw-body capture,
CLI/root README, affected roadmap claims, this
record, initiative overview and index. No backend, MCP, dependencies/workflows,
hidden routes, upload, guide edits/approval/activation, setup polling, grants,
local authorization/policy decisions, generated keys, retries or TUI.

Reuse the shared one-shot keyed POST and canonical error contract, adding
explicit response bounds to that private transport: existing commands keep
64KiB; guide creation alone permits 2MiB because 100 examples and document
declarations can exceed 64KiB. Bound the input file to 1MiB, require a regular
file, and never include its path/content or an OS error in diagnostics. Other
mutations keep their 8KiB bound. Require exact 201, unchanged bearer/key,
safe UUID selectors, no preflight, redirects, ambient proxy or transport replay.

Validate the complete closed ProjectGuideCreateResponse and nested examples,
documents and setup, including UUIDs/timestamps/hashes, non-null required fields,
nullable fields, unique document UUID identity and ordered declaration indices.
Bind project identity, version, change_summary, declared media types and example
values/order to the request. Bind document labels to the backend's existing
whitespace normalization (`" ".join(label.split())`), preserving original request
bytes; test Unicode whitespace and normalization collisions. Omitted and null
change_summary both return null; text remains exact. Optional example title
defaults to null, labels to an empty array. Null change_summary and example title
are valid; required fields, arrays and array members cannot be null. Creation
returns guide status `draft` and setup status `awaiting_documents` even on replay
of the stored creation receipt. contribution_policy_id,
contribution_policy_version_id, activation_operation_id, approved_by,
effective_at and superseded_at must all be null for this fixed receipt; validate
each syntactically valid non-null substitution and partial/full policy binding
as an unknown invalid response. Hash validation uses the existing syntax helper,
not a new implementation of backend canonical hashing.
Do not infer approval or active authority from a stored replay. Preserve raw
JSON in JSON mode; safely render all response fields in text mode. Invalid or
unexpected responses remain unknown mutation outcomes. Recovery guidance keeps
the same project, file contents and key, never claims rollback or invents an ID.

## Acceptance criteria

- Built-process HTTP tests prove exact body/field omissions and selectors,
nullable values and Unicode, complete safe text/raw JSON, and input/response
bounds (including a legitimate response over 64KiB).
- Reject malformed/duplicate/unknown fields and null required fields/members,
substituted project, version/summary/examples/documents/lifecycle constants,
invalid UUIDs/hashes/times, normalized duplicate
document identities, and unexpected success status. Invalid local input sends
no request and emits no file content/path or credential.
- Canonical 4xx is a known denial; malformed envelopes (including null details),
drop, redirect, oversized response and HTTP/2 GOAWAY are unknown outcomes;
one invocation transmits at most one POST body.
- Extend the one-bootstrap real FastAPI/PostgreSQL CLI journey. Assert OpenAPI
registration, declared targets and awaiting_documents, direct exact replay,
unchanged-body manual replay, mismatch denial and fresh creation denial for
absent/foreign-project/revoked manager authority and suspended actor. Prove a
fresh positive on the scoped grant's own project before a foreign-project
denial and a fresh positive before revocation. Revocation leaves the actor
active: regrant, prove a fresh positive, suspend, prove denial, reactivate,
then revoke the restored grant. Restore the spare actor's active/ungranted
baseline through public operations. No fake authority or private product writes.
- Existing process/API suite, Go verify/tidy-diff/vet/build, Ruff/format,
workflow guards, links/stale/Commitrail and full hosted Backend remain required.
No unit-test suite, coverage quota, skipped proof or weakened CI.

## Risk and review routing

Risk L1: larger untrusted response, file input, bearer handling, false setup
success or duplicate mutation after lost replies. Plan feasibility review before
code; architecture/docs, security and QA/test-delta focused assignments after
shared checks and a clean target. Human focus: declaration is not upload or
approval, exact file/body replay custody, full response identity and safe output.

## Evidence

Verification uses the existing built-process suite and isolated database runner:

```sh
cd cli
go mod verify
go mod tidy -diff
go vet ./...
go build -trimpath -o /tmp/workstream-cli ./cmd/workstream
cd ../backend
WORKSTREAM_CLI_EXECUTABLE=/tmp/workstream-cli python scripts/run_isolated_tests.py --metadata-json /tmp/workstream-cli-isolation.json --timeout-seconds 300 -- python -m pytest -q ../cli/tests/integration
```

Execution and exact-head review evidence belong in the PR, not durable active
state. Assess all affected roadmap claims in this PR; upload, policy approval,
activation and binary distribution remain separate boundaries.

Plan review corrected label normalization, nullable field exceptions, complete
response relationships and reachable authority/lifecycle controls. The intended
creation receipt binds inputs; it is not a live setup-state observation.
Implementation review additionally reproduced acceptance of valid-but-impossible
approval/activation facts. The creation parser now rejects those facts rather
than accepting them merely because UUID/time syntax is valid; a focused process
regression distinguishes the defective binary from the repair.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ Hints do not grant authority; requirements do not expose hidden submission uploa
`workstream project create --name TEXT --slug TEXT --idempotency-key UUID`
creates a draft project shell through the public API, with explicit manual
replay and uncertain-outcome handling; it does not approve or activate a guide.
`workstream project guide create PROJECT_ID --input FILE --idempotency-key UUID`
declares a draft guide, task examples and document upload targets. It does not
upload documents or approve the guide; returned setup waits for those documents.
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
56 changes: 54 additions & 2 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

An independent Go client for Workstream's public REST API, for humans and
agents using the terminal. It provides human self-profile reads and editing,
plus draft project creation, exact-project inspection, authority reads, manager task browsing and
plus draft project/guide declaration, exact-project inspection, authority reads, manager task browsing and
contributor work discovery, claim/start and governing context/intake requirements:

| Command | Public API |
Expand All @@ -12,6 +12,7 @@ contributor work discovery, claim/start and governing context/intake requirement
| `workstream project access PROJECT_ID` | `GET /api/v1/actors/me/authorization-context?project_id=PROJECT_ID` |
| `workstream project show PROJECT_ID` | `GET /api/v1/projects/PROJECT_ID` |
| `workstream project create --name TEXT --slug TEXT --idempotency-key UUID` | `POST /api/v1/projects` |
| `workstream project guide create PROJECT_ID --input FILE --idempotency-key UUID` | `POST /api/v1/projects/PROJECT_ID/guides` |
| `workstream project tasks PROJECT_ID` | `GET /api/v1/projects/PROJECT_ID/tasks` |
| `workstream project task PROJECT_ID TASK_ID` | `GET /api/v1/projects/PROJECT_ID/tasks/TASK_ID` |
| `workstream task ready PROJECT_ID` | `GET /api/v1/projects/PROJECT_ID/tasks/ready` |
Expand Down Expand Up @@ -70,7 +71,8 @@ and preserving the successful API JSON.

Exit status is `0` for success, `1` for API/network/response failure, and `2`
for invalid arguments or configuration. A request times out after 12 seconds;
responses are bounded to 64 KiB and requests are not automatically retried.
responses default to a 64 KiB bound (guide declaration uses the documented
2MiB exception below), and requests are not automatically retried.
Use `--help`, `--version` and `completion bash|zsh|fish|powershell` without a
credential or network connection.

Expand All @@ -93,6 +95,56 @@ object; text escapes terminal controls. Foreign or revoked authority remains
a server denial with empty stdout, not an empty successful project.
This command does not list projects, edit setup, activate guides or claim tasks.

## Declare a guide and its setup inputs

```sh
workstream project guide create PROJECT_ID --input guide.json --idempotency-key GUIDE_CREATE_UUID --output json
```

The regular UTF-8 JSON file contains the public guide-creation request, not the
guide document's contents or a storage URL. For example:

```json
{
"version": "evaluation-guide",
"change_summary": "Initial project instructions",
"task_examples": [
{"content": "Evaluate the supplied experiment's evidence.", "title": "Evidence evaluation", "labels": ["research"]}
],
"documents": [
{"label": "Guide.pdf", "media_type": "application/pdf"}
]
}
```

`version`, `task_examples` and `documents` are required. `change_summary` and
example `title` may be omitted or null; example `labels` defaults to an empty
array. Unknown/duplicate members, malformed JSON and null required fields or
array members are rejected. The file is sent unchanged; the API owns semantic
limits and validation. PDF, DOCX and PPTX declarations use the media types in
OpenAPI. The API normalizes document-label whitespace; example text is preserved.

The CLI bounds this input to 1MiB and this response to 2MiB because declarations
include example text and document selectors. These are client wire envelopes,
not backend policy limits. Existing operations retain their 64KiB response
limit and small mutations their 8KiB input limit.

The API checks current Project Manager authority for the exact project, including
manual replay. A successful HTTP 201 returns the complete draft creation receipt,
declared document IDs and `setup.status = awaiting_documents`. JSON output
preserves the response; text renders every field with terminal escaping.
Policy, activation, approval, effective and supersession fields must be null
in this fixed creation receipt, including stored replay.
The command does not upload files, poll setup, approve policies or activate a
guide. The stored creation receipt is not a live readiness or authority snapshot.

Retain the project selector, exact input contents and caller-owned UUID key.
There is no preflight, generated key or automatic retry, including HTTP/2 replay.
Changed input conflicts. Lost, malformed, redirected or unexpected replies
report an unknown outcome, not rollback; manually replay only the unchanged
input and key. A complete canonical 4xx is a known denial. Local file errors
never echo file paths, contents or OS error details.

## Create a draft project shell

```sh
Expand Down
8 changes: 6 additions & 2 deletions cli/internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,10 @@ func (c *Client) request(ctx context.Context, method, path, query string, body [
}

func (c *Client) requestWithKey(ctx context.Context, method, path, query string, body []byte, key string, successStatus int) (json.RawMessage, error) {
return c.requestWithResponseLimit(ctx, method, path, query, body, key, successStatus, maxResponseBytes)
}

func (c *Client) requestWithResponseLimit(ctx context.Context, method, path, query string, body []byte, key string, successStatus, responseLimit int) (json.RawMessage, error) {
mutation := method == http.MethodPatch || method == http.MethodPost
target := c.origin + path
if query != "" {
Expand Down Expand Up @@ -342,8 +346,8 @@ func (c *Client) requestWithKey(ctx context.Context, method, path, query string,
return nil, &Failure{Code: "service_unavailable", OutcomeUnknown: mutation}
}
defer response.Body.Close()
responseBody, err := io.ReadAll(io.LimitReader(response.Body, maxResponseBytes+1))
if err != nil || len(responseBody) > maxResponseBytes {
responseBody, err := io.ReadAll(io.LimitReader(response.Body, int64(responseLimit)+1))
if err != nil || len(responseBody) > responseLimit {
return nil, &Failure{Code: "invalid_api_response", Status: response.StatusCode, OutcomeUnknown: mutation}
}
correlation := response.Header.Get("X-Correlation-ID")
Expand Down
Loading
Loading