Commit Graph
26 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 c73ffed806 close the comms channel, keep what was still open
The sidecar-app-store channel ran one night, from per-user Linux accounts to a
member's first agent turn, and is deleted now the work has landed. A spent
channel left in place gets read as current, which is worse than none.

Three things lived only in those docs and move to TODO.md rather than
disappearing: deprovisionOsAccount (observed on production — a deleted member
kept a shell, a running container and 454M of data, with their uid free to
reissue), the terminal replaying query sequences as keystrokes, and agent
sessions not being durable, which is one missing property behind three symptoms.
The deprovision spec itself already lives in docs/.

CLAUDE.md's section is rewritten from "here is the current channel" to how to
run one, since the answer to "which channels exist" is now none. What is worth
keeping is the protocol that emerged: numbered alternating files, parity as the
author, a reply even when there is nothing to say, and termination on a
checkable condition rather than on someone deciding it feels finished.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 01:58:00 +00:00
pastilhasandClaude Opus 5 701933d30d commit and push when it goes wrong too
The instruction was standing and nowhere in the repo, so every session started
by waiting to be asked. Written down because the reason is not obvious: a
branch held back because it is unfinished, untested or a dead end is exactly
the branch whose history is worth having. A reverted commit and its message
explain why an approach was abandoned; a quietly discarded attempt teaches the
next person nothing, and they will try it again.

The obligation that comes with it is saying what state the work is in — in the
message, and in COMMS/ when another agent will pick it up — rather than letting
a clean commit imply it is finished.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:32:21 +00:00
pastilhasandClaude Opus 5 48ed171b38 point CLAUDE.md at COMMS, so a new session finds it without being told
The channel is only useful if it is read, and relying on the owner to remember to say
"check COMMS" in an opening prompt puts the mechanism back where it started. CLAUDE.md is
loaded automatically, so the pointer belongs there: what the directory is for, that newest
date wins, and which streams exist.

Also states the split it is easy to get wrong — durable reasoning in docs/, coordination in
COMMS — and that a spent handoff should be deleted rather than left to be mistaken for
current.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:09:46 +00:00
pastilhasandClaude Opus 5 d56be0301d retire the single-user claim from the docs it outlived
CLAUDE.md asserted "single-user is a hard invariant, not a stage" while
users held six rows and role_capabilities held grants. Every doc that
repeated it is corrected here, in prose and in the code comments that
carried the same claim.

The accurate statement is narrower: one owner who bypasses every check,
other accounts holding only what their role is granted, and a set of
capabilities — terminal, chat, files, tasks, items, desktop, browser — that
are structurally ungrantable because they execute as the owner's OS user.

TODO.md gains a Multi-user section for what the read turned up: no way to
create a second account, dashboards.id colliding across users, authorize.ts
untested, pty/vault/opencode taking no identity, Radicale still owner_only.

claude-sidecar-isolation.md's open question is answered rather than left
open — the per-email spawn model is dead weight, because chat is an
execution capability and no second account can ever reach it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 21:58:44 +00:00
pastilhas b419af32de sweep the dead code in section 8
Each item re-verified before deleting; three of the ten entries were stale
and are corrected in place rather than silently fixed.

- WorkspaceLayout's isMobile/mobilePanelId/onMobileBack: none of its ten
  callers set them, so the mobile collapse they fed was permanently off in
  that renderer. WorkspaceView passes the same props to WorkspaceRenderer
  itself, where they are live.
- fixedHeight on AppRegistryEntry, and getFixedHeight with it: no app has
  ever declared one, so it only contributed undefined. The flex-column
  branch it shared with fitContent stays, keyed on fitContent alone.
- getDefaults: getAllDashboardState already folds the defaults row into the
  one payload the client fetches, which is why it never got a caller.
- upsertScreen's terminals/hostTerminals: never read is right, never
  written was not — it inserted them, which is why all 15 rows hold {}.
  The columns are left in place; dropping them needs a db:push, and this
  tree holds another agent's uncommitted schema file.
- SELECTED_DASHBOARD_KEY: H2 (01365cb) replaced it with ?selected= four
  months ago and it has had no reader since.
