Every item agreed in the 14 September standup, plus the open items reconciled from the 1 September sync, with the pull request, test run or ticket that proves each one was delivered — and, just as plainly, what was raised on that call and is not delivered yet. This is a living document; it is updated as work lands. Written for a non-engineer first; the code is explained where it matters.
staging branch, reviewed and green. They are not built into staging.vantagepost.ai yet: seven other merges landed on staging during the same day (PRs #91, #92, #93, #94, #95, #102, #104), which shifted the build base under every open branch several times and forced one branch (#101) to be re-merged against the moving trunk. Rather than build on a base that kept changing, each branch was re-verified against the final state of staging and held for a single, ordered merge. Recommended order, each after the previous one lands and its CI re-runs: #89 → #90 → #96, #97, #98 → #100 → #99 → #101.
Each row is one thing the standup agreed would happen. "Delivered" means the work exists in a reviewed pull request or a filed ticket, with the receipt in the last column.
| Commitment | Status | Receipt |
|---|---|---|
| Finish the missing article types so every type can be created everywhere | Delivered | PR #89 — review found one gap (the copilot's own create tool), fixed on the branch; CI green |
| Two keyword mockups: before writing and after writing | Delivered | Section 10; interactive version at claude.ai/artifact/AmXVPnfz5BbUkmwViV8aui, light mode with a dark toggle |
| Fix the remaining generator bugs | Delivered | #96 retry data loss · #97 silent progress · #98 duplicate scheduled articles · #101 Create Article spacing/back · VP-242 verified already fixed |
| Rework the keyword feature, including its fallback when suggestions fail | Delivered | PR #99 — tray, Vince prefill, failure message, one-primary rule; browser-verified on localhost |
| Investigate the share-link/slug bug and file it | Delivered | Traced in code: share links are random tokens and cannot break; the published URL slug is what goes stale. Filed as VP-385 |
| Update Linear with the full feature-flag design, including the "hide the endpoints" question | Delivered | VP-366 comment; implementation in PR #90 |
| Move billing and auto-refunds to Grant | Delivered | VP-286 reassigned with the agreed rules (token-based charge, auto-refund, stop-proof, no dollar amounts, history placement) |
| Find out why the staging account is missing its articles | Delivered | Cause found: the content copy only ever ran as a dry run (apply: false) and the config names a different profile email. Filed as VP-386; the run itself is a production-database write and is held for sign-off |
| Research the internal MCP tool registry, the Bitwage automation branch and WebMCP; recommend one standard | Delivered | VP-387 — memo with options A/B/C and a recommendation (B) |
| Put the outstanding standup items into Linear without duplicating Grant's tickets | Delivered | Section 11; no tickets created for read time or fact check (Grant's VP-382/383 already cover them) |
| Check in with Grant | Scheduled | Material is ready: this report, the mockups and the eight PRs |
Listed so the report reads as a record, not a highlight reel. Each row says where the item stands today.
| Item from the call | Where it stands |
|---|---|
| Written wrap-up of the meeting in Linear | Not written The items were filed as individual tickets instead |
| Confirm the copilot (Vince) can still do everything it could, including editing an article | Not tested No ticket |
| Finish article import/export as JSON | Not built Tracked as VP-305; the labelled-sections requirement was added to it |
| Copy the local articles to staging | Not run Cause found and documented in VP-386; the run writes to the production database and is held for sign-off |
| Verify the engine still writes its own title when the title is blank | Not verified Filed as VP-408 |
| "Link selected" on the SEO tab, which failed live on the call | Not fixed Filed as VP-409 |
| Per-article cost metering and where the transaction history lives | Not built Handed to billing, VP-286 |
| Performance-monitoring hooks in the engine | No ticket |
| Decide the agent/MCP standard (A, B or C) with Grant | Not decided Memo written in VP-387 |
| Re-point the unpublished local branches onto the new sidebar navigation | Not done |
| Follow-up check-in with Grant | Not held This report is the material for it |
From the same call on Grant's side, not yet delivered: replacing the staging OpenAI key (VP-361), a delete control and longer brief on proposal cards, the tooltip that sticks during generation, looping the keyword bank into the research phase, and keyword-aware proposal titles.
What it does. The article type list had shrunk to nine. This restores Industry Analysis, Product Review, Case Study and Opinion Piece, so thirteen types are available, and makes every place that offers a type read from one list instead of keeping its own copy.
What users see. The same thirteen choices in the guided Create page, the Traditional Writer, the schedule dialog, the YouTube-to-article dialog and when asking Vince to write — and each choice actually changes how the article is structured, rather than quietly falling back to Thought Leadership.
generateArticle tool still carried a hand-written list of seven types, two of which never existed ("How-To Guide", "Industry Trends"). It now uses the canonical list.tsc --noEmit clean; jest for copilot, prompts and article-engine: 11 suites / 253 tests passing; CI "Build & Type Check" green after the push.What it does. Adds a Features section to the admin panel with an on/off switch and an audience (everyone / admins only) for each product area. Turning an area off removes it in three places at once, because a flag that only hides a menu item is not really off.
What users see. Nothing, until an admin flips a switch: both flags default to on for everyone. When an area is off, its sidebar entry and dashboard shortcut disappear, its pages redirect to the dashboard, its public share links return 404, and every one of its API routes answers 404 — the same response a route that never existed gives, so from outside a disabled feature is indistinguishable from a missing one.
tsc clean; no migration (the flags table already existed).The problem. Pressing Retry on a finished article deleted its sections and sources first and only then asked the background queue to rebuild it. If the queue was unreachable at that moment, the content was gone and the article sat on "Pending" forever. Two further holes: an article written by the new Article Engine was reset and handed to the old pipeline, which replaced its content with something different; and the SEO score from the old text stayed on the row.
What users see. Retry on an old-pipeline article works as before. Retry on an Article Engine article is refused with a plain message ("Retry only re-runs the classic pipeline… generate a new article to run the engine again") before anything is touched. The SEO tab no longer shows a grade for text that no longer exists.
conflict() (409) helper, matching the shape of the existing error helpers; all five client call sites already show the server's message in a toast.['enqueue','delete','delete'] literally; the 409 case asserts zero database writes.app/api: 32 suites / 247 tests; tsc clean.The problem. After "Let's write it", the Write step polls for progress. When the background worker is not running, or its progress write fails, the article sits on Pending with no snapshot forever; the screen shows outline placeholders, a 0% bar and a ticking clock and says nothing. The only signal was a console warning nobody reads.
What users see. After 15 seconds with no progress: "No progress received yet — the background worker may not be running." If updates stop arriving mid-run for 150 seconds: "Still working — no update in n s." If the worker's own progress write is failing: "Progress updates aren't being saved — the run continues, but this screen can't follow it."
buildSeedProgress() so they cannot drift.tsc clean.The problem. The nightly job that turns planned topics into articles did three things inside one retryable unit: create the article row, link it to the planned topic, hand it to the background writer. If the hand-off failed, the retry redid everything — a second article for one topic, billed twice, with the first stranded on Pending forever and the org's free-first-article entitlement burned.
What users see. One planned topic produces exactly one article and one charge, even when the queue hiccups. A topic already being written by hand is skipped rather than written twice. A deleted planned topic no longer aborts the whole night's run for every other customer.
already_in_flight.enqueueArticleGeneration helper (never throws); a failed hand-off deletes the empty row and returns the topic to queued, and the run summary counts it as failed, not created.skipped: item_deleted and the loop continues; transient errors still rethrow so the queue retries them.tsc clean. Integration test written and gated (it writes to the shared production database, so it was not executed here).The problem. The Create page sent no keyword signal at all: both outline calls hard-coded an empty keyword list and the create request never carried target keywords. The only way to steer an article's keywords was to type them into the topic as prose. The engine already accepted primary, secondary and long-tail keywords; nothing in the interface filled them.
What users see. A Find ideal keywords button next to "Chat with Vince". It opens a tray under the topic box showing keywords from the organisation's bank that match the topic, grouped Primary / Secondary / Long-tail with search volume and difficulty. Auto (recommended) keeps today's behaviour for anyone who never opens it; I'll pick my own unlocks the checkboxes. Search for more keywords runs a paid DataForSEO discovery, only when clicked. When Vince suggests keywords in chat, a "Vince suggested N" row shows them already selected, with Undo.


