Skip to content
Open
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
35 changes: 35 additions & 0 deletions tasks/extension-setup-wizard/00-working-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Working Protocol

## Accepted Collaboration Contract

- The agent may explore, investigate, reason, and run bounded experiments
without requesting permission.
- Source or durable project mutation begins only after product design, HLD, and
an implementation-ready plan are complete and Sir explicitly says to start.
Task-packet maintenance is exempt.
- Each conversation round carries one bounded decision, or one small coherent
batch whose cognitive load is comparable to one decision.
- Every round reports the previous result before advancing to the next question.
- Sir's statements are proposals and evidence, not automatic authority; the
agent must independently evaluate them and may disagree with reasons.
- The agent owns proposing coherent solutions. Sir reviews and steers those
proposals; the agent must not outsource design work as a series of tiny
questions.
- Decisions, evidence, open questions, and plan revisions remain synchronized
in this packet.
- The agent stops only for meaningful review, a material decision, missing
information that changes the design, or a mutation gate.

## Current Mode

Product design -> HLD -> implementation plan are complete. The packet is at
Sir's plan-review gate. No source implementation is authorized until a new
explicit start after that review.

## Repository Boundary

This packet lives in `ext-reg` only as the active cross-repository control
surface for the current task. It does not transfer setup ownership to the
Registry. Expected implementation owners are primarily `client-web` and
`core-py`; stable cross-unit product or technical truth may later be promoted to
the shared InKCre docs through its normal ownership workflow.
79 changes: 79 additions & 0 deletions tasks/extension-setup-wizard/10-current-system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Current-system Evidence

Evidence was read from each repository's `origin/main` on 2026-08-13. Exact
baselines were ext-reg `f8aebd0`, Core `63f57b2`, Client `4fdc083`, and
organization policy `81258c1`. The local
`client-web` and `core-py` working branches are not based on current main, and
the Core worktree also contains unrelated task-packet changes; this task did not
modify either worktree.

## Deployment and Web UI

- One deployment-wide `extensions` row stores canonical Extension Name, exact
version, enabled Peer IDs, nickname, config, and config schema.
- That row currently has no Extension `state` field. `sources.state` is the
existing deployment-durable precedent, used for cursors such as the latest
collected item. Its current whole-object getter/setter does not provide an
atomic cross-Peer mutation contract and should not be copied unchanged.
- `client-web` currently offers install, list, JSON config editing, exact version
change, per-current-Peer enable/disable, and uninstall.
- `WebExtensionHost` resolves the exact Registry Release, checks the Web Host
SDK range before fetching the Module Federation Distribution, runs its
lifecycle, then commits the current Peer enablement.
- The Twitter Web Distribution exposes its default lifecycle module and tweet
content component. Its lifecycle is effectively empty; there is no setup
view, setup protocol, or setup status.

## Core Host and Twitter Runtime

- Core's Python Host installs and consumes a native wheel, validates its Host
SDK association, and publishes its Extension-owned routes, Sources, and
Resolvers only while the current Core Peer is enabled.
- Twitter's Core Extension config selects `official` or `twikit` and currently
includes Twitter application credentials and alternative Twikit login
fields.
- With the official backend enabled, the Extension publishes:
- `GET /twitter/auth/authorize`
- `GET /twitter/auth/callback`
- `POST /twitter/bookmark`
- The callback exchanges the authorization code, fetches the Twitter user, and
returns the token response.
- The resulting access token, refresh token, user ID, and handle live only on
the in-process `OfficialAPI` singleton. Their intended persistence calls are
commented out. Core disable closes and forgets the singleton.
- The bookmark Source cannot collect without this authenticated client. Source
cursor state such as `latest_tweet_id` is already durable Source state, but
OAuth credentials are not.

## Immediate Consequences