- ui/sidebar.tsx and the stray ui/hooks/ beside it. use-mobile was not
  orphaned as claimed — the sidebar imported it — and the use-toast in
  there was a near-identical copy of the live one.
- findChildById's unreachable duplicate condition, and the doc comment that
  described the wrong behaviour rather than the code being wrong.

Left deliberately: DragOverlay/LayoutEditor (gated on 5.3, an owner
decision) and the two chat-owned channels, whose docs are fixed here even
though the publishers are not mine to delete.
2026-08-07 11:26:33 +00:00
pastilhasandClaude Opus 5 30fcab2bd3 mark 5.6 resolved, and stop three docs claiming a channel that has no publisher
The todo entry said the file-viewer registration was dead; tracing it confirmed that and turned up the
reason it looked alive — the ephemeral file viewer is a different mounting path entirely. Recorded, with
what was checked in the database before deleting anything.

CLAUDE.md, navigation-audit.md and workspace-panels.md all listed FILE_VIEWER_CHANNEL among the
legitimate refresh/signal channels. It never had a publisher, and no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:07:36 +00:00
pastilhasandClaude Opus 5 7c99429872 write down the north star: agents coordinating with each other
docs/agent-coordination.md is the objective the workspace/panel work serves —
several agents on one dashboard handing work to each other instead of routing
every step through the human, with the human authoring the workflow at the top.
Written from the owner's own words during the 2026-08-07 conversation; where a
section records a decision, that decision is his.

It settles the questions that were blocking: sessions may be reaped and resumed
from the durable sessionKey→claudeSessionId map (no heartbeat), the address is
the human-assigned panel name rather than the panel id, roles are prompts rather
than features, and the protocol is turn-boundary-only by construction — which
routes around the mid-output restart failure instead of fixing it.

Cross-referenced from CLAUDE.md, workspace-panels.md and workspace-panel-todo.md
so it is findable from any of them. The todo is still ordered by defect severity
and now says so; the re-rank against the objective is deferred, not forgotten.

Also corrects two items dated 2026-08-08 to the day they were actually found.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 06:58:01 +00:00
pastilhas ce7968ac90 document how the workspace/panel framework works 2026-08-07 04:10:36 +00:00
pastilhasandClaude Opus 5 10ff23c5dd gitea: the app — repos, code, issues, pulls, notifications
Replicates what Gitea's own web UI offers, on top of the sidecar's /_api pass-through.