/content/v2 rather than rebuilt; pure state helpers (groupKeywords, toggleKeyword, applySuggestedKeywords, dropStaleMatches, withSinglePrimary, extraPrimaryNote) so every rule is unit-tested.targetKeywords and recommendedKeywordIds; the v2-engine shape is built by the same rules and ready for the day the flow targets it.prefillArticleForm tool gained a keywords field; the mascot passes it through; the page applies them into the tray.aria-expanded on the toggle, labelled checkboxes, visible focus ring, panel id only referenced while open. Light-mode theming checked (semantic tokens only).tsc clean.The problem. Opening the local app meant the emailed one-time code every time. Every screenshot, demo rehearsal and automated check that needed a signed-in page went through an inbox. There was no bypass anywhere in the auth configuration.
What developers see. http://localhost:3050/api/auth/dev-login signs the local browser in as the profile named in AUTH_DEV_AUTOLOGIN_EMAIL and lands on the dashboard; the login page shows a small "Dev sign-in (local only)" link while the feature is on. Customers see nothing: every deploy path pins NODE_ENV=production at build time, so the route compiles to a 404 in the staging and production images regardless of what is in the environment file.
NODE_ENV !== 'production' and the email variable is set; never creates a user (unknown email → 404), a disabled profile → 403..env.example and the local-dev workarounds guide.tsc clean.The problem. The Create page stacked four nested paddings before its heading, so it started ~80 px lower than the Articles tab in the same page. The step rail was pinned 65 px from the top for a header that no longer exists. And there was no way out of the writer except the sidebar.
What users see. Create starts at exactly the height the Articles tab does. A top-left ← Articles link on step one (a real link: cmd-click works) and Start over on later steps, disabled while an outline is streaming or an article is being created so it cannot strand a charged article. On phones the control and the rail clear the fixed menu button.



