life sync / POST /v1/sync is documented with an admin scope but has no handler #85

Closed
opened 2026-09-23 06:19:42 +00:00 by yuris · 1 comment
Owner

Context

Found during the release audit on main@6d74288 (the audit that closed the six follow-up issues). life sync is the only remaining documented-but-unimplemented surface: an automated pass over docs/technical/cli-api.md's route table (26 routes) found every other documented route has a real handler.

Current state, all three places disagreeing in a consistent-looking way:

Where What it says
docs/technical/cli-api.md:53 a real route-table row: life sync --input - → POST /v1/sync, admin scope
docs/technical/cli-api.md:61 sync is in the set of state-changing routes that require Idempotency-Key
src/api/auth.rs:607 (Method::POST, "/v1/sync", Required(Scope::Admin)) — a scope entry, so the surface is half-wired
router no handler registered → the fallback answers 501 NOT_IMPLEMENTED (pinned by tests/api_tests.rs:63::test_v1_stub_returns_501)
CLI life sync parses but has no route: it answers dependency_unavailable and exits 5, disclosed in skills/life/references/commands.md's Not implemented in this client binary table
docs/technical/cli-api.md (response table) specifies a response shape: operation_id, observed counts, per-collection coverage/cursor state, no invented historical events

It fell between three planned issues that each mention /v1/sync only in passing — #2 (scaffold), #7 (discovery), #35 (scope table, which is where the orphaned scope entry came from). No issue has ever owned the implementation, so the 501 is invisible in the tracker.

The semantics are already specified and are not trivial:

  • docs/technical/calendar.md:39 — initial import records observed baselines, not historical creation events; an advertised sync-collection may supply incremental changes, otherwise enumerate full href/ETag sets; a sync REPORT followed by GET is not atomic; compare ETags, repeat bounded reconciliation on mismatch, surface concurrent_change uncertainty after the retry bound.
  • docs/technical/timeline-consistency.md:89 — a sync_state table is already specified in the schema.
  • docs/technical/timeline-consistency.md:154 — commit observation events, projection changes and cursor advancement together; for paged sync, commit a cursor only when it represents all durably processed changes; interrupted pages remain resumable or repeat idempotently.

Decision required first

This issue is a fork, and the choice must be recorded here before any code is written. It is a product decision, not an implementation detail:

  • (a) Implement it. The full slice: incremental/rebaseline modes, admin scope, Idempotency-Key, durable cursor state, per-collection coverage, resumable/idempotent paged commits, no invented historical events.
  • (b) Descope it for release. Remove the route row from cli-api.md, drop the orphaned Scope::Admin entry, and make the CLI verb a usage error (or remove it) so the contract and the binary agree.

Recommendation: (b) now, (a) as its own slice later — shipping with an advertised admin route that answers 501 is worse than not advertising it, and the honest form of "not yet" is a spec that does not promise it. But if sync is on the critical path for the first release, (a) is the real fix.

Acceptance criteria

Whichever branch is chosen, spec, CLI and skill must agree in one commit — that disagreement is what made this invisible for the whole project.

If (b) descope:

  • POST /v1/sync is gone from cli-api.md's route table and from the Idempotency-Key set, or is explicitly marked as future work with no route row.
  • The Scope::Admin entry for /v1/sync is removed from src/api/auth.rs.
  • life sync no longer parses as a working verb (usage error), or its not-implemented row states the command is unavailable rather than implying a route exists.
  • tests/skill_package_tests.rs's not-implemented assertion is updated to the new exact set (it currently asserts exactly {sync}).
  • tests/api_tests.rs::test_v1_stub_returns_501 is retargeted or removed, since the surface it probes no longer exists.

If (a) implement:

  • POST /v1/sync is registered with admin scope and requires Idempotency-Key; the CLI verb transmits and returns one envelope.
  • Incremental and rebaseline modes both work; mode is validated and unknown values are refused rather than defaulted.
  • Cursor advancement is committed in the same transaction as the observation events and projection changes; a paged sync interrupted mid-way is either resumable or idempotently repeatable, proven by a test that kills it between pages.
  • Per-collection coverage/cursor state is reported; a collection that failed is named rather than silently omitted.
  • No historical creation events are invented for pre-existing resources — only observed baselines.
  • Conformance gate: sync-collection is used only when advertised by the backend, otherwise full href/ETag enumeration (per calendar.md).

Blocked by

None. Related: #2, #7, #35 (which mention the route without owning it), #45 (the pagination/snapshot machinery a paged sync would reuse), #6 (recovery scanner).

