release: promote development to main (academy tutorials + ConNext pivot + partners on app/solution pages) - #118
release: promote development to main (academy tutorials + ConNext pivot + partners on app/solution pages)#118WilcoLouwerse wants to merge 358 commits into
Conversation
…pec-introductie # Conflicts: # src/css/site.css
…ijn-translations # Conflicts: # src/css/site.css
* academy: add spec-driven development tutorial Companion tutorial to the new nextcloud-vue.conduction.nl/docs/architecture/configuration-over-code page. Teaches the schema-first, manifest-second authoring workflow without writing any Vue, and frames the same JSON contract as the safe sandbox for AI agents, citizen developers, and the OpenBuilt visual editor. Slug 'spec-driven-development' so the docs site can link to conduction.nl/academy/spec-driven-development. * academy: reframe spec-driven tutorial around OpenSpec The first draft framed "spec-driven development" as the JSON-manifest authoring workflow. That was wrong. Rewrite around OpenSpec as practised by Hydra: humans write Markdown specs (RFC 2119 + GIVEN/WHEN/THEN) and ADRs, an AI agent implements to them, and a sequential quality + gatekeeping harness validates the result. Covers: the OpenSpec directory layout (living specs vs deltas), the org-wide vs per-app ADR tiers governing samenhang, one-feature-one-spec, the /opsx-explore thinking stance, the /opsx-apply implementer with ADR-031 declarative business logic, the n8n/Windmill codeless path via WorkflowEngineInterface, and the 13 mechanical gates + team-reviewer + team-security review harness. * academy: prerequisites, general ADR framing, opsx phase + flow diagrams - Prerequisites rewritten: app-design-principles + manifest/schemas as knowledge prereqs, an LLM tool (Claude/Mistral/Codex/other), and the OpenSpec skills installed from the Hydra repo. - ADR section generalised: describes the org/app two-tier pattern abstractly (no link to the org set), links OpenConnector's 16 real per-app ADRs as the concrete application-level example. - New "workflow, phase by phase" section using the SetupSteps component for the opsx phase sequence (explore → scaffold → plan → apply → verify → archive), plus a stacked cn-domain-tree diagram showing the ADR tiers feeding every phase and apply outputting manifest + schema with optional code/workflows. - New "feature request to delivered functionality" section using a cn-pipeline for the end-to-end flow. - MDX fix: wrap `*`-bearing literals inside <code> in JS expressions so the asterisks aren't parsed as emphasis. * build: bump docusaurus-preset to ^3.20.0 for SetupSteps; disable npm cooldown The spec-driven tutorial uses the SetupSteps component, which only exists in @conduction/docusaurus-preset >= 3.19.0. The site was pinned at 3.17.0, so SetupSteps imported as undefined and crashed the MDX render at runtime (passed webpack compile, failed in the browser). - Bump @conduction/docusaurus-preset 3.17.0 -> 3.20.0. - Temporarily disable the .npmrc min-release-age=1 supply-chain cooldown so 3.20.0 (published <24h ago) installs in CI. Tracked for re-enable tomorrow in #115. * fix(academy): load diagram web components browser-only to fix SSG build The top-level `import '@conduction/docusaurus-preset/diagrams'` ran `class CnHex extends HTMLElement` at module-eval during static site generation, where HTMLElement is undefined — crashing SSG for every /academy/ path (ReferenceError: HTMLElement is not defined, cascading to "Cannot access 'assets' before initialization" on tag/author pages). BrowserOnly defers rendering, not the import. Fix: drop the top-level import and require() the diagrams module inside each BrowserOnly render function, so the side-effect only runs in the browser.
Supervisor won't dispatch retry:queued or rebuild:queued while needs-input or fail labels are still set — add the explicit cleanup command sequence (incl. HYDRA_LABEL_PREFIX variants) to tutorial 6 and cross-reference from tutorial 2.
docs(academy): OpenSpec introductie tutorial (parts 1 + 2)
…stance Updated the local nextcloud tutorial for better use for non technical persons
The swizzled BlogPostPage in src/theme/BlogPostPage/index.jsx wraps MDX in .content-detail-body and bypasses the default BlogPostItem/Content component that adds the .markdown class. The brand blockquote CSS in src/css/site.css is scoped to .markdown blockquote, so raw `>` callouts in the Nextcloud tutorial fell through to Infima's washed-out grey default and rendered without border/background. Convert all six callouts in both the EN source and the NL translation to `:::info`, `:::note`, `:::tip`, `:::warning`, and `:::danger` admonitions — the same pattern used by the Hydra, Claude Skills, and OpenSpec tutorials, which never use raw blockquotes for this reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This reverts commit 0924438.
The installed version of @conduction/docusaurus-preset had drifted to 3.17.0 while package.json required ^3.20.0, so the SetupStep and SetupSteps components used by the spec-driven-development tutorial were missing from the resolved bundle, producing dev-server runtime errors on the academy listing page. Running npm install pulls the preset up to 3.20.0 and adds search-insights as a transitive dependency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion NL translation for the EN canonical at academy/2026-05-22-spec-driven-development/index.mdx, completing the NL/EN pair so this tutorial appears on /nl/academy/ alongside the Hydra, OpenSpec, and Claude Skills series. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…27) The Conduction column was advertising '/academy?type=case-study' as 'Case studies', but case studies live on the Academy filter and 'Way of Work' is now the canonical 'how Conduction operates' surface on docs.conduction.nl. Point readers at the handbook landing instead of the academy filter. - docusaurus.config.js: footer 'Case studies' -> 'Way of Work', href https://docs.conduction.nl/WayOfWork/way-of-work/. - i18n/nl: add NL translation 'Werkwijze' for the new label.
We are no longer supporting OpenTalk as an integrated ExApp, so remove it from the platform overview, the ExternalAppShelf, and the app-downloads dataset. Rewrites the 'Why through the app store?' paragraph to keep the Nextcloud Talk pitch without the OpenTalk-on- Matrix alternative.
The "Typed data, surfaced everywhere" Showcase had a single AppMock
per tab plus an AgentTrace for the LLM tab. The first two tabs
underplayed the integration story by showing one app each in
isolation; the marketing punch is "this register data shows up
everywhere", which a multi-mock scene reads better than a single
app frame.
Mail and Files tab now composes:
- openregister-metadata SidebarMock (md, centred)
- nextcloud-mail and nextcloud-files WidgetMocks (sm, left edge,
overlapping the sidebar)
- openregister-activity WidgetMock (sm, behind the sidebar at z=0)
Windmill and n8n tab now composes:
- openconnector-run-detail SidebarMock (md, centred)
- openregister-activity and openconnector-runs WidgetMocks (sm,
left edge)
- procest-werkvoorraad WidgetMock (sm, behind the sidebar at z=0)
LLMs tab keeps the AgentTrace; the trace already reads as a mock
purpose-built for "watch the LLM working", and a scene
composition would lose that punchline.
Bumps @conduction/docusaurus-preset to ^2.6.0 (was ^2.1.0) for
WidgetMock + SidebarMock + MockScene + IntegrationIcon exports;
the components themselves landed earlier under that major.
* feat: show partners on every app and solution page Drops a <PartnersFor> section directly below the "What it does" block on all 16 app pages and all 5 solution pages, wired to partners-catalog.js so /apps/<slug> and /solutions/<slug> stay in sync with /partners from one source of truth. Replaces the hand-rolled partner-logo strip on openwoo. When the catalog has no partners for a subject (decidesk, doriath, larpingapp, nldesign, openbuilt, pipelinq, procest, scholiq, shillinq, softwarecatalog, zaakafhandelapp, anonimiseren, archief, mkb-workspace, zaakafhandeling), the section collapses to the BecomePartner CTA only, so every product surface keeps recruiting partners. Site shims PartnersForApp / PartnersForSolution are a one-line MDX drop-in that resolves the slug to the brand-cased name, runs the matching reverse-lookup hook, and locale-keys the EN/NL copy. Pairs with the <PartnersFor> preset component shipped in @conduction/docusaurus-preset. * chore(deps): bump @conduction/docusaurus-preset to ^3.18.0 Adds <PartnersFor> from the preset, required by the PartnersForApp / PartnersForSolution shims on every app and solution page in this branch.
…y-spec-driven-dev
…-dev content(academy): add spec-driven-development blog post
i18n(academy): NL/EN translation pairs for Hydra, OpenSpec, Claude Skills
Code Review — PR #118: release: promote development to mainOverviewRelease-merge van Sterke punten
Aandachtspunten1.
|
| Risico | Kans | Impact | Mitigatie |
|---|---|---|---|
| Supply-chain via uitgeschakelde cooldown | laag | hoog | Re-enable vóór merge naar main; verifieer met npm audit signatures |
Stale llms.txt schaadt AI-citaten |
hoog | laag | Sync solution-lijst in dezelfde commit |
/nl/* deep-links 404 na rename |
laag | laag | NL-redirects toevoegen of bewust accepteren |
| Preset 3.3 → 3.20 verbergt regressie | laag | midden | CI dekt het al via build + validator (groen) |
Verdict
Approve onder voorwaarde: re-enable min-release-age (issue #115) en sync static/llms.txt vóór merge naar main. De overige punten zijn nits / opvolg-werk. CI is groen en de auditability is sterk — dit is het soort release-PR dat goed te reviewen blijft ook door iemand die de afgelopen weken niet meekeek.
🤖 Generated with Claude Code
The Docusaurus dev server only serves one language at a time, which makes the navbar language switcher look broken locally. Document the `npm run preview` / `npm run serve` path for testing both languages on one port. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirror the structure of /quality (DetailHero + Showcase + Section + FAQ from @conduction/docusaurus-preset). Lead with the SLA promises (uptime, P1 response, security, capacity, maintenance, updates), then the full SLA text. Genericized away from product-specific mentions ([product] placeholder) and linked the OpenWoo SLA at openwoo.app/Product/SLA. The Dutch version is the legally binding text.
…uality Mirror DetailHero + Showcase + Section + FAQ from @conduction/docusaurus-preset. Lead with the GDPR commitments (purpose-bound, no sharing unless necessary, minimum retention, ISO 27001-aligned security, consent + transparency, your GDPR rights), then the full privacy policy + DPIA + risk procedure. The Dutch version is the legally binding text.
…169) The post carries a '## Sources' endnotes section but was missing the appsCta: inline frontmatter and the AppCrossLinks mount, so the auto-appended CTA block rendered below the endnotes instead of above them — the layout defect conduction-website#165 established the convention to prevent. Every other sources-bearing post already follows it; this one predates the convention. Apps chosen for topic fit (open source cataloguing and procurement) rather than the usual hermiq/openregister default. Verified in the built HTML, not just the frontmatter: the CTA heading renders at offset 27233 and the Sources heading at 37832.
…wolves post (#170) The academy detail header was hardcoded off contentType (webinar -> WebinarHero, opinion -> FeaturedCard, else ContentDetailHero). It is now selectable per post through frontmatter, with that same mapping kept as the default, so every existing post renders exactly what it did before: hero: featured | detail | webinar | none heroTone: cobalt | cobalt-dark | cobalt-deep | cobalt-50 | mint | orange heroIcon: guide | case-study | webinar | tutorial | blog heroImage: site-absolute path or absolute URL, replaces the hex icon heroImageAlt: alt text for heroImage heroEyebrow: eyebrow label on the featured variant heroAccent: orange | cobalt, featured variant only Unrecognised values warn at build time and fall back, matching the contract the 'ai' key already follows. 'hero: webinar' degrades to 'detail' when no YouTube id can be extracted, so a mistyped videoUrl shows a normal header instead of an empty player. Also adds the Dutch translation of 'Two wolves live in every public open source project', at the same slug per the i18n convention, with the untranslated engine post linked at its English URL. Verified with five throwaway probe posts (since removed): variant override in both directions, hero: none, heroImage/alt, and bogus values warning + falling back. Regression-checked that the four posts with a videoUrl still render WebinarHero and the opinion posts still render FeaturedCard.
… redirect the old URLs (#171) * content(academy): reclassify the hurt series as blog, add VideoEmbed, redirect the old URLs Completes the in-flight work from an interrupted session (preserved on wip/preserve-2026-08-11-hurt-reclassification) and adds the redirects it still needed. - The six 'This is going to hurt' parts move from contentType: opinion / slug: opinion/... to contentType: blog / slug: blog/..., with their cross-links between parts rewritten to match. Content is otherwise the post-#167 text plus two further info wells and a restructured al-Bashir passage. - New src/components/VideoEmbed: an in-body 16:9 youtube-nocookie embed for supporting evidence in the prose, where WebinarHero is for posts whose video IS the page. Used once so far, in part 1. - The six old /academy/opinion/... URLs were live for a day, so each gets a client redirect to its new /academy/blog/... address, and static/academy/blog/ gets the same stub treatment static/academy/opinion/ already has (-> /academy/?type=blog). Verified on a full build: all six posts build at the new slug in both locales, all 12 redirect stubs (6 x 2 locales) point at the new URLs, the /academy/blog/ stub is emitted, the one <VideoEmbed> in source renders one iframe, and the broken-link count is unchanged at 38 (the pre-existing /about/# anchors), i.e. this introduces none. * content(academy): keep the featured hero on the reclassified hurt series The six parts were live for a day with the FeaturedCard hero they got from contentType: opinion. Reclassifying them to blog would have swapped that for the standard ContentDetailHero, changing the look of published pages as a side effect of a taxonomy change. 'hero: featured' pins the appearance while the taxonomy moves, which is what the configurable hero was added for. Verified on a full build: all six render FeaturedCard at their new /academy/blog/ slugs; broken-link count unchanged at 38.
… post for OpenBuild (#172) The post argues for funding the people already doing the work and building your own capacity to consume and adapt the software, which is what OpenBuild speaks to; the Software Catalog suggestion sat further from the argument. Applied to both locales. Verified on a full build: the openbuild CTA renders and softwarecatalog is gone on both /academy/opinion/feed-the-wolf-that-ships and its /nl/ counterpart; broken-link count unchanged at 38.
Every academy hero drew its glyph from contentType, so all 52 tutorials shared one icon and every blog post shared another. This adds a named glyph vocabulary and gives each of the 74 posts its own. - New src/theme/BlogPostPage/heroGlyphs.jsx: 37 line-art glyphs on one shared 1.6 stroke with round caps/joins, so an otherwise unrelated set reads as one family. They inherit currentColor, so they work on every hex tone. - heroIcon: now resolves a glyph name first (wolf, pipeline, vault, …), then the five contentType names, so heroIcon: tutorial keeps working. An unknown name warns at build time and falls back to the contentType icon, so a typo shows the old generic glyph rather than an empty hex. - Related-posts cards use the same glyph as the post they link to. - All 74 posts assigned. Series share a glyph (build-an-app -> blocks, hydra -> pipeline, openspec -> spec), while individual pieces get their own: the two-wolves post gets a wolf, and the hurt series runs horse, coins, key, shield, brain, sunrise across its six parts. Verified on a full build by checking each post's rendered HTML for a signature path from the glyph its frontmatter names: 70 hex-hero posts render their assigned glyph and the 4 webinar posts render their video hero (WebinarHero replaces the hex, so their glyph only reaches the related-posts cards). Zero heroIcon warnings, so every name resolved rather than silently falling back. Broken-link count unchanged at 38.
…ost (#174) Adds a section to part 3 on the clearest form of the pattern the post describes: business software that used to run on machines you owned, now being converted to subscription on a published timetable. - Atlassian as the lead example: Server off sale in 2021 and out of support 15 February 2024, customers pointed at Data Center, and Data Center itself now off sale to new customers 30 March 2026, last renewals 30 March 2028, expiring 28 March 2029 — at which point the products and their Marketplace apps go READ-ONLY. Software on hardware you bought, in a building you pay for, switching to read-only on a date the vendor chose. - Broadcom ending VMware perpetual licences weeks after the acquisition, Microsoft retiring perpetual Exchange for a Subscription Edition that a 2019 licence does not carry over to, and Intuit closing QuickBooks Desktop to new US subscribers. - Keeps the post's existing habits: no villains (Atlassian gave years of notice), and an honest counterweight (Bitbucket DC carved out, self-managed GitLab/Odoo/Nextcloud still sold). - Price-increase figures are attributed as reported ranges, not stated as audited numbers. - durationMinutes 8 -> 10; seven new sources, ConNext stays last. Also converts academy/2026-06-07-big-tech-de-deur-uit-1/index.mdx from CRLF to LF — the only file in academy/ with CRLF endings. Verified content-identical: 'git diff --ignore-cr-at-eol' reports 0 insertions and 0 deletions, and its door glyph still renders after the rewrite. Verified on a full build: the section and all seven source links render, the sources list is exactly 25 entries, broken links unchanged at 38 and zero heroIcon warnings.
…alling (#175) Adds SharePoint to the self-hosted-software section of part 3. It is the sharpest case in the section and it lands this summer: - SharePoint Server 2016 and 2019 leave support in July 2026 with no extended-security-update programme, per Microsoft's own lifecycle page. - The only supported on-premises successor is the Subscription Edition, which Microsoft's licensing guidance says runs only while you hold active Software Assurance — and if that coverage lapses you must UNINSTALL it and may only run SharePoint Server 2019, the version that just went out of support. So the perpetual right the licence leaves you is the right to run software nobody patches any more, which is the post's own End of Ownership formula (paying the price of owning for the rights of renting) made literal. Both claims are cited to Microsoft directly — learn.microsoft.com lifecycle and microsoft.com/licensing/guidance — rather than to reseller summaries. The body says 'July 2026' rather than a specific day because Microsoft's lifecycle table renders 7/15/2026 in Pacific time while the date communicated everywhere else is 14 July; the month is unambiguous and the sources carry the detail. Verified on a full build: the paragraph, both source links and the closing line all render; the sources list is 27 entries ending on ConNext; broken links unchanged at 38, zero heroIcon warnings.
… passages in part 5 (#176) Editorial layout change made by Ruben in the working checkout, brought onto development on top of the hero-glyph work. - 'Datacenter brain, walking weapons, lights-out factories…' moves up into the two-column split block, next to the war/Neuralink passage it concludes, instead of sitting alone beneath it. - 'The natural objection to "one idiot away"…' moves into the split block under its own heading, so the section opens inside the column rather than above it. - Two <br/> tags for spacing between the joined paragraphs. Applied as a patch onto current development rather than copied wholesale, because the checkout it came from predates #173 and its part 5 had no heroIcon; 'heroIcon: brain' is preserved. Verified on a full build: both moved paragraphs render inside a hfaw-split block and appear exactly once each, both <br/> tags survive MDX, the brain glyph still renders, broken links unchanged at 38 and zero heroIcon warnings.
…dates Bumps the npm_and_yarn group with 3 updates in the / directory: [brace-expansion](https://github.com/juliangruber/brace-expansion), [fast-uri](https://github.com/fastify/fast-uri) and [js-yaml](https://github.com/nodeca/js-yaml). Updates `brace-expansion` from 1.1.14 to 1.1.18 - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](juliangruber/brace-expansion@v1.1.14...v1.1.18) Updates `fast-uri` from 3.1.4 to 3.1.5 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](fastify/fast-uri@v3.1.4...v3.1.5) Updates `js-yaml` from 4.1.1 to 4.3.1 - [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md) - [Commits](nodeca/js-yaml@4.1.1...4.3.1) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 1.1.18 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: fast-uri dependency-version: 3.1.5 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: js-yaml dependency-version: 4.3.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <support@github.com>
Both shipped as stubs with an explicit 'placeholder while we draft the full version' line in the body: the parhelion case study (pending the customer's permission) and the OpenTelemetry drain-processor note. Neither had been filled in, so they were publishing an empty promise.
Docusaurus styles post bodies with direct-child selectors (.markdown > p), so anything nested inside a wrapper div in an MDX post is a grandchild and matches nothing: paragraphs, headings and lists all computed margin 0 and ran together with no gaps. Part 5 had been working around this with <br/> between paragraphs, which produces a line break rather than a paragraph: the gap was whatever the line height happened to be, and the markup claimed a structure it did not have. Both spacers are removed and the text is real paragraphs again. Applies to all six academy posts using these wrappers. Only inherited typography is restored, so each post's own layout rules still win.
A La Suite design language ported onto Nextcloud in one working session: no fork, no patch set, no new applications. Walks the three sovereign workspace programmes (openDesk, La Suite, MijnBureau) through vision, architecture and portal, then the merge itself, the defects it surfaced, and why a shared finish is the precondition for a shared app store. Seven hand-authored SVG diagrams, colour-coded by component provenance. Every claim sourced; the widely repeated La Suite user figures and the 732M savings claim are deliberately absent because they did not verify. Also carries two site-wide fixes to the academy layout: - the AI disclosure rendered BOTH the light and dark treatment, because the display:none hiding one lost on specificity to the sizing rule. Since *-white-transparent.svg is a white disc with near-black letters, the 'hidden' mark showed as a second set of dark AI letters. Now one image, its treatment chosen in JS, so it cannot double again. - the mark's copy says what the assistance was for and links to /ai.
…_and_yarn-c724bfd08b chore(deps): bump the npm_and_yarn group across 1 directory with 3 updates
docs(apps): add an App health page with per-branch Code Quality status
…L + EN (#181) * content(apps): rewrite all 21 product pages for the business owner, in NL and EN The reader of /apps/<slug> runs a business of 10 to 500 people and has never heard of OpenRegister. Every page now follows the three product-page rules added to the identity kit (taalgebruik §16.1): their vocabulary, not ours; every example proves added value (today / instead / so); every scene grounded in shipped functionality. What changed on every page - "Three ways <App> earns its place" is now the platform trio in the reader's words: Automate (event-driven flows), Make it yours (change any page and field, keep changes through updates), Talk to it (the assistant that acts in the app, with per-assistant permissions and approval). Card eyebrows are verbs, not product names; CTAs point outward, never back at the page. - "How <App> plugs into the workspace" rebuilt from verified integrations only (mail, contacts, calendar, files, Talk, Deck), with the value each one adds. Claims not shipped were softened or removed (recorded in the research dossier so nobody re-inflates them). - Hero: canonical taglines from the identity kit, Beta chip on every app, install / partner-demo / GitHub CTA trio, AppMock illustration on all pages (eight new variants ship in @conduction/docusaurus-preset 3.29.0; those heroes render the fallback frame until the site bumps). - Voice: no em-dashes, no Dutch nouns in English copy, no platform internals in body copy, sentence case, short sentences. - Eight thin stubs (doriath, hermiq, portaliq, scholiq, shillinq, planix, hrmq, openbuild) gain the full section set. zaakafhandelapp shrinks to a successor page pointing at Procest. Developer surfaces (MCP tool shelves, the OpenRegister "full spec" grid) leave the marketing pages. - All 21 Dutch pages regenerated as full translations (je/jij, fixed NL labels, canonical NL taglines). Every page passes the mechanical gate (MDX compile, em-dashes, jargon, CTA labels, self-links, u-form for NL) and both locales build clean. * build(preset): bump @conduction/docusaurus-preset to ^3.31.0 First release since 3.28.0 (npm trusted publisher pointed at a deleted workflow filename; fixed today). Brings the eight AppMock hero variants (hermiq, portaliq, scholiq, shillinq, doriath, planix, hrmq, app-versions): zero product pages render the fallback frame now, was seven. Signed provenance on the release.
…carousels (#182) Applies the product owner's live review of the first rollout, on preset 3.32.0 (FlowMock, BuildMock, KanbanMock, LeafMock, widget carousel, new hero layout). Every page: "Four ways <App> earns its place" with a new Portaliq self-service card tailored per app; Automate cards show the animated flow; Make-it-yours cards show the OpenBuild drag-and-drop animation; workspace showcase items get distinct leaf atomics; the widget shelf becomes "We ship the widgets you need", a slow carousel carrying every widget the app ships (from the manifests), ending in the design-your-own line; pairing is OpenBuild / Hermiq / LaunchPad only; partners sit at the bottom; install CTAs deep-link to each app's own app-store listing where one exists; demo CTAs deep-link to /partners/?app=<slug>, which now pre-filters the directory; every sibling-app mention is a link; assistant scenes are spoken, not typed. PipelinQ additionally gains its customer-centre story (tickets, incidents, questions, complaints), the quotes-contracts-invoices line with Shillinq, and the Humaniq hours-to-invoices reporting line, and its hero shows the animated pipeline. Honest exceptions: doriath keeps three ways (a zero-knowledge vault has no client self-service); portaliq keeps the trio (it IS the story); nldesign's shelf keeps its truthful token framing (zero widgets); hermiq/portaliq/planix/hrmq keep the generic install link (no store listing); launchpad too (its listing still sits under retired mydash). Both locales pass the mechanical gates (42/42) and the full dual-locale build is clean with zero hero fallbacks.
) * test(e2e): product-page skeleton suite + partners deeplink polish, preset 3.33 Adds e2e/product-pages.spec.js: 42 per-page skeleton tests (renders, hydrates, hero never falls back, the ways-section and carousel controls where each page has them, demo deeplink), a carousel-operates test (pause = zero drift, nudge moves one card), and partners deeplink tests. Partners page: a known ?app= slug is now always acknowledged — filtered with an escape hatch when partners carry the app, a get-started note when none do yet (PipelinQ today); unknown slugs are ignored entirely. Bumps the preset to ^3.33.0 (carousel controls + auto-panels, kanban alignment and € KPI tiles). * build(preset): bump to ^3.35.0 — every atomic animated 3.34.0 animated the high-traffic mocks (Portaliq, leaves, AgentTrace, LaunchPad set, FlowMock approval hold) and 3.35.0 finished the fleet: storytelling loops on every app mock, the HRMQ and DeciDesk content reworks, widget list-refresh and sidebar animations, and the committed animation roadmap. Full e2e suite: 109/109 against this build.
#184) Applies the 2026-08-21 fleet renaming board to /apps, /connext, the academy, solutions, partners, footer, i18n and llms.txt. Slugs, URLs, subdomains, GitHub refs, i18n keys and filenames are byte-identical; per-app detail pages carry a one-time (formerly X)/(voorheen X) note. Pages reading names from @conduction/docusaurus-preset pin the new name until the preset's apps-registry is renamed upstream. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
… rename (#185) * fix: Planix becomes Planninq (not Planiq); correct Pipelinq casing; pin names in e2e PlanIQ is Anaplan's trademark in the same planning category, so the app takes the -inq form, consistent with Pipelinq/Shillinq/Larpinq. The repo is already renamed to planninq, so the GitHub CTA now points there. PipelinQ was spelled with a mixed-case Q in 56 places against the app's own lowercase-q brand; brand/taalgebruik.md documented the wrong rule and is corrected too. Adds e2e/product-names.spec.js: the rename is display-name-only, so a regression does not 404 - the page renders perfectly with the wrong brand, and nothing else in the suite would notice. Each app is asserted by the presence of its new name (an empty page passes an absence check), each route is asserted to still resolve on its old slug, and the apps that were deliberately not renamed are pinned so a future sweep cannot overreach. * fix(e2e): remove the space-invaders flake at its two sources The suite failed on roughly one run in thirteen, a different test each time, always with a mangled command or a missing echo. Two races, both in the test's synchronisation rather than in the app: 1. openBannerFromFooter waited for the terminal input to be attached, but the keydown handler lives on window and is registered by an effect that React runs after paint. Attached is not listening, so keystrokes in that gap were dropped. It now proves the shell is listening - probe a character, wait for the buffer to echo it, clear it - instead of assuming it. 2. Three assertions read terminalText() as a one-shot snapshot right after typing, racing the render that paints the echo. They now use retrying expect(locator).toContainText, which asserts the same thing and waits for it. 65 consecutive space-invaders runs green, against 3 failures in 39 before; full suite 131 passed.
Every www.conduction.nl deploy has failed since ~15 Aug. The cause is not a missing secret — it is a NAME MISMATCH. The workflow asked for CLOUDFLARE_API_TOKEN; the org secrets are CF_API_TOKEN and CF_ACCOUNT_ID, and ConductionNL/.github's own deploy-docs.yml already maps them under exactly those names. wrangler therefore received an empty token on every run. Why it stayed invisible: the deploy step is `if: github.event_name != 'pull_request'`, so PR checks were green throughout and only the post-merge push run failed — the same absence-as-success shape as a skipped check. CLOUDFLARE_ACCOUNT_ID is kept as a fallback for the account id, since that one does exist at repo level. Header comment and docs/DEPLOYMENT.md corrected to name the real secrets. Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
…oduction on development (#187) * ci: list the Pages projects the token can reach, and gate production on development This workflow has never deployed successfully — 30 of 30 runs red — so every merged content PR since #181, including all the -iq display names, was built and thrown away. www.conduction.nl still serves a build with none of them. Merging #186 fixed the secret NAME: wrangler now authenticates and resolves the Conduction account. Cloudflare still rejects /accounts/*/pages/projects/conduction-website with Authentication error 10000, while the same CF_API_TOKEN deploys the conduction-docs project from ConductionNL/.github. A valid token that cannot see one project means the project name or the token's project scope is wrong — and `conduction-website` is the only name this workflow has ever used. `pages project list` prints the names the token can actually reach, so the next run answers the question from the log instead of from the dashboard. It is continue-on-error because a diagnostic that can fail the build is a second failure mode, not a diagnosis. The deploy step is also gated on `development` now. It deploys to the production `main` alias, so a workflow_dispatch from any branch published that branch to www.conduction.nl. * ci: ask Cloudflare what this token can see, Pages and Workers both `pages project list` — the LIST endpoint, with no project name in it — returned the same Authentication error 10000. So the failure is not a wrong project name: the token cannot read Pages on this account at all. Which fits the one place the same token demonstrably works. docs.conduction.nl is not a Pages project: ConductionNL/.github deploys it as a WORKER (`wrangler deploy`, `name = "conduction-docs"`, `[assets]`), and its own error message names the permission it needs — Account > Workers Scripts > Edit. A token with Workers Scripts and without Pages behaves exactly like this one. If www.conduction.nl is likewise a Worker, this workflow has been calling the wrong API since it was written, and the fix is a wrangler deploy rather than a new token permission. This step asks Cloudflare directly — token permission groups, Pages projects, Worker scripts — instead of guessing between the two. * ci: probe zones and worker custom domains too The first probe answered the main question: Pages returns Authentication error 10000 on the LIST endpoint, so the token has no Pages permission at all, while Workers returns ok and lists 42 scripts. Every docs site in the fleet is a WORKER named <app>-docs — openconnector-docs, procest-docs, docudesk-docs and so on — which is why deploy-docs.yml works with this same token and this workflow never has. There is no conduction-website worker in that list, and www.conduction.nl is nonetheless live. So either it is a Pages project this token cannot see, or a worker whose custom domain says so. This probe asks which, and also whether the token can read DNS zones — that decides whether the 14 missing <newname>.conduction.nl docs hosts can be created from CI or need the dashboard. * ci: name the real cause when the Pages deploy is refused Measured, after 30 failed runs out of 30: /accounts/*/pages/projects -> Authentication error 10000 /accounts/*/workers/scripts -> ok, 42 scripts /zones -> ok CF_API_TOKEN carries Workers and zone-read and no Pages permission at all. It is not a wrong project name: the Pages LIST endpoint names no project and is refused too. `conduction-website` is real — it exists and serves www.conduction.nl, conduction.nl, academy., commonground. and connext. This is also why ConductionNL/.github publishes docs.conduction.nl with the SAME secret: every docs site in the fleet is a WORKER (`wrangler deploy`, name `<app>-docs`), and Workers is exactly what this token can do. The fix is one dashboard edit — add 'Cloudflare Pages: Edit' to CF_API_TOKEN — so the preflight states that in the log rather than leaving the next reader with "Authentication error", which reads as a bad secret and is not one. The deploy is also gated to `development`: it publishes to the production `main` alias, so a workflow_dispatch from any branch replaced www.conduction.nl with that branch. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
* feat(apps): move the twelve renamed app pages to their -iq paths The display names moved in #184/#185; the URLs did not. /apps still linked to /apps/openbuild, /apps/procest, /apps/hrmq and nine more, so every product page read "Buildiq" at an /apps/openbuild address. Twelve pages renamed, every internal href rewritten (131 of them across the app pages, /connext, /commonground, the solutions pages, support and llms.txt), and twelve client-side redirects added so the published URLs keep working. THE PRESENTATION KEYS IN src/data/apps-catalog.js DELIBERATELY DID NOT MOVE, and that is the whole care in this change. They are the join key against `apps[].id` in data/app-downloads.json — which is the NEXTCLOUD APP-STORE id, not the GitHub repo slug, and the store still lists the old ids. getApps() filters out any entry with no store match and no downloads, so renaming those keys would have dropped all twelve apps off /apps entirely, with no error and no empty state — just a shorter grid. Verified after the build: still 21 apps, now on the new paths. The redirects are also what keeps @conduction/docusaurus-preset working. Its apps-registry still ships the old /apps/<old> hrefs and feeds <AppCrossLinks/> and the academy product filter; those now resolve through a 301 rather than breaking, until the preset ships a release with the new paths. Build green, all 15 AI-baseline checks pass, and every old path verified redirecting to its new one in the built output. * test(e2e): follow the app pages to their new paths, and pin the redirect The `build` job failed with 11 Playwright failures, all the same shape: product-pages.spec.js iterates a map keyed by app slug and visits `/apps/<slug>`. Those keys were still the old slugs, so every one of them now lands on a redirect stub whose content assertion cannot match. product-names.spec.js needed the opposite treatment. It asserted, in as many words, that "the rename must not have moved the URL" — correct when only the display name had moved on 2026-08-21, and false since the paths moved. A test that pins the old behaviour after the behaviour deliberately changed protects nothing; it just fails until someone edits it. So it now asserts the REDIRECT rather than the absence of one, and checks BOTH sides: `/apps/<old>` reaches `/apps/<new>` and shows the product name, and `/apps/<new>` serves it directly. That is the behaviour worth defending — the old URLs were published, and @conduction/docusaurus-preset's apps-registry still points at them from <AppCrossLinks/> and the academy product filter. Verified locally: 56 passed across both specs. * fix(apps): move the openconnector page too, and its Dutch translations Two gaps in the path move, both found by the e2e suite rather than by reading. openconnector -> integriq was missing from the rename set entirely. The app was renamed like the other twelve, so the spec's map expected /apps/integriq — which did not exist. Page moved, 20 hrefs rewritten, redirect added. And the Dutch pages live in a parallel tree, i18n/nl/docusaurus-plugin-content-pages/apps/, which still held the old filenames. Docusaurus falls back to the English page when a locale has no file at that path, so /nl/apps/buildiq/ silently rendered the English copy — no error, no missing page, just a Dutch route serving English. All thirteen moved, 115 hrefs rewritten across the locale tree. Also corrected an assumption the spec had inherited from me: it derived the partners deep link and the AppMock illustration from the PAGE SLUG. Those two are keyed on the APP ID, which deliberately did not move — `?app=` filters the partners directory and <AppMock app> resolves an illustration out of @conduction/docusaurus-preset, and both only know the old ids. Moving them would have filtered on a value the partners page has never seen and rendered the "Unknown app" fallback frame, which the assertion two lines above explicitly forbids. The spec now carries an explicit PARTNER_APP_ID map saying so. 81 passing across both specs, from 25 failing. --------- Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
GitHub is the only host for this org; Codeberg was a mirror and is not
used, including for issues. Many of these URLs also still carried
pre-rename repo names, so a plain host swap would not have been enough.
Repointed 272 URL occurrences across 80 files (en + nl in lockstep):
- Org/repo links codeberg.org/Conduction/<slug>
-> github.com/ConductionNL/<new-slug>
- Pre-rename slugs nldesign->thematiq, doriath->keepiq,
docudesk->filinq, openconnector->integriq
- File links /src/branch/<B>/<p> -> /blob/<B>/<p>
- Directory links /src/branch/<B>/<dir> -> /tree/<B>/<dir>
- Raw links /raw/branch/<B>/<p> -> /raw/<B>/<p>
- SSH clone URLs git@codeberg.org:Conduction/x.git
-> git@github.com:ConductionNL/x.git
- Link text "X on Codeberg" -> "X on GitHub" only where the
label sat on a URL that moved
Issue-number decision: Codeberg issue numbers do NOT correspond to
GitHub issue numbers, so rewriting the host would have pointed readers
at a real but unrelated GitHub issue — worse than a dead link. The five
decidesk issue links (#44/#45/#71/#72) therefore lost their URL and kept
the fact as plain text, marked as pre-migration Codeberg numbering.
Every target was verified to exist before linking: 15 public URLs
checked with curl (all 200) and 16 private-repo (hydra) paths checked
with `gh api`. Two links whose targets no longer exist were retargeted
at the containing directory rather than at a guessed filename
(hydra docs/operations/retry-and-rebuild.md, and the app template's
.github/workflows/release.yml, now release-beta/release-stable).
Two branch corrections found during verification: openregister's
calendar-provider spec and integriq's openspec/architecture live on
`development`, not `main` as the Codeberg links claimed.
Deliberately left (see PR body): the generated data/app-downloads.json,
the Codeberg API collector script, the two-forge tutorial with parallel
Codeberg/GitHub tabs, the Codeberg-account onboarding step in
workstation tutorial 2, a hydra record sample whose repo field sits
beside an issue number, and an author's personal Codeberg profile.
…180) * content(apps): rewrite the eight product pages for the reader, not for us Applies the product-page voice rule: **their vocabulary, not ours**. The reader runs a business of 10 to 500 people and has never heard of OpenRegister. DocuDesk's description was "Generate, anonymise, sign, and template documents on Nextcloud. Templates ship for the most-used Dutch government documents." — three platform nouns and a verb ("template") that is not one, describing the product by how it is built. It now says what the reader gets: contracts and letters fill themselves from your records, clients sign on their phone, personal data is blacked out for you. The same pass across DocuDesk, Hermiq, Launchpad, OpenBuild, OpenRegister, Pipelinq, Shillinq and SoftwareCatalog. Platform names now appear in exactly one place per page — the CTA label linking to that app. Each page also pulls its name and metadata from `getApp(...)` rather than repeating the string in the hero, the breadcrumb and the CTA. A product renamed in three places is a product renamed in two of them. * content(academy): Vae Victis, parts 1-3 Three new posts — Pax Silica, Bread and Butter, Alea Iacta Est — plus the Frankendesk logo the earlier post referenced without shipping, and edits to parts 2, 3 and 6 of "This Is Going To Hurt".
…video (#168) * feat(site): app descriptions in AppCrossLinks (interim), VideoEmbed component, first video in hurt part 1 - apps-registry-interim + AppCrossLinksInterim via webpack replacement (same mechanism as hermiq-glyph-interim): every app gets a one-line description and tags in ONE site config file, and the About-this-app card renders the description - VideoEmbed: responsive YouTube/Vimeo embed (nocookie/dnt), safe fallback link for unknown hosts - Part 1 embeds the Amazon warehouse-robot video beside the robots paragraph as the pattern example * fix(site): interim AppCrossLinks imports via relative node_modules paths (preset exports map blocks deep subpaths) * fix(site): Blogs chip includes opinion posts (blog as umbrella type); Opinions chip narrows * fix(site): one reader-facing category — drop the Opinions chip, Blogs matches blog+opinion; opinion stays the internal flavor for eyebrow/hero * feat(site): blog posts get the opinion presentation — FeaturedCard hero with type eyebrow for both blog and opinion * feat(site): deprecate the opinion content type — all 9 posts retyped to blog, umbrella filter reverted, hero tolerates stragglers * content(academy): Monday blocks beside the closing text in all six parts; AI-written-code info well in part 1 * content(academy): part-5 layout — goalposts scorecard list + 1980 test, al-Bashir case well, alarm wells and hardware list beside the text * feat(site): migrate opinion/ slugs to blog/ with no-chain redirects; feed-the-wolf review round — wolves HexCards, Gaia-X and saboteur wells, Monday block - All 9 essay slugs move to /academy/blog/*; every /academy/opinion/* URL 301s straight to the final slug (no chains), earlier pre-opinion URLs re-pointed - Static stubs: /academy/opinion/ and /academy/blog/ both land on /academy/?type=blog - Zero residual opinion links in content; connext CTA updated - feed-the-wolf gets the current furniture: hfaw style block, code/policy wolf HexCard pair, Gaia-X-in-numbers and saboteur's-checklist wells beside the text, closing split with a What-you-can-do-on-Monday block * fix(mdx): repair two posts my conflict resolution structurally broke The build failed with "Expected a closing tag for `<div>`" on two academy posts. That damage was mine: I resolved 16 conflicts with a regex that keeps the development side of each hunk, which is fine for a frontmatter line and wrong for a hunk that straddles JSX block boundaries. It left an orphan `<div className="hfaw-split">` in one file and a `</aside>` where a `</div>` belongs in the other. Both source versions were structurally sound; only my merged result was not. So each file is restored from whichever side actually carries the content: - part-5-the-singularity <- development (348 lines, newer than the 345 here) - governance-nix-implosion <- this branch (179 lines vs development's 125, since the branch is where the added content lives), with its frontmatter aligned to development's contentType: opinion + heroIcon: wolf Swept every other .mdx for the same damage. Six files report unbalanced div counts, and all six are byte-identical to development — they use self-closing `<div ... />`, which a naive open/close count reads as a missing close. Not regressions, and not touched. * fix(routes): put the opinion posts back on the opinion/ slugs The build failed on a redirect to a path that does not exist: These paths are redirected to but do not exist: - /academy/opinion/feed-the-wolf-that-ships/ Cause is mine. The previous commit resolved the conflicts toward development — which keeps `opinion` as a live content type and redirects to /academy/opinion/* — but restored one post's body from this branch, and that side still carried `slug: blog/feed-the-wolf-that-ships`. Config pointed at opinion/, the post answered on blog/, and Docusaurus refuses to emit a redirect to a route nothing serves. Rather than fix the one path the build named, I compared every academy post's slug against development. Three disagreed, all the same way, and the other two would have failed the same build on the next redirect it checked: the-platform-moment blog/ -> opinion/ government-open-source-needs-an-engine blog/ -> opinion/ feed-the-wolf-that-ships blog/ -> opinion/ All three now match development. Also restored `appsCta: inline` on the wolf post, which the body swap dropped. * fix(routes): retarget the three moved posts, and drop only their self-redirects Moving the three posts to opinion/ left the config redirecting to blog/ paths that no longer resolve, so the build refused: "These paths are redirected to but do not exist". Two changes, both scoped to the three posts that actually moved: - retarget /academy/blog/{slug} -> /academy/opinion/{slug} - drop `from: /academy/opinion/{slug}` entries, which now point away from the canonical page rather than toward it My first attempt removed every `/academy/opinion/* -> /academy/blog/*` entry, which was wrong: the six `this-is-going-to-hurt-part-N` posts really do live at blog/, so those redirects are correct and carry the old opinion URLs. That pass would have 404'd all six. Reverted and re-applied to the three moved posts only. Verified with a full local `docusaurus build --locale en`: "[SUCCESS] Generated static files", no invalid-redirect error and no MDX failure.
…it (#191) * feat(connext): a downloadable ecosystem compose and the tutorial for it Adds static/connext/connext-compose.yml — one file that brings up the eleven apps the Connext page names, on one Nextcloud, enabled in dependency order — and an academy tutorial that walks through it. NOT A DEVELOPMENT ENVIRONMENT, AND IT CANNOT BE MADE INTO ONE Nothing is bind-mounted. Nextcloud installs and updates an app by DELETING its directory and extracting a fresh archive over it, so pointing that at a checkout means an app-store update deletes the checkout. Measured 2026-08-27 on a development machine: \OC\Updater::upgradeAppStoreApp fired on a container restart and removed every top-level file from a bind-mounted app, including its .git directory. Release archives rather than clones, for a second reason too: an archive is a COMPLETE app carrying vendor/ and built js/, while a clone carries neither — and a Nextcloud app with no vendor/ does not fail loudly. It warns once and keeps loading, so it looks installed while every service needing a dependency is absent. THREE THINGS THIS FILE LEARNED THE HARD WAY, EACH DOCUMENTED IN PLACE 1. Compose interpolates $VAR inside `configs.content`, so every shell variable in an inline script is blanked before the file is written. Hence `$$`. 2. The GitHub releases list is NOT ordered by creation date, and the "latest release" endpoint excludes prereleases entirely. Taking the first entry installed an older openregister than the leaf apps needed, and the failure surfaced as a missing CLASS in a different app rather than as a version complaint. Sorted by created_at explicitly. 3. An app's repo name, its Nextcloud app id and its archive filename are three different names that moved at different times. thematiq was installed as `nldesign` on the assumption the id had not moved with the repo; it had, and Nextcloud refused it with "appinfo file cannot be read". The <id> in the RELEASED archive is the only authority — a local checkout can sit on a branch where it has not moved yet. The tutorial is English, matching the existing per-app tutorials, and includes the verification step that distinguishes "the page loaded" from "the install worked" — an empty directory API reads as "no federation peers" when it actually means the register configuration was never imported. * fix(academy): use markdown links, not angle-bracket autolinks MDX parses `<http://…>` as the start of a JSX tag, and the build fails with: Unexpected character `/` (U+002F) before local name, expected a character that can start a name, such as a letter, `$`, or `_` Three table rows carried the autolink form. Converted to `[text](url)`. Verified by building the site locally rather than round-tripping CI: the build now succeeds, /academy/set-up-the-connext-ecosystem/ renders, and /connext/connext-compose.yml is served (12,488 bytes) so the download URL the tutorial gives actually resolves.
…installs (#192) The tutorial's expected `apps present:` line still listed `nldesign`. The compose was corrected to install that app under `thematiq` — its released appinfo/info.xml declares `<id>thematiq</id>`, and Nextcloud refuses an archive whose id does not match the directory — but the tutorial's transcript was not updated with it. Copied verbatim from a real run rather than retyped: ==> apps present: filinq hermiq integriq launchpad opencatalogi openregister pipelinq portaliq shillinq thematiq zaakafhandelapp A sample output that disagrees with the tool is worse than none: a reader who compares the two has no way to tell which of them is wrong, and the honest answer here was "the documentation".
* docs(academy): a Run-locally tutorial for each of the 21 apps
Each tutorial stands one app up from its own demo compose file, on real
Nextcloud with OpenRegister behind it, and ends with a content check rather
than a status check -- Nextcloud serves its page shell before an app decides
whether it has anything to render, so an app URL returns 200 even when it
resolves to nothing.
Grounded in the app's real docs/features.json rather than an invented feature
tour. The two apps whose features.json is empty (portaliq, keepiq) get a
hand-written short list instead of an empty section.
Also corrects the Connext ecosystem tutorial's apps: list. It named integriq,
filinq and thematiq, and the website's app registry is pre-rename -- getApps()
resolves a slug via APPS_REGISTRY and does .filter(Boolean), so an unknown
slug is dropped silently, with no error and no chip. Those three were being
discarded. They are now openconnector, docudesk and nldesign, which is what
the registry answers to, and zaakafhandelapp is added since the ecosystem
compose installs it.
* docs(academy): add the truncate marker and drop feature titles from Outcomes
73 of the 98 academy posts carry a {/* truncate */} marker; these did not, so
the blog list would have rendered each one in full. Adds it after the intro,
where the rest of the corpus puts it.
Also replaces the Outcomes that were built from features.json titles. A raw
title rendered as an outcome reads as "Flow Authoring." -- the name of a
feature, not something the reader walks away having done. The feature list
stays where it belongs, under "what to look at first".
* docs(academy): point Thematiq at the page it actually serves
Thematiq declares <admin-section>theming</admin-section> and ships no app
route, so /apps/thematiq/ answers 404 even when authenticated. Measured on a
booted demo. The tutorial now opens Settings -> Administration -> Theming,
which answers 200.
---------
Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
Summary
Promotes ~123 commits from
developmenttomain. Highlights:/academylinks.the-platform-momentrewrite (addressable-market segmentation) + DownloadPanel CTA polish.llms.txt,robots.txt).anonimiserenandopenwoo; removedlegacy-erp,software-catalog,woo.174 files changed · +20,243 / −1,936
Test plan
mainafter merge/solutions/anonimiserenand/solutions/openwooload; removed solution slugs 404 cleanlystatic/llms.txtandstatic/robots.txtserved at root🤖 Generated with Claude Code