sticky top-[65px] and its comment removed; rail at top-0 on desktop, top-14 on phones.StepBackControl (a breadcrumb <nav>, first in tab order, 44 px touch target, tokens only so light and dark both follow); the old "Back to start options" link removed from the hidden chrome row.tsc clean.Built from the app's own design tokens (the light palette in globals.css, the Geist typefaces, the article-paper styles) so they read as the product, not a wireframe. Each carries numbered callouts that map every control to the existing backend it reuses. Light mode by default with a Light/Dark toggle; interactive version at claude.ai/artifact/AmXVPnfz5BbUkmwViV8aui.


Every ticket carries the current state of the code with file references, what a user experiences, the fix, and acceptance criteria. Duplicate checks were run against the existing backlog before each one.
| Ticket | What it is | Why it exists |
|---|---|---|
| VP-385 | Published article keeps its old URL after a rename; an edited slug breaks the old link | The share-link investigation: share links are random tokens and cannot break; the published slug is set once and never regenerated, and a slug edit 404s the old URL with no redirect |
| VP-386 | Local articles never reached staging: the content copy only ran as a dry run | Root cause of the empty staging account, with the exact steps to run it and the profile-email check to do first |
| VP-387 | One standard way for agents to drive VantagePost | The MCP research memo: what the tool registry does, what the Bitwage branch adds, WebMCP, options A/B/C, recommendation B |
| VP-388 | Choose keywords before writing, work them in afterwards | The keyword feature with architecture map and acceptance; carries the mockups and PR #99 |
| VP-389 | Engine v2 articles never report progress | Found while fixing VP-291: the v2 route never passes a progress callback |
| VP-390 | Hide "Regenerate Article" for engine articles | Follow-up to VP-270: the action is now safely refused, but should not be offered |
| VP-391 | Classic article API accepts any article type | Found reviewing PR #89: unknown types are stored and written as Thought Leadership |
| VP-403 | Dev-only auto sign-in for localhost | PR #100 |
| VP-408 | Check v2 writes its own title when the title is blank | Raised in the standup; not previously tracked |
| VP-409 | "Link selected" on the SEO tab does nothing | Failed live in the standup; the after-writing half of the keyword plan |
| VP-410 | Grade sources for quality and recency | From the 1 September sync; the fact-check gate only checks a link resolves |
| VP-411 | Put the generated JSON-LD onto the published page | From the 1 September sync; structured data is generated but never rendered |
| VP-412 | Move the article engine into services + repositories | From the 1 September sync; agreed to happen before the engine grows further |
| VP-413 | Log-level switch for Engine v2 tracing | From the 1 September sync; verbose tracing needs turning down before launch |
| VP-286 | Bill v2 articles once and refund failures — reassigned to Grant | Per the standup, with the agreed rules and the transaction-history placement question added |
| VP-366 | Feature flags — updated | Three-layer design and the endpoint-hiding answer |
Notes were also added to existing tickets where the sync had widened their scope: VP-293 (the live progress detail that VP-399 described), VP-305 (labelled export sections), VP-285 (Bitwage articles with executive summary and FAQ), VP-244 (config-driven model ladder and a cheap dev mode).
The earlier sync was re-read in full and checked against every ticket created since. Most of it was already tracked (VP-286–305 were filed from that call on 9 September; Engine v2 is VP-283; article types are VP-240). Four items had no ticket and now do — VP-410, VP-411, VP-412, VP-413 — and four more were folded into the tickets that already cover them (VP-293, VP-305, VP-285, VP-244). Three items remain personal follow-ups outside Linear: deliver the pipeline atlas as HTML + PDF to the shared drive, a section-by-section engineering walkthrough of v2, and the deferred idea of letting an agent select prompt blocks per article.
bunx tsc --noEmit clean and the relevant jest suites green before any push; whole-repository runs where the change was cross-cutting (2,615 on #90, 2,583 on #99).