Commit graph

59 commits

Author SHA1 Message Date
25f281f659 site: enable in-page-console, project-builder, web-designer, qa (qa heartbeat 60) 2026-07-24 08:03:00 -04:00
f287f74448 framework(core): per-project agent roster + unified registry
Phase 3.
- server.mjs loads only site.agents.enabled, passing per-agent settings
- each console agent exports a manifest + register(host, settings); QA reads
  heartbeatMin/autofix from settings (env fallback)
- catalog.mjs folds the runtime (console) agents into agents.json/AGENTS.md via
  their manifest exports — one registry for pipeline + runtime agents
Verified: disabling 'qa' in the roster removes /qa/run + the heartbeat.
2026-07-24 08:03:00 -04:00
7799fd62e3 framework(core): lift site identity into site.mjs (config-driven)
Phase 2. host + agent modules no longer hardcode identity:
- site.mjs derives name/url/host/model/agentUser/home/role/qaEmail from
  site.config.json + astroagent.config.json
- host uses site.model/site.tools/site.home; QA uses site.url/host/qaEmail/routes
- personas templated from site.*; site-specific brand wording (sumi-e, .ca-*,
  'law firms') dropped — brand now comes only from brand/BRAND.md + the brand skill
Core (host.mjs + agents/*) is now site-agnostic; identity lives in config.
2026-07-24 07:57:54 -04:00
542858b1ec framework(core): split console runner into host + pluggable agent modules
Phase 1 of the standalone agent framework. Pure refactor, behaviour identical:
- host.mjs: shared runtime (route registry, one runClaudeJson/runClaudeStream
  replacing 5 inline spawns, build/git/phpCli, SSE job model, scheduler, core
  /ping /auth /logout)
- agents/{in-page-console,project-builder,web-designer,qa}.mjs: each exports
  register(host), owns its routes/triggers/persona
- server.mjs: bootstrap that loads host + registers agents
- web-designer exposes host.drainTasks; qa autofix kicks it
Verified: QA pass, Web Designer queue task, heartbeat, all endpoints.
2026-07-24 07:50:26 -04:00
73403f7f21 Revert "web designer: Phase1 refactor test"
This reverts commit e5d68090e7.
2026-07-24 07:50:18 -04:00
e5d68090e7 web designer: Phase1 refactor test
[published via task queue]
2026-07-24 07:49:40 -04:00
74c6445e0e docs(.memory): QA agent reference + Web Designer self-logging/QA-loop update 2026-07-24 07:12:29 -04:00
57dee86981 web designer: QA fix: a11y — /blog
[published via task queue]
2026-07-24 10:41:41 +00:00
19c0cbb232 qa: auto-fix safe findings + per-finding instructions box
- qa-autofix.php queues Web Designer tasks for safe findings (alt, internal
  links) with 6h dedup; runQaFlow runs it after each pass and kicks the queue
  (QA_AUTOFIX env, on by default)
- adminqa fix() accepts optional human instructions; /admin/qa adds an
  instructions input per finding for the judgment calls
2026-07-24 10:39:15 +00:00
29938fcd7f content: remove outdated GitHub link (footer, resume, JSON-LD) 2026-07-24 10:33:16 +00:00
042cc8a7c0 web designer: QA fix: a11y — /resume
[published via task queue]
2026-07-24 10:30:04 +00:00
182b1485ca qa: flag empty alt (not just missing) — decorative-only exemption removed
An empty alt is valid only for purely decorative images; a content image
(photo/cover/screenshot) with empty alt is a real a11y gap. QA now surfaces
empty and missing alt as distinct findings.
2026-07-24 10:28:12 +00:00
18db0de37b changelog: log the QA agent + sitemap coverage fix 2026-07-24 10:20:39 +00:00
87fceea726 qa fixes: full sitemap coverage + a11y check recognises empty alt
- sitemap.xml.js now includes services, projects (+ DB-driven project pages),
  resume, changelog, faq (was blog-only) — clears 12 QA sitemap warnings
- runQa a11y check treats a bare/empty alt as present (valid decorative image),
  fixing a false positive on the blog cover
2026-07-24 10:20:14 +00:00
d548d8a8a8 qa: hourly in-process heartbeat + run pruning
- runner: QA_HEARTBEAT_MIN (default 60) in-process timer runs a scheduled QA
  pass; one pulse ~90s after boot. Triage (LLM) skipped on clean scheduled runs
  to save tokens; only manual runs or runs with errors get a written summary.
- qa-finish.php prunes to the 50 most recent runs.
- systemd unit exposes QA_HEARTBEAT_MIN for tuning.
2026-07-24 09:47:29 +00:00
56c1926b81 qa: QA agent — HTTP site testing with one-click Web Designer fixes
- migration 013: cja_qa_runs + cja_qa_findings
- runner: runQa (crawl pages/links/images/external/contact-probes/health/
  SEO/meta/sitemap/a11y), qaTriage (LLM summary via qa skill), /devconsole/qa/run
- qa-start/finish/routes.php CLI bridge
- adminqa controller (runs/findings/fix/ignore); fix() chains to the Web
  Designer queue; /admin/qa report page + nav link
- qa skill; contact form probed via honeypot (writes nothing)
2026-07-24 03:30:13 +00:00
f832752118 changelog: log this session's admin fixes + attribution/self-logging 2026-07-24 02:52:55 +00:00
75dcfc3098 Revert "web designer: Changelog self-log test"
This reverts commit d0924ffadf.
2026-07-24 02:47:24 +00:00
d0924ffadf web designer: Changelog self-log test
[published via task queue]
2026-07-24 02:46:57 +00:00
df5e5f9456 changelog: 'by' attribution + Web Designer self-logs via the skill
- migration 012: cja_changelog.actor column
- seed-changelog: optional 5th 'by' element (defaults to Carlos Arias);
  display shows 'Added · time · by <actor>'
- changelog skill documents the actor field + attribution logic (agents name
  themselves, human/CLI edits are Carlos Arias)
- Web Designer prompt now logs each change to the changelog via the skill,
  attributed to 'Website Designer Agent'; runner reseeds cja_changelog when the
  seed file changed (the agent has no shell)
2026-07-24 02:45:53 +00:00
2ad551dc5f skills: add changelog skill (seed-changelog process, format, voice) 2026-07-24 02:39:01 +00:00
11b2f960a9 web designer: The service Website Design should be linked to /services/website-design for SEO
[published via task queue]
2026-07-24 02:36:26 +00:00
15292f66fc admin: fix login screen showing both panels + add dashboard links
- .panel's display:flex was overriding the [hidden] attribute, so the Sign-in
  form and 'You're in' panel showed at once regardless of session. Force
  [hidden] { display:none !important } so they toggle.
- 'You're in' now links to Web Designer, Manage projects, and the site.
2026-07-24 02:35:20 +00:00
a906ef026f admin: fix white-on-white select dropdown in dark mode
Native <option> popups used the browser default (white) background; in dark
mode the var(--ink) text rendered near-white on it. Give the select a themed
background and style option explicitly. Fixes the Web Designer Page picker and
the project editor status dropdown.
2026-07-24 02:34:04 +00:00
497f55364b changelog: admin + Web Designer arc; document the engine update
- seed-changelog: 5 entries (admin console, self-editing agent, uploads,
  project builder, Web Designer + task queue)
- .memory/webdesigner.md: core Astroagent console update — architecture,
  decisions, safety model, files, and the site-coupling caveat
2026-07-24 01:28:09 +00:00
fa85561d12 Revert "web designer: Add test FAQ with image"
This reverts commit 7a3d5dbf4c.
2026-07-24 00:22:59 +00:00
7a3d5dbf4c web designer: Add test FAQ with image
[published via task queue]
2026-07-24 00:22:28 +00:00
364fd1b425 web designer: guard ignores uploaded media assets (app/public/media) 2026-07-24 00:20:52 +00:00
50f6db55d8 web designer: /admin/designer workspace (submit brief + poll queue) 2026-07-24 00:19:46 +00:00
7fc99415b4 web designer: task queue engine + CLI-native design skills
- .claude/skills/: brand skill (from BRAND.md) + ui-ux symlink, so the CLI
  loads design skills natively
- cja_tasks queue (migration 011) + admintasks controller (create/list/get/cancel)
- api/cli/tasks-next.php (atomic claim) + tasks-finish.php
- runner: buildDesignPrompt, runDesignTask (clean-tree guard, build-gate,
  auto-publish commit or revert), drainTasks loop, POST /devconsole/tasks/run
2026-07-24 00:16:39 +00:00
ad0892a539 Revert "web designer: Queue engine test"
This reverts commit d4ee3ff960.
2026-07-24 00:14:44 +00:00
d4ee3ff960 web designer: Queue engine test
[published via task queue]
2026-07-24 00:14:28 +00:00
faef1c3229 admin: Web Designer agent builds full project pages from a brief
- console /build-project: Read-only agent reads uploaded images and authors a
  complete cja_projects record (copy, kind/period/role, categories, stack,
  grouped skills, metrics, per-image captions). Server re-zips gallery srcs.
- adminprojects save()/get(): handle skills + metrics JSON columns
- New Project form: 'Build with Web Designer' + full editable preview
  (skills groups, metric cards, captions merged into gallery)
- editor: round-trip skills + metrics editing
2026-07-23 23:44:50 +00:00
a88cc41557 admin: New Project page with agent-drafted copy + full editor
- /admin/projects/new: name, category, description draft, agent prompt,
  image upload, Instagram link
- console /draft endpoint: one-shot Claude copy drafting (summary/lede/body)
- editor now edits all project text fields, not just gallery
- adminprojects create() + expanded save()
2026-07-23 23:23:33 +00:00
a2da708418 Phase 2/3: projects media admin (galleries + Instagram)
- /admin/projects list + /admin/projects/edit gallery editor (AdminLayout chrome,
  session-guarded). Upload/replace images per slot, captions, add/remove,
  Instagram reel URL. Save -> cja_projects -> rebuild -> live.
- adminprojects API (list/get/save); media path validation (local paths only).
- runner /rebuild endpoint: build public/ + commit after structured edits.
- Ownership: app/ + public/ now owned by carlos-arias-agent:caweb so the agent
  can rebuild its own output; www serves via the caweb group. Documented in
  SETUP.md — never build as root.

Verified end to end: upload image -> save to gallery -> rebuild -> image live on
the project page and served.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 22:55:32 +00:00
dc69d1fd0d Phase 2: media upload pipeline
- cja_media table; POST /api/media/upload (admin-gated, multipart).
- Images optimised with GD (downscale to 1600px, re-encode; WebP for
  transparency, JPG for photos). Video via ffmpeg (scale, compress, drop audio).
- Stored in app/public/media/ (source tree) so uploads survive rebuilds and are
  copied into public/ on build.
- requireAdmin() moved to PublicController base (fixes a static/non-static
  clash with AdminAuth). Type detection uses getimagesize (fileinfo ext absent).

Note: php-fpm must be in the caweb group to write app/public/media (restart
after adding www to caweb).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 22:45:45 +00:00
b967419717 Phase 1: admin login page + secret-link token auth
- /admin login page (public, noindex): password form + signed-in panel that
  activates the in-page console. Verified in-browser: login -> panel -> the
  astroagent handle appears on other pages.
- Secret-link login: visit /admin/<token> to sign in without a password.
  adminauth token() validates a bcrypt-hashed token, sets the session, 302s to
  /admin. nginx routes the token path to PHP. set-admin-token.php generates it.
- Password login kept as a fallback.

Verified: token link -> session -> console access; bad token -> /admin?e=1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 22:15:50 +00:00
e57856173a Phase 1: console runner service + nginx gate + systemd
- agents/console/server.mjs: Node runner (as carlos-arias-agent, 127.0.0.1:3011).
  run/stream(SSE)/publish/discard/ping/auth. Agent edits files (no Bash); the
  runner builds isolated previews and git-commits on publish. Git ops scoped to
  content paths (app, brand, api/db, api/cli) — never infra or secrets.
- adminauth check() — nginx auth_request target (204 admin / 401 not).
- nginx: /devconsole + /_preview gated by auth_request, runner proxied,
  previews admin-only. Runner never exposed directly.
- systemd unit (hardened: NoNewPrivileges, ProtectSystem, scoped ReadWritePaths).

Verified end to end: admin login -> agent edits about.astro -> isolated preview
(admin-gated) -> discard reverts. Unauthenticated access is 401 throughout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:53:32 +00:00
59dbfa2071 Phase 1 foundation: confined agent proven
Records the operational setup (agents/console/SETUP.md). Server-side, not in git:
- confined carlos-arias-agent user (nologin, no sudo) runs headless Claude
- claude relocated to /usr/local/bin (root's install was unreachable)
- caweb shared group: agent edits+builds, www serves, no chown-to-www needed
- valid setup-token stored in agents/.env (git-ignored)

Verified: agent reads repo, writes source, runs isolated preview build, output
served by nginx. Full edit->preview->serve loop works end to end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:36:37 +00:00
59019d2f32 Phase 0: admin console foundation
- cja_admin + cja_admin_log tables (dedicated single-admin tier + audit trail)
- AdminAuth controller: /api/adminauth login/logout/me, session-based,
  rate-limited, every attempt logged
- set-admin-password CLI (bcrypt, run manually so the password never enters
  an agent context)
- requireAdmin() guard for future privileged console endpoints
- agent env scaffold (git-ignored)

Foundation only — no agent runner or features yet. Preview/publish, media,
and the dashboard come in phases 1-3 per agents/console/PLAN.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:23:48 +00:00
43ae1e2015 Baseline: full site build (brand, pages, DB, agent-editable)
Establishes the deploy baseline on main so the admin agent's publish/rollback
has a clean starting point. Everything built to date: sumi-e brand system,
homepage, projects (DB-driven case studies), resume, about, services +
website-design detail, contact form + DB, changelog, favicon + share card.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DoFYZY9gkGPNDqZ7NuEa9a
2026-07-23 20:21:28 +00:00
2c969c0753 feat: content-pipeline/ → agents/ — formalize the agent system in the seed
Adopt the agents/ architecture proven on medellin.co (reference impl):

- Move the content engine to a top-level agents/ dir: orchestrators, prompts,
  config, run.sh, admin console, shared libs. All content-pipeline literals
  repointed (config paths, scripts, admin, LLM-facing prompts/image.md string,
  configure.mjs, new-site.sh, astroagent tokenFile, .gitignore runtime block).
- Every script carries a parseable @agent-manifest header: name, title, class
  (content|operational|runtime|plumbing), trigger, model, prompts, skills (MCP),
  tools, reads/writes tables. 5 content agents + 3 plumbing scripts.
- New agents/catalog.mjs generates the catalog from the headers:
  agents/AGENTS.md (human, grouped by class) + agents/agents.json (machine
  manifest — a clone diffs it against a source to find missing tools/tables/MCP
  before running). configure.mjs regenerates the catalog on every identity
  stamp. No DB table, no watcher.
- config.json gains paths.stateDir/newsDir; publish-tick, write-daily, and
  news-radar read them instead of hardcoding.
- Full cut: content-pipeline/ deleted (the seed has no live crons, so no
  hybrid period needed). Docs updated (AGENTS.md structure + pipeline section,
  README paths).

Clones migrating from content-pipeline/: see medellin.co's
.memory/handoffs/agents-directory-migration.md for the cutover playbook
(one cron set active at a time; migrate drafts/state after repointing cron).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMQeUnUrAeexcZ7P2Hxa6G
2026-07-11 17:09:15 -05:00
41f1b941fa Merge: trusted-IP throttle exemption + session hardening 2026-07-11 09:16:30 -05:00
5d1505d666 Merge: user auth (email + Google OAuth), Mailer, CLI bridges 2026-07-11 09:16:30 -05:00
fc63f3ca03 Merge: full schema snapshot (structure only) 2026-07-11 09:16:30 -05:00
1f33296e30 feat: user auth (email + Google OAuth), Mailer, and CLI bridges
Generic framework features, code-only — the tables come from the schema snapshot:

- controllers/account.php — email signup (verified) + Google OAuth (server-side auth-code
  flow, async-popup friendly); native password_hash sessions on sp_users; sp_oauth_accounts +
  sp_user_tokens; PublicController origin+throttle guards; {ok,data,error} envelope.
- Helpers/Mailer.php — transactional email: Brevo HTTP API (Guzzle) -> SMTP fallback
  (PHPMailer) -> logs; config in sp_settings (encrypted).
- cli/rebuild.php — queue a static rebuild (sp_settings rebuild_pending flag) after DB edits.
- cli/resources.php — read a curated source registry (mde_resources) per agent_type.
- db/seed_google_oauth.php, db/seed_email.php — env-seeded encrypted secrets into sp_settings.
- templates/emails/verify.html — verification email template.
- composer.json — declare guzzlehttp/guzzle ^7.10 (account.php + Mailer use GuzzleHttp\Client).

Depends on the schema-snapshot branch (sp_users / sp_oauth_accounts / sp_user_tokens /
sp_settings / mde_resources) plus `composer require guzzlehttp/guzzle`. No data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMQeUnUrAeexcZ7P2Hxa6G
2026-07-11 09:14:25 -05:00
c542092a2a chore(db): add full schema snapshot (structure only) to bring the base current
Structure-only export from a live SeedProject site (68 tables: the sp_ framework — users,
oauth, tokens, settings, roles/permissions, api_requests, orgs, saas/billing, menus, kathe —
plus the mde_ content model: content, directory, events, comments, reactions, categories…).

Every table is CREATE TABLE IF NOT EXISTS, so it composes with the existing 001/002 migrations
and only fills in what's missing. This modernizes the base off the legacy `usergen`/`api_requests`
schema and is the prerequisite for porting the auth system, Mailer, and resource registry upstream.
No data included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMQeUnUrAeexcZ7P2Hxa6G
2026-07-11 09:08:46 -05:00
3f69000d27 feat: never throttle trusted server-local callers + harden session cookie
Two self-contained framework fixes, no schema dependencies:

- throttle(): exempt trusted callers via new Functions::isTrustedIp() — loopback, the
  server's own IP, and an optional TRUSTED_IPS config allowlist (IPs/CIDRs). Fixes the SSG
  build (which fetches the read API from the box thousands of times per build) tripping the
  public rate limit and baking empty data into the deploy. Public client IPs stay limited.
- public/index.php: set secure session cookie params (httponly, SameSite=Lax, secure on
  https) before session_start, so session/login state rides on a hardened cookie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMQeUnUrAeexcZ7P2Hxa6G
2026-07-11 09:01:49 -05:00
d085d26c78 drawer: sync visual element picker from astroagent
Pick an element on the rendered site, comment, and the agent edits the
source. Includes the discoverability fixes (SVG crosshair, pick-mode
banner) and publish auto-reload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RCfiiNfHmDRj4ZZiFM535z
2026-07-05 00:46:30 +00:00
f41fa652ac fix: repair the fresh-clone flow — track api/public + app/public, fix installer schema
Three bugs found deploying a fresh clone to seedproject.com, each fatal
to the documented "spin up a new site" flow:

- .gitignore: the unanchored `public/` pattern (meant for the root build
  output) also ignored api/public/ (the framework's front controller,
  controllers, models, views) and app/public/ (theme static assets:
  fonts, avatar placeholder). Neither was ever committed, so every fresh
  clone 500'd on all /api routes and 404'd on theme assets. Anchor the
  pattern to /public/ and commit both directories.

- api/install/dump.sql: stray `CREATE DATABASE ochenta80_db123` (SQLyog
  export artifact) aborted `php console app:install` for any
  non-privileged DB user. The schema must import into whatever database
  the installer connects to.

- PluginManager::boot() queries sp_plugins on every request, but no
  shipped schema creates it — even a successful install 500'd on every
  endpoint. Add migration 002_create_sp_plugins.sql matching the columns
  PluginManager reads/writes.

Also empty api/system/errors.json, which shipped with stale error logs
from an unrelated project.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3JqxTe7TKaR7xufcMr7Ds
2026-07-05 00:15:55 +00:00