## Context Found during the release audit on main@6d74288 (the audit that closed the six follow-up issues). life sync is the **only** remaining documented-but-unimplemented surface: an automated pass over docs/technical/cli-api.md's route table (26 routes) found every other documented route has a real handler. Current state, all three places disagreeing in a consistent-looking way: | Where | What it says | |---|---| | docs/technical/cli-api.md:53 | a real route-table row: life sync --input - → POST /v1/sync, admin scope | | docs/technical/cli-api.md:61 | sync is in the set of state-changing routes that **require** Idempotency-Key | | src/api/auth.rs:607 | (Method::POST, "/v1/sync", Required(Scope::Admin)) — a scope entry, so the surface is half-wired | | router | **no handler registered** → the fallback answers 501 NOT_IMPLEMENTED (pinned by tests/api_tests.rs:63::test_v1_stub_returns_501) | | CLI | life sync parses but has no route: it answers dependency_unavailable and exits 5, disclosed in skills/life/references/commands.md's *Not implemented in this client binary* table | | docs/technical/cli-api.md (response table) | specifies a response shape: operation_id, observed counts, per-collection coverage/cursor state, **no invented historical events** | It fell between three planned issues that each mention /v1/sync only in passing — #2 (scaffold), #7 (discovery), #35 (scope table, which is where the orphaned scope entry came from). No issue has ever owned the implementation, so the 501 is invisible in the tracker. The semantics are already specified and are **not** trivial: - docs/technical/calendar.md:39 — initial import records observed baselines, not historical creation events; an advertised sync-collection may supply incremental changes, otherwise enumerate full href/ETag sets; a sync REPORT followed by GET is not atomic; compare ETags, repeat bounded reconciliation on mismatch, surface concurrent_change uncertainty after the retry bound. - docs/technical/timeline-consistency.md:89 — a sync_state table is already specified in the schema. - docs/technical/timeline-consistency.md:154 — commit observation events, projection changes and cursor advancement **together**; for paged sync, commit a cursor only when it represents all durably processed changes; interrupted pages remain resumable or repeat idempotently. ## Decision required first This issue is a fork, and the choice must be recorded here before any code is written. It is a product decision, not an implementation detail: - **(a) Implement it.** The full slice: incremental/rebaseline modes, admin scope, Idempotency-Key, durable cursor state, per-collection coverage, resumable/idempotent paged commits, no invented historical events. - **(b) Descope it for release.** Remove the route row from cli-api.md, drop the orphaned Scope::Admin entry, and make the CLI verb a usage error (or remove it) so the contract and the binary agree. Recommendation: **(b) now, (a) as its own slice later** — shipping with an advertised admin route that answers 501 is worse than not advertising it, and the honest form of "not yet" is a spec that does not promise it. But if sync is on the critical path for the first release, (a) is the real fix. ## Acceptance criteria Whichever branch is chosen, **spec, CLI and skill must agree in one commit** — that disagreement is what made this invisible for the whole project. If (b) descope: - [ ] POST /v1/sync is gone from cli-api.md's route table and from the Idempotency-Key set, or is explicitly marked as future work with no route row. - [ ] The Scope::Admin entry for /v1/sync is removed from src/api/auth.rs. - [ ] life sync no longer parses as a working verb (usage error), or its not-implemented row states the command is unavailable rather than implying a route exists. - [ ] tests/skill_package_tests.rs's not-implemented assertion is updated to the new exact set (it currently asserts exactly {sync}). - [ ] tests/api_tests.rs::test_v1_stub_returns_501 is retargeted or removed, since the surface it probes no longer exists. If (a) implement: - [ ] POST /v1/sync is registered with admin scope and requires Idempotency-Key; the CLI verb transmits and returns one envelope. - [ ] Incremental and rebaseline modes both work; mode is validated and unknown values are refused rather than defaulted. - [ ] Cursor advancement is committed in the same transaction as the observation events and projection changes; a paged sync interrupted mid-way is either resumable or idempotently repeatable, proven by a test that kills it between pages. - [ ] Per-collection coverage/cursor state is reported; a collection that failed is named rather than silently omitted. - [ ] No historical creation events are invented for pre-existing resources — only observed baselines. - [ ] Conformance gate: sync-collection is used **only** when advertised by the backend, otherwise full href/ETag enumeration (per calendar.md). ## Blocked by None. Related: #2, #7, #35 (which mention the route without owning it), #45 (the pagination/snapshot machinery a paged sync would reuse), #6 (recovery scanner).
yuris changed title from life sync / POST /v1/sync is documented with an admin scope but has no handler to life sync / POST /v1/sync is documented with an admin scope but has no handler 2026-09-23 06:25:40 +00:00
Author
Owner

Closing as superseded — the decision inside this issue is made, and the work now has a single owner.

This issue was deliberately written as a fork: (a) implement POST /v1/sync, or (b) descope it so the spec, the scope table, the CLI and the skill all agree the surface does not exist. The coordinator initially chose (b) and started a worker; the user reversed that call, so (a) is the chosen branch and the descope work was cancelled before it produced a commit or a PR.

The gap this issue tracked — a route advertised in docs/technical/cli-api.md with an admin scope entry in src/api/auth.rs but no handler, answering 501 — is therefore closed by implementing it, tracked as #88. Nothing is lost by closing: #88 is open and names the same surface, and the evidence gathered here (the three places that disagreed, and the fact it fell between #2, #7 and #35) is preserved for whoever picks it up. Reopen if the implementation is ever abandoned.

Closing as superseded — the decision inside this issue is made, and the work now has a single owner. This issue was deliberately written as a fork: (a) implement POST /v1/sync, or (b) descope it so the spec, the scope table, the CLI and the skill all agree the surface does not exist. The coordinator initially chose (b) and started a worker; the user reversed that call, so (a) is the chosen branch and the descope work was cancelled before it produced a commit or a PR. The gap this issue tracked — a route advertised in docs/technical/cli-api.md with an admin scope entry in src/api/auth.rs but no handler, answering 501 — is therefore closed by implementing it, tracked as #88. Nothing is lost by closing: #88 is open and names the same surface, and the evidence gathered here (the three places that disagreed, and the fact it fell between #2, #7 and #35) is preserved for whoever picks it up. Reopen if the implementation is ever abandoned.
yuris closed this issue 2026-09-23 06:32:10 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#85
No description provided.