brand
無料Apply this site's visual identity — colours, type, logo files and fixed brand strings. Use for any page, component, social asset or document that should look like the site.
日本語の概要は準備中です。原文の説明を表示しています。
Use when auditing or improving example.com's SEO/indexing — Google Search Console issues (404s, sitemap, "discovered not indexed"), on-page checks (title/meta/canonical/OG/H1/internal links), or wiring search-engine notification (IndexNow/GSC/Bing/Yandex). Also use when the operator pastes GSC findings and asks to fix + validate.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Built from the 2026-09-08 session that fixed a legacy /home 404, corrected the
footer/JSON-LD legal entity, audited the 9 marketing pages GSC flagged as
"Discovered - currently not indexed", and wired IndexNow. See
docs/OPS.md §9 for the full incident record and current on-page findings —
this file is the repeatable process, that doc is the state.
Don't guess from rendered HTML alone — cross-check against the source, since
this app mixes pages that set their own metadata export with pages that
silently inherit the parent layout's default (a real bug class here, see §3).
For each URL:
curl -s -D headers.txt -o page.html -w "%{http_code}\n" <url> — confirm
200, not a redirect/error.curl is only seeing an empty
<div id="__next">. In this app every marketing page is an async
Server Component (no "use client"), so this should always pass — if it
doesn't, that's a bug, not expected behavior.<title> — present, unique, under ~60 chars, includes page topic + brand.<meta name="description"> — present, unique, under ~160 chars,
page-specific (not the homepage's generic description).<h1>; H2/H3 nest without skipping levels.<link rel="canonical"> — now emitted on blog posts (per-post, via
generateMetadata) and on the four pages using pageMetadata(), always
ABSOLUTE, which is what fixes the apex/www and tracking-parameter
duplicates GSC flags separately. Re-check coverage each audit: pages still
on a static metadata export may emit a relative canonical or none.og:title/og:description/og:image — present but currently
static site-wide, defined once via metadata.openGraph in
src/app/layout.tsx (separate from that same file's
organizationJsonLd block, which is JSON-LD, not OG tags). No page
currently overrides it.alt text on meaningful images. The two global icon images
(header logo alt="", footer badge alt="YourSite") are correct as
shipped — decorative icon next to visible brand text is the right
pattern, don't flag it.grep -rn "[\"'\]/<path>["'`/]" src/ --include=".tsx" --include=".ts"A page reachable only viasrc/app/sitemap.tsand nowhere else (no header nav, no footer, no CTA) is an orphan — Google deprioritizes crawling pages with zero internal PageRank flow even after discovering them via the sitemap. As of 2026-09-08:/privacy, /terms, /data-deletion, /voiceare orphans;/servicesis well-linked (header nav +CTASection` + homepage).<script>/<style>/tags from the
<body>, count words. Distinguish a genuine WIP stub (e.g. /voice's
"coming soon" copy) from a page that's thin only because of live DB
state (e.g. /careers renders "No open roles right now" when
job_openings has zero status='open' rows — the code isn't broken,
the data is empty).Per-page metadata is no longer only a .tsx constant. src/lib/page-seo.ts
exposes pageMetadata(path, fallback), which reads
site_content['page_seo'][path] and falls back to the hardcoded value.
Why it matters for this process: SEO copy is tuned against GSC data. A title with impressions but no clicks needs rewording; a description Google keeps replacing needs shortening. Hardcoded, each iteration is a deploy — so in practice it never happens. Editable, the loop closes.
export async function generateMetadata(): Promise<Metadata> {
return pageMetadata('/privacy', {
title: 'Privacy Policy',
description: 'How … collects, uses, and protects your data.',
});
}
Three properties to preserve when extending this to more pages:
pageMetadata goes through getSectionContent, which
uses loadAllContent — already fetching every site_content row once per
request. The SEO row rides along.noindex is per-page and DB-driven, so a stub like /voice can be kept
out of the index and flipped when it ships, without a deploy.Applied so far: /privacy, /terms, /data-deletion, /voice. Blog posts use
their own generateMetadata (per-post, from the row). Everything else still
uses a static export — migrate as you touch them.
metadata exportsrc/app/(marketing)/about/page.tsx, blog/page.tsx, contact/page.tsx,
services/page.tsx have no export const metadata. Both
src/app/layout.tsx and src/app/(marketing)/layout.tsx independently
define title: { default: siteConfig.name, template: '%s | ${siteConfig.name}' }.
With no page override, the child layout's default gets fed through the
template again, producing the literal rendered title "YourSite | YourSite"
plus the homepage's generic description — a real duplicate-title/description
bug across 4 live URLs, not just an SEO nicety. Fix pattern (see
careers/page.tsx for a correct example already in the codebase):
export const metadata: Metadata = {
title: 'Page Topic', // becomes "Page Topic | YourSite"
description: 'Specific, <160-char description of THIS page.',
};
Before declaring this fixed, re-run the audit in §1 against the live URL —
grep the source is necessary but not sufficient; Next.js's title-template
double-application only shows up in the rendered <title>.
| Engine | Programmatic push for new/updated URLs | Auth needed | Status in this repo |
|---|---|---|---|
| Bing | IndexNow (api.indexnow.org/indexnow) | none — key file hosted at public/<key>.txt | Wired — src/lib/indexnow.ts, called from onBlogPostPublished() (blog-publish-hooks.ts, covers cron/admin-UI/MCP publish paths) and from create_job/update_job in src/app/api/mcp/route.ts |
| Yandex | Same IndexNow call (Yandex also consumes api.indexnow.org) | none | Covered by the same wiring above |
No general-purpose push API. The Indexing API exists but Google restricts it to JobPosting/BroadcastEvent structured data only — using it for ordinary pages isn't supported and risks the account getting flagged. Real levers: (a) GSC's manual "Request Indexing" button, one URL at a time, or (b) searchanalytics.query / urlInspection.index.inspect via the Search Console API for read-side reporting and live status checks. | Service account (not interactive OAuth — see below) | Read side wired 2026-09-08 — /admin/analytics/seo (clicks/impressions/CTR/position, top queries/pages, per-URL live indexing check). Reuses the GA4 service account; still needs the one-time Search Console access grant, see docs/OPS.md §9.3. (a) stays human-only regardless. |
Extending IndexNow to other content types: call
pingIndexNow(['/path-1', '/path-2']) from src/lib/indexnow.ts right after
any DB write that makes a URL live/updated. It's fire-and-forget and never
throws, matching the existing onBlogPostPublished() convention — safe to
call inline without awaiting inside a response path.
If/when GSC API automation is wanted (sitemap resubmission,
urlInspection.index.inspect status checks): use a Service Account, not
interactive OAuth — this runs headlessly forever with no repeated login.
One-time setup (needs the operator, not automatable from here):
...@...iam.gserviceaccount.com email, Owner or
Full access.google-auth-library + googleapis.Bing Webmaster API additionally supports SubmitUrl/SubmitUrlBatch
(a real "please crawl this now" for individual URLs, unlike Google) plus
sitemap submission — auth is a flat API key from the Bing Webmaster Tools
dashboard (Settings → API access), no OAuth flow. Worth wiring only if
IndexNow's coverage (Bing already consumes it) turns out insufficient.
until curl ... | grep -q ...; do sleep 5; done via a background
Bash task or Monitor) rather than assuming a push immediately reflects.gh api repos/your-org/site-os/hooks/<id>/deliveries to
confirm the GitHub→Coolify webhook actually fired (200) before waiting on
a deploy that never started.Everything below needs a human with account access. None of it is automatable from here, and each blocks a piece of automation that is already written.
The reporting client uses webmasters.readonly. resubmitSitemap() in
src/lib/search-ping.ts needs read-write, and fails with a 403 that reads like
an auth bug rather than a missing grant.
…@….iam.gserviceaccount.comUntil this is done: sitemap resubmission and URL inspection both fail.
For ordinary pages there is no API. The Indexing API is restricted to
JobPosting and BroadcastEvent; using it elsewhere is unsupported and risks
the account. Google has also stated llms.txt has no effect on Search, either
way.
So after any fix that changes what a crawler sees — a title, a canonical, a new sitemap — take the three or four most valuable URLs into GSC → URL Inspection → Request Indexing. It is the fastest confirmation that a fix worked, and it is human-only by design.
A page reachable only from sitemap.ts is an orphan, and a sitemap entry is a
suggestion, not a crawl. /privacy, /terms, /data-deletion and /voice
have no inbound links.
This is an editorial decision, not a technical one: where should each page be
linked from, and does it deserve a link at all? A footer link for the legal
pages is usually right. /voice is currently noindex because it is a stub.
IndexNow already covers Bing and needs no auth. A Bing Webmaster key adds
SubmitUrl/SubmitUrlBatch and sitemap submission. Settings → API access, flat
key, no OAuth. Only worth it if IndexNow coverage proves insufficient.
Coolify deploys are not instant, and a push that never triggered a build looks identical to a fix that did not work.
gh api repos/<owner>/<repo>/hooks/<id>/deliveries # did the webhook fire 200?
until curl -s https://<domain>/sitemap.xml | grep -q '<loc>.*blog/'; do sleep 5; done
| lever | who | status |
|---|---|---|
| Sitemap includes posts/products/jobs | code | done, hourly revalidate |
| Sitemap refreshed on publish | code | done, revalidatePath('/sitemap.xml') |
| Per-post metadata + canonical | code | done |
| Page metadata editable from DB | code | done, page_seo |
| IndexNow ping (Bing, Yandex) | code | done for blog + jobs; products wired |
| Sitemap resubmission to GSC | code | written, blocked on 5.1 |
| JobPosting indexing ping | code | written, blocked on 5.1 |
| Request Indexing for ordinary pages | human | no API exists |
| Internal linking | human | editorial |
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Apply this site's visual identity — colours, type, logo files and fixed brand strings. Use for any page, component, social asset or document that should look like the site.
日本語の概要は準備中です。原文の説明を表示しています。
Use FIRST when someone asks for a new feature or a change to an existing one — before any schema, code or test. Works out what is actually being asked for, whether to build it or integrate a third party (Trustpilot, Stripe, Calendly and the like), where the data lives, who may do what, and what happens when the vendor is down. Produces a decision, then hands to feature-schema.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a new feature needs a table, column or capability — evolving the schema so the new thing joins the permission, audit and RLS contract instead of sitting outside it. Covers the DDL/DML split, the resource/capability wiring, and the self-verifying migration.
日本語の概要は準備中です。原文の説明を表示しています。
Use after building a feature — explore it in a real browser to find what nobody predicted, then turn each finding into a deterministic test that guards it forever. Covers the agentic pass, what to generate vs hand-write, and how to skip honestly when a capability is missing.
日本語の概要は準備中です。原文の説明を表示しています。
Publishing content or deploying site-os — what caches exist, what refreshes them, and how to prove a change is actually live. Use when publishing a post or product, launching a page, deploying, or when someone says a change "isn't showing".
日本語の概要は準備中です。原文の説明を表示しています。
Handling secrets, keys and credentials for site-os — reading vigyan-secrets.md safely, moving keys into app_secrets, rotating, and responding to an exposure. Use whenever a task touches a key, token, password, .env file, app_secrets, encryption, or the secrets document.
日本語の概要は準備中です。原文の説明を表示しています。