Repository browsing (tree, file view with the FileViewer's shiki renderer, README), commits,
branches, tags, releases, issues and pull requests both per-repo and cross-repo, notifications,
explore/search and organizations. Routes are /gitea/:section plus /gitea/repo/:owner/:name/:tab/:item,
all real Links with the URL as the source of truth — no selection channel.

Markdown is rendered client-side (react-markdown + remark-gfm + rehype-sanitize, rehype-raw
deliberately absent) rather than through the instance's /api/v1/markdown, because consuming that
means dangerouslySetInnerHTML and there is no DOMPurify in the tree with installs frozen. The cost
is Gitea's #123 and @mention cross-references; relative links and images are resolved instead.
The /markdown and /markup allow-list entries stay, so that door is open when a sanitiser lands.

retargetUrls rebases instance-minted URLs onto a browser-reachable origin, IN ONE DIRECTION ONLY.
This instance answers with two: /user and /repos build from its configured ROOT_URL
(http://localhost:9004), /contents from the public host. An unconditional rewrite onto the
connection URL therefore broke the second set to match the first, turning working https links into
dead loopback ones. Only a private/loopback URL is rewritten now, and only when the target is
itself public; when the connection URL is a dial address nothing is touched and the connection
screen says why avatars will not load.

Also carries the frontend half of the one-instance-many-tokens model: the connection form draws a
URL field only for the owner and sends no url key at all for anyone else, ServiceConnection.url is
string | null to match officerdb, and the rebase origin comes from the resolved instanceUrl rather
than connection.url, which is null for a member.

Not verified: no runtime pass since the last four changes, the issues and pull views have never
rendered a row (the instance has none), and the member path has never executed (one account).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 17:10:36 +00:00
pastilhasandClaude Opus 5 6fde7afd7f docs: refresh the pm2 process list
it said fourteen; there are nineteen. photos, notify, caldav, memos and jellyfin have landed
since it was last written. also names the two mybiblepal entries that share this pm2 daemon
and belong to a different project, and points at `pm2 jlist` as the source of truth so the
next reader does not trust a list that goes stale every time a sidecar ships.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 19:27:33 +00:00
pastilhasandClaude Opus 5 f2052fbdaa freeze installs: bun resolves from the lockfile or fails
bunfig.toml sets [install] frozenLockfile = true, so `bun install` resolves from
bun.lock and nothing else — it fails rather than quietly picking up a newer
version, including a transitive one nobody chose.

Config rather than a documented habit, because a supply-chain compromise does not
wait for the one time somebody forgets a flag. On 2026-08-04 eleven cache
packages — keyv, flat-cache, file-entry-cache, cacheable-request, cache-manager,
the @cacheable/* scope and ecto — were published with a preinstall dropper that
harvested npm and GitHub tokens, AWS and Kubernetes credentials, SSH and PEM
keys, .env files and .claude/settings.json, then republished itself through any
npm token it found, reaching 434 further packages across 1381 versions. This
machine was unaffected only because nothing had installed since 2026-08-02.

Verified on bun 1.3.10 rather than assumed, and the first result was wrong: with
NO lockfile present, neither the flag nor the config refuses — bun simply creates
one, so an initial test made the setting look ignored. Against a lockfile that no
longer satisfies package.json, both exit 1 with "lockfile had changes, but
lockfile is frozen". The config is honoured; the earlier reading was a bad test.
`bun install` in this repo still reports 1058 installs, no changes.

Applied to monorepo-mobile too, where flat-cache, file-entry-cache and keyv are
present as eslint/got transitive deps — at versions old enough to be unaffected,
which was luck rather than design.

Documented in platform/CLAUDE.md and the workspace root, including the part that
matters most: do NOT add --no-frozen-lockfile to a script, a Dockerfile or CI to
make the error go away. The error means the lockfile and package.json disagree,
and an unexplained lockfile change in a diff is precisely the signal this exists
to produce.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 17:58:07 +00:00
pastilhasandClaude Opus 5 a9b6494f56 db: stop push re-creating every composite key on every run
`bun db:push` planned 32 statements against a database that already matched the
schema, and stopped on a "do you want to truncate screens?" prompt that answering
could not resolve — the same question came back next run. Root cause found and
fixed rather than worked around.

drizzle-kit mis-diffs named composite unique CONSTRAINTS. It reads one back,
compares it against a schema declaring the identical name, columns and order,
decides they differ, and emits DROP + ADD. Fifteen of those, forever. Reproduced
on a database drizzle had itself created seconds earlier, so it is not drift.
Single-column .unique() is diffed correctly; only unique('name').on(a, b) is
affected. Unique indexes go through a different code path and are stable, so all
fifteen are now uniqueIndex.

A unique index enforces exactly what the constraint did — verified, a duplicate
insert still fails on uq_screens_user_name — and onConflictDoUpdate accepts it as
an arbiter. It cannot be a foreign-key target, but nothing here targets a
composite key; checked before converting.

Separately, user_integrations_server_integration_id_server_integrations_id_fk is
65 characters and Postgres truncates identifiers at 63, so drizzle compared its
generated name against the stored, truncated one and re-created the FK every run.
Declared explicitly as fk_user_integrations_server_integration.

Measured on a scratch database, pushing twice each time:
  before        32 statements, interactive prompt
  after uniqueIndex   4
  after FK fix        2

The two that remain are a composite primaryKey with the same bug and no index
form to escape to — music_now_playing re-creates pk_music_now_playing every push.
Silent, no prompt even with rows, data unaffected, and naming it explicitly does
not help. Documented as expected.

The conversion itself was tested against populated tables, since that is what the
real database will do: no prompt, and all rows survived.

Docs rewritten in src/databases/CLAUDE.md — the rules committed an hour ago
described the broken behaviour and would have been wrong the moment this landed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 14:35:26 +00:00
pastilhasandClaude Opus 5 6af80a20b0 db: add the user role column, and write down how push actually behaves
Adds `role` to users as a text column with a TS enum, matching how `status` is
done — no pgEnum, so adding a role stays a one-line schema change rather than an
ALTER TYPE while the set is still settling. USER_ROLES and UserRole are exported
from officerdb so the API and UI enumerate them from the column definition rather
than a second hand-written list. Defaults to 'Member', the least privileged, so a
row created by a path that does not think about authorisation cannot mint an
admin. NOT YET PUSHED — the column is in the schema, not in the database.

The larger half of this commit is a rule for anyone running `bun db:push`,
because the first time you run it, it looks like your change broke something.

It plans 16 statements against a database that already matches the schema, and
plans them again on the next run. Two separate causes, both diagnosed here:

- it drops and re-adds every NAMED COMPOSITE unique constraint — all 14, from
  uq_screens_user_name to uq_wallet_labels_wallet_kind_ref. Single-column
  .unique() diffs correctly and is untouched; only unique('name').on(a, b) is
  affected. Names, columns and order in the database are identical to what the
  schema declares. drizzle-kit 0.31.9 / drizzle-orm 0.45.1.
- it drops and re-adds one foreign key because
  user_integrations_server_integration_id_server_integrations_id_fk is 65
  characters and Postgres truncates identifiers at 63, so drizzle compares its
  generated name against the stored, truncated one and always differs.

The rules, ranked by how much damage getting them wrong does: never answer "Yes,
truncate the table" — it destroys rows and does not help, since the constraint is
re-added next push either way; never delete a constraint from the schema to
silence the prompt, because upserts depend on it existing; do not reach for
--force until someone has established on a scratch database which branch it takes.
Run with --verbose first and read what it is actually planning.

Pointers added to platform/CLAUDE.md and the workspace root CLAUDE.md, since
those are what a session reads before it ever opens the database directory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 14:22:50 +00:00
pastilhasandClaude Opus 5 3f22a808d6 notify: the sidecar shell, with Discord as the first channel
Step 2 of docs/push-notifications.md. One real channel working end to end before any Apple
or Google credential exists, so the pipe is proven before the hard part.

officer-notify is a PM2 peer with its own loopback listener, announced as notify:server and
proxied at /api/notify. It is a sidecar rather than platform code because the producers are
spread across sidecars — the queue, email, the agent — and a platform-owned notifier would
force every one of them to call back into the platform. That is the inversion just removed
from email; this avoids recreating it.

Channels sit behind one interface (types.ts) so APNs and FCM slot in beside Discord rather
than replacing anything. Each is awaited with its own error boundary and the dispatcher
always resolves: a job that finished has finished whether or not a banner appeared, so a
channel must never be able to break its producer.

text.ts is where the doorbell rule is actually enforced. APNs and FCM both need a title to
render a banner, so "send nothing" was never available — what we control is that the string
is composed HERE from the category alone. A producer sends { type: 'mail', count: 3 } and
the wire carries "3 new emails". It cannot carry a subject line because there is nowhere to
put one.

Device registration lives behind X-Officer-User, trusted because the listener binds loopback.
Platform and environment are validated rather than defaulted: an iOS token from a debug build
fails against production APNs with a silent BadDeviceToken, so a wrong value is a device that
never receives anything and never says why. GET /_officer/devices returns only the last 8
characters of a token — enough to identify a row, not enough to push to it.

Verified end to end against a fake webhook: /_health reports configured channels, a test
notification arrives as {"content":"Officer"}, { type: 'mail', count: 3 } arrives as
{"content":"3 new emails"}, and every validation path returns its own error.

Deletes src/servers/notify/discord.ts, which this supersedes and which had no other callers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 21:36:17 +00:00
pastilhasandClaude Opus 5 aac20bf112 drop eight package.json scripts that point at nothing
Same origin as the phantom docs in the last commit — this architecture was based on another
project and some of its scripts came along without their targets.

- `build:editor` and its three variants run `scripts/build/editor.ts`, which has never
  existed here. CLAUDE.md carried a note saying exactly that, which the note now outlives.
- `start:sidecar` / `stop:sidecar` / `restart:sidecar` / `logs:sidecar` drive
  `systemctl … officer-pty-sidecar`, a systemd unit replaced by PM2. That unit still exists
  on this host, enabled, pointing at a `monorepo/` directory that is gone, and has been
  failing to start ever since — these scripts are how it stayed reachable.

CLAUDE.md's "Sidecar control" line pointed at the four systemd wrappers; it now says PM2 and
points at working-on-officer.md for which process a change needs restarted.

Verified afterwards that every remaining script's target exists. The rest of package.json is
untouched — the edit is nine lines out, no reformatting.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 17:42:24 +00:00
pastilhasandClaude Opus 5 04c0d89057 docs: bring the live docs back in line with the code
First pass of the documentation audit. Every doc was read against what the code actually
does now; this commit fixes the ones worth keeping and deletes the ones that were only
describing a past.

Corrected:
- CLAUDE.md — said seven WebSocket providers (there are eight, and terminal/vault are byte
  relays now, not translating bridges), listed channels/ as "Telegram / WhatsApp / Discord
  bridges" (they are gone; what remains is how /chat drives an agent turn), missed
  officer-wallet in the PM2 list and notify/ in the layout, and described the per-account
  email SQLite stores without saying they are the sidecar's and that nothing in the platform
  opens them. Further Reading pointed at four files that no longer exist and missed the four
  newest.
- docs/working-on-officer.md — PM2 list was four sidecars short, and it still explained the
  officer-claude rename as news. Replaced with the thing a reader actually needs: which
  process to restart for which change, and why restarting officer no longer costs you a
  terminal or an agent session.
- TODO.md — the "dead username plumbing" item was mostly resolved by deleting the channels,
  and two email items pointed at api/email/email-db.ts, which is sidecar/email/store.ts now.
- AGENTS.md — trailing paragraph listed the design notes being deleted here.
- MUSIC_API.md — playlists were entirely undocumented: seven endpoints the phone app has no
  reference for. Added from the sidecar's own contract.
- docs/jobs-unification.md — phases 1-3 shipped, so it now says so at the top. Phase 4 (push
  notifications) is the only reason the file still exists, and email sync is explicitly no
  longer part of it.

Deleted, all superseded rather than merely old:
- PHONE_APP.md — a February plan for apps that now exist, with their own repo and README.
- MARKETING_WEBSITE.md — a plan for a site this repo does not contain.
- SECURITY_AUDIT.md + SECURITY_FIXES.md — a February audit of a codebase since restructured;
  it still cites queue/handlers, which is now empty.
- docs/DOCKERIZATION_PLAN.md — cites pty-sidecar, whatsapp and projects, all deleted.
- SETUP_GUIDE.md — documents systemd units and setup scripts replaced by PM2 and `bun setup`.
  Not harmless: /etc/systemd/system/officer-pty-sidecar.service is still enabled on this host,
  pointing at a `monorepo/` directory that no longer exists, and has been failing to start
  ever since. That guide is how it got there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 16:11:29 +00:00
pastilhas 188b45c113 remove projects and apps end to end
deletes the last of the projects/apps cluster: the published-app store
(/api/apps + /api/app-serve), the project dev-server and its websocket
proxy (/api/dev-server + /api/dev-server-proxy), the shared html-rewrite
they were the only consumers of, and their frontend — the Preview panel,
the UserApp panel/header, useUserApps and the /settings/apps screen.

also drops getUserProjectsDir and getUserAppsDir, the ProjectType and
ProjectDefinition types, and the 'dev-server' websocket provider from
server.tsx. nothing on disk is touched.

1440 deletions, 31 insertions. tsgo clean.
2026-07-31 07:39:29 +00:00
pastilhasandClaude Opus 5 b6dc73915d add officer-invoiceshelf sidecar
owns the invoiceshelf contract: instance url, sanctum token and the company
header that scopes every request. the platform side is the usual thin
auth+forward proxy at /api/invoiceshelf and holds no credentials.

built against the live 2.4.2 instance rather than the 3.0.0-alpha.1 checkout
in _references — the route allow-list came from artisan route:list on the
running container. they differ: 2.4.2 has estimates/{id}/convert-to-invoice
but no invoices/{id}/convert-to-estimate.

three upstream quirks absorbed here:
- accept: application/json is mandatory, or an unauthenticated request 302s
  to an html login instead of returning 401
- origin/referer must never be sent, or statefulapi() switches to session+csrf
  and every request 419s. the proxy forwards neither.
- a wrong company header does not error, it silently returns another company's
  data. the pinned company is explicit and logged.

document pdfs are repaired: 2.4.2 prefixes them with a literal serialised http
response (201 bytes) inside a body already typed application/pdf. we slice to
the %PDF- magic. the report routes don't have the bug.

resources are an allow-list. backups, disks, modules, update/*, installation/*,
mail config, settings writes and ownership transfer stay unreachable, and the
per-resource action list keeps `send` — which really emails the customer —
from being reachable by accident.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 20:34:55 +00:00
pastilhasandClaude Opus 5 c39460ce2d document the navigation conventions in claude.md
the url-as-source-of-truth rules from docs/navigation-audit.md were only in the
audit; anything new was as likely to reach for a selection channel as a link.
also records the workspace-barrel requirement for shared route helpers, and adds
officer-headscale to the pm2 list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:14:48 +00:00
pastilhasandClaude Opus 4.8 86930f5b17 rename officer-claude to officer-anthropic-proxy
the pm2 entry named officer-claude never ran an agent. it starts
sidecar/claude/index.ts, which registers as capabilities: ['proxy'] and only
holds the anthropic proxy secret and forwards api traffic. the process that
actually spawns claude is sidecar/claude/user-instance.ts, which had no pm2
entry at all and was spawned on demand by the main server.

that misnomer is how both CLAUDE.md files ended up claiming that restarting
officer does not disturb a running agent session. it does: the agent was a
grandchild of officer and died with it. correct the name so the next reader
starts from a true model, and fix the claim in both files.

no behaviour change — officer-agent arrives in the next commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 04:28:19 +00:00
pastilhasandClaude Opus 4.8 32749eb665 document that bun format touches every dirty file
The script globs `git diff --name-only HEAD`, so running it after a small change also
reformatted an unrelated file the owner had in progress — whitespace churn landing in
someone else's diff with no explanation. Point at `bunx prettier --write <paths>` instead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 02:41:43 +00:00
pastilhasandClaude Opus 4.8 2a509646e7 slskd: /soulseek search + downloads on the Workspace/Panel framework
Two panels (search + transfers) via WorkspaceView, coordinating over the
soulseek:refresh channel. Adds the page-title rule and documents the
route conventions in CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 17:21:29 +00:00
brunorezioandClaude Opus 5 10400310c5 drop the unapplied migration history
The seven files in officer_db/migrations/ were never applied — this database has
only ever been managed with db:push, so there is no __drizzle_migrations table
and the numbered history had drifted from the real schema (0001 creates a
saved_sessions table the database does not have).

Deleted so there is one story about how the schema gets applied. db:gen can
write a fresh baseline from the current schema.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 23:30:20 +01:00
brunorezioandClaude Opus 5 0e71d87f75 rewrite CLAUDE.md against the current codebase
The old version described a repo that no longer exists: apps/dashboard and
apps/editor, a tracking server on port 5001, an ephemeral_db, and a dashboard
and API running as separate processes. None of that is true.

Replaces it with what the code actually does — one Bun process serving the SPA,
the API and eight WebSocket providers; sidecars for the privileged work; the
split between Postgres and the file-backed items store; and the single-user
invariant stated as an invariant rather than a migration in progress.

Also records two things that are easy to get wrong from reading alone: the
database is maintained with db:push and has never had a migration applied, and
agents run unsandboxed with permissions bypassed on purpose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 23:30:20 +01:00
pastilhasandClaude Opus 4.8 a82ce9ecb0 drop inherited architecture docs that no longer match the code
AUTOMATION_CONTEXT.md and SIDECAR.md described the multi-tenant scope
model, the seed/ tree, the marketplace, and a single sidecar owning the
queue — all superseded. Fix the stale doc pointers in opencode.json and
CLAUDE.md, and point at TODO.md as the source of truth on direction.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 20:25:44 +01:00
pastilhas 9ab0940ca4 first 2026-02-16 19:34:35 +00:00