1. Installation is not setup.
2. Per-Peer enablement is not setup completion either: it only makes the Host
runtime and routes available.
3. A successful OAuth callback currently proves only an ephemeral session, so a
wizard that marks setup complete at that point would lie after restart.
4. Creating a bookmark Source is a distinct action from authentication and is
necessary before scheduled collection can occur.
5. The setup experience crosses the Web Host, at least one Core Peer, the
Twitter Web Distribution, the Twitter Core Distribution, and shared
deployment state. The Registry is only the source of the two Distributions.

## Implementation-readiness Evidence

- The current Core installed-row projection is named `ExtensionState` even
though it has no Extension-produced state. Its SQL store, runtime record,
ExtensionBase config callbacks and exact PostgreSQL role/trigger boundaries
were traced for the HLD rename and mutation plan.
- Core currently logs raw query parameters, and JWT middleware cannot exempt a
dynamically contributed callback. Both exact change points are known.
- Client's `Client.list()` and authenticated request helper already provide the
command-endpoint discovery transport; no new peer registry is needed.
- `InkDialog` 1.2.2 has the required `showCancel`, `showConfirm`,
`closeOnScrim`, default slot and model event happy path; no dialog framework
spike remains.
- Authlib 1.7.2 was executed in isolation with HTTPX mock transport to prove the
planned S256/token/refresh path. PDM 2.27.0 resolved its production dependency
graph in a temporary Core copy.
- The configured SSH Docker provider was reached successfully on 2026-08-13
(Docker engine/client 28.5.2, Compose 2.40.3). It is the exact-image path for
Client contract generation because this Mac has no local Docker CLI.
131 changes: 131 additions & 0 deletions tasks/extension-setup-wizard/20-product-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Product-design Working Model

## Proposed Product Concept

An **Extension Setup** is a resumable, Extension-specific user journey that
turns an already installed and enabled Extension Release into the Extension's
declared minimum usable state across the deployment. It is whole-Extension
setup, not setup of one individual feature or one Peer. It is not a Registry
lifecycle state and is not a synonym for installed, enabled, or running.

A **Setup Wizard** is the accepted presentation of that journey in
`client-web`: an action on an Extension card opens a popup whose content is an
Extension-owned multi-step installer. The setup entry requires at least that
this Web Peer has enabled and loaded the Extension; Core Peer enablement is a
separate prerequisite that the wizard may observe or guide. The durable product
contract is still the whole-Extension outcome and observable progress, rather
than the popup's currently displayed step number.

## Accepted Twitter Completion Direction

Twitter setup is complete when the whole Extension has reached its declared
minimum usable state. For the current Twitter Extension, bookmark collection is
the first concrete proof of that baseline rather than a separately scoped
feature wizard. At minimum all of the following are true:

1. `inkcre/twitter` is installed at one exact deployment version.
2. A selected compatible Core Peer is enabled for that Extension and exposes
the Twitter setup operations.
3. The Twitter application configuration required by the selected backend is
valid.
4. A Twitter account authorization is durable and can be restored by that Core
runtime after restart.
5. Every resource declared necessary for the Twitter Extension's initial useful
operation exists; in the first vertical slice this includes at least one
eligible Twitter bookmark Source.
6. A bounded Extension-level readiness check confirms the authenticated account
and required initial resources can begin useful work; it does not need to
wait for a full historical sync.

The Web Peer is enabled to render the setup experience, but that is only a means
of presentation. The resulting setup facts are deployment-wide rather than
owned by that Web Peer.

## Product Principles Derived from the Proposal

- Display progress from observed domain facts rather than one manually toggled
`setup_complete` boolean.
- Resume at the first unsatisfied prerequisite after reload, reconnect, or
partial failure.
- A user may leave the wizard and return without losing successful steps.
- Setup may use one reachable Core Peer as a command/OAuth endpoint, but that
does not make setup or future Source execution Peer-specific.
- The wizard guides the user through registering an X developer App and entering
its Client ID and Client Secret. Deployment-wide `extensions.config` is the
accepted authority for these user-declared values. Deployment-wide
`extensions.state` owns Extension-produced account credentials, identity and
OAuth transactions. Both use InKCre's trusted authenticated-Peer security
boundary.
- The Extension defines one minimum setup baseline. Optional capabilities added
later may have their own configuration journeys without retroactively making
the whole Extension "not set up".

## Accepted UI Ownership

The narrowest reusable split is:

- `client-web` owns the Extension-card entry and popup container, including
opening, closing, focus containment and mounting/unmounting its content.
- The enabled Web Distribution owns everything inside that popup: stepper,
Back/Next/Cancel/Finish controls, step sequence, content, field validation,
loading and recoverable errors, Core-facing commands, observed completion
facts, and the final Extension-level readiness projection.
- The Extension Host SDK owns the small contribution contract that connects
those two sides. It does not know Twitter fields, OAuth endpoints, Sources, or
database tables.

This avoids hard-coding Twitter setup into `client-web` and avoids a premature
generic wizard engine. Extensions may need materially different setup flows;
their own Host SDK-bound UI is the correct place to express those differences.
Only popup behavior remains consistent across Extensions.

The setup action is available once this Web Peer has successfully enabled and
loaded the Extension, because its Web Distribution contributes the popup
content. This is a minimum availability condition, not a claim that this Web
Peer performs the Extension's Core work. Installed-but-disabled cards should
explain that the Extension must first be enabled here instead of offering a
broken setup action.

## Accepted Minimum Web Extension API

The Web Host SDK adds one optional setup contribution to the already
loaded Web Extension module. Conceptually:

```ts
interface WebExtensionModule {
initialize?(): Promise<void>
activate?(): Promise<void>
deactivate?(): Promise<void>
dispose?(): Promise<void>

setup?: {
component: VueComponent
}
}
```

The Host renders `setup.component` inside its popup. The component owns its
entire flow and emits only a request to close the containing popup. A successful
Finish may request close, but it does not pass a `setup_complete` value back to
the Host; reopening the component reconstructs progress from Core-visible
facts.

This API deliberately does not include `steps`, `currentStep`, validation,
progress persistence, OAuth, Core Peer selection, Source creation, or a generic
setup status. Those are Extension-owned semantics. HLD closes the exact Vue
type and `close` event in [Web Host Contribution and Wizard UI](34-hld-web-setup.md).

## HLD Closure

The formerly open implementation shapes are now closed in HLD:

- the Web contribution is one Vue `Component` plus a `close` event;
- Twitter owns its exact config/state/setup protocol and Authlib OAuth client;
- Core owns one exact public callback contribution and database state
transactions;
- Finish explicitly creates or reuses the first bounded collection job;
- the first release does not expose a newest-versus-history choice.

See [Canonical Extension State](32-hld-extension-state.md),
[Twitter Setup and OAuth Protocol](33-hld-twitter-protocol.md), and
[Web Host Contribution and Wizard UI](34-hld-web-setup.md).
130 changes: 130 additions & 0 deletions tasks/extension-setup-wizard/21-twitter-wizard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Twitter Wizard Proposal

## External Product Constraints

The current X OAuth documentation establishes these facts:

- An X App must have OAuth 2.0 enabled and its redirect URI must exactly match a
callback URL configured in the Developer Console.
- Web applications are confidential clients and must keep their Client Secret
within the application's accepted trust boundary. InKCre's boundary permits
deployment-wide Extension config to contain it and authenticated Peers to
read that config; URLs and unauthenticated surfaces remain outside the
boundary.
- `offline.access` is required to receive a refresh token and restore user access
without another interactive login.
- Bookmark lookup requires a user access token with `bookmark.read`; `tweet.read`
and `users.read` support the returned posts and authenticated user. The current
Extension does not need `bookmark.write` to collect bookmarks.

References:

- [X OAuth 2.0 Authorization Code with PKCE](https://docs.x.com/fundamentals/authentication/oauth-2-0/authorization-code)
- [X user access-token flow](https://docs.x.com/fundamentals/authentication/oauth-2-0/user-access-token)
- [X bookmarks lookup](https://docs.x.com/x-api/posts/bookmarks/quickstart/bookmarks-lookup)

## Accepted First-release Product Boundary

### OAuth App setup

The user registers an X developer account/App, configures the callback URL, and
enters the resulting Client ID and Client Secret as part of the setup wizard.
The wizard must provide the exact callback URL and guidance needed to finish
that external registration.

The credentials configure the deployment-wide Twitter Extension, not the Web
Peer that rendered the wizard. They are persisted in canonical Extension config
under the accepted authenticated-Peer security boundary. Account tokens and
identity produced by OAuth are persisted in canonical Extension state. The
wizard uses validated Twitter Core commands rather than inventing a second
persistence authority. Credentials still must not be placed in URLs,
unauthenticated responses or logs.

### Supported account path

The first release supports the official X OAuth backend only. Do not expose the
existing Twikit username/password/TOTP backend in this wizard. It is a separate,
less stable and higher-risk product path that would multiply credential and
recovery semantics before the official flow works end to end.

Request only `tweet.read users.read bookmark.read offline.access`. Add write
scopes in a later Extension version only when an accepted user-facing feature
actually writes bookmarks.

## Proposed Four-step Wizard

### Step 1 — Prepare

Explain what the Twitter Extension will do and the permissions it will request.
Then guide and inspect prerequisites:

- this Web Peer is already enabled and the setup component is loaded;
- at least one reachable Core Peer can run the same installed Twitter Release;
- Twitter is enabled on at least one suitable Core Peer, or the user explicitly
authorizes enabling it there;
- the user has registered an X developer App with the exact callback URL;
- the user enters Client ID and Client Secret, which are submitted to Core and
reported only as configured/not configured afterward.

If several suitable Core endpoints exist, one may be chosen as the command/OAuth
exchange endpoint. It does not scope setup to that Peer or assign permanent
ownership of the Source or its future jobs; Core's state/domain authorities
retain concurrency responsibility.

### Step 2 — Connect account

Show the exact scopes and start official X OAuth. After callback, display the
authenticated account identity and prove that its durable authorization can be
restored. Existing valid authorization is shown and may be reused; reconnect is
an explicit action rather than an automatic overwrite.

### Step 3 — Bookmark collection

List existing Twitter bookmark Sources. Let the user reuse one or create the
initial Source with the small set of user-relevant inputs:

- nickname;
- collection schedule;

The first release does not expose a newest-versus-history choice. It uses the
current bounded `full=false, result_limit=40` initial job. A later product
decision may add a history policy once collection semantics can support it
without misleading empty-state behavior.

Reopening setup must not create duplicate default Sources. Source identity and
cursor state remain Core-owned domain facts.

### Step 4 — Review and start

Show the chosen account, Source, and schedule. Run a bounded readiness command
that verifies the durable authorization and required Source state. On explicit
Finish, enqueue the first collection job (or confirm an already queued/running
job) and close when Core accepts the command. The wizard does not wait for a
complete bookmark history sync.

## Derived Resume Model

On every open, the Twitter setup component reads one Extension-specific setup
projection and resumes at the first unsatisfied condition:

```text
Core unavailable or disabled -> Prepare
OAuth App unavailable -> Prepare / operator action
No restorable account -> Connect account
No eligible bookmark Source -> Bookmark collection
Ready facts satisfied -> Review and start / already configured summary
```

Transient X or network failure is shown as an operational readiness error, not
by deleting already durable account or Source facts. Revoked/invalid refresh
credentials move the account back to “Reconnect required.”

## Intentional Non-goals

- No generic Host-owned wizard state machine.
- No Twikit setup path in the first release.
- No permanent Source-to-Core-Peer assignment.
- No second credential store alongside canonical Extension config.
- No wait for complete initial synchronization before the wizard may finish.
- No silent enablement of a Core Peer; enabling is an explicit user-authorized
action within Prepare.
Loading