Compare commits

...
Author SHA1 Message Date
jarvis-at-skic 237c6767ab fix: deduplicate synthetic quality exclusions
Build and publish Eldorado documentation / build (pull_request) Successful in 32s
2026-10-04 11:11:42 +00:00
jarvis-at-skic f1743156b6 docs: publish target AI adoption strategy and synthetic measurement examples 2026-10-04 11:08:27 +00:00
jarvis-at-skic 14ba1aa1ef Merge pull request 'docs: split organization pages and simplify diagram controls' (#12) from docs/organization-collaboration-sdlc into main
Build and publish Eldorado documentation / build (push) Successful in 31s
2026-10-04 03:23:27 -07:00
jarvis-at-skic d760c14dab docs: split organization pages and simplify diagram controls
Build and publish Eldorado documentation / build (pull_request) Successful in 33s
2026-10-04 10:22:49 +00:00
jarvis-at-skic db427f99da Merge pull request 'docs: refine organization connector entry and squad alignment' (#11) from docs/refine-organization-connectors into main
Build and publish Eldorado documentation / build (push) Successful in 32s
2026-10-04 02:29:10 -07:00
jarvis-at-skic 033d9bf8fd docs: refine organization connector entry and squad alignment
Build and publish Eldorado documentation / build (pull_request) Successful in 30s
2026-10-04 09:28:34 +00:00
jarvis-at-skic 353a04588c Merge pull request 'docs: align departments and add departmental squads' (#10) from docs/align-departments-add-squads into main
Build and publish Eldorado documentation / build (push) Successful in 33s
2026-10-04 01:51:06 -07:00
jarvis-at-skic 01e2b4df39 docs: align departments and add departmental squads
Build and publish Eldorado documentation / build (pull_request) Successful in 31s
2026-10-04 08:50:28 +00:00
jarvis-at-skic 5bc1263be0 Merge pull request 'docs: separate Product and Technology teams' (#9) from docs/separate-product-technology-teams into main
Build and publish Eldorado documentation / build (push) Successful in 31s
2026-10-02 23:42:00 -07:00
jarvis-at-skic ecc234234e docs: separate two Product and five Technology teams
Build and publish Eldorado documentation / build (pull_request) Successful in 32s
2026-10-03 06:41:10 +00:00
jarvis-at-skic 59b4075113 Merge pull request 'docs: shared Product and Technology squads' (#8) from docs/product-technology-squads into main
Build and publish Eldorado documentation / build (push) Successful in 30s
2026-10-02 10:19:50 -07:00
jarvis-at-skic 6e1503d31b docs: add seven shared Product and Technology squads
Build and publish Eldorado documentation / build (pull_request) Successful in 35s
2026-10-02 17:19:08 +00:00
jarvis-at-skic ea2d1e889b Merge pull request 'docs: organization operating model' (#7) from docs/organization-operating-model into main
Build and publish Eldorado documentation / build (push) Successful in 30s
2026-10-02 08:28:33 -07:00
jarvis-at-skic 6b487643b3 docs: present organization as an operating model rather than a research report
Build and publish Eldorado documentation / build (pull_request) Successful in 31s
2026-10-02 15:27:57 +00:00
jarvis-at-skic f004e14f36 Merge pull request 'research: Eldorado departments, teams and reporting lines' (#6) from research/eldorado-organization into main
Build and publish Eldorado documentation / build (push) Successful in 32s
2026-10-02 05:13:29 -07:00
jarvis-at-skic c2665624e3 research: reconstruct Eldorado organization with sourced roles and explicit assumptions
Build and publish Eldorado documentation / build (pull_request) Successful in 30s
2026-10-02 12:12:57 +00:00
jarvis-at-skic 0af096a285 Merge pull request 'ui: expand diagrams in same-page fullscreen modal' (#5) from ui/diagram-fullscreen-modal into main
Build and publish Eldorado documentation / build (push) Successful in 30s
2026-10-02 03:48:05 -07:00
jarvis-at-skic 70ffea0e16 ui: expand diagrams in same-page fullscreen modal
Build and publish Eldorado documentation / build (pull_request) Successful in 30s
2026-10-02 10:47:33 +00:00
jarvis-at-skic b3e3594571 Merge pull request 'ui: remove homepage documentation button' (#4) from ui/remove-homepage-docs-button into main
Build and publish Eldorado documentation / build (push) Successful in 30s
2026-10-02 03:41:53 -07:00
jarvis-at-skic b1e3cda244 ui: remove homepage documentation button
Build and publish Eldorado documentation / build (pull_request) Successful in 29s
2026-10-02 10:41:15 +00:00
jarvis-at-skic 7fa50f4ed8 Merge pull request 'docs: clarify authoring and Archify trust boundaries' (#3) from docs/archify-review-clarifications into main
Build and publish Eldorado documentation / build (push) Successful in 33s
2026-10-02 03:39:50 -07:00
jarvis-at-skic 4f4d808bdc docs: clarify section authoring and Archify trust boundaries
Build and publish Eldorado documentation / build (pull_request) Successful in 29s
2026-10-02 10:38:44 +00:00
jarvis-at-skic 68f34b6a92 Merge pull request 'feat: Organization, Strategy, Contribution and Archify diagrams' (#2) from feat/archify-organization into main
Build and publish Eldorado documentation / build (push) Successful in 30s
2026-10-02 03:36:24 -07:00
jarvis-at-skic ff7e19b558 test: cover diagram cleanup collision and receipt boundaries
Build and publish Eldorado documentation / build (pull_request) Successful in 29s
2026-10-02 10:35:47 +00:00
jarvis-at-skic 9ad62a2ed6 feat: add section navigation and embedded Archify diagrams
Build and publish Eldorado documentation / build (pull_request) Successful in 29s
2026-10-02 10:33:55 +00:00
jarvis-at-skic 076a7e5329 Merge pull request 'docs: Eldorado Powered By AI' (#1) from docs/ai-strategy-title into main
Build and publish Eldorado documentation / build (push) Successful in 25s
2026-10-02 03:10:52 -07:00
155 changed files with 135678 additions and 30 deletions

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
# Preserve byte-identical third-party sources, including upstream whitespace.
vendor/archify/** -whitespace
+1
View File
@@ -5,3 +5,4 @@ build/
.env
.env.*
!.env.example
static/diagrams/
+7 -1
View File
@@ -25,6 +25,12 @@ Pull requests validate only. Pushes to `main` validate, build, and publish to th
After publication, verify the exact commit’s Actions task and deployed page body; an OAuth redirect is not content verification. Future changes should use pull requests. Never commit credentials or edit generated `build/` output.
## Sections and diagrams
Top-level sections are Organization, Strategy, and Contribution, each with an independent sidebar. The first Organization page is Structure. It presents the organizational operating model: leadership, departments, teams, responsibilities and cross-functional delivery. Research and source excerpts remain under `research/organization/`, outside the published documentation.
Archify 3.0.1 is vendored under `vendor/archify/` from commit `d5a1333d7447c866a765adac7d4d062f2f02e4d2`. MIT and third-party notices are preserved. `diagrams/` holds editable JSON; `pnpm diagrams:build` validates and generates local standalone viewers before every site build. The iframe is sandboxed without same-origin permission. See `docs/contribution/diagrams.md` for capability analysis, authoring, boundaries, and upgrade procedure.
## Design
Inspired by https://www.eldorado.gg/: Lato, near-black navigation, charcoal, gold `#ffc950`, light panels, bold headings. Original documentation artwork and square panels; no official marketplace assets or business claims. See `docs/reference.md`.
Inspired by https://www.eldorado.gg/: Lato, near-black navigation, charcoal, gold `#ffc950`, light panels, bold headings. Original documentation artwork and square panels; no official marketplace assets or business claims. See `docs/core/reference.md`.
@@ -0,0 +1,39 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {"title": "TO-BE: governed AI adoption", "output": "static/diagrams/ai-adoption-target/index.html", "quality_profile": "showcase"},
"components": [
{"id":"people","type":"external","label":"Accountable people","sublabel":"Existing teams; approved work","pos":[40,80],"size":[240,80]},
{"id":"coordinator","type":"backend","label":"One SDLC coordinator","sublabel":"Data & AI; durable task ledger","pos":[440,80],"size":[240,80]},
{"id":"registry","type":"database","label":"Governed skills registry","sublabel":"Pinned bundles + qualified adapters","pos":[840,80],"size":[260,80]},
{"id":"provider","type":"cloud","label":"Model providers","sublabel":"Approved data + retention terms","pos":[40,340],"size":[240,80]},
{"id":"clients","type":"frontend","label":"Local laptop contributors","sublabel":"Copilot / Claude Code / Cline","pos":[440,340],"size":[240,80]},
{"id":"gates","type":"security","label":"Repository + CI gates","sublabel":"Independent exact-revision checks","pos":[440,600],"size":[240,80]},
{"id":"approval","type":"external","label":"Human release approval","sublabel":"Exact artifact; scoped; expires","pos":[840,600],"size":[260,80]},
{"id":"release","type":"security","label":"Release service","sublabel":"Production credentials only here","pos":[840,860],"size":[260,80]},
{"id":"audit","type":"database","label":"Audit + measurement","sublabel":"Restricted evidence; feedback","pos":[40,860],"size":[240,80]}
],
"boundaries": [
{"kind":"security-group","label":"Supply-chain review","wraps":["registry"]},
{"kind":"security-group","label":"Data / egress boundary","wraps":["provider"]},
{"kind":"security-group","label":"Untrusted local proposals","wraps":["clients"]},
{"kind":"security-group","label":"Independent delivery authority","wraps":["gates","approval","release"]}
],
"connections": [
{"id":"approved-work","from":"people","to":"coordinator","label":"Authorize scope","variant":"security"},
{"id":"coord-skills","from":"registry","to":"coordinator","label":"Pinned skills"},
{"id":"task-proposals","from":"coordinator","to":"clients","label":"Route tasks","variant":"dashed"},
{"id":"local-skills","from":"registry","to":"clients","label":"Qualified bundle","fromSide":"bottom","toSide":"right"},
{"id":"inference","from":"clients","to":"provider","label":"Allowed data","variant":"security"},
{"id":"proposals","from":"clients","to":"gates","label":"Propose changes","variant":"dashed"},
{"id":"evidence","from":"gates","to":"approval","label":"Verified evidence"},
{"id":"authorize","from":"approval","to":"release","label":"Approve artifact","variant":"security"},
{"id":"outcomes","from":"release","to":"audit","label":"Outcome evidence"},
{"id":"feedback","from":"audit","to":"people","label":"Review outcomes","variant":"dashed"}
],
"cards": [
{"dot":"cyan","title":"Target, not deployed","items":["One logical coordinating function; resilient ledger and human takeover.","Registry content is guidance, not authority. No laptop or coordinator production credentials."]},
{"dot":"rose","title":"Human authority","items":["Department leaders retain accountability; independent reviewers accept actual artifacts.","CI and release controls fail closed; approval is bound to the exact revision."]},
{"dot":"emerald","title":"Evidence loop","items":["Audit feedback informs people and the next authorized coordinator task.","Approved provider routing applies to coordinator and local inference; no silent policy-changing fallback."]}
]
}
@@ -0,0 +1,230 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Eldorado leadership and delivery structure",
"output": "static/diagrams/organization-reporting/index.html",
"quality_profile": "showcase",
"legend": {
"entries": {
"backend": {
"label": "Leadership / department"
},
"frontend": {
"label": "Role / delivery team"
}
}
}
},
"components": [
{
"id": "cto",
"type": "frontend",
"label": "CTO",
"sublabel": "Technology direction",
"pos": [
40,
40
],
"size": [
240,
80
]
},
{
"id": "head-engineering",
"type": "frontend",
"label": "Head of Engineering",
"sublabel": "Engineering leadership",
"pos": [
400,
40
],
"size": [
240,
80
]
},
{
"id": "group-em",
"type": "frontend",
"label": "Group Eng. Manager",
"sublabel": "Engineering group leadership",
"pos": [
760,
40
],
"size": [
240,
80
]
},
{
"id": "em",
"type": "frontend",
"label": "Engineering Manager",
"sublabel": "Team leadership and delivery",
"pos": [
1120,
40
],
"size": [
240,
80
]
},
{
"id": "engineers",
"type": "frontend",
"label": "Software engineers",
"sublabel": "Product Engineering team",
"pos": [
1480,
40
],
"size": [
240,
80
]
},
{
"id": "ai-architect",
"type": "frontend",
"label": "AI Architect",
"sublabel": "AI enablement across engineering",
"pos": [
40,
280
],
"size": [
240,
80
]
},
{
"id": "coo",
"type": "backend",
"label": "COO",
"sublabel": "Lukas Deksnys",
"pos": [
400,
280
],
"size": [
240,
80
]
},
{
"id": "ops-qa",
"type": "frontend",
"label": "QA Specialist (Ops)",
"sublabel": "Independent operational quality",
"pos": [
760,
280
],
"size": [
240,
80
]
},
{
"id": "cmo",
"type": "frontend",
"label": "CMO",
"sublabel": "Marketing leadership",
"pos": [
1120,
280
],
"size": [
240,
80
]
},
{
"id": "brand-lead",
"type": "frontend",
"label": "Brand Team Lead",
"sublabel": "Brand and community",
"pos": [
1480,
280
],
"size": [
240,
80
]
}
],
"connections": [
{
"id": "cto-head-engineering",
"from": "cto",
"to": "head-engineering",
"label": "Leads",
"variant": "default"
},
{
"id": "head-engineering-group-em",
"from": "head-engineering",
"to": "group-em",
"label": "Leads",
"variant": "default"
},
{
"id": "group-em-em",
"from": "group-em",
"to": "em",
"label": "Leads",
"variant": "default"
},
{
"id": "em-engineers",
"from": "em",
"to": "engineers",
"label": "Leads",
"variant": "default"
},
{
"id": "cto-ai-architect",
"from": "cto",
"to": "ai-architect",
"label": "Leads",
"variant": "default"
},
{
"id": "coo-ops-qa",
"from": "coo",
"to": "ops-qa",
"label": "Leads",
"variant": "default"
},
{
"id": "cmo-brand-lead",
"from": "cmo",
"to": "brand-lead",
"label": "Leads",
"variant": "default"
}
],
"cards": [
{
"dot": "cyan",
"title": "Leadership and delivery",
"items": [
"Arrows run from leader to role or team.",
"Functional leaders develop people, standards and delivery capability."
]
},
{
"dot": "amber",
"title": "Interdepartmental collaboration",
"items": [
"Product Owners, Designers and Tech Leads align on product outcomes.",
"Data & AI enables AI adoption across Technology."
]
}
]
}
@@ -0,0 +1,472 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Eldorado organization structure",
"output": "static/diagrams/organization-structure/index.html",
"quality_profile": "showcase",
"legend": {
"entries": {
"backend": {
"label": "Leadership / department"
},
"frontend": {
"label": "Role / delivery team"
}
}
}
},
"components": [
{
"id": "management",
"type": "backend",
"label": "Management",
"sublabel": "CEO / COO",
"pos": [
820,
40
],
"size": [
320,
80
]
},
{
"id": "product",
"type": "backend",
"label": "Product",
"sublabel": "Experience and product outcomes",
"pos": [
40,
280
],
"size": [
280,
80
]
},
{
"id": "technology",
"type": "backend",
"label": "Technology",
"sublabel": "Engineering and platform",
"pos": [
360,
280
],
"size": [
280,
80
]
},
{
"id": "operations",
"type": "backend",
"label": "Operations",
"sublabel": "Service, resolution and trust",
"pos": [
680,
280
],
"size": [
280,
80
]
},
{
"id": "marketing",
"type": "backend",
"label": "Marketing",
"sublabel": "Brand and customer growth",
"pos": [
1000,
280
],
"size": [
280,
80
]
},
{
"id": "finance",
"type": "backend",
"label": "Finance & Accounting",
"sublabel": "Financial performance and controls",
"pos": [
1320,
280
],
"size": [
280,
80
]
},
{
"id": "people",
"type": "backend",
"label": "HR & Administration",
"sublabel": "People and workplace",
"pos": [
1640,
280
],
"size": [
280,
80
]
},
{
"id": "product-team-1",
"type": "frontend",
"label": "Product Management",
"sublabel": "Strategy, priorities and outcomes",
"pos": [
100,
480
],
"size": [
220,
80
]
},
{
"id": "product-team-2",
"type": "frontend",
"label": "Product Design",
"sublabel": "Research and experience design",
"pos": [
100,
620
],
"size": [
220,
80
]
},
{
"id": "technology-team-1",
"type": "frontend",
"label": "Product Engineering",
"sublabel": "Marketplace software delivery",
"pos": [
420,
480
],
"size": [
220,
80
]
},
{
"id": "technology-team-2",
"type": "frontend",
"label": "Platform & IT",
"sublabel": "Reliability and internal systems",
"pos": [
420,
620
],
"size": [
220,
80
]
},
{
"id": "technology-team-3",
"type": "frontend",
"label": "Architecture & Security",
"sublabel": "Technical standards and protection",
"pos": [
420,
760
],
"size": [
220,
80
]
},
{
"id": "technology-team-4",
"type": "frontend",
"label": "Quality Engineering",
"sublabel": "Testing and release quality",
"pos": [
420,
900
],
"size": [
220,
80
]
},
{
"id": "technology-team-5",
"type": "frontend",
"label": "Data & AI",
"sublabel": "Analytics, ML and AI enablement",
"pos": [
420,
1040
],
"size": [
220,
80
]
},
{
"id": "operations-team-1",
"type": "frontend",
"label": "Operations Squad",
"sublabel": "Service, resolution, risk and quality",
"pos": [
740,
480
],
"size": [
220,
80
]
},
{
"id": "marketing-team-1",
"type": "frontend",
"label": "Marketing Squad",
"sublabel": "Brand, acquisition and retention",
"pos": [
1060,
480
],
"size": [
220,
80
]
},
{
"id": "finance-team-1",
"type": "frontend",
"label": "Finance & Accounting Squad",
"sublabel": "Planning, controls and payments",
"pos": [
1380,
480
],
"size": [
220,
80
]
},
{
"id": "people-team-1",
"type": "frontend",
"label": "People & Administration Squad",
"sublabel": "Talent, people and workplace",
"pos": [
1700,
480
],
"size": [
220,
80
]
}
],
"connections": [
{
"id": "management-product",
"from": "management",
"to": "product",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "management-technology",
"from": "management",
"to": "technology",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "management-operations",
"from": "management",
"to": "operations",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "management-marketing",
"from": "management",
"to": "marketing",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "management-finance",
"from": "management",
"to": "finance",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "management-people",
"from": "management",
"to": "people",
"variant": "default",
"fromSide": "bottom",
"toSide": "top"
},
{
"id": "product-1",
"from": "product",
"to": "product-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "product-2",
"from": "product",
"to": "product-team-2",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "technology-1",
"from": "technology",
"to": "technology-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "technology-2",
"from": "technology",
"to": "technology-team-2",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "technology-3",
"from": "technology",
"to": "technology-team-3",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "technology-4",
"from": "technology",
"to": "technology-team-4",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "technology-5",
"from": "technology",
"to": "technology-team-5",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "operations-1",
"from": "operations",
"to": "operations-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "marketing-1",
"from": "marketing",
"to": "marketing-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "finance-1",
"from": "finance",
"to": "finance-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
},
{
"id": "people-1",
"from": "people",
"to": "people-team-1",
"variant": "default",
"fromSide": "bottom",
"toSide": "left"
}
],
"cards": [
{
"dot": "cyan",
"title": "Product",
"items": [
"Product Management",
"Product Design"
]
},
{
"dot": "amber",
"title": "Technology",
"items": [
"Product Engineering",
"Platform & IT",
"Architecture & Security",
"Quality Engineering",
"Data & AI"
]
},
{
"dot": "cyan",
"title": "Operations",
"items": [
"Operations Squad",
"Customer Support; Trust & Resolution",
"Risk & Fraud",
"Operational Quality"
]
},
{
"dot": "amber",
"title": "Marketing",
"items": [
"Marketing Squad",
"Brand & Community; Creative; SEO",
"PPC & Affiliate; Influencer",
"CRM"
]
},
{
"dot": "cyan",
"title": "Finance & Accounting",
"items": [
"Finance & Accounting Squad",
"Financial Planning & Analysis",
"Accounting, Tax & Controls",
"Treasury & Payment Operations"
]
},
{
"dot": "amber",
"title": "HR & Administration",
"items": [
"People & Administration Squad",
"Talent Acquisition",
"People Operations",
"Office & Administration"
]
}
]
}
+19
View File
@@ -0,0 +1,19 @@
---
title: Contribute
description: Contribute evidence, proposals, and documentation changes to Eldorado Powered By AI.
slug: /
---
Use this section to contribute to **Eldorado Powered By AI**.
- Distinguish evidence, proposals, open questions, and agreed decisions.
- Do not present example diagrams as the actual organization or architecture.
- Use a pull request for source changes; automated checks must pass before publication.
## Maintain the portal
[Update and publish the site](/docs/maintain/) or consult the [configuration reference](/docs/reference/).
## Create diagrams
[Author and embed an Archify diagram](./diagrams.md).
+46
View File
@@ -0,0 +1,46 @@
---
title: Archify diagrams
description: Author typed JSON diagrams and embed their local interactive viewer in documentation.
---
## Capabilities
[Archify](https://github.com/tt-a1i/archify) 3.0.1 is vendored at commit `d5a1333d7447c866a765adac7d4d062f2f02e4d2` under `vendor/archify/`, with its MIT license and third-party notices preserved.
| Diagram type | Intended use |
| --- | --- |
| Architecture | Components, boundaries and relationships; also used for the organization structure views |
| Workflow | Processes, branches and approval gates |
| Sequence | Participants and ordered messages |
| Dataflow | Sources, processing and destinations |
| Lifecycle | States and transitions |
The standalone viewer provides pan/zoom, node inspection, search/focus, relationship tracing, light/dark themes, and an export menu. Motion is opt-in. The Organization views exercise architecture mode; other diagram types use the same build interface but do not yet have portal-specific examples. Browser-dependent exports such as video are not acceptance-tested by this integration.
## Author a diagram
Create `diagrams/<name>.<type>.json` using the matching schema and authoring guidance in `vendor/archify/schemas/` and `vendor/archify/references/`. Use stable IDs, explicit relationships, and honest evidence labels. Do not include private information merely because the source repository is private: Pages access is a separate platform policy.
Set `meta.output` to `static/diagrams/<name>/index.html`. The build validates the typed source with the upstream showcase profile, renders locally, checks the artifact, and publishes the standalone HTML. Editable JSON stays in the repository; the portal does not publish source-download files.
```sh
pnpm diagrams:build
pnpm test
pnpm build
```
## Embed it in MDX
```mdx
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
<ArchifyDiagram name="organization-structure" title="Eldorado organization structure" />
```
Supply a text alternative below each diagram. The wrapper uses a local, base-path-aware iframe with a descriptive title, scripts and downloads enabled, and no same-origin or parent-navigation permissions. The top-right expand icon expands the diagram in a full-screen modal on the current page, with the same iframe sandbox. Close it with the Close button or Escape; focus returns to the opener. It does not open a new tab. The separate standalone HTML asset remains directly addressable and is not itself iframe-sandboxed, so publish only reviewed, trusted diagrams. Generated output is ignored by Git and rebuilt in CI.
## Integration boundaries
The portal remains Docusaurus/React/TypeScript with pnpm. It uses the actual upstream renderer, not a look-alike diagram implementation. No Astro site, global agent installation, hosted rendering service, or AI API was added. The integration disables Archify update networking and does not run its installation commands. This does not disable every possible build-time request: upstream can fetch authored, digest-pinned brand URLs. The current example contains none. Review external asset sources deliberately; update-check suppression is not an offline guarantee.
Upgrades are deliberate: review the upstream diff and licenses, update the provenance record and vendor checksums, then run tests, diagram validation, production build, and browser checks. Do not edit upstream files for portal styling.
+10 -1
View File
@@ -19,7 +19,16 @@ Open the development server address printed in the terminal. The site uses the `
## Add or change a page
Edit Markdown in `docs/`. Include `title` and `description` frontmatter and add the page ID to `sidebars.ts`. Keep the documentation’s future scope open until it is agreed.
Include `title` and `description` frontmatter. Place each page in its owning section and add its document ID to that section’s sidebar:
| Section | Content directory | Sidebar configuration |
| --- | --- | --- |
| Organization | `docs/organization/` | `sidebars/organization.ts` |
| Strategy | `docs/strategy/` | `sidebars/strategy.ts` |
| Contribution | `docs/contribution/` | `sidebars/contribution.ts` |
| Portal maintenance and reference | `docs/core/` | `sidebars.ts` |
Keep these content roots disjoint. Preserve existing URLs when moving pages. Specific AI initiatives and organizational reporting lines remain subject to agreement.
## Validate the change
File renamed without changes.
File renamed without changes.
+23
View File
@@ -0,0 +1,23 @@
---
title: Collaboration
description: How Eldorado departments combine their responsibilities to deliver shared business outcomes.
---
Departments collaborate around shared outcomes while retaining their own team ownership and accountability. See [Structure](./structure.mdx) for department and squad responsibilities.
## How the departments work together
| Business outcome | Collaboration |
| --- | --- |
| Better buyer experience | Product defines improvements; Technology delivers them; Operations brings customer feedback; Data & AI measures the result. |
| Stronger seller experience | Product and Technology develop seller tools; Operations supports sellers; Finance manages financial processes. |
| Safe and trustworthy trading | Risk & Fraud, Trust & Resolution, Architecture & Security, and Finance coordinate controls and incident responses. |
| Sustainable growth | Marketing develops demand; Product improves conversion and retention; Finance measures profitability. |
| AI-powered execution | Data & AI supports engineering and develops marketplace intelligence; Operations improves service and quality workflows. |
Department leaders remain accountable for outcomes, controls and service quality as AI becomes part of everyday work.
## Related pages
- [Structure](./structure.mdx) — departments, squads and responsibilities.
- [SDLC — Software Delivery Life Cycle](./sdlc.mdx) — leadership and delivery responsibilities.
+27
View File
@@ -0,0 +1,27 @@
---
title: SDLC — Software Delivery Life Cycle
sidebar_label: SDLC
description: Leadership, reporting lines and team responsibilities supporting software delivery at Eldorado.
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
Software delivery brings together Product and Technology, supported by the departments responsible for marketplace operations and growth. This page describes the leadership and delivery responsibilities that support that work.
## Leadership and delivery structure
<ArchifyDiagram name="organization-reporting" compact title="Eldorado leadership and delivery structure" />
The diagram shows management responsibility from leader to role or team:
- **CTO → Head of Engineering → Group Engineering Managers → Engineering Managers → the Product Engineering team.**
- **CTO → AI Architect**, coordinating engineering AI enablement.
- **COO → Operational Quality**, providing independent quality oversight.
- **CMO → Brand Team Lead**, leading Brand & Community.
Product and Technology retain separate team ownership and reporting lines. Their teams coordinate priorities, design, engineering, quality and data work to deliver business outcomes.
## Related pages
- [Structure](./structure.mdx) — departments, squads and responsibilities.
- [Collaboration](./collaboration.mdx) — how departments coordinate around business outcomes.
+117
View File
@@ -0,0 +1,117 @@
---
title: Structure
description: Eldorado’s organizational operating model, departments, teams and responsibilities.
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
This operating model organizes Eldorado around six departments: **Product, Technology, Operations, Marketing, Finance & Accounting, and HR & Administration**. Leadership aligns their priorities around marketplace growth, customer trust and efficient delivery.
## Leadership
| Role | Leadership | Responsibility |
| --- | --- | --- |
| Chief Executive Officer | Vladas Jurkevičius | Company direction, business performance, strategic priorities and executive alignment. |
| Chief Operating Officer | Lukas Deksnys | Marketplace operations, customer service, resolution quality and operational performance. |
Functional leaders translate company priorities into departmental plans. Team leads own execution, people development and service quality within their teams.
## Organization overview
<ArchifyDiagram name="organization-structure" title="Eldorado organization structure" />
**Management coordinates the six departments.** Each department has a clear business responsibility and a set of teams that deliver it.
## Departments and teams
Product has **two teams** and Technology has **five teams**. Operations, Marketing, Finance & Accounting, and HR & Administration each have **one squad** covering their departmental workstreams. Each team belongs to one department, with its own leadership and responsibilities. The departments collaborate on delivery without sharing team ownership.
### Product
**Purpose:** improve the buyer and seller experience and turn marketplace opportunities into valuable product capabilities.
| Team | Responsibilities |
| --- | --- |
| Product Management | Product strategy, roadmaps, priorities and outcome measurement across discovery, categories, orders, seller tools, payments and operational workflows. |
| Product Design | User research, interaction design, design consistency and accessible buyer and seller experiences. |
Product Management owns what to improve and why. Product Design shapes the experience. Both work with Technology teams while remaining within Product.
### Technology
**Purpose:** build and operate a secure, reliable marketplace and improve the effectiveness of engineering.
| Team | Responsibilities |
| --- | --- |
| Product Engineering | Build and maintain marketplace capabilities, including search, orders, seller tools, checkout, wallet and operational tooling. |
| Platform & IT | Platform services, delivery infrastructure, observability, reliability, performance, internal systems, employee devices and identity administration. |
| Architecture & Security | Technical standards, system design, security architecture and secure engineering practices. |
| Quality Engineering | Test strategy, automation, release quality and defect prevention. |
| Data & AI | Analytics, experimentation, ranking, recommendations, marketplace ML and safe AI-assisted engineering workflows. |
The CTO owns technology direction. The Head of Engineering leads engineering delivery through Group Engineering Managers and Engineering Managers. The AI Architect coordinates AI enablement within Data & AI, working with Architecture & Security and the other Technology teams. These are leadership roles within the five-team structure, not additional teams.
### Operations
**Purpose:** keep marketplace trading dependable and resolve customer issues fairly and efficiently.
**Operations Squad** delivers the department’s responsibilities across the following workstreams.
| Workstream | Responsibilities |
| --- | --- |
| Customer Support | Buyer and seller assistance, knowledge management, self-service and support workflow automation. |
| Trust & Resolution | Disputes, appeals, refunds, warranties, chargebacks and complex customer cases. |
| Risk & Fraud | Transaction and account risk, fraud prevention, investigations and operational controls. |
| Operational Quality | Independent case reviews, quality standards, calibration, service accuracy and improvement tracking across operational teams. |
The COO coordinates Operations. Team leads manage specialists and daily service delivery. Operational Quality reports directly to the COO and reviews quality across teams.
### Marketing
**Purpose:** attract and retain customers, build the Eldorado brand and grow marketplace demand.
**Marketing Squad** delivers the department’s responsibilities across the following workstreams.
| Workstream | Responsibilities |
| --- | --- |
| Brand & Community | Brand positioning, campaigns, organic social, community and brand partnerships. |
| Creative | Campaign concepts, visual assets and performance creative. |
| SEO | Organic discovery, search visibility and content performance. |
| PPC & Affiliate | Paid acquisition, affiliate channels and return on marketing investment. |
| Influencer | Creator relationships, influencer programs and campaign delivery. |
| CRM | Customer lifecycle communication, retention, reactivation and campaign personalization. |
The CMO coordinates Marketing. The Brand Team Lead owns Brand & Community, with shared support from Creative and the other marketing workstreams.
### Finance & Accounting
**Purpose:** maintain financial control, support business decisions and keep marketplace money flows dependable.
**Finance & Accounting Squad** delivers the department’s responsibilities across the following workstreams.
| Workstream | Responsibilities |
| --- | --- |
| Financial Planning & Analysis | Budgets, forecasts, profitability, management reporting and investment analysis. |
| Accounting, Tax & Controls | Accounting, financial reporting, tax, audit coordination and internal controls. |
| Treasury & Payment Operations | Cash management, reconciliation, payout oversight, payment-provider coordination and financial risk. |
The CFO coordinates Finance & Accounting and works with executive leadership on business performance. Finance, Operations and Product share responsibility for effective payment and withdrawal processes, each within its own area of expertise.
### HR & Administration
**Purpose:** help teams attract, develop and retain people and provide an effective working environment.
**People & Administration Squad** delivers the department’s responsibilities across the following workstreams.
| Workstream | Responsibilities |
| --- | --- |
| Talent Acquisition | Recruitment, candidate experience, hiring coordination and workforce demand support. |
| People Operations | Employee onboarding, development, performance processes, policies and employee experience. |
| Office & Administration | Workplace services, office operations and administrative coordination. |
HR partners with department leaders on team capacity and development. People Operations works with Platform & IT on employee onboarding, role changes and offboarding.
## Related pages
- [Collaboration](./collaboration.mdx) — how departments work together on business outcomes.
- [SDLC — Software Delivery Life Cycle](./sdlc.mdx) — leadership and delivery responsibilities.
+33
View File
@@ -0,0 +1,33 @@
---
id: index
title: AI adoption strategy
description: Govern skills first; outcomes before activity; explicit target status.
slug: /
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
### Govern skills first, expand authority only with evidence
Eldorado's target is to make AI-assisted work dependable across the organization: useful guidance, clear human ownership, controlled execution and measurable outcomes. The first investment is a governed skills system, not an autonomous replacement for the organization. A skill packages reusable working knowledge, prerequisites and verification steps. It helps an agent perform a task; it does not grant permission or guarantee that the work is correct.
The target combines one logical SDLC coordinating agent with developer choice on local laptops. Copilot, Claude Code and Cline remain client options within a qualified support matrix. They receive the same reviewed portable skill content through client-specific adapters. Repository, CI and release services remain the enforcement points, independently of what any agent says or which local tool produced a change.
The strategy describes the intended operating model and the evidence required to introduce it. It does not establish that these controls are installed, that every client is supported, or that AI has improved Eldorado's performance. The current reference is the documented six-department organization. Rollout begins by validating actual clients, responsibilities, data boundaries and available evidence.
Success means accepted outcomes with maintained quality, controlled risk and understood total cost. Faster activity without reliable outcomes is not success. More skill invocations, generated lines or tool subscriptions are not productivity targets. People remain accountable for product, customer, financial and personnel decisions.
## Navigate the strategy
- [Target operating model](./target-operating-model.mdx) — One logical coordinator; local client choice; independent authority; all departments.
- [Skills governance](./skills-governance.mdx) — Contract, lifecycle, decision rights, adapters, locking, exceptions.
- [Skills quality](./skills-quality.mdx) — Risk tiers, independent evaluation, repeated trials, adversarial conformance.
- [Skills adoption](./skills-adoption.mdx) — Workflow-led coaching, client choice, feedback, manual path.
- [Measurement](./measurement.mdx) — Full KPI contracts, minimal events, illustrative calculations, privacy and limits.
- [Skills maintenance](./skills-maintenance.mdx) — Review, drift, deprecation, rollback and incident handling.
- [Roadmap](./roadmap.mdx) — Evidence-gated phases, no promised dates or budgets.
</div>
+352
View File
@@ -0,0 +1,352 @@
---
id: measurement
title: Measurement
description: Full KPI contracts, minimal events, illustrative calculations, privacy and limits.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
### Decide what counts before counting
Use the metric contracts below for exact definitions. Every report states its eligible population, observation window, numerator and denominator or distribution sample count, source coverage, exclusions and calculation version. Zero denominators return “not available.” Missing telemetry is unknown, not inactivity, compliance or success. Examples are explicitly synthetic and illustrative.
Begin with task acceptance, repository, CI, deployment and incident systems because they can independently establish outcomes. Add catalog/version inventory and task-level declarations. Client telemetry is supplementary and version-dependent; enabling rich tool details may collect sensitive commands and identifiers. Do not enable it merely to count skills. A normalized adapter may report approved skill identifiers without forwarding raw arguments.
Validated success uses all eligible initiated tasks whose predeclared observation window has matured. Failed and abandoned tasks remain nonsuccess. Unresolved or unobserved outcomes remain in the denominator, with known-outcome coverage shown alongside the conservative confirmed-success share. They are not relabeled as observed failure. A task cancelled before start is excluded; cancellation after start remains visible. Count a task once across retries and multiple skills. For timeliness, also show unfinished work and age; accepted-only lead-time distributions have survivorship limitations.
Track all five service-level DORA measures: commit-to-production lead time, deployment frequency, change fail rate, failed deployment recovery time and deployment rework rate. Do not substitute PR-open-to-merge time or all-incident MTTR. Pair them with matured escaped-quality rates, policy effects, total cost per accepted outcome, support coverage, adoption, human experience and catalog maintenance. Keep customer/business outcomes linked to their departmental hypotheses; do not infer conversion, trust or revenue improvement from delivery telemetry.
### Baseline and interpretation
Before a pilot, freeze eligibility and acceptance rules and verify event joins. Collect prospective no-skill/current-practice evidence over a complete comparable workflow cycle and until the preregistered precision/sample requirement is met. Do not select a convenient end date after seeing results. Retain task class, service, skill, client/model configuration and concurrent changes. A matched or randomized comparison may be used when practical and ethical; label observational trends as descriptive and report confounding, selection and uncertainty.
Elapsed time and human effort are different quantities. Parallel agents may reduce waiting while increasing review or compute. Include failed attempts and human review/rework in cost, do not multiply a multi-skill task's value across every invocation, and do not convert survey perceptions into proven time savings. Small or incomplete populations yield evidence limits, not an ROI percentage.
### Event and privacy contract
Use a versioned event envelope and required fields per event. Source producer plus event ID deduplicates transport retries. Conflicting payloads under the same key are quarantined. Attempts have distinct IDs under one task. Domain IDs deduplicate deployments, defects and approvals even when multiple feeds describe them. Late arrivals produce versioned aggregate corrections; never silently overwrite a published result. Preserve occurrence and receipt times, reject impossible orderings and flag clock skew. Missing terminal events remain unresolved until the work owner reconciles them.
Many-to-many task/attempt/skill/client associations preserve exact immutable versions. Unknown version stays null with a reason; never assign the latest revision retroactively. Client declarations, server facts and self-reports remain separately labeled. A skill-loaded event is evidence of loading only. The minimal prototype can operate without actor identity except where a lawful, necessary adoption denominator requires pseudonymous cohort membership.
The privacy starting proposal is 30 days for raw structured analytics events and 12 months for aggregated trends, subject to the responsible owners' review. Keep the approved catalog/approval record for the supported revision's lifetime and its required audit period, which must be decided separately. Incident evidence uses a restricted, explicitly authorized hold and deletion schedule, not an indefinite analytics exception. Suppress reports below five people and prohibit drill-down or overlapping cohort slices that reconstruct identities. Five is a proposed minimum, not a guarantee against re-identification. No raw prompts, code, screens, customer cases or secrets are collected by default. Publish the purpose, lawful collection basis, access/deletion route and retention before collecting. Consent is not presumed to be an appropriate employment-law basis.
## Metric contracts
Each contract is a prospective collection design. No live source or observed baseline is established. N and D refer to the numerator and denominator shown for that metric.
### Validated task success
Confirmed independently accepted tasks as a share of all eligible initiated tasks in a matured start cohort; a conservative confirmed-success share, not observed failure rate.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured task_ids with valid independent task.accepted evidence by as_of |
| Denominator | All distinct eligible task_ids started in window with started_at + preregistered task_observation_hours &lt;= as_of |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Tasks in a preregistered workflow cohort, started in window; each counted once across retries, skills and clients. |
| Exclusions | Cancelled before start, sandbox/evaluation tasks and out-of-scope workflows; never post-start failures/abandonments. |
| Events and source | task.started; task.accepted; task.failed; task.abandoned; task.cancelled; work-management acceptance record |
| Owner / cadence | Quality Engineering / Weekly pilot; monthly departmental outcome review |
| Missing evidence | Unresolved/missing terminal outcomes remain in denominator with unknown count and known-outcome coverage; do not label them failed. Invalid/missing start excluded from calculation but flagged as orphan evidence. |
| Gaming and limits | Split easy tasks or omit starts; reconcile starts to approved backlog and audit acceptance rubrics. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Maturation horizon is workflow-specific and must be approved; no automatic abandonment on timeout. |
### Skill selection precision
Correct activations among activations on an independently labeled evaluation corpus.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations for the requested workflow |
| Denominator | True-positive plus false-positive activations |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | All candidate/client labeled evaluation task-trials with known activation and relevance. |
| Exclusions | Unlabeled/unknown activations excluded with coverage; no cherry-picked failed trials. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Unknown activation/relevance is unscorable; publish missing count and deny qualification if planned coverage incomplete. |
| Gaming and limits | Choose easy labels or hide negative cases; freeze corpus and independently review labels. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Does not show execution success; precision depends on corpus prevalence. |
### Skill selection recall
Relevant task-trials in which the intended skill activates.
| Contract | Definition |
| --- | --- |
| Numerator | True-positive activations |
| Denominator | True-positive plus false-negative task-trials |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Independently labeled relevant evaluation task-trials for a skill/client tuple. |
| Exclusions | Irrelevant tasks; missing relevance or activation reported as unscorable, not negative. |
| Events and source | eval.selection; versioned labeled corpus and runner |
| Owner / cadence | Data & AI / Each skill/client qualification |
| Missing evidence | Report evaluation coverage; null if no relevant labeled tasks. |
| Gaming and limits | Overtrigger to maximize recall; always pair with precision and negative cases. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Loading the right skill is not adherence. |
### Supported bundle coverage
Inventoried active installations with verified effective approved, supported and nonrevoked bundle/adapter tuple.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible installation_ids with valid inventory evidence within approved freshness window and matching support matrix |
| Denominator | All expected active installation_ids in the managed pilot inventory |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Active installation inventory frozen for report, including invisible/offline entries until authoritative offboarding. |
| Exclusions | Verified decommissioned installations and nonpilot assets; lack of telemetry alone never excludes. |
| Events and source | inventory.observed; catalog.published; catalog.revoked; device inventory |
| Owner / cadence | Platform & IT / Weekly and at revocation |
| Missing evidence | Unknown/stale versions count outside numerator, inside denominator. Show inventory discovery coverage versus eligible device roster; if roster unknown metric is provisional. |
| Gaming and limits | Remove silent devices or trust declared versions; reconcile managed inventory and separately label attestation trust. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Even verified file digests do not prove a private model followed the content. |
### Approved-workflow adoption
Eligible participants with at least one approved-skill workflow use; usefulness assessed separately through accepted outcomes and feedback.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible pseudonymous participants with workflow-linked skill.used on approved revision |
| Denominator | Distinct eligible participants in frozen invited cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Consented or otherwise lawfully enrolled pilot cohort for the period; supported workflow available. |
| Exclusions | Outside pilot and formally recorded leave before period; nonresponders remain eligible, not excluded. |
| Events and source | skill.used; cohort.snapshot; optional qualified client adapter or declaration |
| Owner / cadence | Data & AI / Monthly |
| Missing evidence | Show observed lower bound and telemetry/response coverage; missing usage unknown rather than non-use. |
| Gaming and limits | Invocation quotas and coercion; no target usage count, split server/client/self-report origins. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Self-report is not verified use; installation/loading is not use or benefit. Suppress small groups. |
### Change lead time
Arithmetic mean commit-to-first-successful-production-deployment duration; also publish median/p90 and sample count.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of elapsed hours from committed_at to first successful production deployment for each service+commit |
| Denominator | Count of eligible service+commit pairs with valid mappings |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/change |
| Eligible population | Changes first successfully deployed to a service in reporting window; rollout retries collapse to same logical deployment. |
| Exclusions | Nonproduction, duplicates, no-op deployment with no new commits; missing mappings shown as coverage loss. |
| Events and source | change.committed; deployment.completed; VCS immutable commit timestamps and release manifest |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing commit mapping or negative/skewed duration excluded from numeric sample, reported explicitly; no zero substitution. |
| Gaming and limits | Timestamp rewriting and cherry-picking fast work; lock change mapping and show undeployed backlog age. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not PR cycle time, effort saved, or a complete view of unfinished changes. p90 uses nearest-rank sorted sample. |
### Deployment frequency
Distinct successful logical production deployments per complete service-day of observation.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct service_id+deployment_id successful production deployments in window |
| Denominator | Observed service-days in window |
| Expression | N / D |
| Aggregation | ratio |
| Unit | deployments/service-day |
| Eligible population | Declared production services and complete UTC observation days; report each service before pooled views. |
| Exclusions | Test/staging, failed attempts without production effect, duplicate callbacks and retries of the same rollout. |
| Events and source | deployment.completed; authoritative deployment service |
| Owner / cadence | Platform & IT / Weekly per service |
| Missing evidence | Missing feed periods not zero; report coverage and no unqualified overall rate when period incomplete. |
| Gaming and limits | Split deployments without value; pair with success, rework and failure measures. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. More deployments alone do not establish better performance; affected unsuccessful changes remain in change-fail population. |
### Change fail rate
Production deployment effects requiring unplanned remediation, rollback or hotfix, not solely outage declarations.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct matured production deployment_ids linked to qualifying immediate intervention |
| Denominator | All distinct production deployment_ids that reached production in the matured deployment cohort |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Deployments with production effect during window and approved intervention follow-up horizon elapsed. |
| Exclusions | Failed preproduction attempts, duplicate callbacks and no-effect retries; failures after production effect stay included. |
| Events and source | deployment.completed; deployment.failed; incident.linked; release and incident records |
| Owner / cadence | Quality Engineering / Weekly; finalized after horizon |
| Missing evidence | Known failures / all eligible deployments is a lower bound where incident linkage incomplete; label provisional and report unknown links. |
| Gaming and limits | Avoid incident links or relabel intervention planned; reconcile rollbacks/hotfixes independently. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Define immediate intervention and follow-up horizon before pilot; no universal incident-to-AI attribution. |
### Failed deployment recovery time
Mean elapsed time from production deployment failure onset to service restoration, with median/p90 and unresolved count/age.
| Contract | Definition |
| --- | --- |
| Numerator | Sum of recovery_at minus failure_started_at in hours for recovered eligible deployment failures |
| Denominator | Count of recovered eligible deployment failures with valid timestamps |
| Expression | N / D |
| Aggregation | mean |
| Unit | hours/failure |
| Eligible population | Production deployment failures beginning in window; latest verified restoration if a recovery is rescinded. |
| Exclusions | Nondeployment incidents; invalid timestamp pairs reported missing, not treated as instantaneous recovery. |
| Events and source | deployment.failed; service.restored; incident service |
| Owner / cadence | Platform & IT / Weekly with unresolved failures |
| Missing evidence | Unresolved failures reported separately and excluded only from recovered-duration distribution; prevent declaring improvement without their visibility. |
| Gaming and limits | Premature recovery declarations or omit hard cases; require operational readback and carry unresolved ages. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Not all-incident MTTR; recovered-only sample has survivorship bias. p90 nearest rank. |
### Deployment rework rate
Unplanned production deployment effects triggered by a production incident as a share of deployment effects.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct incident-linked deployment_ids classified unplanned remediation |
| Denominator | All distinct production deployment_ids with production effect in window |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Production deployment effects in window with immutable planned/unplanned classification and incident linkage. |
| Exclusions | Nonproduction, no-effect retries and duplicates; do not exclude unclassified changes silently. |
| Events and source | deployment.completed; incident.linked; release intent classification |
| Owner / cadence | Platform & IT / Weekly |
| Missing evidence | Unknown classification stays denominator; observed numerator is lower bound until reconciliation; show classification coverage. |
| Gaming and limits | Call hotfixes planned; freeze reason and independently reconcile incident/deployment links. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Distinct from failure rate: one failed deployment can lead to multiple separate rework deployments. |
### Changes with escaped quality issues
Accepted changes with at least one confirmed post-acceptance defect during approved follow-up, split by severity.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct eligible change_ids with one or more linked confirmed defects in follow-up |
| Denominator | Accepted change_ids with completed predeclared follow-up horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Accepted changes in acceptance window whose follow-up is complete by as_of. |
| Exclusions | Immature changes shown separately; duplicates/unconfirmed reports excluded but counted as pending triage. |
| Events and source | change.accepted; defect.confirmed; defect tracking and acceptance evidence |
| Owner / cadence | Quality Engineering / Monthly mature cohorts |
| Missing evidence | Unknown linkage is a coverage warning; no fabricated clean outcome. Show absolute defect and severe incident counts as well. |
| Gaming and limits | Shorten follow-up or downgrade severity; freeze horizon and rubric and independently sample defects. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Counts affected changes, not raw defects per change; no presumption defects are AI-caused. |
### Confirmed policy effects
Eligible evaluated attempts with an actual confirmed prohibited effect; report attempts, blocked attempts and effects separately.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct attempt_ids with confirmed prohibited action effect |
| Denominator | All evaluated eligible attempt_ids including failed and abandoned runs |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Governed skill trials or supported workflow attempts, reported separately for eval and live populations. |
| Exclusions | Out-of-scope runs; detection gaps remain coverage issues, not exclusions from known initiated population. |
| Events and source | attempt.started; policy.violation; gate.blocked; security audit and incident triage |
| Owner / cadence | Architecture & Security / Immediate incident action; weekly aggregates |
| Missing evidence | Unknown outcomes remain visible; zero detected effects never proves safety. Missing audits make rate provisional. |
| Gaming and limits | Hide incidents or inflate harmless trials; absolute severity counts and fixed task mix accompany rate. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Blocked attempts are not actual effects; any critical effect blocks promotion regardless of aggregate rate. |
### Cost per accepted outcome
Allocated total workflow cost including unsuccessful attempts, licenses, compute, tooling and human review/rework divided by accepted outcomes.
| Contract | Definition |
| --- | --- |
| Numerator | Sum allocated costs in one approved reporting currency for all tasks/attempts in matured start cohort |
| Denominator | Distinct independently accepted tasks in same cohort |
| Expression | N / D |
| Aggregation | ratio |
| Unit | currency/accepted outcome |
| Eligible population | Matured eligible started-task cohort, with all attempt costs through as_of and allocated shared period costs. |
| Exclusions | Unrelated projects and documented taxes/transfers outside chosen accounting basis; never failed/abandoned attempts. |
| Events and source | cost.recorded; task.accepted; finance invoices, usage ledger, sampled effort with approved rates |
| Owner / cadence | Finance & Accounting Squad / Monthly with late invoice restatement |
| Missing evidence | Missing cost buckets produce partial estimate plus coverage, not a total. Mixed currency requires dated approved conversion; zero accepted outcomes = null with full cost shown. |
| Gaming and limits | Count tokens only or hide labor/failures; reconcile invoice and allocation totals without double counting. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Allocation and sampled labor carry uncertainty; no ROI without independently measured business benefit. |
### Verification confidence and cognitive load
Three separate anonymous 1–5 item distributions: confidence in verifying output, manageable workload, usefulness. Primary item: verification confidence mean; report full distributions and response rate.
| Contract | Definition |
| --- | --- |
| Numerator | Sum valid 1–5 verification-confidence responses, one per participant per survey wave |
| Denominator | Count valid responses to that item |
| Expression | N / D |
| Aggregation | mean |
| Unit | points (1–5) |
| Eligible population | Invited eligible cohort at fixed survey wave; voluntary responses. |
| Exclusions | Invalid/out-of-range answers and superseded duplicate submission; nonresponders stay in response-rate denominator. |
| Events and source | survey.response; anonymous survey with single-use token and aggregate invitation count |
| Owner / cadence | People & Administration Squad / After pilot workflow cycle; periodic follow-up |
| Missing evidence | Report valid response count / invited count and item nonresponse; no imputation. Suppress people cohorts below five. |
| Gaming and limits | Pressure to respond positively; no manager access to individual responses, avoid identifying free text. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Perception and selection bias; not measured time savings. No composite productivity index. |
### Skills within substantive review window
Active approved skill revisions with an accountable owner and completed substantive review not past due.
| Contract | Definition |
| --- | --- |
| Numerator | Distinct active approved skill revisions with current owner, review evidence and review_due_at &gt;= as_of |
| Denominator | All active approved skill revisions in catalog snapshot |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | Approved revisions currently supported for use at as_of. |
| Exclusions | Experimental and retired revisions; deprecated/revoked in-use counts reported independently, not hidden. |
| Events and source | catalog.published; catalog.reviewed; catalog.deprecated; catalog.revoked; version inventory |
| Owner / cadence | Data & AI / Monthly and each release |
| Missing evidence | Missing owner/date/evidence outside numerator; show ownerless, deprecated-in-use and revoked-in-use absolute counts. |
| Gaming and limits | Rubber-stamp review dates; require linked regression/security/usefulness evidence. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. A current review is not proof of effectiveness; consumer inventory may miss shadow copies. |
### Workflow-specific business acceptance
Independently accepted domain outcomes against a predeclared business rubric, separate from technical completion.
| Contract | Definition |
| --- | --- |
| Numerator | Eligible completed-observation task_ids meeting the department-specific outcome rubric |
| Denominator | All eligible initiated workflow task_ids with matured business-outcome horizon |
| Expression | N / D * 100 |
| Aggregation | percent |
| Unit | % |
| Eligible population | One named departmental workflow and predeclared start cohort; never pool unlike rubrics. |
| Exclusions | Cancelled-before-start and unrelated workflows; unsuccessful or abandoned eligible work remains. |
| Events and source | task.started; business.accepted; departmental case/content/reconciliation/onboarding review record |
| Owner / cadence | Product Management / Each pilot cycle with owning department |
| Missing evidence | Unknown follow-up stays denominator with unresolved count; no claim actual business outcome changed without source evidence. |
| Gaming and limits | Weaken rubric or count drafts as customer benefit; lock rubric and independently audit acceptance. Descriptive signal, not causal AI productivity attribution. Source completeness, task mix and sampling limit inference; report uncertainty. Domain owner signs acceptance: Operations service accuracy; Marketing claims/rights; Finance reconciliation accuracy; HR policy correctness; Product usability/value. Not revenue uplift. |
## Run the synthetic measurement example
**Executed synthetic example — not Eldorado results.** The bundled fixture produces 1 independently accepted task from 4 matured starts: 25% confirmed success. One unresolved task remains in that denominator; known-outcome coverage is 75%. Selection precision and recall are each 1 / 2 = 50%. Allocated synthetic cost is EUR 60 / 1 accepted outcome = EUR 60 per accepted outcome. These deliberately small samples demonstrate arithmetic and missingness, not a baseline, target or ROI claim.
From the private documentation checkout, run `node scripts/ai-kpi.mjs` and `node --test scripts/ai-kpi.test.mjs`. The fixture `scripts/fixtures/ai-kpi-synthetic.json` is labeled synthetic; every result carries the same label and calculation version. This prototype has no network collectors and does not enable employee telemetry.
The executable covers validated success, selection precision/recall, change fail and rework rates, escaped quality, policy effects, cost and human experience. Other catalog metrics remain contracts rather than implemented collectors; output lists the omissions. Snapshot inputs are simplified, pre-reconciled records, not a production ingestion system. Conflicting duplicates reject the calculation rather than silently choosing a value.
</div>
+54
View File
@@ -0,0 +1,54 @@
---
id: roadmap
title: Roadmap
description: Evidence-gated phases, no promised dates or budgets.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
### Foundation
**Entry evidence** — Strategy scope accepted for planning; existing teams identified.
**Work** — Inventory actual client hosts/builds, provider/data terms, repositories, authority, owners and event availability; select low-risk workflows and metric definitions.
**Exit evidence and hold condition** — Named accountable people and independent reviewers, bounded pilot charter, forbidden actions, data/retention decision and reproducible baseline plan. Hold if accountability or lawful collection is unresolved.
### Governed skills pilot
**Entry evidence** — Foundation evidence approved; no privileged autonomous effects.
**Work** — Build a small registry, immutable contract, client adapters, evaluations, coaching and synthetic measurement verification.
**Exit evidence and hold condition** — Qualified client/skill tuples, mandatory safety checks, independent task acceptance, reconciled coverage and user feedback. Hold or narrow scope if quality or data coverage is inadequate.
### Reliable distribution
**Entry evidence** — Pilot quality and usefulness support continuing.
**Work** — Canary updates, effective-version readback, version locking, exception expiry, stale/offline handling and rollback.
**Exit evidence and hold condition** — Tested revocation and recovery, no bypass of central privileged gates, owners can support the catalog. Hold if effective versions or controls cannot be verified.
### Logical SDLC coordination
**Entry evidence** — Distribution, authority and measurement evidence stable.
**Work** — Observe and assemble evidence first, then propose and route work within scoped identities and budgets.
**Exit evidence and hold condition** — Exact-artifact approvals, idempotency, duplicate/outage recovery, separation of duties, audit and kill-switch drills pass; humans can take over. No authority expansion based on usage alone.
### Departmental expansion
**Entry evidence** — Each department supplies a workflow owner, outcome, data boundary and capacity.
**Work** — Add Operations, Marketing, Finance & Accounting and HR & Administration workflows with domain-specific review; Product and Technology continue quality improvement.
**Exit evidence and hold condition** — Departmental accepted outcomes, guardrails, total cost and experience justify continuation; sensitive decisions stay human. Stop or roll back workflows without adequate evidence.
There are no invented launch dates, budgets, staffing commitments or guaranteed gains. Phase gates permit narrowing or stopping. Re-evaluate earlier phases when the underlying client, provider, data scope or authority changes.
</div>
+20
View File
@@ -0,0 +1,20 @@
---
id: skills-adoption
title: Skills adoption
description: Workflow-led coaching, client choice, feedback, manual path.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
Start with a small set of useful workflows chosen by the existing teams. Product Management and the departmental owner identify the outcome; Data & AI helps turn it into a skill; Quality Engineering establishes acceptance; Platform & IT qualifies the client path; Architecture & Security clears the data boundary. People & Administration Squad coordinates learning and accessible feedback.
Onboarding should teach people to select the right skill, inspect the actual artifact, recognize untrusted instructions, use the exception and incident routes and recover without AI. A practice task ends with verified work and an explanation of limits, not just a logged invocation. Demonstrate the same portable skill on the supported local clients without promising identical agent behavior.
Use short worked examples, peer pairing, office hours and a clear catalog search path. Gather reasons for non-use and rejected suggestions without penalizing them. Preserve a manual path. Training feedback improves the skill only through review, not automatic self-modification of the approved catalog. Departmental workflow outcomes matter more than uniform uptake.
No developer rankings, individual productivity scores, minimum invocation quotas or inference of effort from token counts are part of this strategy. Report cohort-level evidence with privacy protection and balanced quality, flow, risk and cost. A participant may reasonably stop using a skill that creates more work or uncertainty.
</div>
+73
View File
@@ -0,0 +1,73 @@
---
id: skills-governance
title: Skills governance
description: Contract, lifecycle, decision rights, adapters, locking, exceptions.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
### A contract that travels, controls that stay authoritative
Maintain one catalog entry per stable skill ID, with immutable revisions and a concise portable `SKILL.md` body. Required content includes when to use and when not to use the skill, prerequisites, bounded steps, expected artifacts, verification, failure handling and escalation. The catalog contract below defines owner and independent reviewer, risk tier, supported clients and versions, dependencies, digest, provenance and license, evaluations, approval, review date, deprecation and rollback.
The release manifest pins the content, dependencies and adapter revisions by full immutable commit or digest. Names use an `eldorado-` prefix to reduce collisions. Friendly versions aid communication but never replace the digest. No arbitrary upstream automatic update is admitted into the approved bundle. Changes to scripts, resources or dependencies count as skill changes, not harmless documentation edits.
Adapters place content in the qualified client's discovery paths and implement only verified client-specific behavior. Qualification records the host, exact build, OS, model configuration, discovery, explicit invocation, collisions, disabled skills, permission behavior, context compaction and reload behavior. A common file format is not common precedence or a portable hooks API. Claude and Cline skill-name precedence differs from rule/memory behavior; Copilot support varies by surface. Test the actual build, not the brand name.
### Lifecycle and decision rights
A proposal names the problem, owner, data scope and testable outcome. Experimental revisions run only in approved disposable environments. Promotion requires independent review, static and executable dependency checks, per-client behavioral evaluations and a bounded canary. Approval records bind the exact artifact and permitted uses. Deprecated revisions carry a migration path and deadline; retired revisions are unavailable for new work. Any active revision can be quarantined immediately.
The author never supplies the only approval. Data & AI maintains catalog state; the owning team is accountable for content; Quality Engineering approves evaluation evidence; Architecture & Security approves higher-risk boundaries; Platform & IT verifies distribution and revocation. A business owner accepts workflow outcomes. These functions can be covered by existing staff, but incompatible author/approver duties must remain separate for the same release.
An exception contains its ID, skill/client revision, scope, business reason, risk owner, compensating checks, approver, start and expiry, affected users/projects and rollback route. It does not disable protected release, payment or identity controls. Expired or unverifiable exceptions deny privileged actions. Repeated exceptions trigger a supported-design change rather than permanent informal bypass.
### Distribution, stale clients and rollback
Stage a verified bundle atomically, check its digest, load it through the qualified adapter and read back its effective revision. Test already-open sessions; changing files is not proof of reloading instructions. Record immutable last-known-good content and adapter dependencies. Canary upgrades precede wider distribution.
A cached verified bundle may support explicitly permitted low-risk work if its model and tool dependencies are available. It does not make a cloud-backed client work offline. Stale or unsupported sessions cannot obtain centrally mediated privileged access. Reconnection revalidates support, revocation and policy before credentials are issued. Central artifact review protects delivery even when a laptop declaration is absent or false; a client report cannot prove which private prompt was actually followed.
Rollback restores the approved manifest and dependencies, invalidates affected sessions, revokes unsafe revisions, runs a smoke test and verifies effective state. It cannot undo data disclosure or commands already executed. Those require incident response.
## Catalog contract
| Contract | Definition |
| --- | --- |
| skill_id | Stable globally unique eldorado-&lt;domain&gt;-&lt;purpose&gt;; never reused for a different purpose |
| name | Human-readable name |
| purpose | Problem, expected outcome and intended users |
| triggers | Positive and negative invocation conditions |
| owner | Existing department/team plus assigned accountable person before approval |
| reviewer | Independent person and existing team; cannot equal release author |
| backup | Named coverage within existing team before production support |
| risk_tier | R1\|R2\|R3; maximum implied by data, tool and effect scope |
| data_classification | Allowed input/output/provider boundary and forbidden data |
| authority_envelope | Allowed resources/operations and forbidden effects; enforced externally |
| supported_clients | Array of client, host, exact tested builds, OS, model configuration, adapter digest and qualification evidence |
| dependencies | All skill/resource/script/tool/MCP/plugin dependencies pinned by immutable digest plus license and source |
| revision | Full immutable source commit and content/artifact digest; friendly semver optional |
| tests_evals | Suite/corpus/rubric/runner/judge revisions, client tuple, seeds or trial IDs, results and independent evidence refs |
| approval | Approver identities, exact digest and policy revision, approved scope, time and expiry/invalidation condition |
| provenance_license | Origin repository/author, upstream revision, acquisition/review history, rights/license for content and bundled resources |
| maintenance_date | Last substantive review timestamp plus next review due and trigger conditions |
| deprecation | State, reason, replacement, migration instructions, consumers and retirement deadline if applicable |
| rollback | Approved known-good bundle+adapter+dependency digests, revocation/reload procedure and tested evidence |
| procedure | Prerequisites, bounded steps, expected artifacts, verification, failure handling, escalation |
## Promotion and retirement
| Transition | Required evidence |
| --- | --- |
| proposed → experimental | Owner, purpose, risk/data scope, disposable environment and preregistered test plan |
| experimental → approved | Independent review, all mandatory tests, qualified clients, canary, exact-digest approval |
| approved → deprecated | Owner gives migration/replacement and retirement conditions |
| deprecated → retired | Consumers reconciled or explicit risk-approved cutover; no new invocation |
| any active state → quarantined | Incident or unsafe/unknown provenance; central deny immediately |
| quarantined → experimental | Reviewed correction; new immutable revision; requalify, never auto-restore approval |
</div>
+28
View File
@@ -0,0 +1,28 @@
---
id: skills-maintenance
title: Skills maintenance
description: Review, drift, deprecation, rollback and incident handling.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
Every approved skill has an accountable owning team, independent reviewer, review due date, supported client matrix and rollback bundle. Review both usefulness and safety, not just metadata freshness. Proposed maximum review intervals are recorded by risk tier; incidents, dependency changes, model/client drift or repeated failures trigger earlier review.
Data & AI reviews catalog health with the owning teams. Quality Engineering samples accepted outputs and adds confirmed failures to regression sets without exposing sensitive content. Platform & IT watches unsupported, deprecated and revoked revisions and tests distribution recovery. Architecture & Security reviews emerging misuse and exception expiry. Remove redundant or unused entries deliberately, after examining whether poor discoverability or fit explains non-use.
A reported unsafe behavior opens an incident with artifact, policy and client versions. Architecture & Security coordinates containment; Platform & IT revokes authority, stops scheduling and quarantines affected revisions; the owning department handles customer or business consequences; Quality Engineering reproduces the failure; Data & AI coordinates catalog correction and trend analysis. Preserve minimal restricted evidence and involve People & Administration Squad for employee-data concerns and Finance & Accounting Squad for financial effects. Restart requires a reviewed fix, regression evidence, verified rollback/revocation and accountable human authorization. Closing the incident does not erase the historical failure from measurement.
## Proposed maximum review intervals
Owner ratification is required before adoption. Review earlier after incidents or material dependency changes.
| Tier | Maximum days between substantive reviews |
| --- | --- |
| R1 | 90 |
| R2 | 60 |
| R3 | 30 |
</div>
+60
View File
@@ -0,0 +1,60 @@
---
id: skills-quality
title: Skills quality
description: Risk tiers, independent evaluation, repeated trials, adversarial conformance.
---
<div className="strategy-content">
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
Quality has four separate questions: was the right skill selected, was the procedure executed safely, did the artifact pass independent checks, and did the workflow produce useful outcomes? A successful invocation answers none of the remaining questions by itself.
The proposed qualification contract uses three tiers. R1 is read-only or drafting with nonsensitive approved material; R2 creates code or material with customer impact but no privileged external effect; R3 touches privileged, financial, sensitive-personal or production decisions. R3 remains human-authorized and out of the initial autonomous scope.
Every tier requires all mandatory deterministic safety, schema, authorization and provenance checks to pass, with no observed critical unauthorized action. An observed critical effect blocks promotion. The proposed pilot evaluation floors below set success thresholds; these are design defaults for owner ratification, not externally validated guarantees or approved service levels. If sample coverage, acceptance criteria or responsible reviewers are missing, qualification remains incomplete. No aggregate score can trade off a failed mandatory safety check.
Use sanitized, predeclared task sets containing positive triggers, near misses, negative cases and the normal workload mix. Compare no-skill, current-approved and candidate paths with the same starting artifacts; repeat stochastic trials and retain failed and abandoned attempts. Independently inspect output state. Deterministic tests grade checkable constraints; calibrated humans grade usefulness, maintainability and experience. A model judge is supplementary, versioned and checked against human disagreements, never the sole safety or promotion authority.
Include adversarial repository text, malicious referenced code, wrong permissions, client toggles, name shadowing, unsupported versions, hook timeout/crash/malformed response, stale sessions, offline recovery and rollback. Run central checks against forged/missing client declarations. Hook presence is not fail-closed enforcement: failure semantics must be tested on each client, and central authorization remains independent.
Report denominators, task mix, trial counts, per-client results and uncertainty. A passing benchmark is evidence within a tested envelope, not proof that all future tasks are safe. Material model, client, adapter or dependency changes trigger requalification.
## Proposed qualification floors
These are local design defaults for responsible-owner ratification, not approved service levels or guarantees. All mandatory checks must pass regardless of aggregate score.
### R1
**Distinct tasks** — 30
**Trials per task/client** — 3
**Artifact success** — 90%
**Selection precision / recall** — 90% / 90%
### R2
**Distinct tasks** — 60
**Trials per task/client** — 3
**Artifact success** — 95%
**Selection precision / recall** — 95% / 90%
### R3
**Distinct tasks** — 100
**Trials per task/client** — 5
**Artifact success** — 99%
**Selection precision / recall** — 99% / 95%
Artifact success denominator is every planned executed trial, retaining failures and abandonment. Trials of same task are correlated: show per-task and per-client strata; do not inflate independent sample size. Selection floors use independent labels. Report 95% uncertainty intervals clustered by task for outcome comparisons; no regression versus current approved path beyond a preregistered owner-approved margin. Missing margin/precision rule prevents promotion.
</div>
+113
View File
@@ -0,0 +1,113 @@
---
id: target-operating-model
title: Target operating model
description: One logical coordinator; local client choice; independent authority; all departments.
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
<div className="strategy-content">
## Coordination without transferred authority
The diagram is an overview; the equivalent delivery path below remains readable on narrow screens. Expand the diagram to inspect its trust boundaries and use its zoom controls for individual relationships.
1. Accountable people authorize the work scope; one logical coordinator routes tasks.
2. The reviewed registry supplies pinned skills and qualified adapters to coordinator and local clients.
3. Laptop contributors propose changes; independent repository and CI checks verify the exact revision.
4. A human approves that artifact; only the release service holds production credentials.
5. Restricted audit and measurement evidence returns to people and informs the next authorized task.
<ArchifyDiagram name="ai-adoption-target" title="TO-BE AI adoption: human accountability, no direct production authority for laptops or coordinator" />
**TO-BE · Target operating model.** Introduction depends on the [roadmap gates](./roadmap.mdx); these pages do not describe installed controls or measured company results.
### One coordination function, distributed contributions
Data & AI owns the coordinating function, with the existing AI Architect function connecting it to Technology leadership. The logical coordinator maintains approved work records, dependencies, evidence requests, bounded retries and escalation. It can summarize, propose work and route decisions. It cannot declare its own work approved, change protected checks, authorize production release or replace the responsible human.
One logical coordinator does not mean one fragile process. A durable task ledger, explicit state transitions, leases and idempotency keys allow recovery or multiple replicas without duplicate delivery. Inference outages stop new agent decisions, not the organization's ability to take over. People can resume work through the normal deterministic workflow. Evidence and authorization services fail closed for privileged actions; missing evidence never becomes permission.
The delivery path is: an approved problem and acceptance criteria; an authorized task; local or isolated worker proposals; independently reviewed repository changes; exact-revision CI evidence; a human release decision; execution by the release service; then operational feedback. Product Management accepts the outcome hypothesis and business result. Product Design reviews experience and accessibility. Product Engineering owns the code and maintainability. Quality Engineering assesses quality independently. Architecture & Security assesses risk. Platform & IT operates the delivery boundary. No arrow from a laptop or coordinator goes directly to production authority.
### Trust and authority boundaries
People authenticate to the work system; the coordinator receives only the project context it needs. Untrusted issue text, repository content, web pages and tool output are task data, never an authorization source. Local clients operate under device controls and project-scoped access. Hard containment requires OS/container isolation, filesystem and network restrictions and separate short-lived identities; a skill folder, prompt or working directory is not a sandbox.
The registry distributes reviewed immutable content and adapters, not ambient credentials. Executable scripts, plugins, MCP servers and dependencies are separately reviewed and pinned. Model-provider routing is allowlisted for the data classification, with approved retention and training-use terms before sensitive data crosses that boundary. Fallback to another provider must not silently change the data policy.
Central controls verify protected-branch review, actual artifact identity, exact commit and policy revision, required checks and a scoped human approval. Candidate changes cannot edit their own authoritative gate or reuse approval for a different artifact. Deployment credentials belong to the release service, not the local agent. Approval expires or is invalidated by changed content. Emergency stop revokes mutation credentials and stops scheduling independently of the model, preserves evidence and enables human takeover.
### Department contributions
### Product / Product Management
**Contribution and retained human decision** — Own pilot priorities, acceptance criteria and outcome hypotheses; decide product value.
**First bounded workflow and evidence** — Draft acceptance criteria from sanitized discovery notes; independent completeness review and accepted-task success.
### Product / Product Design
**Contribution and retained human decision** — Own design quality, accessibility and research handling.
**First bounded workflow and evidence** — Review a proposed flow against an accessibility checklist; verify findings against the interface.
### Technology / Product Engineering
**Contribution and retained human decision** — Own skill authoring, code review and maintainability; remain accountable for generated code.
**First bounded workflow and evidence** — Test scaffolding and change summaries in a disposable branch; independent tests and escaped-quality follow-up.
### Technology / Platform & IT
**Contribution and retained human decision** — Own supported client inventory, distribution, identity, isolation, CI/release integration, transport and emergency revocation.
**First bounded workflow and evidence** — Qualify a pinned bundle and exercise rollback; inventory and exact-revision gate evidence.
### Technology / Architecture & Security
**Contribution and retained human decision** — Own risk classification, data boundaries, security review and bounded exceptions.
**First bounded workflow and evidence** — Review injected-input and unauthorized-tool cases; independent control evidence and policy-effect counts.
### Technology / Quality Engineering
**Contribution and retained human decision** — Own regression/adversarial suites, rubric calibration and acceptance evidence.
**First bounded workflow and evidence** — Evaluate candidate versus approved skills on repeated representative tasks; retain all failures.
### Technology / Data & AI
**Contribution and retained human decision** — Own catalog coordination, coordinator evaluation and measurement design, through existing AI enablement responsibilities.
**First bounded workflow and evidence** — Curate a small skill set and analyze a prospective pilot; no new AI department.
### Operations / Operations Squad
**Contribution and retained human decision** — Own service acceptance, disputes, fraud, refunds and customer communications.
**First bounded workflow and evidence** — Draft an answer from sanitized resolved cases; Operational Quality workstream checks correctness before any send. No automated dispute/refund decision.
### Marketing / Marketing Squad
**Contribution and retained human decision** — Own brand, factual claims, content rights, publication and spend approvals.
**First bounded workflow and evidence** — Draft a campaign brief against approved brand material; human claim/rights review before publication.
### Finance & Accounting / Finance & Accounting Squad
**Contribution and retained human decision** — Own allocation of total costs, financial evidence and segregation of approvals.
**First bounded workflow and evidence** — Explain a sanitized reconciliation exception; no posting, payout or payment authority.
### HR & Administration / People & Administration Squad
**Contribution and retained human decision** — Own coaching, acceptable use, feedback privacy and people decisions; partner with Platform & IT on access lifecycle.
**First bounded workflow and evidence** — Draft onboarding guidance from approved policy; human accuracy review. No hiring or performance decision automation.
Product keeps two teams; Technology keeps five; the other four departments keep one squad each. Coordination, skill ownership, approval and cover are assignments inside these teams, not new standing departments, roles or headcount. Department leaders retain accountability and designate real approvers before a pilot starts.
See the existing [organization structure](/organization/structure/), [department collaboration](/organization/collaboration/) and [SDLC responsibilities](/organization/sdlc/).
</div>
+10 -4
View File
@@ -13,21 +13,27 @@ const config: Config = {
onBrokenLinks: 'throw',
markdown: {hooks: {onBrokenMarkdownLinks: 'throw'}},
presets: [['classic', {
docs: {sidebarPath: './sidebars.ts', routeBasePath: 'docs', editUrl: 'https://gitea.lego-cloud.eu/eldorado-v1/documentation/_edit/main/'},
docs: {path: 'docs/core', sidebarPath: './sidebars.ts', routeBasePath: 'docs', editUrl: 'https://gitea.lego-cloud.eu/eldorado-v1/documentation/_edit/main/'},
blog: false,
theme: {customCss: './src/css/custom.css'},
} satisfies Preset.Options]],
plugins: [
['@docusaurus/plugin-content-docs', {id: 'organization', path: 'docs/organization', routeBasePath: 'organization', sidebarPath: './sidebars/organization.ts'}],
['@docusaurus/plugin-content-docs', {id: 'strategy', path: 'docs/strategy', routeBasePath: 'strategy', sidebarPath: './sidebars/strategy.ts'}],
['@docusaurus/plugin-content-docs', {id: 'contribution', path: 'docs/contribution', routeBasePath: 'contribution', sidebarPath: './sidebars/contribution.ts'}],
],
themes: [['@easyops-cn/docusaurus-search-local', {
hashed: 'filename', indexDocs: true, indexBlog: false, indexPages: true,
docsRouteBasePath: 'docs', language: ['en'], highlightSearchTermsOnTargetPage: true,
docsRouteBasePath: ['docs', 'organization', 'strategy', 'contribution'], language: ['en'], highlightSearchTermsOnTargetPage: true,
}]],
themeConfig: {
colorMode: {defaultMode: 'light', respectPrefersColorScheme: true},
navbar: {
title: 'Eldorado', logo: {alt: 'Documentation home', src: 'img/docs-mark.svg'},
items: [
{to: '/docs/overview/', label: 'Documentation', position: 'left'},
{to: '/docs/maintain/', label: 'Maintainer guide', position: 'left'},
{type: 'docSidebar', docsPluginId: 'organization', sidebarId: 'organizationSidebar', label: 'Organization', position: 'left'},
{type: 'docSidebar', docsPluginId: 'strategy', sidebarId: 'strategySidebar', label: 'Strategy', position: 'left'},
{type: 'docSidebar', docsPluginId: 'contribution', sidebarId: 'contributionSidebar', label: 'Contribution', position: 'left'},
{href: 'https://gitea.lego-cloud.eu/eldorado-v1/documentation', label: 'Repository', position: 'right'},
],
},
+8 -4
View File
@@ -7,12 +7,15 @@
"node": ">=22"
},
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"start": "pnpm diagrams:build && docusaurus start",
"build": "pnpm diagrams:build && docusaurus build",
"serve": "docusaurus serve",
"typecheck": "tsc --noEmit",
"verify": "node scripts/verify.mjs",
"verify:build": "node scripts/verify.mjs --build"
"verify": "pnpm test && node scripts/verify.mjs",
"verify:build": "node scripts/verify.mjs --build",
"diagrams:build": "node scripts/build-diagrams.mjs",
"test": "node --test scripts/*.test.mjs",
"kpi:example": "node scripts/ai-kpi.mjs"
},
"dependencies": {
"@docusaurus/core": "3.10.2",
@@ -32,6 +35,7 @@
"@types/react": "19.1.13",
"typescript": "5.9.3"
},
"pnpm": {"overrides": {"serialize-javascript": "7.0.5"}},
"browserslist": {
"production": [
">0.5%",
+9 -14
View File
@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
serialize-javascript: 7.0.5
importers:
.:
@@ -4412,9 +4415,6 @@ packages:
resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==}
engines: {node: '>=10'}
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
range-parser@1.2.0:
resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==}
engines: {node: '>= 0.6'}
@@ -4678,8 +4678,9 @@ packages:
resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==}
engines: {node: '>= 0.8.0'}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
serialize-javascript@7.0.5:
resolution: {integrity: sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==}
engines: {node: '>=20.0.0'}
serve-handler@6.1.7:
resolution: {integrity: sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==}
@@ -8836,7 +8837,7 @@ snapshots:
globby: 13.2.2
normalize-path: 3.0.0
schema-utils: 4.5.0
serialize-javascript: 6.0.2
serialize-javascript: 7.0.5
webpack: 5.111.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.28))(html-minifier-terser@7.2.0)(postcss@8.5.28)
core-js-compat@3.50.0:
@@ -8902,7 +8903,7 @@ snapshots:
jest-worker: 29.7.0
postcss: 8.5.28
schema-utils: 4.5.0
serialize-javascript: 6.0.2
serialize-javascript: 7.0.5
webpack: 5.111.1(clean-css@5.3.3)(cssnano@6.1.2(postcss@8.5.28))(html-minifier-terser@7.2.0)(postcss@8.5.28)
optionalDependencies:
clean-css: 5.3.3
@@ -11291,10 +11292,6 @@ snapshots:
quick-lru@5.1.1: {}
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
range-parser@1.2.0: {}
range-parser@1.2.1: {}
@@ -11642,9 +11639,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
serialize-javascript@7.0.5: {}
serve-handler@6.1.7:
dependencies:
+229
View File
@@ -0,0 +1,229 @@
# Eldorado AI adoption — research handoff
Research only. Retrieved 4 October 2026 UTC. This document separates documented vendor capability, empirical evidence, and recommendations. Nothing here certifies an Eldorado rollout, installed client, enabled telemetry, staffing appointment, measured baseline, or realized productivity improvement. Sources and literal supporting passages are in `sources.json`; fetched text, retrieval outcomes and citation ledger are retained alongside it.
## 1. Local organizational baseline and scope
Read-only remote fetch and status inspection found a clean documentation checkout on `main`, with HEAD and `origin/main` both `14ba1aa1ef66c91525135f743d15c77e32db78d3`. Inspected all three current Organization pages: `structure.mdx`, `collaboration.mdx`, `sdlc.mdx`, plus channel wiki `/opt/data/wiki/channels/1555510466904727622.md`. This is documentation evidence, not independent verification of actual personnel, systems, or deployed controls. No repository file was changed.
Preserve the existing structure:
- Product: Product Management and Product Design.
- Technology: Product Engineering, Platform & IT, Architecture & Security, Quality Engineering, Data & AI.
- Operations: Operations Squad; Marketing: Marketing Squad; Finance & Accounting: Finance & Accounting Squad; HR & Administration: People & Administration Squad.
- Existing leadership documentation locates AI enablement coordination with the AI Architect within Data & AI, alongside established engineering leadership. Do not invent a new AI department, standing committee, delivery team, or confirmed headcount.
The requested target is ONE logical SDLC coordinating agent, with people using local Copilot, Claude Code or Cline agents. Logical coordination does not require a single fragile process and does not transfer human accountability to a model. The recommendations below develop this target; they are not a description of present operations.
## 2. Principal conclusions
1. Standardize a portable skill content contract, not the fiction of one universal client policy. Agent Skills defines `SKILL.md`, metadata and resources; `allowed-tools` is experimental, and the discovery directory is not mandated by the format.[1][21]
2. Treat instructions as guidance. GitHub explicitly warns of nondeterministic instruction following, and Claude describes memory files as context rather than enforced configuration.[3][5] Recommended enforcement belongs in independently administered repository, CI, artifact and release controls.
3. Maintain client-specific adapters and an explicit supported version matrix. Current official docs disagree even about collision precedence: Claude enterprise > personal > project; Cline global skill > project skill; Cline rules use the opposite workspace-over-global rule.[4][8][9]
4. Assess skill selection, execution, artifact quality and business outcomes separately. Repeated trials and environment-state verification matter more than an agent reporting success.[11]
5. Measure a balanced portfolio of flow, quality, risk, cost and human experience. DORA now uses five delivery metrics; SPACE explicitly rejects a single productivity metric.[12][24] Adoption counts or accepted code are not causal productivity evidence.
6. Use comparative pilots, not an imported percentage ROI. Controlled studies have reported substantial speedups and slowdowns in different settings, and METR's newer study explains why selection and concurrent-agent timing make current effects difficult to estimate.[16][17][22]
## 3. Compatibility and enforcement matrix
Every capability below is documented upstream, not exercised in Eldorado. A supported client means a particular host, version, model configuration and OS combination that passes local conformance tests; a product brand alone is insufficient.
| Dimension | GitHub Copilot | Claude Code | Cline |
| --- | --- | --- | --- |
| Skills and scope | Project `.github/skills`, `.claude/skills`, `.agents/skills`; personal `~/.copilot/skills`, `~/.agents/skills`. About page includes cloud agent, code review, CLI, app, VS Code and JetBrains agent mode.[2] | Common format plus invocation controls, subagents and dynamic context extensions. Native project/personal `.claude/skills`; enterprise and nested scopes also documented.[4] | Workspace `.cline/skills`, global `~/.cline/skills`; explicit invocation and enabled-by-default toggles documented.[8] |
| Always-on instructions | Repository-wide `.github/copilot-instructions.md`, path-specific files and agent instructions have surface-dependent support. Do not apply the GitHub.com hierarchy to every IDE.[3] | `CLAUDE.md` and related discovered memory files are concatenated into context, not overriding configuration files.[5] | Workspace/global rules combine, with workspace taking precedence for conflicts; toggles may disable rules.[9] |
| Name collision / precedence | Inspected docs establish instruction precedence on GitHub.com, not a universal skill-name collision order. Test duplicate personal/project skills before approval.[3] | Enterprise skill over personal over project for same-name collisions; distinct from memory-file concatenation.[4][5] | Global skill over project skill, but workspace rules over global rules. Do not conflate them.[8][9] |
| Trust and execution | GitHub warns shared skills are not verified, may contain injection or malicious scripts, and shell preapproval can remove a protective confirmation.[26] | Trust/permission dialogs and sandbox capability exist, but approved shell access can exceed working directory; unattended sessions differ.[7] | User toggles do not establish an enterprise security boundary. Current hook landing page redirects to SDK plugin documentation, which describes executable plugins.[8][10][23] |
| Hooks and enforcement | No universal Copilot hook parity established by inspected sources. Do not promise a portable hook API or use absence of research as proof none exists. | PreToolUse supports blocking, but command/http/MCP timeout does NOT block automatically; plain exit 1 is generally nonblocking without valid decision JSON. Agent SDK callbacks differ.[6] | SDK documents lifecycle hooks and explicit `fail_closed` policy. This is not proof every IDE/CLI installation exposes identical configuration or failure semantics.[23] |
| Distribution | Repository content can travel with code. `gh skill` public preview documents installation, source provenance, `@SHA` and `--pin`; pinned skills are skipped in updates.[26] | Repository/managed deployment plus client-specific facilities. Use an external immutable bundle manifest; do not presume Copilot's installer or plugin semantics apply unchanged.[4] | Commit workspace skill directories for team sharing. SDK plugins can come from npm/git/file sources; these require separate software-supply-chain review.[8][23] |
| Telemetry | Usage API spans multiple surfaces, but rich IDE detail requires telemetry; server-side active-user recovery does not fill missing dimensional data.[19] | OTel supports configured usage/cost/tool export; detailed skill names/tool arguments require explicit collection settings. Version-specific behavior is substantial.[20] | SDK lifecycle hooks provide instrumentation building blocks, not proof of a ready-made comparable enterprise skill dashboard or retention contract.[23] |
| Portable guarantee | Readable common content only; neither skill selection nor correct execution follows from installation.[3][26] | Shared format does not standardize extension behavior or guarantee instruction adherence.[4][5] | Shared content and client behavior remain different; toggles and name shadowing affect what executes.[8][9] |
### Version pinning, updates, rollback and stale laptops — recommended operational contract
These are recommended design choices, not features claimed to exist universally:
- Maintain one reviewed registry with stable skill IDs, human owner/reviewer, purpose, risk tier, provenance/license, immutable source revision, artifact digest, dependencies, supported client/OS/version range, evaluation revision/results, approval record, last review date, deprecation state, migration guide and rollback digest. Keep client-specific invocation and tool configuration outside the portable core, generated and tested as adapters.
- Release a manifest mapping stable IDs to exact immutable content and adapter revisions. Pin full commit/digest, not a mutable branch or tag alone. GitHub's preview installer provides pin/provenance mechanisms, but private Gitea compatibility and availability on Eldorado laptops have not been established.[26] A controlled vendored bundle is the simpler cross-client recommendation until proven otherwise.
- Promotion: author branch → independent review → format/resource validation → script/dependency/security checks → per-client behavioral and adversarial evals → canary → approval → distribution. Never permit a proposed skill to approve itself or edit its own promotion checks without independent review.
- Updates: compare signed/verified manifest and content digest; show release notes; stage atomically; restart/reload affected sessions where required by the client. Test whether already-open sessions retain old instructions. Do not infer reload from files changing.
- Rollback: restore a previously approved manifest AND adapter/dependency versions, revoke the unsafe revision centrally, expire affected sessions, rerun a smoke test and read back effective version. Reverting a file does not undo commands already executed or leaked data.
- Offline laptop: a verified cached bundle can support permitted low-risk local drafting, provided required local model/tool dependencies actually work offline. Local files being present do not mean cloud-backed inference is offline-capable. No claim of native offline inference or guaranteed durable telemetry buffering is made for any client.
- Stale/unsupported bundle: show a visible warning and prevent high-risk centrally mediated actions; allow explicitly approved low-risk work only. Recommended grace periods are risk decisions to settle, not fabricated vendor defaults. On reconnect, revalidate revocation and support status before obtaining privileged credentials or submitting gated delivery actions.
- Laptop evidence is fallible and may be altered by its owner. CI should verify repository-delivered manifest/content and actual output, but cannot prove which private prompt or skill a laptop truly used. Treat client attestations as advisory unless an independently verified device/runtime trust mechanism exists. Central enforcement must still protect work with no attestation.
- Avoid conflicting names and broad user-directory installation; prefix governed IDs and detect shadowing. Keep an explicit exception route with owner, scope, justification, expiry and compensating checks. An exception must not quietly disable protected-release controls.
## 4. Conflicts, weak evidence and what not to conclude
| Claim / conflict | Classification and resolution |
| --- | --- |
| “Project skills universally override user skills.” | Contradicted as a universal claim. Agent Skills implementation guide says this; current Claude and Cline product docs specify personal/global priority for skill collisions. Weight actual client documentation for that client, then verify behavior locally.[21][4][8] |
| “GitHub organization instructions enforce company policy everywhere.” | Unsupported. GitHub documents limited surfaces, personal/repository precedence, nondeterminism and head-branch review instructions. Use repository ownership and independently protected gates instead.[3] |
| “All clients support the same skills/hooks/telemetry.” | Unsupported. Standard content exists, but scope, extensions, hooks and instrumentation differ. Cline's hook page now points to SDK plugins; do not repeat historical hook file recipes without version testing.[1][10][23] |
| “Copilot supports JetBrains skills.” | About-skills page explicitly includes JetBrains; adding-skills overview currently omits it. This is documentation asymmetry, not sufficient evidence to declare unsupported. Verify the exact target host/version during adapter qualification.[2][26] |
| “Hook failure blocks dangerous work.” | Not generically true. Claude documents nonblocking timeout and exit-code cases; Cline SDK offers an explicit fail-closed setting. Test timeout, crash and malformed output separately.[6][23] |
| “AI makes developers 55.8% faster.” | Supported paraphrase only for Peng et al.'s controlled JavaScript HTTP-server task; abstract inspected. Not a software-organization forecast.[17] |
| “AI makes experienced developers 19% slower today.” | Misleading temporal/generalized claim. METR's early-2025 RCT involved 16 developers and 246 issues; the authors now label those results outdated as current capability evidence.[16] |
| “METR's newer results prove a universal speedup.” | Unsupported. Follow-up raw estimates suggest shorter task time, but intervals include no effect and authors warn participant/task selection and parallel-agent timing compromise the estimate.[22] |
| “Independent study proves a 21% Eldorado improvement.” | Unsupported. Paradis et al.'s abstract reports approximately 21% shorter task time for 96 Google engineers, with a large interval and explicit ecosystem/generalization caveats. Primary abstract, not full methods, inspected.[18] |
| “Agent says done / skill was loaded, therefore compliant.” | Unsupported. Outcome must be inspected in the environment; selection, reading, following, tool execution and outcome are separate observations.[11] |
Research hygiene correction: the initial URL seed for arXiv `2410.12944` was mistakenly labelled as a Cui field-experiments source. The retrieved body is Paradis et al.'s Google RCT; `sources.json` records the correct identity. No Cui statistic is used. Likewise, ACM SPACE returned HTTP 403; an author-hosted Microsoft Research publication summary was retrieved instead. These are bounded acquisition limitations, not invented evidence.
## 5. Target coordination, authority and organization mapping
Recommended trust flow: people and approved work records → one logical SDLC coordinator → task-scoped local-agent work proposals → repository review → independently administered CI/artifact evidence → authorized human release decision → release service. The coordinator should maintain workflow state, dependencies, requested evidence, retries and escalation. It should not be sole author, evaluator and approver, nor hold unrestricted production credentials.
Use durable IDs, idempotent task transitions and bounded retry budgets so multiple coordinator replicas cannot publish twice. Keep a human takeover and deterministic queue/authorization path if inference is unavailable. Project-level context sharing must be deliberately scoped; shared coordination is not permission to mix secrets, HR records, customer disputes and source code.
| Existing owner | Recommended accountability, without creating teams |
| --- | --- |
| Product Management | Choose outcome hypotheses and eligible work; accept product value and prioritize pilots rather than maximize AI usage. |
| Product Design | Validate usability/accessibility and customer research handling; review generated experience changes and design-task skills. |
| Product Engineering | Own code, practical skill authoring and local-agent use; review generated changes and remain responsible for maintainability. |
| Platform & IT | Own bundle delivery, device/client support, identity, workload isolation, central CI/release integration, telemetry transport and emergency revocation. |
| Architecture & Security | Own trust boundaries, data/permission risk classification, supply-chain approval requirements and security exceptions; review privileged skill proposals. |
| Quality Engineering | Own independent regression/adversarial suites, quality rubrics, gate evidence, failure sampling and evaluation reproducibility. |
| Data & AI | Coordinate the skills catalog and logical SDLC-agent evaluation with the documented AI Architect function; own statistical analysis and model/client experiment design. |
| Operations Squad | Supply sanitized service cases and operational acceptance; own human decisions on disputes, refunds, fraud and customer communications, not the coordinator. |
| Marketing Squad | Own brand/claim review, campaign approval and content rights; start with drafting/review skills rather than autonomous spending or publication. |
| Finance & Accounting Squad | Validate cost allocation and investment evidence; retain payout, accounting and financial approval segregation. |
| People & Administration Squad | Own training, onboarding, acceptable use and privacy-sensitive adoption feedback, with Platform & IT on access lifecycle; no individual developer ranking. |
Department leaders retain business accountability. Staffing capacity, named approvers and deputies need human confirmation; the operating-model role description is not evidence that every position is filled. For synthesis, map recommendations to existing responsibilities rather than publish uncertain staffing as fact.
### Threat and failure controls
NIST supplies a voluntary lifecycle governance framework and GenAI companion, not certification; the RMF core organizes work around govern, map, measure and manage.[13][14][25]
OWASP identifies external-content injection and recommends least privilege, human approval and adversarial testing.[15]
Recommended controls by failure mode:
- Malicious skill, script, dependency, MCP server or changed upstream tag: allowlisted source, immutable digest, independent review, license/provenance inventory, restricted network/runtime and no automatic arbitrary installs. A reviewed Markdown file does not make referenced executable code safe.
- Prompt injection from issues, repository instructions, web pages or tool results: treat as untrusted input; separate data from authority; limit credentials and egress; require independent authorization. GitHub code review's use of PR-head instructions is a concrete reason not to trust AI review as the sole gate.[3][15]
- Fabricated success, omitted tests, cherry-picked evidence or judge manipulation: verify actual artifact/CI state tied to exact commit and policy revision; keep evaluation checks outside candidate-controlled changes; sample human review.
- Overbroad local shell permissions: isolate process/OS identity, filesystem, network and secrets. A profile, skill folder or current directory is not containment. Claude's own docs distinguish permission prompts from sandbox controls.[7]
- Duplicate or looping agent work, runaway spend and unavailable coordinator: task idempotency, bounded retries/time/cost, lease expiry, human recovery and read-only/degraded mode. Never downgrade a release gate because the model or evidence service is unavailable.
- Model/client/context drift or stale session instructions: version-qualified adapters; requalification on material updates; compaction/long-session tests; version-aware incident analysis.
- Privacy leakage through telemetry or shared memory: minimal structured events, pseudonymous IDs, restricted retention and access; no raw prompts, secrets or source contents by default. Explicitly review the privacy cost before enabling detailed tool/skill-name export.[20]
- Kill switch: independently revoke mutation/release credentials and stop scheduling; disable affected revisions and preserve evidence. Sending “stop” to a model is not the security boundary.
## 6. Skill quality, evaluation and lifecycle recommendations
A useful skill must state when to use it, when not to use it, prerequisites, exact steps, expected artifacts, verification, failure handling and escalation. Keep the portable body small and reference bounded resources. The format specification supports progressive disclosure and recommends concise main files.[1]
Proposed evaluation layers:
1. Static contract: parse metadata; verify stable identity/owner/review date; validate links and resource paths; detect embedded secrets, prohibited dependencies, unsafe commands and unsupported client-only fields.
2. Selection tests: positive trigger tasks, near misses and negative tasks; report false activation and missed activation separately. A larger catalog should be tested for routing regressions, not presumed better.
3. Execution tests: run representative sanitized tasks in disposable environments with fixed starting repositories; verify real outputs, not self-reports. Include missing prerequisite, invalid input, tool timeout, denied permission, malformed evidence and interrupted session.
4. Safety tests: injected repository text, malicious referenced scripts, conflicting global/project skills, user toggle, unsupported version, expired/revoked bundle, offline network, context compaction and stale in-memory version.
5. Outcome grading: deterministic tests for mechanically checkable constraints; independent human review for product/design/maintainability; model judges only with explicit rubric and human calibration. Record disagreements and judge version.[11]
6. Comparison: same tasks and conditions for no-skill/current-approved/candidate versions; repeated trials, failure/abandonment retained; separate client/model/skill changes. Report sample size, variability, confidence and task mix, not only a mean pass percentage.[11]
Recommended promotion policy: all mandatory deterministic safety/authorization checks must pass; any observed critical unauthorized action blocks promotion pending investigation. This is a proposed policy, NOT proof of zero residual risk. Quantitative success-rate thresholds should be set after pilot variance and business impact are known, by risk tier; do not invent universally validated percentages. Privileged release/payment/customer-impact workflows need stronger review than read-only documentation assistance.
Lifecycle: proposed → experimental → approved → deprecated → retired, with quarantine available from every active state. Review triggered by incident, tool/model/client change, dependency change, usage feedback or scheduled owner review. Each approved skill needs an owner and backup, change history, support window, migration route and known-good rollback. Deprecation should warn, offer replacement and track consumers before retirement; urgent security revocation overrides convenience. Do not retain unowned skills merely because they remain popular.
Adoption should begin with training on how to inspect results, report failures and refuse unsafe requests. Build examples from actual team workflows; offer office hours and opt-in feedback; record why a skill was rejected. Measure whether people can perform and verify work, not whether they obey a target number of invocations. Avoid automatically promoting agent-written corrections into the governed catalog.
## 7. KPI instrumentation options and recommended choices
### Practical source tiers
A. Start with authoritative repository/CI/deployment/incident events. These can measure delivery outcomes without observing every laptop prompt. No event feed has been inspected here; feasibility must be verified with the current hosting platforms and retention.
B. Add a governed catalog/version inventory and opt-in task-level skill/client declarations. This supplies adoption and support coverage, but declarations are not proof of execution or adherence.
C. Add client adapters where supported: Copilot usage APIs and Claude OTel may help; their fields, licensing, enabled state and missingness differ.[19][20] Cline SDK hooks are an instrumentation option requiring engineering and exact-version qualification, not a proven universal collector.[23]
D. Use short anonymous surveys, work sampling and pilot comparisons for cognitive load, confidence and rework not visible in logs. SPACE's multidimensional premise supports this combination; surveys alone cannot establish time saved.[24][16]
### Candidate measurement contracts for synthesis
These are recommended options, not observed metrics or approved targets. All denominators must use a predeclared eligible population and reporting window; zero denominators produce “not available,” never zero performance.
| Measure | Definition and useful source | Owner / interpretation / missingness |
| --- | --- | --- |
| Validated task success | Independently accepted successful tasks / all eligible initiated tasks with matured observation window; count failed/abandoned tasks as nonsuccess and show still-open separately. Task ID + acceptance evidence, not agent message. | Quality Engineering; weekly pilot report. Show task type, sample size and unobserved outcomes separately; exclude cancelled-before-start work by stated rule, not post-hoc cherry-picking. |
| Skill selection precision / recall | Correct relevant activations / all activations; relevant tasks with activation / all relevant tasks, on a labeled evaluation set. | Data & AI with Quality Engineering; per candidate/client release. Not obtainable from skill-loaded logs without relevance labels. |
| Supported bundle coverage | Eligible active installations with verified supported revision / eligible active installations inventoried. | Platform & IT; weekly. Unknown/invisible laptops remain unknown and inventory coverage is separately reported; do not treat silence as compliant. |
| Useful adoption | Distinct eligible participants using an approved skill in an eligible workflow / eligible participants during window, plus qualitative usefulness. | Data & AI and People & Administration Squad; monthly aggregate. Separate declared, observed and inferred usage; suppress small cohorts and never rank people. |
| Change lead time | Commit-to-production elapsed time per deployed change; report distribution and population. Repository + deployment SHA association. | Platform & IT / Product Engineering; service-level trend. Missing SHA mapping is missing coverage, not instantaneous delivery. DORA definition, not PR-open-to-merge time.[12] |
| Deployment frequency | Production deployments / stated period, service-scoped. | Platform & IT; report with quality measures. Exclude retries/duplicate events; no incentive to split meaningless deployments.[12] |
| Change fail rate | Production deployments requiring immediate intervention / all production deployments. | Quality Engineering / Platform & IT; require incident/rollback linkage and intervention definition. Missing incident links limit confidence.[12] |
| Failed deployment recovery time | Recovery timestamp minus failed-deployment failure timestamp; distribution over relevant failed deployments. | Platform & IT; unresolved failures shown separately, never dropped as if recovered. Not all-incident MTTR.[12] |
| Deployment rework rate | Unplanned incident-driven production deployments / all production deployments. | Platform & IT; preserve planned/unplanned classification and incident link. DORA fifth metric, distinct from change fail rate.[12] |
| Escaped quality issues | Severity-stratified post-acceptance defects / accepted changes with completed follow-up, plus absolute incident counts. | Quality Engineering; agreed maturation window. Do not compare immature and mature cohorts or imply every defect was AI-caused. |
| Safety / policy violations | Confirmed prohibited actions or attempted bypasses / evaluated eligible runs, with severe incidents also as absolute counts. | Architecture & Security; immediate incident response and periodic trend. Separate detected attempts, blocked attempts and actual effects; zero observed is not proof of no risk. |
| Cost per accepted outcome | Allocated license + model/tool/compute + human review/rework costs / independently accepted outcomes; include costs of failed/abandoned attempts in numerator. | Finance & Accounting Squad; monthly. Show uncertain allocation and missing spend; do not call model tokens the whole cost. |
| Human experience | Aggregate survey distribution for cognitive load, confidence in verification and perceived usefulness, with response count / invited eligible population. | People & Administration Squad / Data & AI; periodic. Self-selection and perception bias explicit; no employee surveillance dashboard. |
| Maintenance health | Approved skills within review window / active approved skills; separately count ownerless, deprecated-in-use and revoked-in-use revisions. | Data & AI / Platform & IT; catalog release and periodic review. Review stamp alone is not quality evidence. |
Recommended minimal event schema: `event_id`, `schema_version`, `event_type`, `occurred_at`, `received_at`, `producer`, `task_id`, `attempt_id`, `project_id`, `pseudonymous_actor_id` where necessary, `client/host/version`, `model_id/version_if_exposed`, `skill_id/revision/digest`, `adapter_revision`, `policy_revision`, `repo_commit`, `outcome`, `reason_code`, `evidence_ref`, `cost_amount/currency/basis`, `measurement_origin` (server/client/self-report), and `consent_or_collection_basis`. Fields can be nullable with reason; do not fabricate a model revision hidden by a provider.
Recommended ingestion rules: deduplicate by producer + event_id; model retries as attempts, not extra completed tasks; preserve late events and recompute versioned aggregates; use server receipt and skew checks rather than trusting laptop clocks blindly. A lost terminal event is unknown until reconciled, not automatically success or abandonment. Distinguish paused, failed, abandoned, cancelled-before-start and accepted. A skill-loaded event records loading only. A task can involve multiple skills/versions and agents; avoid attributing the entire outcome or labor time to every invocation.
Suggested privacy starting point for human approval: short raw structured-event retention (for example 30 days), longer aggregate trends (for example 12 months), no raw prompts/code/screens by default, role-limited incident evidence, and explicit deletion/access process. These durations are policy proposals, not legal requirements or existing Eldorado settings. Confirm employment/privacy constraints with responsible owners before collection.
Pilot comparison recommendation: define task classes and quality acceptance first; collect a prospective baseline and eligible population; compare matched or randomized cohorts when ethical/practical; record concurrent process/client/model changes. Keep quality and risk guardrails fixed. Report descriptive trends separately from causal estimates. Measure elapsed lead time and human effort separately because parallel agents break the assumption that elapsed time equals labor saved; METR's follow-up explicitly raises this issue.[22]
For executable downstream examples, use clearly synthetic fixtures covering duplicates, retries, unknown outcomes, multiple skills, zero denominator, immature quality window and late deployment/incident events. Assert exact expected numerator/denominator and units. Do not display synthetic values as Eldorado baseline data.
## 8. Recommended sequence and evidence gates
- Foundation: inventory actual clients, repositories, data sensitivity, available events and accountable owners. Exit only with a supported pilot boundary and explicit forbidden actions, not with a catalog full of untested skills.
- Low-risk pilot: small set of useful read-only/drafting/test-assistance skills; reviewed immutable bundles; exact-client smoke and adversarial tests; human review and failure reporting. Exit with reproducible quality evidence and usable measurement coverage.
- Governed distribution: add version-aware adapters, onboarding, staged upgrades, rollback/revocation drills and exception expiry. Exit when stale/offline/unsupported clients cannot bypass central privileged gates.
- Logical SDLC coordination: coordinator starts by observing and assembling evidence, then proposing work and routing approvals. Expand mutation only after idempotency, separation of duties, scoped credentials, outage recovery and kill-switch tests pass.
- Broader departmental use: Operations, Marketing, Finance & Accounting and HR & Administration adopt domain-specific skills with their own human decisions, data boundaries and outcome measures. Do not copy coding autonomy into payments, personnel decisions or disputes.
No invented rollout dates, budget, staffing capacity or required improvement percentage. Scale only when quality, risk and cost evidence support it; stop or narrow scope when benefits are not demonstrated.
## 9. Source coverage and unresolved questions
Coverage: 26 retrieved primary-source records with 50 literal evidence passages. Includes Agent Skills specification and integration guidance; GitHub support/instructions/install/metrics; Claude skills/memory/hooks/security/monitoring; Cline skills/rules/hooks and linked SDK plugins; agent-evaluation methodology; current DORA definitions; author-hosted SPACE summary; NIST framework/core/GenAI profile landing page; OWASP prompt injection; two primary research abstracts plus METR historical study and 2026 follow-up. This is substantive source-body review, but not a systematic literature review, independent replication, legal assessment or live client qualification. Full PDFs for the two arXiv papers and NIST GenAI profile were not reviewed; claims are limited accordingly. ACM's 403 is recorded and resolved only to the author-hosted summary, not bypassed or silently called full text.
Open decisions/evidence needed before implementation:
1. Which actual client hosts, versions, models, OSs, subscriptions and managed-device controls are in use? What is the allowed support window?
2. Which repository/CI/release systems and protected-branch controls are authoritative? Can required checks and policy definitions be altered by a candidate PR or bypassed by administrators?
3. Who fills each approving/operating function and who provides cover? What pilot capacity is available? The documented organization does not establish personnel availability.
4. What data may reach each model provider or telemetry endpoint; what retention/training/region and contractual terms apply? No provider contract was inspected.
5. Which low-risk workflows have enough representative tasks and independent quality labels? What is the actual eligible population and baseline coverage?
6. What timeout, stale-bundle grace, critical-risk rejection and emergency-revocation policies will owners approve? Can laptop access be independently revoked?
7. Does the chosen distributor support private Gitea securely, and can every supported client report effective skill/adapter versions without collecting sensitive content?
8. What causal claims, if any, can the proposed pilot design justify given self-selection, learning, task mix and parallel work?
9. Which detailed Cline/Copilot hooks and offline/resume behavior exist in the precise deployed builds? Sources here do not establish cross-client equivalence.
Synthesis handoff: keep this research trail outside reader-facing portal prose. Use ordinary operating-model language for the strategy, preserve all existing teams and leadership uncertainty, visibly distinguish target maturity from current state, and retain recommendation/source mapping internally. No portal publication is part of this research card.
## Sources
[1] https://agentskills.io/specification — Agent Skills specification
[2] https://docs.github.com/en/copilot/concepts/agents/about-agent-skills — Copilot Agent Skills
[3] https://docs.github.com/en/copilot/concepts/prompting/response-customization — Copilot custom instructions
[4] https://code.claude.com/docs/en/skills — Claude Code skills
[5] https://code.claude.com/docs/en/memory — Claude Code memory
[6] https://code.claude.com/docs/en/hooks — Claude Code hooks
[7] https://code.claude.com/docs/en/security — Claude Code security
[8] https://docs.cline.bot/customization/skills — Cline skills
[9] https://docs.cline.bot/customization/cline-rules — Cline rules
[10] https://docs.cline.bot/customization/hooks — Cline hooks
[11] https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents — Demystifying evals for AI agents
[12] https://dora.dev/guides/dora-metrics — DORA software delivery metrics
[13] https://www.nist.gov/itl/ai-risk-management-framework — NIST AI RMF
[14] https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence — NIST Generative AI Profile
[15] https://genai.owasp.org/llmrisk/llm01-prompt-injection — OWASP prompt injection
[16] https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study — Experienced developer RCT
[17] https://arxiv.org/abs/2302.06590 — AI developer productivity experiment
[18] https://arxiv.org/abs/2410.12944 — Field experiments in software development
[19] https://docs.github.com/en/copilot/concepts/billing-and-usage/copilot-usage-metrics/copilot-metrics — Copilot metrics concepts
[20] https://code.claude.com/docs/en/monitoring-usage — Claude Code monitoring
[21] https://agentskills.io/client-implementation/adding-skills-support — Agent Skills security
[22] https://metr.org/blog/2026-02-24-uplift-update — METR productivity update 2026
[23] https://docs.cline.bot/sdk/plugins — Cline SDK plugins
[24] https://www.microsoft.com/en-us/research/publication/the-space-of-developer-productivity-theres-more-to-it-than-you-think — SPACE research publication
[25] https://airc.nist.gov/airmf-resources/airmf/5-sec-core — NIST AI RMF Core
[26] https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills — Copilot adding skills
+564
View File
@@ -0,0 +1,564 @@
[
{
"id": 1,
"title": "Agent Skills specification",
"url": "https://agentskills.io/specification",
"publisher": "Agent Skills",
"retrieved_at": "2026-10-04T10:27:53.189577+00:00",
"claims": [
{
"claim": "The specification defines a skill directory with SKILL.md, not a universal deployment or enforcement service.",
"evidence": "A skill is a directory containing, at minimum, a SKILL.md file:",
"limitations": "Format compatibility does not certify discovery, precedence, tool permissions, safety, or instruction compliance."
},
{
"claim": "allowed-tools is experimental in the common specification.",
"evidence": "allowed-toolsNoSpace-separated string of pre-approved tools the skill may use. (Experimental)",
"limitations": "Format compatibility does not certify discovery, precedence, tool permissions, safety, or instruction compliance."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/01.txt",
"body_sha256": "0913e84055bc7eb05649aa49123a783dc1a413e4c31d0861f9ac367849c7b00e",
"evidence_type": "primary source body"
},
{
"id": 2,
"title": "Copilot Agent Skills",
"url": "https://docs.github.com/en/copilot/concepts/agents/about-agent-skills",
"publisher": "GitHub",
"retrieved_at": "2026-10-04T10:27:53.333904+00:00",
"claims": [
{
"claim": "GitHub documents skill support across specified Copilot surfaces, not all modes indiscriminately.",
"evidence": "Agent skills work with Copilot cloud agent, Copilot code review, the GitHub Copilot CLI, the GitHub Copilot app, and agent mode in Visual Studio Code and JetBrains IDEs.",
"limitations": "Vendor documentation, not an Eldorado installation test. Separate adding-skills page omits JetBrains in its overview; validate exact host/version."
},
{
"claim": "Copilot documents multiple project skill paths.",
"evidence": "Project skills, stored in your repository (.github/skills, .claude/skills, or .agents/skills)",
"limitations": "Vendor documentation, not an Eldorado installation test. Separate adding-skills page omits JetBrains in its overview; validate exact host/version."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/02.txt",
"body_sha256": "26c86bc2afa4813c46361429f015f687164a69f39030fa05dbc1f513a04c0ca3",
"evidence_type": "primary source body"
},
{
"id": 3,
"title": "Copilot custom instructions",
"url": "https://docs.github.com/en/copilot/concepts/prompting/response-customization",
"publisher": "GitHub",
"retrieved_at": "2026-10-04T10:27:53.960265+00:00",
"claims": [
{
"claim": "GitHub explicitly warns that custom-instruction following is nondeterministic.",
"evidence": "Due to the non-deterministic nature of AI, Copilot may not always follow your custom instructions in exactly the same way every time they are used.",
"limitations": "Instruction precedence is surface-specific and is not enforceable authorization. Head-branch instructions create a review trust risk."
},
{
"claim": "The GitHub.com instruction hierarchy prioritizes personal over repository over organization instructions.",
"evidence": "Multiple types of custom instructions can apply to a request sent to Copilot. Personal instructions take the highest priority. Repository instructions come next, and then organization instructions are prioritized last. However, all sets of relevant instructions are provided to Copilot.",
"limitations": "Instruction precedence is surface-specific and is not enforceable authorization. Head-branch instructions create a review trust risk."
},
{
"claim": "Copilot review reads instructions and skills from the PR head branch.",
"evidence": "When reviewing a pull request, Copilot reads repository custom instructions, agent instructions, and agent skills from the head branch (the branch with your changes), not the base branch. For example, when merging my-feature-branch into main, Copilot uses the instructions and skills in my-feature-branch, so you can test changes to them in the same pull request without merging them first.",
"limitations": "Instruction precedence is surface-specific and is not enforceable authorization. Head-branch instructions create a review trust risk."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/03.txt",
"body_sha256": "1c2c190052653b8baad1478dc53b7a0657b741be0dbb0ef202b3f503c6b3379c",
"evidence_type": "primary source body"
},
{
"id": 4,
"title": "Claude Code skills",
"url": "https://code.claude.com/docs/en/skills",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:53.088978+00:00",
"claims": [
{
"claim": "Claude skill name collisions prioritize enterprise over personal over project.",
"evidence": "Two of enterprise, personal, and projectEnterprise over personal, and personal over project. With deploy in both ~/.claude/skills/ and the project’s .claude/skills/, /deploy runs the personal one",
"limitations": "Vendor extensions and changing versions prevent assuming cross-client parity. No local client exercised."
},
{
"claim": "Claude extends the common skill format with client-specific functionality.",
"evidence": "Claude Code skills follow the Agent Skills open standard, which works across multiple AI tools. Claude Code extends the standard with additional features like invocation control, subagent execution, and dynamic context injection. See Using skill frontmatter outside Claude Code for which frontmatter fields are part of the standard and which are Claude Code extensions.",
"limitations": "Vendor extensions and changing versions prevent assuming cross-client parity. No local client exercised."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/04.txt",
"body_sha256": "4ac3de187f2ba050da149e1bea3364a959366be03963e7a76dc9f9041bdb4e1f",
"evidence_type": "primary source body"
},
{
"id": 5,
"title": "Claude Code memory",
"url": "https://code.claude.com/docs/en/memory",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:52.994293+00:00",
"claims": [
{
"claim": "Claude memory instructions are context, not enforced configuration.",
"evidence": "Claude Code has two complementary memory systems. Both are loaded at the start of every conversation. Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead. The more specific and concise your instructions, the more consistently Claude follows them.",
"limitations": "Context order does not establish deterministic behavior; conflicting prose may still produce arbitrary choices."
},
{
"claim": "Claude concatenates discovered memory files rather than treating them as override-only configuration.",
"evidence": "All discovered files are concatenated into context rather than overriding each other. Across the directory tree, content is ordered from the filesystem root down to your working directory. For the foo/bar/ example, foo/CLAUDE.md appears in context before foo/bar/CLAUDE.md, so instructions closer to where you launched Claude are read last. Within each directory, CLAUDE.local.md is appended after CLAUDE.md, so your personal notes are the last thing Claude reads at that level.",
"limitations": "Context order does not establish deterministic behavior; conflicting prose may still produce arbitrary choices."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/05.txt",
"body_sha256": "b19869f25886f95f8d93cec941482cbf9472b507ab8d643b446b1450b915e0df",
"evidence_type": "primary source body"
},
{
"id": 6,
"title": "Claude Code hooks",
"url": "https://code.claude.com/docs/en/hooks",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:53.059984+00:00",
"claims": [
{
"claim": "Claude PreToolUse command/http/MCP hook timeout is not fail-closed.",
"evidence": "A timed-out command, http, or mcp_tool hook doesn’t block the tool call. The call continues through the normal permission flow, so don’t count on a stalled hook to act as a gate.",
"limitations": "Event and hook-family distinctions matter; no universal fail-closed promise. No hook was installed or tested here."
},
{
"claim": "A conventional exit code 1 alone does not reliably block most Claude hook events.",
"evidence": "For most hook events, exit code 2 is the only exit code that blocks through the code alone. Without valid JSON on stdout, Claude Code treats exit code 1 as a non-blocking error and proceeds with the action, even though 1 is the conventional Unix failure code. If your hook is meant to enforce a policy, use exit 2. The worktree events differ: any non-zero exit code from WorktreeCreate aborts worktree creation, and any non-zero exit code from WorktreeRemove makes worktree removal fail if the directory still exists afterward.",
"limitations": "Event and hook-family distinctions matter; no universal fail-closed promise. No hook was installed or tested here."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/06.txt",
"body_sha256": "ae1919a5b06aa6d4a213bfafcaab1d2473f0b89d53b9fcc35358a19ab9ca3e5d",
"evidence_type": "primary source body"
},
{
"id": 7,
"title": "Claude Code security",
"url": "https://code.claude.com/docs/en/security",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:52.953904+00:00",
"claims": [
{
"claim": "Claude working-directory approval is not OS-level containment.",
"evidence": "Working directory boundary: In Manual mode, Claude Code asks you before its file tools read or write outside the folder where it was started and its subfolders. The boundary is a permission prompt, so a Bash command you approve can still write anywhere your user account can",
"limitations": "Vendor capability is not evidence the sandbox is enabled; approved shell commands inherit actual OS authority."
},
{
"claim": "OS-level Bash restrictions require sandboxing.",
"evidence": "To restrict Bash commands at the operating system level, turn on sandboxing",
"limitations": "Vendor capability is not evidence the sandbox is enabled; approved shell commands inherit actual OS authority."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/07.txt",
"body_sha256": "a8a2ad7d077347f1d3ea43549b0be1070e03c7737647f73ef40035d5dafb5f40",
"evidence_type": "primary source body"
},
{
"id": 8,
"title": "Cline skills",
"url": "https://docs.cline.bot/customization/skills",
"publisher": "Cline",
"retrieved_at": "2026-10-04T10:27:53.250798+00:00",
"claims": [
{
"claim": "Cline global skill overrides same-name project skill.",
"evidence": "When a global skill and project skill have the same name, the global skill takes precedence. This lets you keep general-purpose skills globally while using project-specific skills in .cline/skills/ so the whole team can use them.",
"limitations": "Current docs disagree with Agent Skills guide generalization. Exact installed Cline version remains unknown."
},
{
"claim": "Discovered Cline skills have user-controlled enable/disable toggles.",
"evidence": "Every skill has a toggle to enable or disable it. This lets you control which skills are active without deleting the skill directory. Skills are enabled by default when discovered.",
"limitations": "Current docs disagree with Agent Skills guide generalization. Exact installed Cline version remains unknown."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/08.txt",
"body_sha256": "38e6af22c7e9f78e90e899a7bd285e6556bfd64197fbc3f7cf279cc9ff29d416",
"evidence_type": "primary source body"
},
{
"id": 9,
"title": "Cline rules",
"url": "https://docs.cline.bot/customization/cline-rules",
"publisher": "Cline",
"retrieved_at": "2026-10-04T10:27:53.308915+00:00",
"claims": [
{
"claim": "Cline workspace rules override conflicting global rules, unlike skill name precedence.",
"evidence": "When both workspace and global rules exist, Cline combines them. Workspace rules take precedence when they conflict with global rules. See Storage Locations for more guidance.",
"limitations": "Rule precedence is not skill precedence; both are guidance and can be toggled."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/09.txt",
"body_sha256": "c1a2353c024491a42961e5ea171c341c66bbed2647464d200bf90fdbaa8a48e3",
"evidence_type": "primary source body"
},
{
"id": 10,
"title": "Cline hooks",
"url": "https://docs.cline.bot/customization/hooks",
"publisher": "Cline",
"retrieved_at": "2026-10-04T10:27:53.370475+00:00",
"claims": [
{
"claim": "Cline hooks landing page delegates details to SDK plugins.",
"evidence": "See details under SDK Plugins page.",
"limitations": "Landing page alone proves no event schema or IDE compatibility. SDK page was followed separately."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/10.txt",
"body_sha256": "2ebd8cd1c0b11fec466a3b577971382d2c4ec1f9911679b10100029e9b3be095",
"evidence_type": "primary source body"
},
{
"id": 11,
"title": "Demystifying evals for AI agents",
"url": "https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:53.451618+00:00",
"claims": [
{
"claim": "Repeated trials are needed for variable agent outputs.",
"evidence": "Each attempt at a task is a trial. Because model outputs vary between runs, we run multiple trials to produce more consistent results.",
"limitations": "Vendor-authored methodology and examples, not independent proof of any skill gain or target threshold."
},
{
"claim": "Agent outcome evaluation must inspect environment state rather than trust claimed completion.",
"evidence": "The outcome is the final state in the environment at the end of the trial. A flight-booking agent might say “Your flight has been booked” at the end of the transcript, but the outcome is whether a reservation exists in the environment’s SQL database.",
"limitations": "Vendor-authored methodology and examples, not independent proof of any skill gain or target threshold."
},
{
"claim": "Agent evals combine code, model and human graders.",
"evidence": "Agent evaluations typically combine three types of graders: code-based, model-based, and human. Each grader evaluates some portion of either the transcript or the outcome. An essential component of effective evaluation design is to choose the right graders for the job.",
"limitations": "Vendor-authored methodology and examples, not independent proof of any skill gain or target threshold."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/11.txt",
"body_sha256": "7940a33d8e19ff68aac5e3c095e0fa771565a4b2416e8a781e9ed02d55668a12",
"evidence_type": "primary source body"
},
{
"id": 12,
"title": "DORA software delivery metrics",
"url": "https://dora.dev/guides/dora-metrics/",
"publisher": "DORA",
"retrieved_at": "2026-10-04T10:27:53.335018+00:00",
"claims": [
{
"claim": "DORA now documents five software-delivery performance metrics.",
"evidence": "These metrics have evolved alongside the technology landscape: shifting from the original four keys to the current five-metric model. To understand the research behind changes like the move from MTTR to Failed Deployment Recovery Time, read A history of DORA’s software delivery metrics.",
"limitations": "Service/team outcome measures, not causal AI-effect estimates or individual productivity scores."
},
{
"claim": "DORA change lead time runs from commit to production deployment.",
"evidence": "Change lead time: The amount of time it takes for a change to go from committed to version control to deployed in production.",
"limitations": "Service/team outcome measures, not causal AI-effect estimates or individual productivity scores."
},
{
"claim": "DORA deployment rework measures unplanned incident-driven deployments.",
"evidence": "Deployment rework rate: The ratio of deployments that are unplanned but happen as a result of an incident in production.",
"limitations": "Service/team outcome measures, not causal AI-effect estimates or individual productivity scores."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/12.txt",
"body_sha256": "0535efe22d238f38d1de5ec655efeb44a313e59f8a71085e902bde5c7a693cb9",
"evidence_type": "primary source body"
},
{
"id": 13,
"title": "NIST AI RMF",
"url": "https://www.nist.gov/itl/ai-risk-management-framework",
"publisher": "NIST",
"retrieved_at": "2026-10-04T10:27:53.731461+00:00",
"claims": [
{
"claim": "NIST AI RMF is voluntary guidance for trustworthiness across the AI lifecycle.",
"evidence": "Led by the Information Technology Laboratory (ITL) AI Program, and in collaboration with the private and public sectors, NIST has developed a framework to better manage risks to individuals, organizations, and society associated with artificial intelligence (AI). The NIST AI Risk Management Framework (AI RMF) is intended for voluntary use and to improve the ability to incorporate trustworthiness considerations into the design, development, use, and evaluation of AI products, services, and systems.",
"limitations": "Voluntary framework, not certification, legal advice, or evidence of Eldorado compliance."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/14.txt",
"body_sha256": "2fed4e9811f05579384a23b9c3b81600ddf0f011fc4cfe638352a817f91ed603",
"evidence_type": "primary source body"
},
{
"id": 14,
"title": "NIST Generative AI Profile",
"url": "https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence",
"publisher": "NIST",
"retrieved_at": "2026-10-04T10:27:53.743664+00:00",
"claims": [
{
"claim": "NIST GenAI profile is a cross-sectoral companion to AI RMF.",
"evidence": "This document is a cross-sectoral profile of and companion resource for the AI Risk Management Framework (AI RMF 1.0) for Generative AI, pursuant to President Biden's Executive Order (EO) 14110 on Safe, Secure, and Trustworthy Artificial Intelligence. The AI RMF was released in January 2023, and is intended for voluntary use and to improve the ability of organizations to incorporate trustworthiness considerations into the design, development, use, and evaluation of AI products, services, and systems.",
"limitations": "Publication landing-page abstract inspected; full PDF not read. Do not attribute detailed risk taxonomy to this excerpt."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/15.txt",
"body_sha256": "7998580f4dc83770568dc361ecae1d46e1d652a2df12a278263b88de6cbfb624",
"evidence_type": "primary source body"
},
{
"id": 15,
"title": "OWASP prompt injection",
"url": "https://genai.owasp.org/llmrisk/llm01-prompt-injection/",
"publisher": "OWASP",
"retrieved_at": "2026-10-04T10:27:53.529810+00:00",
"claims": [
{
"claim": "External sources can carry prompt injections.",
"evidence": "Indirect prompt injections occur when an LLM accepts input from external sources, such as websites or files. The content may have in the external content data that when interpreted by the model, alters the behavior of the model in unintended or unexpected ways. Like direct injections, indirect injections can be either intentional or unintentional.",
"limitations": "Mitigations reduce exposure; no guarantee that prompt injection is eliminated."
},
{
"claim": "OWASP recommends least privilege.",
"evidence": "Provide the application with its own API tokens for extensible functionality, and handle these functions in code rather than providing them to the model. Restrict the model’s access privileges to the minimum necessary for its intended operations.",
"limitations": "Mitigations reduce exposure; no guarantee that prompt injection is eliminated."
},
{
"claim": "OWASP recommends human approval for high-risk operations.",
"evidence": "Implement human-in-the-loop controls for privileged operations to prevent unauthorized actions.",
"limitations": "Mitigations reduce exposure; no guarantee that prompt injection is eliminated."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/16.txt",
"body_sha256": "5bfcc42a422165d0a6b25f28fbff144fc0dec5289f1a672a40203e1d64200c07",
"evidence_type": "primary source body"
},
{
"id": 16,
"title": "Experienced developer RCT",
"url": "https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/",
"publisher": "METR",
"retrieved_at": "2026-10-04T10:27:54.289507+00:00",
"claims": [
{
"claim": "METR early-2025 RCT found 19% longer task time in its setting.",
"evidence": "We conduct a randomized controlled trial (RCT) to understand how early-2025 AI tools affect the productivity of experienced open-source developers working on their own repositories. Surprisingly, we find that when developers use AI tools, they take 19% longer than without—AI makes them slower. We view this result as a snapshot of early-2025 AI capabilities in one relevant setting; as these systems continue to rapidly evolve, we plan on continuing to use this methodology to help estimate AI acceleration from AI R&D automation.",
"limitations": "Historical narrow setting, tools from early 2025; superseded as a current estimate by authors. No Eldorado forecast."
},
{
"claim": "Study involved 16 experienced developers and 246 issues on their repositories.",
"evidence": "To directly measure the real-world impact of AI tools on software development, we recruited 16 experienced developers from large open-source repositories (averaging 22k+ stars and 1M+ lines of code) that they’ve contributed to for multiple years. Developers provide lists of real issues (246 total) that would be valuable to the repository—bug fixes, features, and refactors that would normally be part of their regular work. Then, we randomly assign each issue to either allow or disallow use of AI while working on the issue. When AI is allowed, developers can use any tools they choose (primarily Cursor Pro with Claude 3.5/3.7 Sonnet—frontier models at the time of the study); when disallowed, they work without generative AI assistance. Developers complete these tasks (which average two hours each) while recording their screens, then self-report the total implementation time they needed. We pay developers $150/hr as compensation for their participation in the study.",
"limitations": "Historical narrow setting, tools from early 2025; superseded as a current estimate by authors. No Eldorado forecast."
},
{
"claim": "METR now explicitly marks those historical results as not current.",
"evidence": "We have released results that are current as of early 2026, in a continuation of this study. We believe these historical results no longer reflect the current impact of AI models on open-source developer productivity.",
"limitations": "Historical narrow setting, tools from early 2025; superseded as a current estimate by authors. No Eldorado forecast."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/17.txt",
"body_sha256": "f9abab8a3ad312279800d8327122ebae70c13804d9f91fda1788db1a466a178b",
"evidence_type": "primary source body"
},
{
"id": 17,
"title": "AI developer productivity experiment",
"url": "https://arxiv.org/abs/2302.06590",
"publisher": "Peng et al.",
"retrieved_at": "2026-10-04T10:27:53.477297+00:00",
"claims": [
{
"claim": "Peng et al. controlled HTTP-server task study reports 55.8% faster completion with Copilot.",
"evidence": "Abstract:Generative AI tools hold promise to increase human productivity. This paper presents results from a controlled experiment with GitHub Copilot, an AI pair programmer. Recruited software developers were asked to implement an HTTP server in JavaScript as quickly as possible. The treatment group, with access to the AI pair programmer, completed the task 55.8% faster than the control group. Observed heterogenous effects show promise for AI pair programmers to help people transition into software development careers.",
"limitations": "Primary paper abstract inspected, not full methods. Bounded JavaScript task; not whole-SDLC productivity."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/18.txt",
"body_sha256": "76c71ee9844c5066d184ec5006945c48959dfcd75854842463cd756f348a3140",
"evidence_type": "primary research abstract"
},
{
"id": 18,
"title": "How much does AI impact development speed? An enterprise-based randomized controlled trial",
"url": "https://arxiv.org/abs/2410.12944",
"publisher": "Paradis et al. / arXiv",
"retrieved_at": "2026-10-04T10:27:53.495104+00:00",
"claims": [
{
"claim": "Paradis et al. Google RCT abstract reports about 21% shorter task time with a large confidence interval, among 96 engineers.",
"evidence": "Abstract:How much does AI assistance impact developer productivity? To date, the software engineering literature has provided a range of answers, targeting a diversity of outcomes: from perceived productivity to speed on task and developer throughput. Our randomized controlled trial with 96 full-time Google software engineers contributes to this literature by sharing an estimate of the impact of three AI features on the time developers spent on a complex, enterprise-grade task. We found that AI significantly shortened the time developers spent on task. Our best estimate of the size of this effect, controlling for factors known to influence developer time on task, stands at about 21\\%, although our confidence interval is large. We also found an interesting effect whereby developers who spend more hours on code-related activities per day were faster with AI. Product and future research considerations are discussed. In particular, we invite further research that explores the impact of AI at the ecosystem level and across multiple suites of AI-enhanced tools, since we cannot assume that the effect size obtained in our lab study will necessarily apply more broadly, or that the effect of AI found using internal Google tooling in the summer of 2024 will translate across tools and over time.",
"limitations": "Primary paper abstract inspected, not full methods. Google-specific task/tool setting; no universal gain claim. Retrieval seed title was wrong and corrected from body."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/19.txt",
"body_sha256": "e3ac83e7a8c9a613ed67b179321599538d4132e0caf858c4d335452c924c6839",
"evidence_type": "primary research abstract"
},
{
"id": 19,
"title": "Copilot metrics concepts",
"url": "https://docs.github.com/en/copilot/concepts/billing-and-usage/copilot-usage-metrics/copilot-metrics",
"publisher": "GitHub",
"retrieved_at": "2026-10-04T10:27:54.384631+00:00",
"claims": [
{
"claim": "Copilot richest metrics depend on enabled client telemetry.",
"evidence": "Copilot usage metrics are derived from telemetry across multiple Copilot surfaces, including IDE, Copilot CLI, and agent apps activity. Most metrics come from client-side IDE telemetry, and end users need telemetry enabled in their IDE for the richest data in these metrics.",
"limitations": "No access to Eldorado subscription, settings, enabled exporters, reports or coverage. Adoption is not impact."
},
{
"claim": "Server-side active-user coverage does not ensure complete feature/LOC breakdowns.",
"evidence": "Users surfaced through server-side telemetry are fully counted toward your active user totals (such as daily active users, daily_active_users ). When available, they may also appear in totals_by_ide (including the most recently detected IDE and Copilot extension versions in per-user reports). However, other dimensional breakdowns—such as totals_by_feature and lines-of-code metrics—remain empty until richer telemetry is available for them. Top-level totals and breakdowns for users already captured by client telemetry are unchanged.",
"limitations": "No access to Eldorado subscription, settings, enabled exporters, reports or coverage. Adoption is not impact."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/20.txt",
"body_sha256": "556fe5be28952b3b7d980b86b8a1a15713f0cff8359a7a0e02e09af7d6916ace",
"evidence_type": "primary source body"
},
{
"id": 20,
"title": "Claude Code monitoring",
"url": "https://code.claude.com/docs/en/monitoring-usage",
"publisher": "Anthropic",
"retrieved_at": "2026-10-04T10:27:53.636289+00:00",
"claims": [
{
"claim": "Claude supports configured OTel usage/cost/tool telemetry.",
"evidence": "Track Claude Code usage, costs, and tool activity across your organization by exporting telemetry data through OpenTelemetry (OTel). Claude Code exports metrics as time series data via the standard metrics protocol, events via the logs/events protocol, and optionally distributed traces via the traces protocol.",
"limitations": "Capability depends on version/configuration. Tool/skill-name export may expose confidential identifiers; no observed Eldorado telemetry."
},
{
"claim": "Skill-name/tool detail collection has explicit opt-in and privacy implications.",
"evidence": "OTEL_LOG_TOOL_DETAILSEnable logging of tool parameters and input arguments in tool events and trace span attributes: Bash commands, MCP server and tool names, skill names, user-authored workflow names, and tool input. Also enables custom, plugin, and MCP command names on user_prompt events, and real agent, skill, plugin, and MCP server and tool names on the cost and token counters (default: disabled). For Claude Desktop’s built-in servers, in sessions Claude Desktop owns, mcp_server_name/mcp_tool_name emit on tool_decision/tool_result even with the flag off. The exception requires Claude Code v2.1.214 or later1 to enable",
"limitations": "Capability depends on version/configuration. Tool/skill-name export may expose confidential identifiers; no observed Eldorado telemetry."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/21.txt",
"body_sha256": "197b3be58cae62470e82ac24074ca81024b030701ab3e2729116067dac798b48",
"evidence_type": "primary source body"
},
{
"id": 21,
"title": "Agent Skills security",
"url": "https://agentskills.io/client-implementation/adding-skills-support",
"publisher": "Agent Skills",
"retrieved_at": "2026-10-04T10:27:53.937245+00:00",
"claims": [
{
"claim": "The implementation guide claims project-over-user skill precedence as universal.",
"evidence": "The universal convention across existing implementations: project-level skills override user-level skills.",
"limitations": "Implementation guidance rather than normative format; universal precedence claim contradicted by current Claude and Cline docs."
},
{
"claim": "Agent Skills format does not mandate discovery paths.",
"evidence": "The .agents/skills/ paths have emerged as a widely-adopted convention for cross-client skill sharing. While the Agent Skills specification does not mandate where skill directories live (it only defines what goes inside them), scanning .agents/skills/ means skills installed by other compliant clients are automatically visible to yours, and vice versa.",
"limitations": "Implementation guidance rather than normative format; universal precedence claim contradicted by current Claude and Cline docs."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/22.txt",
"body_sha256": "fb2909a93d445a4b18a7cab89c22a7e92502e99d2caa586cf785e8aa8ae7a77b",
"evidence_type": "primary source body"
},
{
"id": 22,
"title": "METR productivity update 2026",
"url": "https://metr.org/blog/2026-02-24-uplift-update/",
"publisher": "METR",
"retrieved_at": "2026-10-04T10:29:00.438454+00:00",
"claims": [
{
"claim": "METR 2026 follow-up calls its productivity signal unreliable because of selection and time-measurement issues.",
"evidence": "Unfortunately, given participant feedback and surveys, we believe that the data from our new experiment gives us an unreliable signal of the current productivity effect of AI tools. The primary reason is that we have observed a significant increase in developers choosing not to participate in the study because they do not wish to work without AI, which likely biases downwards our estimate of AI-assisted speedup. We additionally believe there have been selection effects due to a lower pay rate (we reduced the pay from $150/hr to $50/hr), and that our measurements of time-spent on each task are unreliable for the fraction of developers who use multiple AI agents concurrently.",
"limitations": "Authors explicitly warn selection and parallel-agent timing compromise causal magnitude. Neither raw estimate is an Eldorado baseline."
},
{
"claim": "2026 raw estimates suggest speedup, but intervals include no effect and selection limits generalization.",
"evidence": "Our raw results show some evidence for speedup. Our early 2025 study found the use of AI causes tasks to take 19% longer, with a confidence interval between +2% and +39%. For the subset of the original developers who participated in the later study, we now estimate a speedup of -18% with a confidence interval between -38% and +9%. Among newly-recruited developers the estimated speedup is -4%, with a confidence interval between -15% and +9%.",
"limitations": "Authors explicitly warn selection and parallel-agent timing compromise causal magnitude. Neither raw estimate is an Eldorado baseline."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/23.txt",
"body_sha256": "7a4ce4ac6ba1a7a2410e17982493ab0d905d5d6418f28dda7460ca2aa4068306",
"evidence_type": "primary source body"
},
{
"id": 23,
"title": "Cline SDK plugins",
"url": "https://docs.cline.bot/sdk/plugins",
"publisher": "Cline",
"retrieved_at": "2026-10-04T10:28:59.766735+00:00",
"claims": [
{
"claim": "Cline SDK plugins document fail_closed hook policy.",
"evidence": "Use fail_closed for policy-enforcement hooks where bypassing the hook is unsafe.",
"limitations": "SDK capability cannot be assumed identical in every Cline IDE/CLI build. Fail-closed behavior needs adapter conformance tests."
},
{
"claim": "Cline plugin installation accepts npm/git/file sources.",
"evidence": "Plugins can also be installed from file URLs, npm, git, or local paths using cline plugin install. See Plugins for install commands, the manifest format, and directory layout.",
"limitations": "SDK capability cannot be assumed identical in every Cline IDE/CLI build. Fail-closed behavior needs adapter conformance tests."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/24.txt",
"body_sha256": "445ba1089e4157e9eac938d1b7f1adf482769f3ae6d495d4bef5d666614fbd6a",
"evidence_type": "primary source body"
},
{
"id": 24,
"title": "SPACE research publication",
"url": "https://www.microsoft.com/en-us/research/publication/the-space-of-developer-productivity-theres-more-to-it-than-you-think/",
"publisher": "Microsoft Research",
"retrieved_at": "2026-10-04T10:29:02.189612+00:00",
"claims": [
{
"claim": "SPACE rejects productivity measurement with a single activity metric.",
"evidence": "Developer productivity is about more than an individual’s activity levels or the efficiency of the engineering systems relied on to ship software, and it cannot be measured by a single metric or dimension. The SPACE framework captures different dimensions of productivity, and here we demonstrate how this framework can be used to understand productivity in practice and why using it will help teams better understand developer productivity and create better measures to inform their work and teams.",
"limitations": "Author-hosted publication summary retrieved after ACM 403; supports multidimensional principle, not detailed instrument validity."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/25.txt",
"body_sha256": "1f8176ad5b14a5f4053162f670b95bdef81e191104f682c506b11099525b734e",
"evidence_type": "primary research abstract"
},
{
"id": 25,
"title": "NIST AI RMF Core",
"url": "https://airc.nist.gov/airmf-resources/airmf/5-sec-core/",
"publisher": "NIST",
"retrieved_at": "2026-10-04T10:29:00.075579+00:00",
"claims": [
{
"claim": "NIST RMF organizes risk activity into govern, map, measure, manage.",
"evidence": "highest level to govern, map, measure, and manage AI risks. Governance is designed to be a cross-cutting function",
"limitations": "Risk-framework recommendations are not proof controls are deployed or staffing exists."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/26.txt",
"body_sha256": "c80bf3d3d59511eb3932cfd285893aa2a329ca9d997939c7c8c7718d704f857b",
"evidence_type": "primary source body"
},
{
"id": 26,
"title": "Copilot adding skills",
"url": "https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/add-skills",
"publisher": "GitHub",
"retrieved_at": "2026-10-04T10:29:00.472587+00:00",
"claims": [
{
"claim": "gh skill --pin skips pinned skills during updates.",
"evidence": "To lock a skill to a specific version (or commit SHA) so it is skipped during updates, use --pin:",
"limitations": "gh skill is public preview, requires documented minimum CLI version; Gitea interoperability not verified. Pinning is not artifact safety."
},
{
"claim": "GitHub warns shared skills are unverified and may be malicious.",
"evidence": "Skills are not verified by GitHub and may contain prompt injections, hidden instructions, or malicious scripts. Always inspect the content of a skill before installation using gh skill preview.",
"limitations": "gh skill is public preview, requires documented minimum CLI version; Gitea interoperability not verified. Pinning is not artifact safety."
},
{
"claim": "gh skill records source ref and tree SHA in frontmatter.",
"evidence": "When you install a skill with gh skill, provenance metadata is written into the skill's SKILL.md frontmatter, including the source repository, ref, and tree SHA. The gh skill update command uses this metadata to check for upstream changes.",
"limitations": "gh skill is public preview, requires documented minimum CLI version; Gitea interoperability not verified. Pinning is not artifact safety."
}
],
"evidence_file": "/opt/data/channels/eldorado-storm-v1/research/ai-adoption/bodies/27.txt",
"body_sha256": "7a5d8597f2ed72edb4610124e83f2abc282311739eabb561b3997ab6cbcbc46b",
"evidence_type": "primary source body"
}
]
+190
View File
@@ -0,0 +1,190 @@
# Eldorado AI adoption — strategy implementation blueprint
Status: target operating model for review; not an approved rollout or an inventory of deployed controls. This handoff is documentation design only. It does not authorize installing agents, collecting employee telemetry, changing permissions or enabling autonomous delivery.
## Implementation contract
Create eight substantive pages in the independent Strategy navigation, in this order: `index`, `target-operating-model`, `skills-governance`, `skills-quality`, `skills-adoption`, `measurement`, `skills-maintenance`, `roadmap`. The structured companion is `strategy-model.json`; its KPI definitions and identifiers are authoritative for the measurement prototype. Preserve Organization Structure, Collaboration and SDLC and their exact department/team names. Cross-link rather than duplicate reporting charts. The existing organizational documentation is an operating model, not certification of staffing.
Use the existing Archify JSON renderer and shared icon-only modal control. Diagram logical ID: `ai-adoption-target`; editable source: `diagrams/ai-adoption-target.architecture.json`. Do not expose downloadable JSON. Keep source mapping and open-decision material outside docs/static/search. Reader-facing prose below can be adapted into MDX without bibliography, evidence codes, recruitment commentary or repeated caveats. Use a visible **Target operating model** introduction and prospective verbs where status matters. Never present synthetic calculations as Eldorado results.
## 1. Overview — index
### Govern skills first, expand authority only with evidence
Eldorado's target is to make AI-assisted work dependable across the organization: useful guidance, clear human ownership, controlled execution and measurable outcomes. The first investment is a governed skills system, not an autonomous replacement for the organization. A skill packages reusable working knowledge, prerequisites and verification steps. It helps an agent perform a task; it does not grant permission or guarantee that the work is correct.
The target combines one logical SDLC coordinating agent with developer choice on local laptops. Copilot, Claude Code and Cline remain client options within a qualified support matrix. They receive the same reviewed portable skill content through client-specific adapters. Repository, CI and release services remain the enforcement points, independently of what any agent says or which local tool produced a change.
The strategy describes the intended operating model and the evidence required to introduce it. It does not establish that these controls are installed, that every client is supported, or that AI has improved Eldorado's performance. The current reference is the documented six-department organization. Rollout begins by validating actual clients, responsibilities, data boundaries and available evidence.
Success means accepted outcomes with maintained quality, controlled risk and understood total cost. Faster activity without reliable outcomes is not success. More skill invocations, generated lines or tool subscriptions are not productivity targets. People remain accountable for product, customer, financial and personnel decisions.
## 2. Target operating model — target-operating-model
### One coordination function, distributed contributions
Data & AI owns the coordinating function, with the existing AI Architect function connecting it to Technology leadership. The logical coordinator maintains approved work records, dependencies, evidence requests, bounded retries and escalation. It can summarize, propose work and route decisions. It cannot declare its own work approved, change protected checks, authorize production release or replace the responsible human.
One logical coordinator does not mean one fragile process. A durable task ledger, explicit state transitions, leases and idempotency keys allow recovery or multiple replicas without duplicate delivery. Inference outages stop new agent decisions, not the organization's ability to take over. People can resume work through the normal deterministic workflow. Evidence and authorization services fail closed for privileged actions; missing evidence never becomes permission.
The delivery path is: an approved problem and acceptance criteria; an authorized task; local or isolated worker proposals; independently reviewed repository changes; exact-revision CI evidence; a human release decision; execution by the release service; then operational feedback. Product Management accepts the outcome hypothesis and business result. Product Design reviews experience and accessibility. Product Engineering owns the code and maintainability. Quality Engineering assesses quality independently. Architecture & Security assesses risk. Platform & IT operates the delivery boundary. No arrow from a laptop or coordinator goes directly to production authority.
### Trust and authority boundaries
People authenticate to the work system; the coordinator receives only the project context it needs. Untrusted issue text, repository content, web pages and tool output are task data, never an authorization source. Local clients operate under device controls and project-scoped access. Hard containment requires OS/container isolation, filesystem and network restrictions and separate short-lived identities; a skill folder, prompt or working directory is not a sandbox.
The registry distributes reviewed immutable content and adapters, not ambient credentials. Executable scripts, plugins, MCP servers and dependencies are separately reviewed and pinned. Model-provider routing is allowlisted for the data classification, with approved retention and training-use terms before sensitive data crosses that boundary. Fallback to another provider must not silently change the data policy.
Central controls verify protected-branch review, actual artifact identity, exact commit and policy revision, required checks and a scoped human approval. Candidate changes cannot edit their own authoritative gate or reuse approval for a different artifact. Deployment credentials belong to the release service, not the local agent. Approval expires or is invalidated by changed content. Emergency stop revokes mutation credentials and stops scheduling independently of the model, preserves evidence and enables human takeover.
### Department contributions
| Department / existing team | Contribution and retained human decision | First bounded workflow and evidence |
| --- | --- | --- |
| Product / Product Management | Own pilot priorities, acceptance criteria and outcome hypotheses; decide product value. | Draft acceptance criteria from sanitized discovery notes; independent completeness review and accepted-task success. |
| Product / Product Design | Own design quality, accessibility and research handling. | Review a proposed flow against an accessibility checklist; verify findings against the interface. |
| Technology / Product Engineering | Own skill authoring, code review and maintainability; remain accountable for generated code. | Test scaffolding and change summaries in a disposable branch; independent tests and escaped-quality follow-up. |
| Technology / Platform & IT | Own supported client inventory, distribution, identity, isolation, CI/release integration, transport and emergency revocation. | Qualify a pinned bundle and exercise rollback; inventory and exact-revision gate evidence. |
| Technology / Architecture & Security | Own risk classification, data boundaries, security review and bounded exceptions. | Review injected-input and unauthorized-tool cases; independent control evidence and policy-effect counts. |
| Technology / Quality Engineering | Own regression/adversarial suites, rubric calibration and acceptance evidence. | Evaluate candidate versus approved skills on repeated representative tasks; retain all failures. |
| Technology / Data & AI | Own catalog coordination, coordinator evaluation and measurement design, through existing AI enablement responsibilities. | Curate a small skill set and analyze a prospective pilot; no new AI department. |
| Operations / Operations Squad | Own service acceptance, disputes, fraud, refunds and customer communications. | Draft an answer from sanitized resolved cases; Operational Quality workstream checks correctness before any send. No automated dispute/refund decision. |
| Marketing / Marketing Squad | Own brand, factual claims, content rights, publication and spend approvals. | Draft a campaign brief against approved brand material; human claim/rights review before publication. |
| Finance & Accounting / Finance & Accounting Squad | Own allocation of total costs, financial evidence and segregation of approvals. | Explain a sanitized reconciliation exception; no posting, payout or payment authority. |
| HR & Administration / People & Administration Squad | Own coaching, acceptable use, feedback privacy and people decisions; partner with Platform & IT on access lifecycle. | Draft onboarding guidance from approved policy; human accuracy review. No hiring or performance decision automation. |
Product keeps two teams; Technology keeps five; the other four departments keep one squad each. Coordination, skill ownership, approval and cover are assignments inside these teams, not new standing departments, roles or headcount. Department leaders retain accountability and designate real approvers before a pilot starts.
## 3. Skills governance — skills-governance
### A contract that travels, controls that stay authoritative
Maintain one catalog entry per stable skill ID, with immutable revisions and a concise portable `SKILL.md` body. Required content includes when to use and when not to use the skill, prerequisites, bounded steps, expected artifacts, verification, failure handling and escalation. The companion model defines the complete metadata contract: owner and independent reviewer, risk tier, supported clients and versions, dependencies, digest, provenance and license, evaluations, approval, review date, deprecation and rollback.
The release manifest pins the content, dependencies and adapter revisions by full immutable commit or digest. Names use an `eldorado-` prefix to reduce collisions. Friendly versions aid communication but never replace the digest. No arbitrary upstream automatic update is admitted into the approved bundle. Changes to scripts, resources or dependencies count as skill changes, not harmless documentation edits.
Adapters place content in the qualified client's discovery paths and implement only verified client-specific behavior. Qualification records the host, exact build, OS, model configuration, discovery, explicit invocation, collisions, disabled skills, permission behavior, context compaction and reload behavior. A common file format is not common precedence or a portable hooks API. Claude and Cline skill-name precedence differs from rule/memory behavior; Copilot support varies by surface. Test the actual build, not the brand name.
### Lifecycle and decision rights
A proposal names the problem, owner, data scope and testable outcome. Experimental revisions run only in approved disposable environments. Promotion requires independent review, static and executable dependency checks, per-client behavioral evaluations and a bounded canary. Approval records bind the exact artifact and permitted uses. Deprecated revisions carry a migration path and deadline; retired revisions are unavailable for new work. Any active revision can be quarantined immediately.
The author never supplies the only approval. Data & AI maintains catalog state; the owning team is accountable for content; Quality Engineering approves evaluation evidence; Architecture & Security approves higher-risk boundaries; Platform & IT verifies distribution and revocation. A business owner accepts workflow outcomes. These functions can be covered by existing staff, but incompatible author/approver duties must remain separate for the same release.
An exception contains its ID, skill/client revision, scope, business reason, risk owner, compensating checks, approver, start and expiry, affected users/projects and rollback route. It does not disable protected release, payment or identity controls. Expired or unverifiable exceptions deny privileged actions. Repeated exceptions trigger a supported-design change rather than permanent informal bypass.
### Distribution, stale clients and rollback
Stage a verified bundle atomically, check its digest, load it through the qualified adapter and read back its effective revision. Test already-open sessions; changing files is not proof of reloading instructions. Record immutable last-known-good content and adapter dependencies. Canary upgrades precede wider distribution.
A cached verified bundle may support explicitly permitted low-risk work if its model and tool dependencies are available. It does not make a cloud-backed client work offline. Stale or unsupported sessions cannot obtain centrally mediated privileged access. Reconnection revalidates support, revocation and policy before credentials are issued. Central artifact review protects delivery even when a laptop declaration is absent or false; a client report cannot prove which private prompt was actually followed.
Rollback restores the approved manifest and dependencies, invalidates affected sessions, revokes unsafe revisions, runs a smoke test and verifies effective state. It cannot undo data disclosure or commands already executed. Those require incident response.
## 4. Skills quality — skills-quality
Quality has four separate questions: was the right skill selected, was the procedure executed safely, did the artifact pass independent checks, and did the workflow produce useful outcomes? A successful invocation answers none of the remaining questions by itself.
The proposed qualification contract uses three tiers. R1 is read-only or drafting with nonsensitive approved material; R2 creates code or material with customer impact but no privileged external effect; R3 touches privileged, financial, sensitive-personal or production decisions. R3 remains human-authorized and out of the initial autonomous scope.
Every tier requires all mandatory deterministic safety, schema, authorization and provenance checks to pass, with no observed critical unauthorized action. An observed critical effect blocks promotion. The model gives proposed pilot evaluation floors and success thresholds; these are design defaults for owner ratification, not externally validated guarantees or approved service levels. If sample coverage, acceptance criteria or responsible reviewers are missing, qualification remains incomplete. No aggregate score can trade off a failed mandatory safety check.
Use sanitized, predeclared task sets containing positive triggers, near misses, negative cases and the normal workload mix. Compare no-skill, current-approved and candidate paths with the same starting artifacts; repeat stochastic trials and retain failed and abandoned attempts. Independently inspect output state. Deterministic tests grade checkable constraints; calibrated humans grade usefulness, maintainability and experience. A model judge is supplementary, versioned and checked against human disagreements, never the sole safety or promotion authority.
Include adversarial repository text, malicious referenced code, wrong permissions, client toggles, name shadowing, unsupported versions, hook timeout/crash/malformed response, stale sessions, offline recovery and rollback. Run central checks against forged/missing client declarations. Hook presence is not fail-closed enforcement: failure semantics must be tested on each client, and central authorization remains independent.
Report denominators, task mix, trial counts, per-client results and uncertainty. A passing benchmark is evidence within a tested envelope, not proof that all future tasks are safe. Material model, client, adapter or dependency changes trigger requalification.
## 5. Adoption — skills-adoption
Start with a small set of useful workflows chosen by the existing teams. Product Management and the departmental owner identify the outcome; Data & AI helps turn it into a skill; Quality Engineering establishes acceptance; Platform & IT qualifies the client path; Architecture & Security clears the data boundary. People & Administration Squad coordinates learning and accessible feedback.
Onboarding should teach people to select the right skill, inspect the actual artifact, recognize untrusted instructions, use the exception and incident routes and recover without AI. A practice task ends with verified work and an explanation of limits, not just a logged invocation. Demonstrate the same portable skill on the supported local clients without promising identical agent behavior.
Use short worked examples, peer pairing, office hours and a clear catalog search path. Gather reasons for non-use and rejected suggestions without penalizing them. Preserve a manual path. Training feedback improves the skill only through review, not automatic self-modification of the approved catalog. Departmental workflow outcomes matter more than uniform uptake.
No developer rankings, individual productivity scores, minimum invocation quotas or inference of effort from token counts are part of this strategy. Report cohort-level evidence with privacy protection and balanced quality, flow, risk and cost. A participant may reasonably stop using a skill that creates more work or uncertainty.
## 6. Measurement — measurement
### Decide what counts before counting
Use the machine-readable KPI catalog for exact definitions. Every report states its eligible population, observation window, numerator and denominator or distribution sample count, source coverage, exclusions and calculation version. Zero denominators return “not available.” Missing telemetry is unknown, not inactivity, compliance or success. Examples are explicitly synthetic and illustrative.
Begin with task acceptance, repository, CI, deployment and incident systems because they can independently establish outcomes. Add catalog/version inventory and task-level declarations. Client telemetry is supplementary and version-dependent; enabling rich tool details may collect sensitive commands and identifiers. Do not enable it merely to count skills. A normalized adapter may report approved skill identifiers without forwarding raw arguments.
Validated success uses all eligible initiated tasks whose predeclared observation window has matured. Failed and abandoned tasks remain nonsuccess. Unresolved or unobserved outcomes remain in the denominator, with known-outcome coverage shown alongside the conservative confirmed-success share. They are not relabeled as observed failure. A task cancelled before start is excluded; cancellation after start remains visible. Count a task once across retries and multiple skills. For timeliness, also show unfinished work and age; accepted-only lead-time distributions have survivorship limitations.
Track all five service-level DORA measures: commit-to-production lead time, deployment frequency, change fail rate, failed deployment recovery time and deployment rework rate. Do not substitute PR-open-to-merge time or all-incident MTTR. Pair them with matured escaped-quality rates, policy effects, total cost per accepted outcome, support coverage, adoption, human experience and catalog maintenance. Keep customer/business outcomes linked to their departmental hypotheses; do not infer conversion, trust or revenue improvement from delivery telemetry.
### Baseline and interpretation
Before a pilot, freeze eligibility and acceptance rules and verify event joins. Collect prospective no-skill/current-practice evidence over a complete comparable workflow cycle and until the preregistered precision/sample requirement is met. Do not select a convenient end date after seeing results. Retain task class, service, skill, client/model configuration and concurrent changes. A matched or randomized comparison may be used when practical and ethical; label observational trends as descriptive and report confounding, selection and uncertainty.
Elapsed time and human effort are different quantities. Parallel agents may reduce waiting while increasing review or compute. Include failed attempts and human review/rework in cost, do not multiply a multi-skill task's value across every invocation, and do not convert survey perceptions into proven time savings. Small or incomplete populations yield evidence limits, not an ROI percentage.
### Event and privacy contract
Use the event envelope and per-event required fields in the model. Source producer plus event ID deduplicates transport retries. Conflicting payloads under the same key are quarantined. Attempts have distinct IDs under one task. Domain IDs deduplicate deployments, defects and approvals even when multiple feeds describe them. Late arrivals produce versioned aggregate corrections; never silently overwrite a published result. Preserve occurrence and receipt times, reject impossible orderings and flag clock skew. Missing terminal events remain unresolved until the work owner reconciles them.
Many-to-many task/attempt/skill/client associations preserve exact immutable versions. Unknown version stays null with a reason; never assign the latest revision retroactively. Client declarations, server facts and self-reports remain separately labeled. A skill-loaded event is evidence of loading only. The minimal prototype can operate without actor identity except where a lawful, necessary adoption denominator requires pseudonymous cohort membership.
The privacy starting proposal is 30 days for raw structured analytics events and 12 months for aggregated trends, subject to the responsible owners' review. Keep the approved catalog/approval record for the supported revision's lifetime and its required audit period, which must be decided separately. Incident evidence uses a restricted, explicitly authorized hold and deletion schedule, not an indefinite analytics exception. Suppress reports below five people and prohibit drill-down or overlapping cohort slices that reconstruct identities. Five is a proposed minimum, not a guarantee against re-identification. No raw prompts, code, screens, customer cases or secrets are collected by default. Publish the purpose, lawful collection basis, access/deletion route and retention before collecting. Consent is not presumed to be an appropriate employment-law basis.
## 7. Maintenance — skills-maintenance
Every approved skill has an accountable owning team, independent reviewer, review due date, supported client matrix and rollback bundle. Review both usefulness and safety, not just metadata freshness. Proposed maximum review intervals are recorded by risk tier; incidents, dependency changes, model/client drift or repeated failures trigger earlier review.
Data & AI reviews catalog health with the owning teams. Quality Engineering samples accepted outputs and adds confirmed failures to regression sets without exposing sensitive content. Platform & IT watches unsupported, deprecated and revoked revisions and tests distribution recovery. Architecture & Security reviews emerging misuse and exception expiry. Remove redundant or unused entries deliberately, after examining whether poor discoverability or fit explains non-use.
A reported unsafe behavior opens an incident with artifact, policy and client versions. Architecture & Security coordinates containment; Platform & IT revokes authority, stops scheduling and quarantines affected revisions; the owning department handles customer or business consequences; Quality Engineering reproduces the failure; Data & AI coordinates catalog correction and trend analysis. Preserve minimal restricted evidence and involve People & Administration Squad for employee-data concerns and Finance & Accounting Squad for financial effects. Restart requires a reviewed fix, regression evidence, verified rollback/revocation and accountable human authorization. Closing the incident does not erase the historical failure from measurement.
## 8. Roadmap — roadmap
| Phase | Entry evidence | Work | Exit evidence and hold condition |
| --- | --- | --- | --- |
| Foundation | Strategy scope accepted for planning; existing teams identified. | Inventory actual client hosts/builds, provider/data terms, repositories, authority, owners and event availability; select low-risk workflows and metric definitions. | Named accountable people and independent reviewers, bounded pilot charter, forbidden actions, data/retention decision and reproducible baseline plan. Hold if accountability or lawful collection is unresolved. |
| Governed skills pilot | Foundation evidence approved; no privileged autonomous effects. | Build a small registry, immutable contract, client adapters, evaluations, coaching and synthetic measurement verification. | Qualified client/skill tuples, mandatory safety checks, independent task acceptance, reconciled coverage and user feedback. Hold or narrow scope if quality or data coverage is inadequate. |
| Reliable distribution | Pilot quality and usefulness support continuing. | Canary updates, effective-version readback, version locking, exception expiry, stale/offline handling and rollback. | Tested revocation and recovery, no bypass of central privileged gates, owners can support the catalog. Hold if effective versions or controls cannot be verified. |
| Logical SDLC coordination | Distribution, authority and measurement evidence stable. | Observe and assemble evidence first, then propose and route work within scoped identities and budgets. | Exact-artifact approvals, idempotency, duplicate/outage recovery, separation of duties, audit and kill-switch drills pass; humans can take over. No authority expansion based on usage alone. |
| Departmental expansion | Each department supplies a workflow owner, outcome, data boundary and capacity. | Add Operations, Marketing, Finance & Accounting and HR & Administration workflows with domain-specific review; Product and Technology continue quality improvement. | Departmental accepted outcomes, guardrails, total cost and experience justify continuation; sensitive decisions stay human. Stop or roll back workflows without adequate evidence. |
There are no invented launch dates, budgets, staffing commitments or guaranteed gains. Phase gates permit narrowing or stopping. Re-evaluate earlier phases when the underlying client, provider, data scope or authority changes.
## Internal architecture brief for implementer
Use a small overview with six groups: (1) accountable people and approved work; (2) one logical coordinator and durable ledger; (3) governed registry plus qualified adapters; (4) local client contributors; (5) independent repository/CI/release boundary with separate human approval; (6) audit/measurement feedback. Put external model-provider access across a labeled data/egress boundary, with approved routing. Show registry-to-coordinator and registry-to-local-client distribution; coordinator-to-work proposals; clients-to-repository proposals; CI-to-human approval evidence; approval-to-release service; resulting evidence-to-audit and audit-to-people/coordinator feedback. Distinguish proposal/evidence arrows from authorization arrows. No coordinator-to-production or laptop-to-production edge. Diagram alt text must state target status, human accountability and lack of direct production authority. Keep full contract detail in prose, not tiny diagram boxes. Organization page links identify existing teams, not a new hierarchy.
## Internal evidence mapping and open decisions
Source IDs below refer to `research/ai-adoption/sources.json`. They support principles and documented capabilities, not local deployment or approval. Organization mapping was read from `repositories/documentation/docs/organization/{structure,collaboration,sdlc}.mdx` at `14ba1aa1ef66c91525135f743d15c77e32db78d3`.
| Recommendation | Evidence | Design decision / limit |
| --- | --- | --- |
| Portable contract plus exact-client adapters | 1, 2, 4, 8, 9, 21, 26 | Content format is portable; precedence and enforcement are not. Vendored immutable distribution is the proposed default; no Gitea installer compatibility established. |
| Independent gates, least privilege and human authority | 3, 5, 6, 7, 15, 23 | Central authorization and kill switch are target controls; local hooks are supplementary and may fail open. |
| One logical coordinator, resilient task ledger and bounded authority | Task direction; 11, 15, 25 | Replica/idempotency/recovery topology is a design choice, not an observed installation or a vendor guarantee. |
| Exact existing-team ownership | Three organization pages; findings section 5 | Assignments extend the documented model without asserting named staffing, capacity or approval. |
| Immutable provenance, review, dependency checks and rollback | 1, 26, 15 | Approval digest and distribution manifest are proposed operational contracts. Pinning alone does not make content safe. |
| Layered repeated evaluation and independent output checks | 11, 15 | Risk tiers, evaluation floors, review intervals and proposed percentages are local design defaults requiring ratification, not thresholds from the literature. |
| Balanced KPIs and all five delivery measures | 12, 24 | Service/team evidence; no individual ranking or AI causal claim from activity. |
| Comparative baseline, effort/elapsed distinction and no forecast ROI | 16, 17, 18, 22 | Studies vary by context; abstract-only evidence for 17/18 and known 2026 selection/timing limitations. Do not reproduce percentages as expected gains. |
| Version-aware optional client telemetry | 19, 20, 23 | Actual licensing, settings and host coverage unknown; normalize only minimally necessary events. |
| Lifecycle ownership, privacy and gates before expansion | 13, 14, 25; 20 | NIST voluntary framework is not certification; 14 inspected at landing-page level. Retention and cohort floor are proposals for review. |
Open decisions (not blockers to writing target documentation; blockers to their corresponding rollout gates):
1. Platform & IT with Architecture & Security: actual supported hosts/builds/OSs, identity boundaries, authoritative CI/release controls, protected-policy administration and stale-bundle access rule.
2. Department leaders with Data & AI: named owner/reviewer/deputy coverage, pilot capacity and selected workflows; no new headcount is implied.
3. Architecture & Security with People & Administration Squad and the relevant data owner: provider processing terms, data classification, lawful telemetry basis, retention, incident holds and access/deletion handling. Obtain specialist legal advice where needed; no new permanent legal role is invented.
4. Quality Engineering with Data & AI and each workflow owner: ratify or replace the proposed risk-tier thresholds, task distributions, minimum samples, uncertainty criterion and quality follow-up windows before using them for promotion.
5. Finance & Accounting Squad: cost allocation, currency normalization, treatment of shared licenses, effort sampling and confidence required for investment decisions.
6. Product Management with departmental owners: business outcome hypotheses and whether a matched/randomized pilot is practical. No tool choice is coerced for an unreviewed experiment.
7. Platform & IT: private registry distribution method, digest verification, revocation semantics, coordinator recovery and feasible minimal adapters. Do not assume vendor-preview installation or Cline SDK telemetry is already available.
## Executable handoff and verification
`build_strategy_model.py` writes the structured contract and illustrative values. `sample_calculations.py` is an executable reference/test plan using only labeled synthetic in-memory fixtures. The implementation child should port the same assertions into the documentation repository's established test tooling and add a CLI/fixture interface, rather than treating this reference as a production collector. Required cases are declared in the model. Do not ship research files or raw event fixtures into public static assets inadvertently.
Acceptance for this synthesis: both requested files parse/exist; eight page IDs and all eleven existing teams are exact; every KPI has the full calculation/evidence/privacy contract; source IDs resolve; illustrative arithmetic is executed; synthetic edge-case tests pass; documentation repository remains unchanged. Publication, browser review, diagrams and production measurement infrastructure belong to the downstream implementation task, not this synthesis.
File diff suppressed because it is too large. Load diff
+7
View File
@@ -0,0 +1,7 @@
# Organization background research
The source/excerpt ledger and `reconstruction-snapshot.mdx` preserve the original public-evidence investigation. They are background working material, outside the published docs and static assets.
The reader-facing `docs/organization/structure.mdx` is an organizational operating model, not a research report or an employee roster. Per the user's presentation direction, it describes leadership, departments, teams and responsibilities directly without evidence markers, vacancies or citations. Internet research informs the model; this editorial change does not independently verify staffing, incumbent names or every reporting relationship.
The original snapshot records which categories and relationships were evidenced, job-advertised or inferred. Retain that provenance for future maintenance, without importing it into the published page, diagram metadata or search index.
@@ -0,0 +1,102 @@
---
title: Structure
description: Distinguish Eldorado’s publicly evidenced organization from advertised roles and planning assumptions.
---
import ArchifyDiagram from '@site/src/components/ArchifyDiagram';
**Research snapshot: 2026-10-02.** This is a public-evidence reconstruction for AI strategy planning, **not a verified employee roster** or an internally approved organization chart.
## What is known
Eldorado names **Vladas Jurkevičius — co-founder and CEO** and **Lukas Deksnys — co-founder and COO** on its official About page.[1]
Its official careers site uses seven hiring categories: **Management, Product, Technology, Operations, Marketing, Finance & Accounting, and HR & Administration**. Those labels are directly evidenced; treating them as six peer departments beneath Management is a planning arrangement, not a published reporting hierarchy.[4]
### Evidence legend
- **E — Publicly evidenced:** a named leader, team, function or careers category explicitly appears in an official source. This does not establish every reporting relationship.
- **J — Job-advertised:** a vacancy describes a role, mandate or reporting line. The position may be unfilled or newly created.
- **A — Assumed:** a proposed grouping or missing team boundary used for planning, not a claim about Eldorado’s current structure. Dashed connectors identify assumed placements.
## Department overview
<ArchifyDiagram name="organization-structure" title="Eldorado organization — evidence and assumptions" />
**Text alternative:** Management is shown above Product, Technology, Operations, Marketing, Finance & Accounting, and HR & Administration. All six placements beneath Management are assumed. The department names come from the careers taxonomy; the notes in the viewer identify evidenced teams and proposed subdivisions. This view does not assign every function directly to the CEO or COO.
## Departments and teams
The following separates **named teams** from **roles, capabilities and domains**. A function mentioned in a vacancy is not automatically a separately staffed team.
| Department / function | Publicly evidenced teams, roles or domains | Assumed completion for planning |
| --- | --- | --- |
| **Management** | **E:** CEO Vladas Jurkevičius and COO Lukas Deksnys. **J:** CTO and CMO are referenced as reporting managers.[1][8][12] CFO is advertised.[5] No incumbent names are inferred for those three roles. | **A:** a leadership coordination layer across the six functions. Exact executive reporting lines, Board membership and decision rights remain unverified. |
| **Product** | **E:** Order/Orders team; Categories design team, with Boosting & Currency scope; Data Team appears in the ranking Data Scientist vacancy metadata. Data classification and product design evidence.[9][13][14] **J:** Sellers and Operations product-owner domain, including Seller Portal, integrations, onboarding, balances/withdrawals, invoicing and support tooling.[15] | **A:** treat Sellers & Operations as a cross-functional product squad for planning, not a proven standalone team. Do not duplicate Data as a separate peer department. |
| **Technology** | **J:** Head of Engineering → Group Engineering Managers → Engineering Managers → engineers. AI Architect reports to CTO and starts solo. Technical Architect is named as an infrastructure/security partner; IT Engineer is advertised for identity, device management and internal SaaS.[6][8][10] | **A:** CTO → Head of Engineering. Proposed shared platform/reliability and security capability groups, with staffing and reporting unverified. Do not turn AI Architect into an established AI department. |
| **Operations** | **E:** Customer Support, Trust & Resolution, Risk & Fraud. **J:** QA Specialist (Operations) reports directly to COO; separate support/resolution Team Lead mandates cover specialist teams.[11][17][18] | **A:** COO ownership of the wider Operations function and its team-lead escalation chain. Only the QA Specialist’s direct COO line is explicit in the reviewed sources. |
| **Marketing** | **J:** Brand Team Lead reports to CMO. **E:** the wider Marketing team names Creative, SEO, PPC & Affiliate, Influencer, and CRM; brand scope includes organic social, community and brand partnerships.[12] | **A:** model these named disciplines as planning workstreams under Marketing. Separate team managers, headcounts and each discipline’s direct CMO line are not established. |
| **Finance & Accounting** | **E:** Finance team / hiring category. **J:** CFO remit includes planning, reporting, cash/capital, tax, compliance, financial risk and payments; it describes partnership with CEO and Board, not an explicit reporting line.[5] | **A:** FP&A; Accounting/Tax/Controls; Treasury/Payment Operations as three capability groups. These are inferred subdivisions, not proven staffed teams. Financial risk is not the same thing as the Operations Risk & Fraud team. |
| **HR & Administration** | **E:** Talent Acquisition team / role category. **J:** full-cycle IT and non-IT recruitment, partnering with hiring managers.[16] | **A:** People Operations and Office/Administration alongside Talent Acquisition. Separate teams and the HR leader’s reporting line remain unverified. |
The Sellers and Operations posting establishes a **product domain**, not a named Sellers department. Its mandate crosses seller-facing features, operational tooling and marketplace infrastructure.[15]
## Reporting lines supported by vacancies
<ArchifyDiagram name="organization-reporting" title="Eldorado reporting lines — advertised and assumed" />
**Read arrows as manager → role/team.** Solid **J** connectors are explicitly described in vacancies. The dashed **A** link between CTO and Head of Engineering is the only inferred link in this diagram. Separate rows are not connected because their upward reporting lines were not established.
| Manager / leader | Role or team below it | Status and scope |
| --- | --- | --- |
| Head of Engineering | Group Engineering Manager | **J:** explicitly reports directly to Head of Engineering; the vacancy describes managing 4–5 Engineering Managers.[6] |
| Group Engineering Manager | Engineering Manager | **J:** explicitly named manager in the Engineering Manager vacancy.[7] |
| Engineering Manager | Software engineers | **J:** leads a team of around five engineers. This is the advertised span, not a count of all current engineering teams.[7] |
| CTO | AI Architect | **J:** explicitly direct; a new individual-contributor role, starting solo with a possible future team.[8] |
| COO | QA Specialist (Operations) | **J:** explicitly direct; independent quality work across Customer Support, Trust & Resolution, and Risk & Fraud.[11] |
| CMO | Brand Team Lead | **J:** explicitly direct; brand, organic social, community and brand partnerships remit.[12] |
| Customer Support / Trust & Resolution Team Leads | Their specialists | **J:** support/resolution leadership advert describes teams of 5–10 specialists; the resolution-specific advert also states 5–10. Their own upward manager is not stated.[17][18] |
| CTO | Head of Engineering | **A:** plausible technology reporting arrangement, retained only as a planning assumption. |
Product Owner, Tech Lead and Product Designer are described as collaborating on product direction. That is evidence of cross-functional work, **not** evidence that one line-manages the others.[13][14]
## What this means for AI strategy
**Research interpretation, not an approved roadmap:** use the evidenced organizational owners rather than inventing one central AI department.
- **Engineering enablement:** the AI Architect vacancy focuses on engineering output, adoption, standards and security under the CTO—not ownership of every business AI initiative.[8]
- **Marketplace intelligence:** Product/Data has an advertised ranking and recommendations remit, including experimentation and learning-to-rank.[9]
- **Operations quality:** operational QA explicitly includes auditing AI-handled support and LLM-assisted scoring; customer support leadership includes AI-assisted support and automation.[11][17]
- **Planning assumption:** coordinate these three areas with Product, Operations, Technology and Finance owners. Whether to create a cross-company AI steering group is a future decision, not a finding about current organization.
## Limits and validation questions
- **No complete internal chart was found in the reviewed public sources.** This is not a claim that none exists. Research prioritized official About, careers, vacancy and legal pages; it is not an exhaustive employee or vacancy census.
- **Job postings are time-bound recruitment evidence.** A vacancy can describe a future role or team. Names, staffing, budgets and every executive reporting line still need company confirmation.
- **No reliable total headcount is asserted.** The careers homepage itself contains both “150+” and “200+” descriptions; do not use either to derive department sizes.[2]
- **Legal entities are not departments.** The Terms of Service names GWD Processing FZCO, Eldorado Sales LLC and Halfway House OÜ collectively as Eldorado. This establishes contracting language, not a parent/subsidiary hierarchy or the employing entity of each team.[3]
- **Keep scope distinctions:** marketplace Risk & Fraud, financial risk, AI security, and corporate compliance may collaborate but should not be collapsed into one assumed reporting chain.
Before using this as an operating model, confirm: **(1)** which advertised roles are filled, **(2)** executive and functional reporting lines, **(3)** product squad membership and engineering matrix, **(4)** ownership of Payments, Legal/Compliance and Data, and **(5)** the real team roster and capacity. Replace each **A** only when evidence is supplied.
## Sources
[1] https://www.eldorado.gg/about-us — Eldorado About us
[2] https://careers.eldorado.gg — Eldorado Careers homepage
[3] https://www.eldorado.gg/terms-of-service — Eldorado Terms of Service
[4] https://careers.eldorado.gg/jobs — Eldorado Jobs — official hiring categories
[5] https://careers.eldorado.gg/jobs/8394692-chief-financial-officer — Chief Financial Officer
[6] https://careers.eldorado.gg/jobs/8376024-group-engineering-manager — Group Engineering Manager
[7] https://careers.eldorado.gg/jobs/8290012-engineering-manager — Engineering Manager
[8] https://careers.eldorado.gg/jobs/8175478-ai-architect — AI Architect
[9] https://careers.eldorado.gg/jobs/8261309-data-scientist-offer-ranking-recommendations — Data Scientist (Offer Ranking & Recommendations)
[10] https://careers.eldorado.gg/jobs/8363463-it-engineer — IT Engineer
[11] https://careers.eldorado.gg/jobs/8358613-qa-specialist-operations — QA Specialist (Operations)
[12] https://careers.eldorado.gg/jobs/8450323-brand-team-lead — Brand Team Lead
[13] https://careers.eldorado.gg/jobs/8404928-product-designer-orders-team — Product Designer (Orders team)
[14] https://careers.eldorado.gg/jobs/8253165-product-designer — Product Designer
[15] https://careers.eldorado.gg/jobs/8389376-product-owner-sellers-and-operations — Product Owner (Sellers and Operations)
[16] https://careers.eldorado.gg/jobs/8487069-talent-acquisition-partner-junior-mid — Talent Acquisition Partner (Junior - Mid)
[17] https://careers.eldorado.gg/jobs/8381712-customer-support-team-lead — Customer Support Team Lead
[18] https://careers.eldorado.gg/jobs/8483510-trust-resolution-team-lead — Trust & Resolution Team Lead
+261
View File
@@ -0,0 +1,261 @@
{
"version": 1,
"sources": [
{
"id": 1,
"url": "https://www.eldorado.gg/about-us",
"title": "Eldorado About us",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Eldorado.gg was founded by two Lithuanian co-founders, Vladas Jurkevičius (CEO) and Lukas Deksnys (COO).",
"added": "2026-10-02"
}
]
},
{
"id": 2,
"url": "https://careers.eldorado.gg",
"title": "Eldorado Careers homepage",
"accessed": "2026-10-02",
"quotes": [
{
"text": "A global team of 150+ professionals shaping the future of in-game commerce at Eldorado.gg",
"added": "2026-10-02"
},
{
"text": "A team of 200+ professionals",
"added": "2026-10-02"
}
]
},
{
"id": 3,
"url": "https://www.eldorado.gg/terms-of-service",
"title": "Eldorado Terms of Service",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Together, GWD Processing FZCO, Eldorado Sales LLC and Halfway House OÜ are collectively referred to in this Agreement as “Eldorado,” unless otherwise specified.",
"added": "2026-10-02"
}
]
},
{
"id": 4,
"url": "https://careers.eldorado.gg/jobs",
"title": "Eldorado Jobs — official hiring categories",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Finance & Accounting (1)\nHR & Administration (1)\nMarketing (2)\nOperations (5)\nProduct (3)\nTechnology (9)\nManagement",
"added": "2026-10-02"
}
]
},
{
"id": 5,
"url": "https://careers.eldorado.gg/jobs/8394692-chief-financial-officer",
"title": "Chief Financial Officer",
"accessed": "2026-10-02",
"quotes": [
{
"text": "This is a key executive role with a broad mandate across financial planning, reporting, cash and capital management, tax, compliance, risk, payments and the Finance team.",
"added": "2026-10-02"
}
]
},
{
"id": 6,
"url": "https://careers.eldorado.gg/jobs/8376024-group-engineering-manager",
"title": "Group Engineering Manager",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We're growing and looking for a Group Engineering Manager to lead an engineering group of several teams, manage 4-5 Engineering Managers, and report directly to Head of Engineering.",
"added": "2026-10-02"
}
]
},
{
"id": 7,
"url": "https://careers.eldorado.gg/jobs/8290012-engineering-manager",
"title": "Engineering Manager",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We’re growing and looking for an Engineering Manager to lead a team of around five software engineers and report to a Group Engineering Manager.",
"added": "2026-10-02"
}
]
},
{
"id": 8,
"url": "https://careers.eldorado.gg/jobs/8175478-ai-architect",
"title": "AI Architect",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We're creating a new role at Eldorado.gg: AI Architect, reporting directly to the CTO.",
"added": "2026-10-02"
},
{
"text": "You'll start solo: this is an individual-contributor role on paper, with outsized influence in practice. No direct reports required from day one, but a mandate that touches the whole organization.",
"added": "2026-10-02"
},
{
"text": "Own the security posture of AI usage across engineering, in partnership with Technical Architect on infra and security.",
"added": "2026-10-02"
}
]
},
{
"id": 9,
"url": "https://careers.eldorado.gg/jobs/8261309-data-scientist-offer-ranking-recommendations",
"title": "Data Scientist (Offer Ranking & Recommendations)",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Team\nProduct\nRole\nData Team",
"added": "2026-10-02"
}
]
},
{
"id": 10,
"url": "https://careers.eldorado.gg/jobs/8363463-it-engineer",
"title": "IT Engineer",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Own the joiner–mover–leaver process end to end;",
"added": "2026-10-02"
}
]
},
{
"id": 11,
"url": "https://careers.eldorado.gg/jobs/8358613-qa-specialist-operations",
"title": "QA Specialist (Operations)",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Our Operations teams - Customer Support, Trust & Resolution, and Risk & Fraud - are scaling fast, and each measures quality its own way.",
"added": "2026-10-02"
},
{
"text": "We're creating a new role at Eldorado.gg: QA Specialist (Operations), reporting directly to the COO.",
"added": "2026-10-02"
},
{
"text": "Audit AI-handled support conversations and root-cause failures into content, routing, or model issues;",
"added": "2026-10-02"
}
]
},
{
"id": 12,
"url": "https://careers.eldorado.gg/jobs/8450323-brand-team-lead",
"title": "Brand Team Lead",
"accessed": "2026-10-02",
"quotes": [
{
"text": "You will manage a team of several people and report to our CMO.",
"added": "2026-10-02"
},
{
"text": "Lead and develop a high-performing team spanning brand, organic social, community and brand partnerships;",
"added": "2026-10-02"
},
{
"text": "Collaborate closely with the wider Marketing team (Creative, SEO, PPC & Affiliate, Influencer, CRM) as well as Product, Data, and other cross-functional stakeholders.",
"added": "2026-10-02"
}
]
},
{
"id": 13,
"url": "https://careers.eldorado.gg/jobs/8404928-product-designer-orders-team",
"title": "Product Designer (Orders team)",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We're growing and looking for a Product Designer to join our Order team.",
"added": "2026-10-02"
},
{
"text": "Shape product direction as an equal stakeholder alongside the Product Owner and Tech Lead, owning the end-to-end design process for the order page, dispute flows, and broader post-purchase delivery experience;",
"added": "2026-10-02"
}
]
},
{
"id": 14,
"url": "https://careers.eldorado.gg/jobs/8253165-product-designer",
"title": "Product Designer",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We’re growing and looking for a Product Designer to join our Categories design team.",
"added": "2026-10-02"
}
]
},
{
"id": 15,
"url": "https://careers.eldorado.gg/jobs/8389376-product-owner-sellers-and-operations",
"title": "Product Owner (Sellers and Operations)",
"accessed": "2026-10-02",
"quotes": [
{
"text": "You’ll own the seller platform domain — the systems and tools that power how sellers operate on Eldorado.",
"added": "2026-10-02"
},
{
"text": "This includes Seller Portal and dashboards, direct seller integrations, onboarding and verification tools, balances and withdrawals, invoicing, and customer support tooling.",
"added": "2026-10-02"
}
]
},
{
"id": 16,
"url": "https://careers.eldorado.gg/jobs/8487069-talent-acquisition-partner-junior-mid",
"title": "Talent Acquisition Partner (Junior - Mid)",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Team\nHR & Administration\nRole\nTalent Acquisition",
"added": "2026-10-02"
}
]
},
{
"id": 17,
"url": "https://careers.eldorado.gg/jobs/8381712-customer-support-team-lead",
"title": "Customer Support Team Lead",
"accessed": "2026-10-02",
"quotes": [
{
"text": "We’re growing our Customer Operations function and looking for two experienced Team Leads to join our Customer Support and Trust & Resolution teams.",
"added": "2026-10-02"
},
{
"text": "Explore AI-assisted support and automation opportunities;",
"added": "2026-10-02"
}
]
},
{
"id": 18,
"url": "https://careers.eldorado.gg/jobs/8483510-trust-resolution-team-lead",
"title": "Trust & Resolution Team Lead",
"accessed": "2026-10-02",
"quotes": [
{
"text": "Lead, coach, and grow a team of 5-10 Trust & Resolution Specialists;",
"added": "2026-10-02"
}
]
}
]
}
+143
View File
@@ -0,0 +1,143 @@
#!/usr/bin/env node
/** SYNTHETIC reference only. No collectors, network access or production authorization.
* Input is a deliberately simplified, reconciled fixture, NOT a production event schema.
* Conflicts fail closed (no output); reconciliation/quarantine storage is not implemented.
*/
import { readFileSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
export const LABEL = 'SYNTHETIC — illustrative, not Eldorado observations';
export const CALCULATION_VERSION = 'synthetic-1.0.0';
export const ratio = (n, d, scale = 100) => d === 0 ? null : n / d * scale;
export function time(value) {
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|\+00:00)$/.test(value)) throw new Error('UTC timestamp required');
const t = Date.parse(value);
if (!Number.isFinite(t) || new Date(t).toISOString().slice(0,19) !== value.slice(0,19)) throw new Error('Invalid timestamp');
return t;
}
const canonical = value => JSON.stringify(value, (_, v) => v && typeof v === 'object' && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort(([a],[b]) => a.localeCompare(b))) : v);
const without = (row, keys) => Object.fromEntries(Object.entries(row).filter(([k]) => !keys.includes(k)));
export function dedup(events) {
const kept = new Map();
for (const e of events) {
if (!e.producer || !e.event_id) throw new Error('Missing transport identity');
if (time(e.received_at) < time(e.occurred_at)) throw new Error('Receipt before occurrence');
const key = canonical([e.producer, e.event_id]), prior = kept.get(key);
if (prior && canonical(without(prior,['received_at'])) !== canonical(without(e,['received_at']))) throw new Error('Conflicting duplicate requires quarantine');
if (!prior || time(e.received_at) < time(prior.received_at)) kept.set(key,e);
}
return [...kept.values()];
}
function result(n, d, as_of, {unit = '%', scale = 100, unknown = 0, exclusions = {}, ...extra} = {}) {
return {label:LABEL, as_of, calculation_version:CALCULATION_VERSION, numerator:n, denominator:d, unit, value_or_null:ratio(n,d,scale), unknown_count:unknown, exclusions, coverage:{known_percent:ratio(d-unknown,d)}, warnings:unknown ? ['Unknown records remain visible; this is not a failure/non-use rate.'] : [], ...extra};
}
function taskCohort(events, start, end, as_of) {
const lo=time(start), hi=time(end), cutoff=time(as_of);
if (lo >= hi) throw new Error('Invalid half-open window');
const starts=new Map(), terminal=new Map();
for (const e of dedup(events)) {
if (time(e.occurred_at)>cutoff || time(e.received_at)>cutoff) continue;
if (e.event_type.startsWith('task.') && !e.task_id) throw new Error('Missing task ID');
if (e.event_type==='task.started') {
if (!Number.isFinite(e.horizon_hours) || e.horizon_hours<0) throw new Error('Missing/invalid observation horizon');
const prior=starts.get(e.task_id);
if (prior && (time(prior.occurred_at)!==time(e.occurred_at) || prior.horizon_hours!==e.horizon_hours)) throw new Error('Conflicting semantic task start');
starts.set(e.task_id,e);
} else if (['task.accepted','task.failed','task.abandoned','task.cancelled'].includes(e.event_type)) {
if (e.event_type==='task.accepted' && e.independent_evidence!==true) continue;
const prior=terminal.get(e.task_id);
if (prior && prior.event_type!==e.event_type) throw new Error('Conflicting task terminal state');
if (!prior || time(e.occurred_at)<time(prior.occurred_at)) terminal.set(e.task_id,e);
}
}
for (const [id,e] of terminal) if (starts.has(id) && time(e.occurred_at)<time(starts.get(id).occurred_at)) throw new Error('Terminal before start');
const eligible = new Set([...starts].filter(([,e]) => time(e.occurred_at)>=lo && time(e.occurred_at)<hi && time(e.occurred_at)+e.horizon_hours*3600000<=cutoff).map(([id])=>id));
return {starts, terminal, eligible};
}
// Snapshot rows are pre-reconciled at fixture as_of. Optional receipt/occurrence
// timestamps support late-record tests; missing timestamps are NOT inferred events.
function unique(rows, fields=['id']) {
const map=new Map();
for (const r of rows) {
if (fields.some(k=>typeof r[k]!=='string' || !r[k])) throw new Error('Missing semantic identity');
const key=canonical(fields.map(k=>r[k])), payload=without(r,['producer','event_id','received_at']);
if (map.has(key) && canonical(without(map.get(key),['producer','event_id','received_at']))!==canonical(payload)) throw new Error('Semantic conflict requires reconciliation');
const prior=map.get(key);
if (!prior || (r.received_at && prior.received_at && time(r.received_at)<time(prior.received_at))) map.set(key,r);
}
return [...map.values()];
}
const visible = (r, as_of) => (!r.received_at || time(r.received_at)<=time(as_of)) && (!r.occurred_at || time(r.occurred_at)<=time(as_of));
const within = (t,start,end) => time(t)>=time(start) && time(t)<time(end);
function horizon(r) {
if (!Number.isFinite(r.followup_hours) || r.followup_hours<0) throw new Error('Missing/invalid followup horizon');
return r.followup_hours*3600000;
}
export function association(rows) {
const map=new Map();
for (const r of rows) {
const tuple={skill_id:r.skill_id,revision:r.revision ?? null,version_status:!Object.hasOwn(r,'revision') ? 'missing' : r.revision===null ? 'unknown' : 'known',client:r.client,missing_reason:r.missing_reason ?? null};
if (!map.has(r.task_id)) map.set(r.task_id,new Map());
map.get(r.task_id).set(canonical(tuple),tuple);
}
return Object.fromEntries([...map].map(([id, tuples])=>[id,[...tuples.values()].sort((a,b)=>canonical(a).localeCompare(canonical(b)))]));
}
export function deployments(rows) {
const effects=unique(rows,['service_id','deployment_id']).filter(r=>r.environment==='production' && r.production_effect===true);
return {effects:effects.length,successful:effects.filter(r=>r.succeeded===true).length,change_fail_rate:ratio(effects.filter(r=>r.intervention===true).length,effects.length),rework_rate:ratio(effects.filter(r=>r.planned===false && Boolean(r.incident_id)).length,effects.length)};
}
export function matureDefects(changes,defects,as_of,start='0001-01-01T00:00:00Z',end=as_of) {
const eligible=unique(changes).filter(c=>visible(c,as_of) && within(c.accepted_at,start,end) && time(c.accepted_at)+horizon(c)<=time(as_of));
const affected=new Set();
for (const d of unique(defects)) {
const c=eligible.find(c=>c.id===d.change_id);
if (c && d.confirmed===true && visible(d,as_of) && time(d.detected_at)>=time(c.accepted_at) && time(d.detected_at)<=time(c.accepted_at)+horizon(c)) affected.add(c.id);
}
return [affected.size,eligible.length];
}
export function survey(rows,invited,as_of) {
if (!Number.isInteger(invited) || invited<0) throw new Error('Invalid invitation count');
const all=unique(rows), valid=all.filter(r=>Number.isInteger(r.score) && r.score>=1 && r.score<=5);
if (all.length>invited) throw new Error('Responses exceed invitations');
const suppressed=valid.length<5;
return result(suppressed?null:valid.reduce((n,r)=>n+r.score,0),valid.length,as_of,{unit:'points (1–5)',scale:1,value_or_null:suppressed?null:ratio(valid.reduce((n,r)=>n+r.score,0),valid.length,1),suppressed,exclusions:{invalid_answers:all.length-valid.length},coverage:{response_percent:ratio(valid.length,invited)},distribution:suppressed?null:Object.fromEntries([1,2,3,4,5].map(s=>[s,valid.filter(r=>r.score===s).length])),warnings:suppressed?['People cohort below five suppressed.']:[]});
}
export function calculate(f) {
if (f.synthetic!==true || f.schema_version!=='1.0') throw new Error('Explicit synthetic schema 1.0 fixture required');
const {start,end,as_of}=f, kpis={};
kpis['validated-task-success']=success(f.events,start,end,as_of);
const trials=unique(f.selection).filter(r=>visible(r,as_of));
const scored=trials.filter(r=>typeof r.relevant==='boolean' && typeof r.activated==='boolean');
const tp=scored.filter(r=>r.relevant && r.activated).length;
for (const [id,den] of [['selection-precision',scored.filter(r=>r.activated).length],['selection-recall',scored.filter(r=>r.relevant).length]]) {
kpis[id]=result(tp,den,as_of,{unknown:trials.length-scored.length,coverage:{scorable_percent:ratio(scored.length,trials.length)},exclusions:{unscorable:trials.length-scored.length}});
}
const effects=unique(f.deployments,['service_id','deployment_id']).filter(r=>visible(r,as_of) && r.environment==='production' && r.production_effect===true && within(r.deployed_at,start,end));
const mature=effects.filter(r=>time(r.deployed_at)+horizon(r)<=time(as_of));
kpis['change-fail-rate']=result(mature.filter(r=>r.intervention===true).length,mature.length,as_of,{unknown:mature.filter(r=>typeof r.intervention!=='boolean').length,exclusions:{immature:effects.length-mature.length}});
kpis['deployment-rework-rate']=result(effects.filter(r=>r.planned===false && Boolean(r.incident_id)).length,effects.length,as_of,{unknown:effects.filter(r=>typeof r.planned!=='boolean' || (r.planned===false && !Object.hasOwn(r,'incident_id'))).length});
const [affected,accepted]=matureDefects(f.changes,f.defects,as_of,start,end);
const matureChanges=unique(f.changes).filter(r=>visible(r,as_of) && within(r.accepted_at,start,end) && time(r.accepted_at)+horizon(r)<=time(as_of));
kpis['escaped-quality-rate']=result(affected,accepted,as_of,{unknown:matureChanges.filter(r=>r.linkage_complete!==true).length,exclusions:{outside_window_immature_or_late:unique(f.changes).length-matureChanges.length},pending_triage_count:unique(f.defects).filter(r=>visible(r,as_of) && r.confirmed!==true).length});
const attempts=unique(f.attempts).filter(r=>visible(r,as_of));
kpis['policy-effects']=result(attempts.filter(r=>r.actual_effect===true).length,attempts.length,as_of,{unknown:attempts.filter(r=>typeof r.actual_effect!=='boolean').length,blocked_count:attempts.filter(r=>r.blocked===true).length});
const {eligible}=taskCohort(f.events,start,end,as_of);
const costs=unique(f.costs).filter(r=>visible(r,as_of) && (eligible.has(r.task_id) || r.task_id==='shared_pool'));
if (costs.some(r=>r.currency!==f.currency || !Number.isFinite(r.amount) || r.amount<0)) throw new Error('Invalid cost or mixed currency: approved conversion required');
const missing=f.expected_cost_buckets.filter(b=>!costs.some(r=>r.bucket===b));
kpis['cost-per-accepted-outcome']=result(costs.reduce((sum,r)=>sum+r.amount,0),kpis['validated-task-success'].numerator,as_of,{scale:1,unit:`${f.currency}/accepted outcome`,partial_estimate:missing.length>0,coverage:{missing_cost_buckets:missing},warnings:missing.length?['Partial estimate, not total cost; missing cost buckets.']:[]});
kpis['human-experience']=survey(f.survey.responses.filter(r=>visible(r,as_of)),f.survey.invited,as_of);
return {label:LABEL,as_of,calculation_version:CALCULATION_VERSION,window:{start,end},kpis,associations:association(f.associations),not_implemented:['supported-bundle-coverage','useful-adoption','change-lead-time','deployment-frequency','failed-deployment-recovery-time','maintenance-health','department-outcome'],limitations:['Synthetic simplified reconciled snapshots only; not the full production event envelope.','Snapshot rows without timestamps are assumed frozen at as_of, not missing-event evidence.','No causal attribution, uncertainty intervals, authorization or telemetry collection.','Survey implements primary item only; no cross-slice disclosure protection.','Defect severity strata and policy severity counts are not implemented.']};
}
if (process.argv[1] && import.meta.url===pathToFileURL(process.argv[1]).href) {
try {
if (process.argv.length>3) throw new Error('Usage: node scripts/ai-kpi.mjs [synthetic-fixture.json]');
const source=process.argv[2] ?? new URL('./fixtures/ai-kpi-synthetic.json',import.meta.url);
console.log(JSON.stringify(calculate(JSON.parse(readFileSync(source,'utf8'))),null,2));
} catch (error) { console.error(`Synthetic KPI calculation failed: ${error.message}`); process.exitCode=1; }
}
export function success(events,start,end,as_of) {
const {starts,terminal,eligible}=taskCohort(events,start,end,as_of);
const n=[...eligible].filter(id=>terminal.get(id)?.event_type==='task.accepted').length;
const unknown=[...eligible].filter(id=>!terminal.has(id)).length;
return result(n,eligible.size,as_of,{unknown, known_outcome_coverage:ratio(eligible.size-unknown,eligible.size), orphan_terminal_count:[...terminal.keys()].filter(id=>!starts.has(id)).length, excluded_start_count:starts.size-eligible.size, exclusions:{outside_window_or_immature:starts.size-eligible.size}});
}
+153
View File
@@ -0,0 +1,153 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { existsSync, readFileSync } from 'node:fs';
const moduleUrl = new URL('./ai-kpi.mjs', import.meta.url);
import { spawnSync } from 'node:child_process';
import * as api from './ai-kpi.mjs';
const fixture = () => JSON.parse(readFileSync(new URL('./fixtures/ai-kpi-synthetic.json', import.meta.url)));
test('semantic change duplicates preserve the complete quality report including exclusions', () => {
const original = fixture();
const repeated = fixture();
repeated.changes.push({...repeated.changes[0]});
assert.deepEqual(api.calculate(repeated).kpis['escaped-quality-rate'], api.calculate(original).kpis['escaped-quality-rate']);
});
test('CLI report exposes implemented KPI contracts and explicit omissions', async () => {
const m = await import(moduleUrl);
assert.equal(typeof m.calculate, 'function');
const r = m.calculate(fixture());
assert.equal(Object.keys(r.kpis).length, 9);
assert.equal(r.not_implemented.length, 7);
assert.equal(r.kpis['cost-per-accepted-outcome'].value_or_null, 60);
assert.equal(r.kpis['selection-precision'].value_or_null, 50);
assert.equal(r.kpis['selection-recall'].value_or_null, 50);
assert.equal(r.kpis['change-fail-rate'].value_or_null, 100);
assert.equal(r.kpis['deployment-rework-rate'].value_or_null, 100);
assert.equal(r.kpis['escaped-quality-rate'].value_or_null, 100);
assert.equal(r.kpis['policy-effects'].value_or_null, 50);
assert.equal(r.kpis['human-experience'].value_or_null, 4);
});
function event(id,task,kind,day=1,extra={}) {
const timestamp=`2026-01-${String(day).padStart(2,'0')}T00:00:00Z`;
return {producer:'synthetic',event_id:id,task_id:task,event_type:kind,occurred_at:timestamp,received_at:timestamp,...extra};
}
const run = (events,as_of=fixture().as_of) => api.success(events,fixture().start,fixture().end,as_of);
test('transport duplicates and redelivery use earliest receipt in either order',()=>{
const f=fixture(), duplicate={...f.events[0],received_at:'2026-01-06T00:00:00Z'};
assert.deepEqual(run([...f.events,f.events[0]]),run(f.events));
assert.deepEqual(run([...f.events,duplicate]),run(f.events));
assert.deepEqual(run([duplicate,...f.events]),run(f.events));
});
test('transport conflicts fail closed',()=>assert.throws(()=>run([...fixture().events,{...fixture().events[0],task_id:'other'}]),/Conflicting duplicate/));
test('retry and cross-feed semantic duplicate never add tasks',()=>{
const f=fixture();
assert.equal(run([...f.events,event('retry','accepted','attempt.started',2),event('retry-fail','accepted','attempt.failed',2),{...f.events[0],producer:'mirror',event_id:'mirror-start'}]).denominator,4);
});
test('immature task and half-open end excluded',()=>{
const r=run([...fixture().events,event('boundary','boundary','task.started',5,{horizon_hours:0})]);
assert.equal(r.excluded_start_count,2);
assert.equal(r.denominator,4);
});
test('all implemented zero denominator values are null',()=>{
const f=fixture();
for (const k of ['events','selection','deployments','changes','defects','attempts','costs','associations']) f[k]=[];
f.survey={invited:0,responses:[]};
for (const r of Object.values(api.calculate(f).kpis)) assert.equal(r.value_or_null,null);
});
test('orphan terminal does not increase accepted count',()=>{
const r=run([...fixture().events,event('orphan','absent','task.accepted',2,{independent_evidence:true})]);
assert.equal(r.orphan_terminal_count,1); assert.equal(r.numerator,1);
});
test('late arrival restates only when received',()=>{
const events=[...fixture().events,event('late','unknown','task.accepted',3,{independent_evidence:true,received_at:'2026-01-06T00:00:00Z'})];
assert.equal(run(events).numerator,1);
assert.equal(run(events,'2026-01-06T00:00:00Z').numerator,2);
});
test('unknown, absent and mixed versions retained without latest inference',()=>{
const rows=['a','b','a',null].map(revision=>({task_id:'t',skill_id:'s',revision,client:'Cline'}));
rows.push({task_id:'t',skill_id:'s',client:'Cline'});
const r=api.association(rows).t;
assert.equal(r.length,4);
assert.equal(r.filter(x=>x.version_status==='known').length,2);
assert.deepEqual(new Set(r.filter(x=>x.revision===null).map(x=>x.version_status)),new Set(['missing','unknown']));
});
test('invalid timestamps, UTC offsets, receipt order and terminal order rejected',()=>{
for(const t of ['no','2026-02-30T00:00:00Z','2026-01-01T00:00:00+01:00']) assert.throws(()=>api.time(t));
assert.throws(()=>run([event('s','t','task.started',2,{horizon_hours:24}),event('a','t','task.accepted',1,{independent_evidence:true})]),/Terminal before start/);
assert.throws(()=>api.dedup([event('s','t','task.started',2,{received_at:'2026-01-01T00:00:00Z'})]),/Receipt before/);
});
test('missing horizon is not silently mature',()=>assert.throws(()=>run([event('s','t','task.started')]),/horizon/));
test('conflicting terminal and semantic start require reconciliation',()=>{
assert.throws(()=>run([...fixture().events,event('conflict','accepted','task.failed',3)]),/Conflicting task terminal/);
assert.throws(()=>run([...fixture().events,event('conflict','accepted','task.started',2,{horizon_hours:48})]),/semantic task start/);
});
test('no independent acceptance evidence means unknown, not accepted',()=>{
const r=run([event('s','t','task.started',1,{horizon_hours:24}),event('a','t','task.accepted',2)]);
assert.equal(r.numerator,0); assert.equal(r.unknown_count,1);
});
test('pre-start cancellation excluded; post-start cancellation remains denominator',()=>{
const r=run([event('pre','never','task.cancelled',1,{before_start:true}),event('s','started','task.started',1,{horizon_hours:24}),event('post','started','task.cancelled',2,{before_start:false})]);
assert.deepEqual([r.numerator,r.denominator,r.unknown_count],[0,1,0]);
});
test('deployment retries collapse; unknown rework not treated as planned',()=>{
const row=fixture().deployments[0];
assert.deepEqual(api.deployments([row,{...row,producer:'mirror',event_id:'2'}]),{effects:1,successful:1,change_fail_rate:100,rework_rate:100});
assert.equal(api.deployments([{...row,planned:null}]).rework_rate,0);
const f=fixture(); f.deployments[0].planned=null;
const r=api.calculate(f).kpis['deployment-rework-rate'];
assert.equal(r.denominator,1); assert.equal(r.unknown_count,1);
assert.throws(()=>api.deployments([row,{...row,planned:true}]),/Semantic conflict/);
});
test('mature deployment denominator retains unsuccessful production effects',()=>{
const f=fixture(); f.deployments[0].succeeded=false;
assert.equal(api.calculate(f).kpis['change-fail-rate'].denominator,1);
f.deployments[0].followup_hours=1000;
assert.equal(api.calculate(f).kpis['change-fail-rate'].denominator,0);
});
test('mature defect followup is linked, bounded and deduplicated',()=>{
const f=fixture();
f.changes.push({id:'new',accepted_at:'2026-01-04T00:00:00Z',followup_hours:48});
f.defects.push({...f.defects[0],id:'new-defect',change_id:'new'});
assert.deepEqual(api.matureDefects(f.changes,[...f.defects,f.defects[0]],f.as_of),[1,1]);
f.defects[0].detected_at='2026-01-04T00:00:00Z';
assert.deepEqual(api.matureDefects(f.changes,f.defects,f.as_of),[0,1]);
});
test('cost includes failed attempts, flags partial buckets and rejects mixed currencies',()=>{
const f=fixture(); let r=api.calculate(f).kpis['cost-per-accepted-outcome'];
assert.equal(r.numerator,60); assert.equal(r.partial_estimate,true);
f.costs.push({...f.costs[0]});
assert.equal(api.calculate(f).kpis['cost-per-accepted-outcome'].numerator,60);
f.costs.pop(); // Currency validation is separate from conflicting duplicate validation.
f.costs[0].currency='USD'; assert.throws(()=>api.calculate(f),/mixed currency/);
});
test('blocked attempts are not prohibited effects; unknown audits remain denominator',()=>{
const f=fixture(); f.attempts.push({id:'unknown'});
const r=api.calculate(f).kpis['policy-effects'];
assert.deepEqual([r.numerator,r.denominator,r.blocked_count,r.unknown_count],[1,3,1,1]);
});
test('survey suppresses small groups including sum and distribution',()=>{
const f=fixture(), r=api.survey(f.survey.responses.slice(0,4),10,f.as_of);
assert.equal(r.value_or_null,null); assert.equal(r.numerator,null); assert.equal(r.distribution,null);
});
test('required output fields accompany every implemented KPI',()=>{
const required=['label','as_of','calculation_version','numerator','denominator','unit','value_or_null','unknown_count','exclusions','coverage','warnings'];
for (const r of Object.values(api.calculate(fixture()).kpis)) for(const key of required) assert.ok(Object.hasOwn(r,key),key);
});
test('non-synthetic and unknown fixture schemas rejected',()=>{
assert.throws(()=>api.calculate({...fixture(),synthetic:false}),/synthetic/);
assert.throws(()=>api.calculate({...fixture(),schema_version:'future'}),/schema/);
});
test('CLI executes default and explicit fixture and fails visibly for missing input',()=>{
for(const args of [[],[new URL('./fixtures/ai-kpi-synthetic.json',import.meta.url).pathname]]) {
const r=spawnSync(process.execPath,[moduleUrl.pathname,...args],{encoding:'utf8'});
assert.equal(r.status,0,r.stderr); assert.equal(JSON.parse(r.stdout).kpis['validated-task-success'].value_or_null,25);
}
const r=spawnSync(process.execPath,[moduleUrl.pathname,'/nonexistent/synthetic-fixture.json'],{encoding:'utf8'});
assert.equal(r.status,1); assert.equal(r.stdout,''); assert.match(r.stderr,/failed/);
});
test('synthetic task calculator exists and retains failed, abandoned, unknown tasks', async () => {
assert.ok(existsSync(moduleUrl), 'calculator implementation is required');
const { success } = await import(moduleUrl);
const f = JSON.parse(readFileSync(new URL('./fixtures/ai-kpi-synthetic.json', import.meta.url)));
const r = success(f.events, f.start, f.end, f.as_of);
assert.deepEqual([r.numerator, r.denominator, r.unknown_count, r.value_or_null, r.known_outcome_coverage], [1,4,1,25,75]);
});
+29
View File
@@ -0,0 +1,29 @@
import {readdirSync, readFileSync, mkdirSync, copyFileSync, renameSync, rmSync} from 'node:fs';
import {join} from 'node:path';
import {execFileSync} from 'node:child_process';
const cli = 'vendor/archify/bin/archify.mjs';
const types = new Set(['architecture','workflow','sequence','dataflow','lifecycle']);
// This directory is generated-only and ignored by Git. Remove stale artifacts.
rmSync('static/diagrams', {recursive: true, force: true});
mkdirSync('static/diagrams', {recursive: true});
mkdirSync('.cache/archify', {recursive: true});
const names = new Set();
for (const file of readdirSync('diagrams').sort()) {
const match = /^([a-z0-9]+(?:-[a-z0-9]+)*)\.([a-z]+)\.json$/.exec(file);
if (!match || !types.has(match[2])) throw new Error(`Unsupported diagram source: ${file}`);
const [,name,type] = match;
if (names.has(name)) throw new Error(`Duplicate diagram output: ${name}`);
names.add(name);
const input = join('diagrams',file);
const spec = JSON.parse(readFileSync(input,'utf8'));
const output = `static/diagrams/${name}/index.html`;
mkdirSync(`static/diagrams/${name}`, {recursive: true});
if (spec.diagram_type !== type || spec.meta.output !== output) throw new Error(`Diagram type/output mismatch: ${file}`);
const run = args => execFileSync(process.execPath,[cli,...args],{stdio:'inherit',env:{...process.env,ARCHIFY_UPDATE_CHECK_DISABLED:'1'}});
run(['validate',type,input,'--quality','showcase','--json']);
run(['deliver',type,input,output,'--quality','showcase','--json']);
run(['check',output,'--json','--require-provenance']);
renameSync(`static/diagrams/${name}/index.delivery.json`, `.cache/archify/${name}.delivery.json`);
}
copyFileSync('vendor/archify/LICENSE','static/diagrams/ARCHIFY-LICENSE.txt');
copyFileSync('vendor/archify/THIRD_PARTY_NOTICES.md','static/diagrams/THIRD-PARTY-NOTICES.md');
+37
View File
@@ -0,0 +1,37 @@
{
"synthetic": true,
"label": "SYNTHETIC — illustrative, not Eldorado observations",
"schema_version": "1.0",
"start": "2026-01-01T00:00:00Z",
"end": "2026-01-05T00:00:00Z",
"as_of": "2026-01-05T00:00:00Z",
"events": [
{"producer":"synthetic","event_id":"sa","task_id":"accepted","event_type":"task.started","occurred_at":"2026-01-01T00:00:00Z","received_at":"2026-01-01T00:00:00Z","horizon_hours":48},
{"producer":"synthetic","event_id":"sf","task_id":"failed","event_type":"task.started","occurred_at":"2026-01-01T00:00:00Z","received_at":"2026-01-01T00:00:00Z","horizon_hours":48},
{"producer":"synthetic","event_id":"sb","task_id":"abandoned","event_type":"task.started","occurred_at":"2026-01-01T00:00:00Z","received_at":"2026-01-01T00:00:00Z","horizon_hours":48},
{"producer":"synthetic","event_id":"su","task_id":"unknown","event_type":"task.started","occurred_at":"2026-01-01T00:00:00Z","received_at":"2026-01-01T00:00:00Z","horizon_hours":48},
{"producer":"synthetic","event_id":"a","task_id":"accepted","event_type":"task.accepted","occurred_at":"2026-01-02T00:00:00Z","received_at":"2026-01-02T00:00:00Z","independent_evidence":true},
{"producer":"synthetic","event_id":"f","task_id":"failed","event_type":"task.failed","occurred_at":"2026-01-02T00:00:00Z","received_at":"2026-01-02T00:00:00Z"},
{"producer":"synthetic","event_id":"b","task_id":"abandoned","event_type":"task.abandoned","occurred_at":"2026-01-02T00:00:00Z","received_at":"2026-01-02T00:00:00Z"},
{"producer":"synthetic","event_id":"new","task_id":"immature","event_type":"task.started","occurred_at":"2026-01-04T00:00:00Z","received_at":"2026-01-04T00:00:00Z","horizon_hours":48}
],
"selection": [
{"id":"trial-a","relevant":true,"activated":true},
{"id":"trial-b","relevant":false,"activated":true},
{"id":"trial-c","relevant":true,"activated":false},
{"id":"trial-d","relevant":null,"activated":null}
],
"deployments": [{"service_id":"service-a","deployment_id":"deploy-a","environment":"production","production_effect":true,"succeeded":true,"intervention":true,"planned":false,"incident_id":"incident-a","deployed_at":"2026-01-02T00:00:00Z","followup_hours":24}],
"changes": [{"id":"change-a","accepted_at":"2026-01-01T00:00:00Z","followup_hours":48,"linkage_complete":true}],
"defects": [{"id":"defect-a","change_id":"change-a","confirmed":true,"detected_at":"2026-01-02T00:00:00Z"}],
"attempts": [{"id":"attempt-a","actual_effect":false,"blocked":true},{"id":"attempt-b","actual_effect":true,"blocked":false}],
"costs": [{"id":"cost-a","task_id":"accepted","amount":40,"currency":"EUR","bucket":"compute"},{"id":"cost-f","task_id":"failed","amount":20,"currency":"EUR","bucket":"compute"}],
"currency":"EUR",
"expected_cost_buckets":["compute","licenses","tooling","review","rework"],
"survey": {"invited":10,"responses":[{"id":"token-a","score":4},{"id":"token-b","score":5},{"id":"token-c","score":3},{"id":"token-d","score":4},{"id":"token-e","score":4}]},
"associations": [
{"task_id":"accepted","skill_id":"synthetic-skill","revision":"revision-a","client":"Cline"},
{"task_id":"accepted","skill_id":"synthetic-skill","revision":"revision-b","client":"Cline"},
{"task_id":"unknown","skill_id":"synthetic-skill","revision":null,"client":"Claude Code","missing_reason":"not_exposed"}
]
}
+143
View File
@@ -0,0 +1,143 @@
import {test} from 'node:test';
import {createHash} from 'node:crypto';
import assert from 'node:assert/strict';
import {readFileSync, existsSync, mkdtempSync, mkdirSync, writeFileSync, symlinkSync, unlinkSync, rmSync, readdirSync} from 'node:fs';
import {tmpdir} from 'node:os';
import {join, resolve} from 'node:path';
import {spawnSync} from 'node:child_process';
const read = p => readFileSync(p, 'utf8');
test('real build removes stale diagrams, keeps receipts private and rejects collisions', () => {
const root = mkdtempSync(join(tmpdir(), 'eldorado-diagrams-'));
try {
mkdirSync(join(root,'diagrams'));
symlinkSync(resolve('vendor'),join(root,'vendor'),'dir');
const spec = JSON.parse(read('diagrams/organization-structure.architecture.json'));
const writeSpec = name => {
spec.meta.output = `static/diagrams/${name}/index.html`;
writeFileSync(join(root,`diagrams/${name}.architecture.json`),JSON.stringify(spec));
};
const build = () => spawnSync(process.execPath,[resolve('scripts/build-diagrams.mjs')],{cwd:root,encoding:'utf8'});
writeSpec('old');
let result = build(); assert.equal(result.status,0,result.stderr);
assert(existsSync(join(root,'static/diagrams/old/index.html')));
assert(!existsSync(join(root,'static/diagrams/old.json')));
assert(!readdirSync(join(root,'static/diagrams'),{recursive:true}).some(p=>p.endsWith('.delivery.json')));
unlinkSync(join(root,'diagrams/old.architecture.json'));
writeSpec('new');
result=build(); assert.equal(result.status,0,result.stderr);
assert(!existsSync(join(root,'static/diagrams/old')));
assert(!existsSync(join(root,'static/diagrams/old.json')));
writeFileSync(join(root,'diagrams/new.workflow.json'),JSON.stringify(spec));
result=build(); assert.notEqual(result.status,0);
assert(result.stderr.includes('Duplicate diagram output'));
} finally {rmSync(root,{recursive:true,force:true});}
});
test('vendored source matches the reviewed upstream snapshot', () => {
const checksums = JSON.parse(read('vendor/archify-checksums.json'));
for (const [path, hash] of Object.entries(checksums)) {
assert.equal(createHash('sha256').update(readFileSync(`vendor/archify/${path}`)).digest('hex'), hash, path);
}
});
test('three independent section menus and Structure first', () => {
const config = read('docusaurus.config.ts');
for (const section of ['Organization', 'Strategy', 'Contribution']) assert(config.includes(`label: '${section}'`));
assert(!config.includes("label: 'Documentation'"));
assert(!config.includes("label: 'Maintainer guide'"));
assert(read('sidebars/organization.ts').includes("organizationSidebar: ['structure', 'collaboration', 'sdlc']"));
for (const section of ['organization','strategy','contribution']) assert(config.includes(`id: '${section}'`));
});
test('Archify is pinned, licensed and rebuilt, not a remote embed', () => {
const provenance = JSON.parse(read('vendor/archify-provenance.json'));
assert.equal(provenance.commit, 'd5a1333d7447c866a765adac7d4d062f2f02e4d2');
assert(read('vendor/archify/LICENSE').includes('MIT License'));
const scripts = JSON.parse(read('package.json')).scripts;
assert(scripts.build.startsWith('pnpm diagrams:build'));
const wrapper = read('src/components/ArchifyDiagram/index.tsx');
assert(wrapper.includes('sandbox="allow-scripts allow-downloads"'));
assert(!wrapper.includes('allow-same-origin'));
assert(wrapper.includes('useBaseUrl'));
assert(wrapper.includes('showModal()') && wrapper.includes('onClose='));
assert(!wrapper.includes('target="_blank"'));
assert(wrapper.includes('Close full-screen diagram'));
assert(!wrapper.includes('JSON source') && !wrapper.includes('download>'));
assert(!wrapper.includes('Open full viewer'));
assert(wrapper.includes('aria-label={`Expand ${title}`}'));
assert(wrapper.includes('aria-hidden="true"'));
assert(read('src/css/custom.css').includes('justify-content: flex-end'));
});
test('Product owns two teams and Technology owns five separate teams', () => {
const spec = JSON.parse(read('diagrams/organization-structure.architecture.json'));
const page = read('docs/organization/structure.mdx');
assert(!/shared-delivery|squad-/i.test(JSON.stringify(spec)));
assert(!/shared squads|share a delivery structure|cross-functional teams/i.test(page));
for (const [dept, count, heading, next] of [['product',2,'Product','Technology'],['technology',5,'Technology','Operations']]) {
const teams = spec.components.filter(n => n.id.startsWith(`${dept}-team-`));
assert.equal(teams.length, count);
const section = page.split(`### ${heading}\n`)[1].split(`### ${next}\n`)[0];
const rows = section.split('\n').filter(l => l.startsWith('| ')).slice(2);
assert.equal(rows.length, count);
for (const team of teams) {
const parents = spec.connections.filter(e => e.to === team.id);
assert.deepEqual(parents.map(e => e.from), [dept]);
assert(rows.some(row => row.startsWith(`| ${team.label} |`)));
}
}
});
test('departments share one row and each has delivery teams below it', () => {
const spec = JSON.parse(read('diagrams/organization-structure.architecture.json'));
const page = read('docs/organization/structure.mdx');
const departments = ['product', 'technology', 'operations', 'marketing', 'finance', 'people'];
const nodes = new Map(spec.components.map(n => [n.id, n]));
assert.equal(new Set(departments.map(id => nodes.get(id).pos[1])).size, 1);
for (const [i, id] of departments.entries()) {
const department = nodes.get(id);
const teams = spec.connections.filter(e => e.from === id).map(e => nodes.get(e.to));
assert.equal(teams.length, i === 0 ? 2 : i === 1 ? 5 : 1);
for (const team of teams) {
assert(team.pos[1] > department.pos[1] + department.size[1]);
assert(team.size[0] < department.size[0]);
assert.equal(team.pos[0] + team.size[0], department.pos[0] + department.size[0]);
const edge = spec.connections.find(e => e.from === id && e.to === team.id);
assert.equal(edge.toSide, 'left');
assert.equal(edge.fromSide, 'bottom');
assert.equal(spec.connections.find(e => e.from === 'management' && e.to === id).toSide, 'top');
assert.deepEqual(spec.connections.filter(e => e.to === team.id).map(e => e.from), [id]);
assert(page.includes(team.label));
}
}
for (const [i, a] of spec.components.entries()) for (const b of spec.components.slice(i + 1)) {
assert(!(a.pos[0] < b.pos[0] + b.size[0] && a.pos[0] + a.size[0] > b.pos[0] && a.pos[1] < b.pos[1] + b.size[1] && a.pos[1] + a.size[1] > b.pos[1]), `${a.id} overlaps ${b.id}`);
}
});
test('organization content is split into linked focused pages', () => {
const structure = read('docs/organization/structure.mdx');
const collaboration = read('docs/organization/collaboration.mdx');
const sdlc = read('docs/organization/sdlc.mdx');
assert(!structure.includes('## How the departments work together'));
assert(!structure.includes('## Leadership and delivery structure'));
assert(!structure.includes('name="organization-reporting"'));
assert(collaboration.includes('## How the departments work together'));
for (const outcome of ['Better buyer experience', 'Stronger seller experience', 'Safe and trustworthy trading', 'Sustainable growth', 'AI-powered execution']) assert(collaboration.includes(outcome));
assert(sdlc.includes('title: SDLC — Software Delivery Life Cycle'));
assert(sdlc.includes('name="organization-reporting"'));
for (const role of ['Head of Engineering', 'AI Architect', 'Operational Quality', 'Brand Team Lead']) assert(sdlc.includes(role));
for (const [name, page] of [['structure',structure],['collaboration',collaboration],['sdlc',sdlc]]) {
for (const other of ['structure','collaboration','sdlc'].filter(n => n !== name)) assert(page.includes(`./${other}.mdx`));
}
});
test('organization page presents an operating model, not research notes', () => {
const page = read('docs/organization/structure.mdx');
assert(page.includes('operating model') && page.includes('## Departments and teams'));
const prohibited = /assum|job.advert|vacanc|publicly evidenced|research snapshot|## Sources|\[\d+\]|\b[EJA]:\s/i;
assert(!prohibited.test(page));
assert(page.includes('Vladas Jurkevičius') && page.includes('Lukas Deksnys'));
assert(existsSync('research/organization/reconstruction-snapshot.mdx'));
assert(JSON.parse(read('research/organization/sources.json')).sources.every(s => s.quotes?.length));
for (const name of ['organization-structure', 'organization-reporting']) {
const spec = JSON.parse(read(`diagrams/${name}.architecture.json`));
assert(!prohibited.test(JSON.stringify(spec)));
const ids = new Set(spec.components.map(n => n.id));
assert.equal(ids.size, spec.components.length);
for (const edge of spec.connections) assert(ids.has(edge.from) && ids.has(edge.to));
}
});
+40
View File
@@ -0,0 +1,40 @@
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {readFileSync, readdirSync} from 'node:fs';
const read = p => readFileSync(p, 'utf8');
const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
test('eight fixed strategy pages preserve target boundary and separate evidence', () => {
assert.equal(model.pages.length, 8);
assert.equal(readdirSync('docs/strategy').length, 8);
for (const {id} of model.pages) {
const page = read(`docs/strategy/${id}.mdx`);
assert(page.includes(`id: ${id}`));
assert(page.includes('Target operating model'));
assert(!/## Sources|\[\d+\]|Internal evidence mapping/.test(page));
assert(read('sidebars/strategy.ts').includes(`'${id}'`));
}
for (const teams of Object.values(model.organization)) for (const name of teams) {
assert(read('docs/organization/structure.mdx').includes(name));
assert(read('docs/strategy/target-operating-model.mdx').includes(name));
}
});
test('every metric has an actionable reader-facing contract', () => {
const page = read('docs/strategy/measurement.mdx');
assert.equal(model.kpis.length,16);
for (const k of model.kpis) {
const section=page.split(`### ${k.name}\n`)[1]?.split('\n### ')[0];
assert(section,k.name);
for (const field of ['Eligible population','Exclusions','Events and source','Owner / cadence','Missing evidence','Gaming and limits']) assert(section.includes(`| ${field} |`));
assert(section.includes(k.owner));
}
assert(page.includes('synthetic') && page.includes('not a production ingestion system'));
});
test('diagram preserves independent release approval and no agent production authority', () => {
const d=JSON.parse(read('diagrams/ai-adoption-target.architecture.json'));
assert(d.meta.title.startsWith('TO-BE'));
assert.equal(d.components.filter(n=>n.id==='coordinator').length,1);
assert.deepEqual(d.connections.filter(e=>e.to==='release').map(e=>e.from),['approval']);
for (const [from,to] of [['registry','coordinator'],['registry','clients'],['clients','gates'],['gates','approval'],['release','audit'],['audit','people']]) assert(d.connections.some(e=>e.from===from&&e.to===to));
assert(!d.connections.some(e=>['clients','coordinator'].includes(e.from)&&['release','approval'].includes(e.to)));
assert(d.boundaries.some(b=>b.wraps.includes('gates')&&b.wraps.includes('release')));
});
+34 -2
View File
@@ -6,7 +6,7 @@ const config = read('docusaurus.config.ts');
assert(config.includes("baseUrl: '/eldorado-v1/documentation/'"));
assert(config.includes("onBrokenLinks: 'throw'"));
assert(config.includes("hashed: 'filename'"));
for (const file of readdirSync('docs')) {
for (const file of readdirSync('docs', {recursive: true}).filter(f => /\.mdx?$/.test(f))) {
const doc = read(join('docs', file));
assert(/^---\n[\s\S]*?title: .+\n[\s\S]*?description: .+\n/.test(doc), `Missing metadata: ${file}`);
}
@@ -16,9 +16,41 @@ const workflow = read('.gitea/workflows/pages.yml');
assert(workflow.includes('mountpoint -q'));
assert(workflow.includes("gitea.event_name == 'push' && gitea.ref == 'refs/heads/main'"));
if (process.argv.includes('--build')) {
for (const route of ['index.html','docs/overview/index.html','docs/maintain/index.html','docs/reference/index.html','404.html']) {
const model = JSON.parse(read('research/ai-adoption/strategy-model.json'));
const indexes = readdirSync('build').filter(f => /^search-index.*\.json$/.test(f)).map(f => read(join('build',f))).join('\n');
for (const {id, title} of model.pages) {
const route = `strategy/${id === 'index' ? '' : `${id}/`}index.html`;
const page = read(join('build',route));
assert(page.includes(title) && page.includes('Target operating model'), route);
assert(indexes.includes(title), `Missing indexed Strategy page: ${id}`);
for (const match of page.matchAll(/href="(\/eldorado-v1\/documentation\/[^"?#]*)/g)) {
const local = decodeURIComponent(match[1].replace('/eldorado-v1/documentation/',''));
assert(existsSync(join('build', local.endsWith('/') ? `${local}index.html` : local)), `Broken built link: ${local}`);
}
}
assert(!existsSync('build/research'), 'Background research must remain private source');
assert(!readdirSync('build/diagrams', {recursive:true}).some(f => f.endsWith('.json')), 'No public diagram JSON');
assert(read('build/diagrams/ai-adoption-target/index.html').includes('TO-BE'));
for (const route of ['index.html','docs/overview/index.html','docs/maintain/index.html','docs/reference/index.html','404.html','organization/structure/index.html','organization/collaboration/index.html','organization/sdlc/index.html','strategy/index.html','contribution/index.html','contribution/diagrams/index.html','diagrams/organization-structure/index.html']) {
assert(existsSync(join('build',route)), `Missing route: ${route}`);
}
assert(!readdirSync('build/diagrams', {recursive: true}).some(f => f.endsWith('.delivery.json')), 'Private build receipts must not be published');
const diagram = read('build/diagrams/organization-structure/index.html');
assert(diagram.includes('node-product-team-2') && diagram.includes('node-technology-team-5'));
assert(!diagram.includes('shared-delivery'));
assert(!/A: placement|E: careers|J: advertised/.test(diagram));
assert(!diagram.includes('Example leadership'));
for (const [dept, count] of [['product', 2], ['technology', 5]]) {
for (let i = 1; i <= count; i++) assert(diagram.includes(`node-${dept}-team-${i}`));
}
const reporting = read('build/diagrams/organization-reporting/index.html');
assert(reporting.includes('AI Architect') && reporting.includes('Leads'));
assert(!/A: assumed|J: direct/.test(reporting));
const structure = read('build/organization/structure/index.html');
assert(structure.includes('Vladas Jurkevičius') && structure.includes('operating model'));
assert(!/Research snapshot|Job-advertised|careers\.eldorado\.gg|Assumed/.test(structure));
assert(!structure.includes('Illustrative integration example'));
assert(!diagram.includes('/opt/data/'), 'No build paths in published diagrams');
const home = read('build/index.html');
assert(/<title\b[^>]*>Eldorado Powered By AI<\/title>/.test(home));
assert(home.includes('Powered By AI</span></h1>'));
+2
View File
@@ -0,0 +1,2 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
export default {contributionSidebar: ['contribute', 'diagrams']} satisfies SidebarsConfig;
+2
View File
@@ -0,0 +1,2 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
export default {organizationSidebar: ['structure', 'collaboration', 'sdlc']} satisfies SidebarsConfig;
+2
View File
@@ -0,0 +1,2 @@
import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
export default {strategySidebar: ['index', 'target-operating-model', 'skills-governance', 'skills-quality', 'skills-adoption', 'measurement', 'skills-maintenance', 'roadmap']} satisfies SidebarsConfig;
+27
View File
@@ -0,0 +1,27 @@
import React, {useEffect, useId, useRef, useState} from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
export default function ArchifyDiagram({name, title, compact = false}: {name: string; title: string; compact?: boolean}): React.ReactElement {
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) throw new Error('Invalid diagram name');
const source = useBaseUrl(`/diagrams/${name}/`);
const dialog = useRef<HTMLDialogElement>(null);
const opener = useRef<HTMLButtonElement>(null);
const headingId = useId();
const [expanded, setExpanded] = useState(false);
useEffect(() => {
if (!expanded) return;
dialog.current?.showModal();
const previousOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
return () => {document.body.style.overflow = previousOverflow;};
}, [expanded]);
const close = () => {dialog.current?.close();};
return <section className={`archify-panel${compact ? ' archify-panel--compact' : ''}`} aria-label={title}>
<div className="archify-toolbar"><button ref={opener} type="button" className="archify-expand" aria-label={`Expand ${title}`} title="Expand diagram" onClick={() => setExpanded(true)}><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"><path d="M8 3H3v5M16 3h5v5M21 16v5h-5M8 21H3v-5" /></svg></button></div>
<iframe src={source} title={title} sandbox="allow-scripts allow-downloads" loading="lazy" referrerPolicy="no-referrer" />
<dialog ref={dialog} className="archify-modal" aria-labelledby={headingId} onClose={() => {setExpanded(false); opener.current?.focus();}}>
<div className="archify-modal-toolbar"><strong id={headingId}>{title}</strong><button type="button" onClick={close} autoFocus aria-label="Close full-screen diagram">Close ×</button></div>
{expanded && <iframe src={source} title={`${title} — expanded`} sandbox="allow-scripts allow-downloads" referrerPolicy="no-referrer" />}
</dialog>
</section>;
}
+20
View File
@@ -92,8 +92,28 @@
.footer {background: #101014; color: #afadb9; font-size: .8rem; padding: 27px;}
.theme-doc-markdown {max-width: 850px;}
.theme-doc-markdown h1 {letter-spacing: -.035em;}
.strategy-content table {display: table; width: 100%; table-layout: fixed;}
.strategy-content th, .strategy-content td {overflow-wrap: anywhere; vertical-align: top;}
.strategy-content th:first-child, .strategy-content td:first-child {width: 25%;}
@media (max-width: 700px) {
.strategy-content th:first-child, .strategy-content td:first-child {width: 32%;}
.strategy-content th, .strategy-content td {padding: .65rem;}
}
.pagination-nav__link, .menu__link, .theme-admonition, pre {border-radius: 0;}
a:focus-visible, button:focus-visible {outline: 3px solid #b88719; outline-offset: 4px;}
@media (max-width: 996px) {.navbar {padding: 0 1rem;} .hero-inner {gap: 15px; padding: 55px 25px;} .hero-art {transform: scale(.85);} .start-section {padding: 35px 25px;} .start-card {padding: 20px;}}
@media (max-width: 700px) {.hero-inner {grid-template-columns: 1fr; padding: 45px 24px;} .hero-art {display: none;} .hero-copy h1 {font-size: 3rem;} .start-grid {grid-template-columns: 1fr;} .section-heading {align-items: flex-start;} .foundation-badge {display: none;} .start-section {padding: 32px 20px;} .navbar__title {font-size: 1.45rem;} .navbar__logo {height: 31px;} .navbar input {max-width: 145px;} .button--lg {font-size: .95rem;} .button--primary span {padding-left: 10px;}}
.archify-panel {border: 1px solid var(--line); margin: 1.5rem 0;}
.archify-toolbar {display: flex; justify-content: flex-end; padding: 4px; background: var(--surface);}
.archify-panel iframe {width: 100%; height: 720px; border: 0; display: block; background: #f6f6f6;}
.archify-panel--compact > iframe {height: 500px;}
@media (max-width: 700px) {.archify-panel iframe {height: 650px;}}
.archify-expand {display: grid; place-items: center; width: 44px; height: 44px; border: 0; padding: 0; background: transparent; color: var(--ifm-link-color); cursor: pointer;}
.archify-expand:hover {background: var(--ifm-hover-overlay);}
.archify-modal {position: fixed; inset: 0; margin: 0; padding: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none; border: 0; background: var(--surface); color: var(--ifm-color-content);}
.archify-modal[open] {display: flex; flex-direction: column;}
.archify-modal::backdrop {background: #000b;}
.archify-modal-toolbar {display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 12px 20px; border-bottom: 1px solid var(--line);}
.archify-modal-toolbar button {flex-shrink: 0; padding: 8px 16px; background: var(--gold); color: #14131a; font: inherit; font-weight: 700; border: 0; cursor: pointer;}
.archify-panel .archify-modal iframe {flex: 1; min-height: 0; height: 100%; width: 100%;}
@media (prefers-reduced-motion: reduce) {.start-card {transition: none;} .start-card:hover {transform: none;}}
+3 -4
View File
@@ -11,7 +11,6 @@ export default function Home(): React.ReactElement {
<p className="eyebrow">ELDORADO / AI STRATEGY</p>
<h1>Eldorado<br/><span>Powered By AI</span></h1>
<p className="hero-description">Strategy and direction for an AI-powered Eldorado.</p>
<Link className="button button--primary button--lg" to="/docs/overview/">Explore the documentation <span aria-hidden="true">→</span></Link>
<p className="hero-note">A starting point for shaping the strategy.</p>
</div>
<div className="hero-art" aria-hidden="true">
@@ -25,9 +24,9 @@ export default function Home(): React.ReactElement {
<section className="start-section container" aria-labelledby="start-title">
<div className="section-heading"><div><p className="eyebrow">START HERE</p><h2 id="start-title">A clear place to begin.</h2></div><span className="foundation-badge">Foundation · v1</span></div>
<div className="start-grid">
<Link className="start-card" to="/docs/overview/"><span className="card-number">01 / OVERVIEW</span><h3>What’s here</h3><p>Understand the starting point and what remains to be decided.</p><span className="card-action">Read the overview <span aria-hidden="true">↗</span></span></Link>
<Link className="start-card" to="/docs/maintain/"><span className="card-number">02 / MAINTENANCE</span><h3>Make it your own</h3><p>Add a page, preview a change, and publish through the existing pipeline.</p><span className="card-action">Open the guide <span aria-hidden="true">↗</span></span></Link>
<Link className="start-card" to="/docs/reference/"><span className="card-number">03 / REFERENCE</span><h3>Under the hood</h3><p>Find the repository layout, build commands, and publication details.</p><span className="card-action">View the reference <span aria-hidden="true">↗</span></span></Link>
<Link className="start-card" to="/organization/structure/"><span className="card-number">01 / ORGANIZATION</span><h3>Organization</h3><p>Explore the structure workspace and its first interactive diagram.</p><span className="card-action">Explore structure <span aria-hidden="true">↗</span></span></Link>
<Link className="start-card" to="/strategy/"><span className="card-number">02 / STRATEGY</span><h3>Strategy</h3><p>Shape the direction for an AI-powered Eldorado.</p><span className="card-action">View the direction <span aria-hidden="true">↗</span></span></Link>
<Link className="start-card" to="/contribution/"><span className="card-number">03 / CONTRIBUTION</span><h3>Contribution</h3><p>Contribute evidence, proposals, documentation, and diagrams.</p><span className="card-action">Start contributing <span aria-hidden="true">↗</span></span></Link>
</div>
<aside className="scope-note"><span className="scope-symbol" aria-hidden="true">✦</span><div><strong>Shaping the direction.</strong><p>This space is dedicated to AI strategy and direction for Eldorado. Specific initiatives, priorities, and the roadmap remain to be defined.</p></div></aside>
</section>
+110
View File
@@ -0,0 +1,110 @@
{
"LICENSE": "b799ab081703e7821ae5096d2c1abdf14bbdc75e8ea1c4045998c36ed6db9706",
"SKILL.md": "66dfe99b35d7ceff95bcbdb00f138b8b3ec8094629acf69efba1eeda1013aaf3",
"THIRD_PARTY_NOTICES.md": "0b7fb59365dc21c93b0f62526d74ee913aeec9ae532c112bc76afa7edcd56e14",
"assets/JetBrainsMono-OFL.txt": "c1ab7c666206842a02b35b30770dac0d7a10156ed401c9defc3f02a754d89e90",
"assets/template.html": "b0b550d644b77cad401bbc4b21375919ef01db45d235122a0ee42b732cb34233",
"bin/archify.mjs": "97788086acaac4122aafc56c48acfe5fde1979d6aaa67db73aa1f4ab70b34390",
"bin/delivery-update.mjs": "d5ad7d6d7d949a8a8c37af4f80d79a8b4e057245e30ba11866efb40c6771dafa",
"bin/finalize.mjs": "c62710a0e80dc11b79e2c6a1a163fd9f6df46e13de141e3dd4d506554170d824",
"bin/open-artifact.mjs": "b0ab5f37c4f4556f76040dc862744afe9d4b65f319f35e1f9b505e0344b9c409",
"bin/preview.mjs": "2d01245850ab259500f0ff1782bef9f9c6f9caad0d13ad82061ef75a20fbaedb",
"bin/recover-output.mjs": "b3f7db21b3fc5341326207c1259aa787730120f42179eff50e481b0c6ad1e2d4",
"bin/visual-check.mjs": "93d07354e6b94725a8da354f90b7781f2739101f336afccd532ac6de9895a24c",
"brand-marks/README.md": "8b771a16322ce9ea47fdc00b9935e33adf0f3af608f2d735235443e15ef622e2",
"brand-marks/catalog.json": "e1a6c555cbf8ea36fb8100a4a25824e7ab87bebe9871e450ba05b6dea3b036d6",
"delta/architecture-delta.mjs": "94ad7ac625202759386e4ff06431902d0c198c32b62bc99ebf894616cb94deca",
"examples/agent-run.lifecycle.json": "230242a7557663877766c43a9fe7cffe8b4fb66e4866457dccd24c63175b4917",
"examples/agent-tool-call.workflow.json": "d67965d50872c97e4ff3476271e645bf19fab6b3ab44e111bdb44b063e3aa36f",
"examples/async-job-roundtrip.sequence.json": "b8c6c52f1763ede2933c77deb8da59b3e06917bc41d27e66df896136456b859a",
"examples/brand-aware-delivery.architecture.json": "211c126ebdc5f0124617b1067cecfb858143a1a18fd10621d01f5765c7de3b5d",
"examples/cache-miss-request.sequence.json": "f8486daa803fff6c295cecca4604c561fbbf9acb2e83326022b5a87639e8afe6",
"examples/checkout-platform.base.architecture.json": "81d55a2e86fc39f6473b32dfba986a12d3b7a705a95821844865b1dc831c9fe7",
"examples/checkout-platform.head.architecture.json": "eb4ae056035dd3316414a5d7f6599525107b5d1af7bedbd905460ade77daf5c6",
"examples/dataflow-product-analytics.html": "6d6790f1fa3631241dba4065e33ba2ce947a1da032cdd6d7d44c7190f85d3f5d",
"examples/deployment-release.lifecycle.json": "f565a8acfebdb21453e2bccd82d3760b4736e38fe194fb2f0804b57817eba813",
"examples/event-stream.dataflow.json": "3bdd77674282009c7c68d588cf8a3c495bc98a3be1b9c45327fd1801a6f83a23",
"examples/holiday-planning.workflow.json": "b98354f921ca45842fed235db09fd4f19d94adc16865876f438c0d699d05e1ea",
"examples/incident-response.workflow.json": "630be98449925d2716ecb8439f404b2bff5fe65ba905b0ad0baea6d96890c526",
"examples/lifecycle-agent-run.html": "0874c0723b10104ea03c566d1d63d958b6f93a6788c0075c1d1d162ccdb4054c",
"examples/locales/de.json": "d1cf160d3ada814676566b787f203c2aee99f8f5d5b51d2e3e0b3c03f55a33df",
"examples/locales/es.json": "1389068b7dddb5e72ed425d12084ca9104942bef933440a16113d60619f1daf3",
"examples/locales/fr.json": "805495b259b417ccc03744023e5bf803811752c0c4e8fc5be7433e2ac73e875d",
"examples/locales/fr.partial.json": "8958a8354ffccce2ce63ac21b3f8a0f131086f532832445a3fe7feff02200397",
"examples/locales/it.json": "76e45186196675e7a0e2e2281073da38c0971596e6ed6eae11ccc479ceda6eae",
"examples/locales/ja.json": "79418f8eb65dc6c4928a4cab54e4515b4bfffd867814323eda6c8aa3c96fac33",
"examples/locales/ko.json": "958db2030014f6835a0bf62813f58fb80ea66b48a13af6d2586805704709616f",
"examples/locales/pt.json": "efbae92eebc09636e2c3bbc6786a666a6336258bd8c2d451bd78b2921042ab67",
"examples/locales/ru.json": "3bd0eadf6cdfe65511e81a11f04b61bb33cc94e1b89044203d191e4296a3f40d",
"examples/product-analytics.dataflow.json": "8a635db9e51a5b1a8d2955b9d66f5b9a450e0a298432ce8f2e2f0be11f372e05",
"examples/production-deployment.architecture.json": "cb54bad2dfd0c2d02aa5b496bde6839789a0f07f23062343c4458b8d38806c13",
"examples/release-delivery.workflow.json": "dbd5c174af66296caec372e7214cf4df7158b50a563508e16826b95ab8d1cd6c",
"examples/sequence-cache-miss-request.html": "307951a168d2492eb0c41f318260d0a866f6e6057df5253d3528c32a23084ea3",
"examples/web-app-rendered.html": "8233f4f600c89559b64ac650ad11b66ee1423103e8b6c5e53f63740afc06f6da",
"examples/web-app.architecture.json": "95e051b27a9e4a3307e194c0f666fca4af51470be7e2228d400ff48aea5b353b",
"examples/workflow-agent-tool-call-rendered.html": "3edbb38a05430865ee2f620c35de49f8e78dfd8788be14840d9e134b5912230d",
"migrations/workflow-v2.mjs": "0eec02057b8490afca9b093de5ea6e636c7fad2361cd28a04cf2f3e4b6197434",
"package.json": "b609dc658a8e1a2097c304a7873ef039b9d6079832028980f60965d8d3ea886d",
"recipes/scenarios.mjs": "6c655f643dd198cc76a6cfd0f5b945dac1388eaed10bf00f195dcd9f077401b4",
"references/architecture-layout-repair.md": "6c90caa232c004fa43b8770c745b83672919deaf8909f2592a3ad5fcd4031953",
"references/authoring-contract.md": "b5d521401b6d38d2792b2feb18906ad2b9d50a1c265acd42be51b3d49f456eb8",
"references/authoring-defaults.md": "7277ebf440c568fac425af5ad085ff60976ec401fe694c0ea984efff192961b0",
"references/brand-marks.md": "304d48735e07673197715856271532cd2dfb7fbada184936a15dea3b40ec1f06",
"references/delivery-contract.md": "aa167fd1d2c5ab48cf7c53cd25dc9f49a8b52059e848d71d76b2489aa5b68780",
"references/repository-authoring.md": "da8fa6d2bbc4572e5253e53bbc2d08088568f4d5350e61aa4e2029b2a5d2beb1",
"references/update-awareness.md": "57750c5c730b1804b6b1ece3af972d47cfe4d06ef7920fba4418c5b09c842b41",
"references/viewer-runtime.md": "cabdf042069f0029f449755229335248d1568681542ec331e181fdc91efee14a",
"renderers/architecture/grid.mjs": "f6de99c32436feed3378b0ddef516bb4732ff27a89c543b50635d4d14a15a55a",
"renderers/architecture/labels.mjs": "7cf5bb20c3039f49683fe81a461110c42de0a006a58c867c47fdd3089a58bbbc",
"renderers/architecture/render-architecture.mjs": "3831899e2e12cedd2e8225285fc9e50e8cd032f036fac87264b0bd5ddc60fda6",
"renderers/architecture/routing.mjs": "b952b3ec0364827fa7fbff10a28e2a83f0ba11921c67f5ee224e5ef58c833cbb",
"renderers/dataflow/README.md": "5e380fa7fe32c93a2f978a2db5ad7d2e91afa5fc3cae62c4d27c4a2c81502873",
"renderers/dataflow/render-dataflow.mjs": "a8540e7b1a4794c6da6b64707ce17e970ca5e876544fd29554d2503c196f1dde",
"renderers/lifecycle/README.md": "2ce9bcd31529e97c176701a4dceb2f82f70f04a9483441779341eeeb79c54a9a",
"renderers/lifecycle/grid-routing.mjs": "81ae4d3a90aed30471f23dbc3083768d4979e96cf96544f9465713c2cd09e13f",
"renderers/lifecycle/render-lifecycle.mjs": "1ed2e359a5eab6a2bd9473c36c6184498d68748e9175cddf6894aa7b2ba3f670",
"renderers/sequence/README.md": "51d78bc944d10e06ded74ede3ca5b78f916db3e8e45ae099eabc204e4d316ef3",
"renderers/sequence/render-sequence.mjs": "46ed05a34655fbe6c421586366aa81480bca893233d61b9391e21afc667f15a1",
"renderers/shared/atomic-output.mjs": "f804d66d1801ac2dadc9d0a4c334645cfbb7a8cd1a7de112ddfb952c056a580b",
"renderers/shared/brand-marks.mjs": "5cfc3c517ca6fe7e867c208101059dd7747ca3de3c9934b17583d6cf20336620",
"renderers/shared/cli.mjs": "097f968a614008b07972f56de995e5b933c61ee0ba0d16d967c0a97c6d92e9d7",
"renderers/shared/desktop-readability.mjs": "de68f2ed92573ae714659d556f97eca01296f4e0063b94c429370efcc38f909a",
"renderers/shared/diagnostics.mjs": "d052a0e72eec2a2bf29c70dacdd3350100e2c76fc2627d14f79234077c571e3f",
"renderers/shared/engineering-profiles.mjs": "7e99f9d5423f66cc9ab6224da56dc2d5aa6c6faec29178034794e9db6b5b2889",
"renderers/shared/generated-brand-marks.mjs": "0f157d229c12df6f53763a3b88684baa5803a52484c49c4d34ec40b8743beea2",
"renderers/shared/generated-validators.mjs": "33697207b90a0eb0d0566a1c46eaf3cb58f85cef0f43caeec991017ade34c2b9",
"renderers/shared/geometry.mjs": "b25d849e6e256b1eef37fa3bb88712d0617d6eb646a2355a934505fcaa9be75d",
"renderers/shared/i18n.mjs": "03f5356110a08b2cf684a484af5502bb392482de1ef61123707c6da2724c5201",
"renderers/shared/layout-report.mjs": "4b5fa6e1af2b2efdd57d7fac16359089a059a745cc19ad1595ba42c3e483aec7",
"renderers/shared/legend.mjs": "1cc5033a188690fc5f5fef1bd7915a275e6c790bb3061c63c00a97b234625cbe",
"renderers/shared/output-path.mjs": "7422139101abd93493918898a97bed06f2212bfd411d3514cd229b6d57a3797e",
"renderers/shared/path-semantics.mjs": "4ef002af9061c06634a07de6d199fd7d13e6c50561c792fd018118bf3e065f52",
"renderers/shared/portable-path.mjs": "26130cbcdae9b9f082486aeb5feec9c5fe57905d2ed8e30c8081b9710e2e8ac3",
"renderers/shared/repository-evidence.mjs": "19c652af2afec5604e9ce23d356f3cc2c5d6146592531036da20e79f4db3ed1d",
"renderers/shared/repository-location.mjs": "9ee8294c39d1be40f5aca3334fdfc7527ef9b224d6c1e69ca57aefc6a8c77856",
"renderers/shared/route-quality.mjs": "0ae7e16210f8e00bb615da34d5323dee65efe52ceb11a40ea49fdd10b5257daa",
"renderers/shared/sidecar-path.mjs": "c23cac8bcc265a74e2920c58d0968d1ffa06c1328bdd9cfd07755d4df2f22974",
"renderers/shared/spatial-grid.mjs": "e728bf6a729972f95ae09e368ea370490d10ff94d49debe2fab2e9bac923ee1a",
"renderers/shared/text-fit.mjs": "ebd499072ef4fe31c9aabf45a01c204a9c2c142d45f087c3240673f90f477038",
"renderers/shared/utils.mjs": "422b66c6945de473b1f96e4f3641339b6a24c8ccef60795a38ca2386e10bc26b",
"renderers/shared/validator.mjs": "7460d7fcbcdf710649256c5e96142ae26d807a655d3296ee8caf7f5e158622cc",
"renderers/workflow/README.md": "c3ca0e258d3bba43390eda95f308d12388b1c778db972bbd936a5f5ad3344142",
"renderers/workflow/render-workflow.mjs": "df6cb1330231616dcb83592c3cc1dbf4b3e45567779836ae031ff57d2d517b89",
"renderers/workflow/workflow-compiler.mjs": "25646b0a7ee13d62ed7b21c915d40665608ffd57c69af263a8ec4250ceb9214e",
"renderers/workflow/workflow-migration-geometry.mjs": "ebfc5085dd008ae63802f500402fb7bf8ca0ae28460997d867f45e5493b8fbe6",
"schemas/README.md": "eeb4d75e4864128493939f277481ade4c5f023932ed5c0846cc539fdac9ae6aa",
"schemas/architecture.schema.json": "aef992b585a18a4016b8207254fe43a6227b0d45322a1ba95e0f69a80fc8f8c1",
"schemas/common.schema.json": "60912458e44224647a147a4dfccf0b40e0ea9c631767aabdeb5e82456f27c4e6",
"schemas/dataflow.schema.json": "f01b2faafa5fd6e300f6b5045eeca70f70226552411180442d6e528dd45172f1",
"schemas/lifecycle.schema.json": "6b310026a3bc5d4eb7f36a1ed862b8c95d88ce57246ce078ba3094e0d6ff3c90",
"schemas/sequence.schema.json": "411dcda121d7e95170a6850fb1cec51bec2e78222ce74b8465e11bcbd8e6e41a",
"schemas/workflow.schema.json": "e13a1ce3b841376c8c6f6b9cb8152f8c0c7d70a77a2960fecdfe9025de83dc25",
"scripts/check-render-output.mjs": "6ca0507cbbe442e5fa709477e7257b31a3fa929aa7f1a56ddbcbb3328df87a7d",
"scripts/check-update.mjs": "5a1add913beee623cdc9c009bad1980f4538bae1b9862bf92e41c83dd8a90666",
"scripts/delivery-update-child.mjs": "23b2cf0cb79935b90238558b511966bdf04c4158605c0651b1ccdc90b1736e5f",
"scripts/generate-brand-marks.mjs": "0434c31903b831382a91e85a329bcbcb39b32f29c656273a273f896b3b0a42e7",
"scripts/generate-validators.mjs": "fc8f003d7def53abaccac25148517cdccd390d975fc6fc745dee64a71425b9a2",
"scripts/render-examples.mjs": "a173a053fb26c0ac3cd72eb9b4e50411e67ca8c50bec57f05d4ca8ba79db2c0b",
"scripts/update-contract.mjs": "7c5d34c5fa2e08fcd0b42e33a0f3b4685fcecf3ac88f1ed820528df8fdc46cbc",
"skill-release.json": "93a31980e200d0749d887982eba8b6c3b33452a14e3a04d4e6969b9c42c7eacf"
}
+12
View File
@@ -0,0 +1,12 @@
{
"repository": "https://github.com/tt-a1i/archify",
"commit": "d5a1333d7447c866a765adac7d4d062f2f02e4d2",
"version": "3.0.1",
"sourcePath": "archify/",
"license": "MIT",
"excluded": [
"test/",
"package-lock.json"
],
"changes": "None to upstream source; portal integration is outside vendor/. No global skill installation."
}
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2026 tt-a1i (Archify)
Copyright (c) 2025 Cocoon AI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+107
View File
@@ -0,0 +1,107 @@
---
name: archify
description: Create polished, validated architecture, workflow, sequence, data-flow, and lifecycle/state diagrams as explorable standalone HTML with inline SVG, dark/light themes, optional trace motion, and PNG/JPEG/WebP/SVG/WebM export. Accept plain-language requirements or pasted Mermaid flowchart, sequenceDiagram, and stateDiagram input; inspect repository evidence when the diagram must reflect real code. Use when the user asks to visualize system architecture, infrastructure, cloud/security/network topology, technical workflows, API call sequences, request lifecycles, data pipelines, ETL/ELT, data lineage, state machines, or to convert/beautify Mermaid.
license: MIT
metadata:
version: "3.0"
author: tt-a1i
based_on: Cocoon-AI/architecture-diagram-generator (MIT, v1.0)
---
# Archify
Create an interactive HTML diagram from typed JSON. Static output is the default; enable motion only when requested.
Run commands from your working directory. Unless the user names another location, give each new diagram request its own folder `.archify/<type>-<slug>-<YYYYMMDD-HHMMSS>/` there (local time, chosen once when the request starts): keep `candidate.json` and `<slug>.html` in it, set `meta.output` to that relative HTML path, and reuse the folder for every repair rerun. A later request gets a new folder, so earlier versions stay intact. Replace `bin/archify.mjs` in the commands below with the installed package's absolute path, or its path relative to your working directory; input and output paths resolve from that working directory.
For a real codebase, read [Repository authoring](references/repository-authoring.md) while tracing the requested behavior. A system description uses the steps below; an existing JSON uses the handoff path.
## Existing candidate handoff
When the user supplies a frozen candidate, run `finalize` first as one CLI invocation. Its passing receipt completes the automated gates; follow any visual review recommendation under Delivery before claiming visual quality. For repair, follow step 5.
`finalize` includes a bounded update check in its delivery receipt; see Update awareness.
## Fast authoring path
Use this path for ordinary generation. Read branch references only when their stated trigger applies.
1. Choose `architecture`, `workflow`, `sequence`, `dataflow`, or `lifecycle` from the question.
2. Use the exact schema and example paths in the Type router without listing their directories. Read [Authoring defaults](references/authoring-defaults.md) and the mode's example in a bounded batch separate from project documents and complete schemas so neither is truncated; recover any missing section before writing. For Architecture, use the matching showcase example. For Sequence, Dataflow, and Lifecycle, also read the mode and common schemas. Read the relevant schema definition before choosing any new field, enum, or constrained text, especially boundary kinds. Examples teach shape, not facts. Use fresh IDs, wording, and layout. Go directly to the candidate without preliminary help, doctor, starter validation, temporary diagrams, or output-path listing. Query brands only for an explicitly requested mark; read [Brand marks](references/brand-marks.md) for an unknown mark with a user-provided URL.
3. Once the requested scope and, for a real codebase, [source evidence](references/repository-authoring.md) are covered, write the complete candidate directly without planning coordinates in prose. Choose Architecture abstraction and connected placement using Authoring defaults before coordinates: show the main user journey and necessary branches, preserve control roles and behavior-changing conditions, and leave enough room for actual relationship labels. No node, relationship, source, view, card, or boundary count is a target or ceiling. Use automatic routes first; add explicit routing only for necessary branch, return, supplied geometry, or measured repair. Set `meta.quality_profile` to `"showcase"` unless the user requests dense `standard`.
4. Once the complete first candidate is written, run `finalize` directly. Its first gate is showcase validation; successful first drafts need no separate pre-validation. Keep the candidate unchanged while the command runs:
```bash
node bin/archify.mjs finalize <type> <candidate.json> <output.html> --quality showcase --json
```
For a repository-backed candidate, include evidence on the first draft and use the complete first command: `node bin/archify.mjs finalize <type> <candidate.json> <output.html> --repo-root <repo-root> --quality showcase --json`.
A passing receipt proves the included `validate`, `deliver`, strict `check`, and real-browser `browser-check` gates passed. Use its compact summary; run standalone commands only for a separate request or focused failure diagnosis.
5. A non-zero exit is never success. Read compact stdout or `evidence.summaryReceipt`, then [repair the failed gate](references/delivery-contract.md#failed-finalize-and-candidate-repair), including its repair limit. Preserve requested meaning and source evidence. For several tangled Architecture routes, read [Architecture layout repair](references/architecture-layout-repair.md); for measured field or geometry failures, read [Authoring contract](references/authoring-contract.md). Edit the connected neighborhood and rerun the complete `finalize` command from step 4.
## Update awareness
`finalize` and standalone `deliver` include `update` in their receipts. Do not run a separate check for the same delivery. If `update.noticeRequired` is true, read `references/update-awareness.md` and keep one update line in your final response to the user, even after a quality gate fails. For a task with several diagrams, mention the update once in the final response. Snooze or ignore a reminder only when the user explicitly asks; never install or update on your own initiative.
Before the first candidate, use the authoring references and relevant repository source, not Archify implementation or tests. Inspect Archify implementation if diagnostics remain unactionable after focused repairs.
## Type router
| Type | Use for | Schema | Example |
|---|---|---|---|
| `architecture` | Components, services, cloud/security boundaries, infrastructure | `schemas/architecture.schema.json` | System descriptions, services, libraries, and CLI repos: `examples/web-app.architecture.json`; deployment repos: `examples/production-deployment.architecture.json` |
| `workflow` | Processes, approval gates, tool calls, runbooks, CI/CD | `schemas/workflow.schema.json` | `examples/agent-tool-call.workflow.json` |
| `sequence` | API call chains, request lifecycles, async traces, returns | `schemas/sequence.schema.json` | `examples/cache-miss-request.sequence.json` |
| `dataflow` | Pipelines, ETL/ELT, lineage, governance, consumers | `schemas/dataflow.schema.json` | `examples/product-analytics.dataflow.json` |
| `lifecycle` | State/status transitions, retries, waiting and terminal states | `schemas/lifecycle.schema.json` | `examples/deployment-release.lifecycle.json` |
When ambiguous, run `node bin/archify.mjs guide "<scenario>" --json`. Scenario proof examples are structural references, not facts to copy.
## Mermaid input
Read Mermaid for topology and meaning, then author fresh Archify JSON; do not mechanically render Mermaid styling.
- `flowchart` / `graph` → `workflow`, or `architecture` for a component map.
- `sequenceDiagram` → `sequence`; participants become semantic participants and arrows become messages.
- `stateDiagram` → `lifecycle`; states and transitions retain meaning, not Mermaid style.
## Delivery
Use the `finalize` command above for the first candidate and after a repair.
`finalize` stops at the first non-passing gate. Its compact stdout and `<output-stem>.finalize-summary.json` are ordinary evidence. A passing run creates no screenshots and reports `visualReview: "not-requested"`.
When a passing Architecture receipt reports `visualReviewRecommendation.signals.resolvedCrossovers`, copy the candidate aside and apply the hints in one edit that changes only node positions and sizes: every node, relationship (including its `from` and `to`), label, and source stays as it was. Rerun the complete `finalize` once with `--out-dir <folder>/review-2`, because the previous HTML already owns its browser evidence. If that run fails or reports more crossings, restore the copy and finalize it with `--out-dir <folder>/review-3`. Do not start a second placement round. Hints about extra bends alone are optional.
When `layoutReviewRecommendation.action` is `inspect-sequence-width`, follow [Sequence width review](references/delivery-contract.md#sequence-width-review) before handing off a newly authored Sequence.
Perceptual review is optional for ordinary generation, including a newly positioned Architecture. Use [Optional capture evidence](references/delivery-contract.md#optional-capture-evidence), with `--out-dir <folder>/visual-check`, when the user requests visual review, during development audits, or for a concrete route/browser concern. `visualReviewRecommendation` is advisory. Inspect captures before claiming visual quality; otherwise report automated checks only.
Read [Delivery contract](references/delivery-contract.md) for failed gates, standalone commands, provenance/recovery, repeated delivery, exports, or opening. Recovery follows `deliver` → strict provenance `check` → `browser-check`; captures require strict provenance.
For workflow viewport overflow, read [Workflow viewport repair](references/authoring-contract.md#workflow-viewport-repair) before the next layout edit.
Report artifact checks, browser evidence, captures, and actual perceptual review as distinct results. For an explicitly requested immediate preview or active desktop loop, see [Optional opening](references/delivery-contract.md#optional-opening).
## Optional viewer capabilities
`meta.animation: "trace"` is opt-in.
Read `references/viewer-runtime.md` only when the user explicitly asks for Share Cards, Route/Reach cards, motion, deep links, presentation, search/focus, or another Viewer Runtime feature.
## Setup and fallback
No install is required inside the skill package. For setup diagnosis, verify with:
```bash
node bin/archify.mjs doctor
node bin/archify.mjs demo <output-directory>
```
When shell access is unavailable, hand-place architecture SVG into `assets/template.html`, use CSS semantic classes rather than inline colors, and follow the visual review contract in `references/delivery-contract.md`.
## Output
Return the checked HTML as an absolute path, diagram type, validation summary, specification/artifact receipt, browser-evidence status, and truthful visual-review status. Do not claim success for a non-zero command or claim visual inspection you did not perform.
+69
View File
@@ -0,0 +1,69 @@
# Third-party notices
Archify includes optional vector data for third-party brand marks. These marks
are provided only to identify technologies and services in user-authored
diagrams. Their inclusion does not imply sponsorship, endorsement, partnership,
or affiliation with Archify.
The Archify MIT license applies to Archify's own code and content. It does not
replace the copyright licenses, trademark policies, or brand guidelines that
apply to third-party marks. Users are responsible for confirming that their
particular use is permitted.
## Simple Icons
Most of the built-in vector paths and their metadata were generated from
[Simple Icons 16.28.0](https://github.com/simple-icons/simple-icons/tree/16.28.0).
Simple Icons makes its collection work available under
[CC0 1.0 Universal](https://github.com/simple-icons/simple-icons/blob/16.28.0/LICENSE.md).
As Simple Icons explains in its
[disclaimer](https://github.com/simple-icons/simple-icons/blob/16.28.0/DISCLAIMER.md),
CC0 for the collection does not mean that every underlying icon is CC0. License
and brand-guideline metadata may be incomplete or change over time. The absence
of an individual license entry is not a grant of permission.
Archify embeds the selected icons as vector-path data and may render them in a
user-selected color. The following individual licenses were recorded in the
pinned Simple Icons 16.28.0 metadata:
| Mark | Recorded source | Recorded license | Archify treatment |
|---|---|---|---|
| Angular | [Angular press kit](https://angular.dev/press-kit) | [`CC-BY-4.0`](https://creativecommons.org/licenses/by/4.0/) | Embedded as vector-path data; color may be changed by the authored diagram. |
| Apache Airflow | [Apache logos](https://apache.org/logos) | [`Apache-2.0`](https://www.apache.org/licenses/LICENSE-2.0) | Embedded as vector-path data; Apache trademarks remain subject to the [ASF trademark policy](https://www.apache.org/foundation/marks/). |
| Apache Kafka | [Apache logos](https://apache.org/logos) | [`Apache-2.0`](https://www.apache.org/licenses/LICENSE-2.0) | Embedded as vector-path data; Apache trademarks remain subject to the [ASF trademark policy](https://www.apache.org/foundation/marks/). |
| .NET | [.NET brand repository](https://github.com/dotnet/brand/blob/c7d0f51b8ec59531332d05fb27a5b758a7a3d689/logo/dotnet-logo.svg) | [`CC0-1.0`](https://creativecommons.org/publicdomain/zero/1.0/) | Embedded as vector-path data; color may be changed by the authored diagram. |
| JavaScript | [JS community logo](https://github.com/voodootikigod/logo.js/blob/1544bdeed6d618a6cfe4f0650d04ab8d9cfa76d9/js.svg) | [`MIT`](https://github.com/voodootikigod/logo.js/blob/1544bdeed6d618a6cfe4f0650d04ab8d9cfa76d9/LICENSE) | Embedded as vector-path data; color may be changed by the authored diagram. |
| Jenkins | [Jenkins artwork source](https://get.jenkins.io/art/) | [`CC-BY-SA-3.0`](https://creativecommons.org/licenses/by-sa/3.0/) | Embedded as vector-path data; color may be changed by the authored diagram. Jenkins retains its trademark rights. |
| Rust | [Rust project](https://www.rust-lang.org) | [`CC-BY-SA-4.0`](https://creativecommons.org/licenses/by-sa/4.0/) | Embedded as vector-path data; color may be changed by the authored diagram. See the [Rust media guide](https://www.rust-lang.org/policies/media-guide). |
| Vue.js | [Vue logo source](https://github.com/vuejs/art/blob/a1c78b74569b70a25300925b4eacfefcc143b8f6/logo.svg) | [`CC-BY-NC-SA-4.0`](https://creativecommons.org/licenses/by-nc-sa/4.0/) | Embedded as vector-path data; color may be changed by the authored diagram. The non-commercial and share-alike conditions remain applicable; see the [Vue artwork terms](https://github.com/vuejs/art/blob/a1c78b74569b70a25300925b4eacfefcc143b8f6/README.md). |
The source, guideline, and known license fields for every packaged mark are
preserved in `renderers/shared/generated-brand-marks.mjs`.
## OpenAI mark
The OpenAI vector path is recorded from the
[OpenAI brand guidelines](https://openai.com/brand/), not from Simple Icons.
Use remains subject to those current guidelines and any applicable trademark
rights. Its inclusion does not state or imply endorsement by OpenAI.
## JetBrains Mono
Delivered Archify viewer artifacts embed the JetBrains Mono variable font
subsets served by Google Fonts. For characters covered by these subsets, font
selection does not depend on a network request or a locally installed copy.
Uncovered characters (including CJK) still use the system fallback stack;
browser and operating-system rasterization can differ.
JetBrains Mono is maintained at
[github.com/JetBrains/JetBrainsMono](https://github.com/JetBrains/JetBrainsMono)
and is distributed under the SIL Open Font License 1.1. The complete license
text is preserved in `assets/JetBrainsMono-OFL.txt` in the packaged Skill and
in the font CSS carried by standalone HTML and SVG exports.
## No additional rights granted
Brand names, logos, and trademarks remain the property of their respective
owners. This notice records provenance and known terms; it does not grant rights
that Archify does not hold, and it does not state that every packaged mark has
been cleared for every commercial, promotional, or redistributive use.
+93
View File
@@ -0,0 +1,93 @@
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
+13709
View File
File diff suppressed because it is too large. Load diff
+6936
View File
File diff suppressed because it is too large. Load diff
+91
View File
@@ -0,0 +1,91 @@
import { spawn } from 'node:child_process';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const childPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'scripts', 'delivery-update-child.mjs');
const DEADLINE_MS = 1_000;
const MAX_OUTPUT_BYTES = 16 * 1_024;
function unavailable(reason) {
return { status: 'unavailable', installedVersion: null, availableVersion: null,
releaseNotes: null, checkedAt: null, source: null, noticeRequired: false,
noticeText: null, reason };
}
function normalize(result) {
if (result?.status === 'current') return {
status: 'current', installedVersion: result.installedVersion,
availableVersion: result.availableVersion, releaseNotes: null,
checkedAt: result.checkedAt, source: result.source,
noticeRequired: false, noticeText: null,
};
if (result?.status !== 'update_available') {
return unavailable(result?.reason || (result?.status === 'silent' ? 'no-update' : 'invalid-result'));
}
const noticeRequired = result.noticeRequired !== false;
const cached = result.source === 'cache';
const noticeText = noticeRequired
? `Archify ${result.severity === 'security' ? 'security update' : 'update'}: ${result.installedVersion} → ${result.latestVersion}. ${cached
? `A previous check at ${result.checkedAt} found a newer release.`
: 'A newer release is available.'} Release notes: ${result.releaseNotes}. The installed Skill has not changed; ask to snooze or ignore this reminder.`
: null;
return {
status: 'update_available',
installedVersion: result.installedVersion,
availableVersion: result.latestVersion,
releaseNotes: result.releaseNotes,
severity: result.severity,
checkedAt: result.checkedAt,
source: result.source,
noticeRequired,
noticeText,
...(result.eventKey ? { eventKey: result.eventKey } : {}),
...(result.reason ? { reason: result.reason } : {}),
...(result.suppressedUntil ? { suppressedUntil: result.suppressedUntil } : {}),
};
}
export function startDeliveryUpdateCheck({
env = process.env, deadlineMs = DEADLINE_MS, checkerPath = childPath,
} = {}) {
if (env.ARCHIFY_UPDATE_CHECK_DISABLED === '1') return Promise.resolve(unavailable('disabled'));
return new Promise((resolve) => {
let child;
try {
const deadlineNs = process.hrtime.bigint() + BigInt(Math.floor(deadlineMs * 1_000_000));
child = spawn(process.execPath, [checkerPath, String(deadlineNs)], {
env,
stdio: ['ignore', 'pipe', 'ignore'],
windowsHide: true,
});
} catch {
resolve(unavailable('runtime-unavailable'));
return;
}
let output = '';
let timedOut = false;
let overflow = false;
const timer = setTimeout(() => {
timedOut = true;
child.kill('SIGKILL');
}, deadlineMs);
child.stdout.on('data', (chunk) => {
if (Buffer.byteLength(output) + chunk.length > MAX_OUTPUT_BYTES) {
overflow = true;
child.kill('SIGKILL');
} else output += chunk.toString('utf8');
});
child.on('error', () => {});
child.on('close', (code) => {
clearTimeout(timer);
if (overflow) return resolve(unavailable('invalid-result'));
// A synchronous renderer can delay both this callback and the parent's
// timer. A complete child result wins even if the timer fired meanwhile.
if (code === 0 && output.endsWith('\n')) {
try { return resolve(normalize(JSON.parse(output))); }
catch { return resolve(unavailable('invalid-result')); }
}
resolve(unavailable(timedOut || child.signalCode === 'SIGKILL' ? 'timeout' : 'check-failed'));
});
});
}
+895
View File
@@ -0,0 +1,895 @@
import { execFile } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { canonicalFuturePath, pathsAlias, resolveNativeOutputDirectory, resolveOutputPath } from '../renderers/shared/output-path.mjs';
import { boundedSidecarStem } from '../renderers/shared/sidecar-path.mjs';
import {
captureAtomicOutput, captureRegularFileBinding, publishRegularFileBinding,
releaseRegularFileBinding, removeOwnedRegularFile, verifyAtomicOutput,
} from '../renderers/shared/atomic-output.mjs';
import {
browserCheckSidecarPaths,
CAPTURE_VIEWPORTS,
ChromeVisualBrowser,
findChrome,
VISUAL_CHECK_VIEWPORTS,
} from './visual-check.mjs';
import { startDeliveryUpdateCheck } from './delivery-update.mjs';
export const FINALIZE_STAGES = Object.freeze(['validate', 'deliver', 'check', 'browser-check']);
const FINALIZE_UPDATE_DEADLINE_MS = 4_000;
function sha256(buffer) {
return createHash('sha256').update(buffer).digest('hex');
}
function identity(file) {
try {
const bytes = fs.readFileSync(file);
return { path: path.resolve(file), sha256: sha256(bytes), bytes: bytes.byteLength };
} catch {
return { path: path.resolve(file) };
}
}
function durationMs(start) {
return Number((process.hrtime.bigint() - start) / 1000000n);
}
function receiptWriteError(file, state) {
const error = new Error(`Could not publish finalize evidence "${file}" safely (${state.reason?.code || state.status}).`);
error.finalizeCode = 'finalize/receipt-publication';
error.finalizeEvidence = { file, state };
return error;
}
function captureReceipt(file) {
fs.mkdirSync(path.dirname(file), { recursive: true });
const capture = captureAtomicOutput(file, { requestedEntryPolicy: 'regular-or-absent' });
if (capture.status !== 'captured') throw receiptWriteError(file, capture);
return capture;
}
function writeJsonAtomic(file, value, capture, beforeCommit) {
const serialized = Buffer.from(`${JSON.stringify(value, null, 2)}\n`);
const temporary = path.join(path.dirname(capture.commitPath), `.archify-finalize-${randomUUID()}.json`);
let binding;
let stagedIdentity;
try {
fs.writeFileSync(temporary, serialized, { flag: 'wx' });
const metadata = fs.lstatSync(temporary, { bigint: true });
stagedIdentity = { device: metadata.dev, inode: metadata.ino };
if (capture.mode !== null) fs.chmodSync(temporary, capture.mode);
const staged = captureRegularFileBinding(temporary, {
subject: 'finalize-receipt', expectedIdentity: stagedIdentity,
expectedSha256: sha256(serialized), expectedBytes: serialized.byteLength,
...(capture.mode === null ? {} : { expectedMode: capture.mode }),
});
if (staged.status !== 'captured') throw receiptWriteError(file, staged);
binding = staged.binding;
beforeCommit();
const publication = publishRegularFileBinding(binding, temporary, capture.snapshot, { subject: 'finalize-receipt' });
if (!['committed', 'committed-with-warning'].includes(publication.status)) throw receiptWriteError(file, publication);
// Preserve recovery evidence instead of reporting a clean completion when cleanup failed.
if (publication.status === 'committed-with-warning') throw receiptWriteError(file, publication);
const next = captureReceipt(file);
if (!pathsAlias(next.commitPath, capture.commitPath)) {
throw receiptWriteError(file, { status: 'different', reason: { code: 'receipt-slot-changed' } });
}
return next;
} finally {
if (binding) releaseRegularFileBinding(binding);
if (stagedIdentity) removeOwnedRegularFile(temporary, stagedIdentity, { subject: 'finalize-receipt' });
}
}
function parsedReceipt(stdout) {
const source = String(stdout || '').trim();
if (!source) return null;
try { return JSON.parse(source); } catch { return null; }
}
function isReceiptObject(receipt) {
return Boolean(receipt && typeof receipt === 'object' && !Array.isArray(receipt));
}
function validIdentity(value) {
return isReceiptObject(value)
&& typeof value.sha256 === 'string'
&& /^[0-9a-f]{64}$/.test(value.sha256)
&& Number.isSafeInteger(value.bytes)
&& value.bytes >= 0;
}
function identitiesMatch(left, right) {
return validIdentity(left)
&& validIdentity(right)
&& left.sha256 === right.sha256
&& left.bytes === right.bytes;
}
function validReceiptId(value) {
return typeof value === 'string'
&& /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
}
function allChecksPassed(checks) {
return Array.isArray(checks) && checks.length > 0
&& checks.every((check) => isReceiptObject(check) && check.ok === true);
}
function validCheckComposition(receipt, quality) {
const composition = receipt?.composition;
return isReceiptObject(composition)
&& composition.schemaVersion === 1
&& composition.profile === quality
&& composition.status === 'pass'
&& composition.summary?.errors === 0
&& (quality !== 'showcase' || composition.summary?.warnings === 0);
}
function exactViewportCoverage(entries, viewports, predicate) {
if (!Array.isArray(entries) || entries.length !== viewports.length) return false;
return viewports.every((expected) => {
const matching = entries.filter((entry) => (
isReceiptObject(entry)
&& entry.width === expected.width
&& entry.height === expected.height
&& (expected.theme === undefined || entry.requestedTheme === expected.theme)
));
return matching.length === 1 && predicate(matching[0], expected);
});
}
function validBrowserEvidence(receipt) {
return exactViewportCoverage(receipt.containment?.viewports, VISUAL_CHECK_VIEWPORTS, (entry) => (
entry.theme === 'light' && entry.ok === true
))
&& exactViewportCoverage(receipt.readability?.viewports, VISUAL_CHECK_VIEWPORTS, (entry) => (
entry.theme === 'light' && entry.ok === true && entry.readabilityOk === true
))
&& exactViewportCoverage(receipt.viewerChrome?.viewports, VISUAL_CHECK_VIEWPORTS, (entry) => (
entry.theme === 'light' && entry.ok === true && entry.viewerChromeOk === true
))
&& exactViewportCoverage(receipt.themeStates?.viewports,
[...VISUAL_CHECK_VIEWPORTS.map((viewport) => ({ ...viewport, theme: 'light' })),
...CAPTURE_VIEWPORTS.map((viewport) => ({ ...viewport, theme: 'dark' }))],
(entry, expected) => entry.ok === true
&& entry.requestedTheme === expected.theme
&& entry.resolvedTheme === expected.theme);
}
function validDeliveryValidation(receipt, quality, { allowShowcaseWarnings = false } = {}) {
const validation = receipt?.validation;
return isReceiptObject(validation)
&& Number.isInteger(validation.checkCount)
&& validation.checkCount > 0
&& validation.checksPassed === validation.checkCount
&& validation.compositionStatus === 'pass'
&& validation.errors === 0
&& validation.compositionProfile === quality
&& (quality !== 'showcase' || allowShowcaseWarnings || validation.warnings === 0);
}
function validStageReceipt(stage, receipt, quality, options) {
if (!isReceiptObject(receipt) || receipt.ok !== true) return false;
if (receipt.status && receipt.status !== 'pass') return false;
if (stage === 'validate') return receipt.command === 'validate' && Array.isArray(receipt.checks);
if (stage === 'deliver') {
return receipt.command === 'deliver'
&& receipt.schemaVersion === 1
&& validReceiptId(receipt.receiptId)
&& isReceiptObject(receipt.specification)
&& validIdentity(receipt.specification)
&& validIdentity(receipt.artifact)
&& validDeliveryValidation(receipt, quality, options);
}
if (stage === 'check') {
return validIdentity(receipt.artifact)
&& allChecksPassed(receipt.checks)
&& receipt.provenance === 'current'
&& validReceiptId(receipt.deliveryReceiptId)
&& validCheckComposition(receipt, quality);
}
return receipt.command === 'browser-check'
&& receipt.schemaVersion === 1
&& receipt.status === 'pass'
&& receipt.evidenceKind === 'automated-browser'
&& validIdentity(receipt.artifact)
&& receipt.provenance === 'current'
&& validReceiptId(receipt.deliveryReceiptId)
&& receipt.containment?.status === 'pass'
&& receipt.themeStates?.status === 'pass'
&& receipt.readability?.status === 'pass'
&& receipt.viewerChrome?.status === 'pass'
&& validBrowserEvidence(receipt);
}
function identityMismatchDiagnostic({ stage, expected, actual, expectedReceiptId, actualReceiptId, output }) {
return {
code: 'finalize/artifact-binding-mismatch',
severity: 'error',
message: `The ${stage} receipt does not prove the artifact delivered by this finalize run.`,
subject: { stage, artifact: output },
evidence: {
expectedArtifact: expected,
actualArtifact: actual,
...(expectedReceiptId ? { expectedDeliveryReceiptId: expectedReceiptId } : {}),
...(actualReceiptId ? { actualDeliveryReceiptId: actualReceiptId } : {}),
},
supportedFixes: ['finish other delivery attempts for this output, then rerun finalize from the frozen candidate'],
};
}
function showcaseWarningDiagnostics(receipt) {
const issues = receipt.validation.compositionIssues;
const warnings = receipt.validation.warnings;
if (!Array.isArray(issues) || issues.length !== warnings
|| !issues.every((issue) => isReceiptObject(issue)
&& issue.severity === 'warning'
&& typeof issue.code === 'string'
&& issue.code.startsWith('composition/')
&& typeof issue.detail === 'string' && issue.detail.trim())) {
return [{
code: 'finalize/showcase-warnings', severity: 'error',
message: `Showcase delivery reported ${warnings} composition warning(s), but did not provide matching issue details.`,
subject: { stage: 'deliver', check: 'composition' },
evidence: { warnings, reportedIssues: Array.isArray(issues) ? issues.length : null },
supportedFixes: ['run validate on the frozen candidate to inspect the composition warnings, repair them, then rerun finalize'],
}];
}
return issues.map((issue) => {
const { code, severity, detail, ...evidence } = issue;
return {
code, severity: 'error',
message: `Showcase delivery reported ${code}; zero warnings are required.`,
subject: { stage: 'deliver', check: 'composition', ...(issue.nodeId ? { nodeId: issue.nodeId } : {}) },
evidence: { reportedSeverity: severity, ...evidence },
supportedFixes: [detail.replace(/^\[[^\]]+\]\s*/, '')],
};
});
}
function stageBindingDiagnostic({ stage, receipt, expectedArtifact, expectedReceiptId, output, specification, type, deliveryValidation }) {
if (stage === 'deliver') {
if (receipt.type !== type) {
return {
code: 'finalize/delivery-type-mismatch',
severity: 'error',
message: 'The delivery command reported a different diagram type than finalize requested.',
subject: { stage: 'deliver', artifact: output },
evidence: { expectedType: type, actualType: receipt.type },
supportedFixes: ['rerun finalize with a delivery command for the requested diagram type'],
};
}
if (!identitiesMatch(receipt.specification, specification)) {
return {
code: 'finalize/candidate-changed-during-delivery',
severity: 'error',
message: 'The delivery command froze a different candidate than finalize started with.',
subject: { stage: 'validate', candidate: specification.path },
evidence: {
expectedSha256: specification.sha256,
actualSha256: receipt.specification?.sha256,
expectedBytes: specification.bytes,
actualBytes: receipt.specification?.bytes,
},
supportedFixes: ['restore the frozen candidate and rerun finalize'],
};
}
if ((!receipt.output || !pathsAlias(receipt.output, output)) || !identitiesMatch(receipt.artifact, identity(output))) {
return identityMismatchDiagnostic({
stage,
expected: receipt.artifact,
actual: identity(output),
expectedReceiptId: receipt.receiptId,
output,
});
}
return null;
}
if (stage === 'check' && deliveryValidation?.checkCount !== receipt.checks.length) {
return {
code: 'finalize/delivery-validation-mismatch',
severity: 'error',
message: 'The delivery validation count does not match the complete checker receipt for the delivered artifact.',
subject: { stage: 'check', artifact: output },
evidence: {
deliveryCheckCount: deliveryValidation?.checkCount,
checkerCheckCount: receipt.checks.length,
},
supportedFixes: ['restore the complete delivery validation receipt and rerun finalize from the frozen candidate'],
};
}
const receiptArtifactPath = receipt.artifact?.path;
const receiptFile = receipt.file;
const pathMatchesOutput = stage === 'check'
? typeof receiptFile === 'string' && pathsAlias(receiptFile, output)
: typeof receiptArtifactPath === 'string' && pathsAlias(receiptArtifactPath, output);
if (!pathMatchesOutput
|| !identitiesMatch(receipt.artifact, expectedArtifact)
|| receipt.deliveryReceiptId !== expectedReceiptId) {
return identityMismatchDiagnostic({
stage,
expected: expectedArtifact,
actual: receipt.artifact,
expectedReceiptId,
actualReceiptId: receipt.deliveryReceiptId,
output,
});
}
return null;
}
function finalArtifactDiagnostic({ output, expectedArtifact, expectedReceiptId, type, deliveryPath, inspectDelivery }) {
// Recheck dev's pending/lock, physical identity, and regular-file provenance barriers at handoff.
const provenance = inspectDelivery?.(output);
if (provenance && (!provenance.ok || provenance.receiptId !== expectedReceiptId)) {
return provenance.diagnostics?.[0] || identityMismatchDiagnostic({
stage: 'finalize', output, expected: expectedArtifact,
expectedReceiptId, actualReceiptId: provenance.receiptId,
});
}
const currentArtifact = identity(output);
let delivery;
try {
delivery = JSON.parse(fs.readFileSync(deliveryPath, 'utf8'));
} catch {
delivery = null;
}
if (identitiesMatch(currentArtifact, expectedArtifact)
&& delivery?.schemaVersion === 1
&& delivery.command === 'deliver'
&& delivery.status === 'current'
&& delivery.receiptId === expectedReceiptId
&& delivery.type === type
&& typeof delivery.output === 'string' && pathsAlias(delivery.output, output)
&& identitiesMatch(delivery.artifact, expectedArtifact)) return null;
return {
code: 'finalize/final-artifact-mismatch',
severity: 'error',
message: 'The artifact or delivery receipt changed after the final gate completed.',
subject: { stage: 'finalize', artifact: output },
evidence: {
expectedArtifact,
currentArtifact,
expectedDeliveryReceiptId: expectedReceiptId,
expectedType: type,
...(delivery?.receiptId ? { currentDeliveryReceiptId: delivery.receiptId } : {}),
...(delivery?.type ? { currentType: delivery.type } : {}),
...(delivery?.artifact ? { currentDeliveryArtifact: delivery.artifact } : {}),
},
supportedFixes: ['finish other delivery attempts for this output, then rerun finalize from the frozen candidate'],
};
}
function stageStatus(stage, exitCode, receipt, quality) {
if (exitCode === 2 || receipt?.status === 'skipped') return 'skipped';
return exitCode === 0 && validStageReceipt(stage, receipt, quality) ? 'pass' : 'fail';
}
function failureDiagnostics(stage, result, receipt, quality) {
if (Array.isArray(receipt?.diagnostics) && receipt.diagnostics.length) return receipt.diagnostics;
const invalidReceipt = (result.status ?? 1) === 0 && !validStageReceipt(stage, receipt, quality);
return [{
code: invalidReceipt ? 'finalize/invalid-stage-receipt' : 'finalize/stage-failure',
severity: 'error',
message: invalidReceipt
? `The ${stage} stage exited successfully without a valid passing receipt.`
: `The ${stage} stage did not complete successfully.`,
subject: { stage },
evidence: {
exitCode: result.status ?? 1,
...(result.signal ? { signal: result.signal } : {}),
...(result.error?.message ? { reason: result.error.message } : {}),
...(String(result.stdout || '').trim() ? { stdout: String(result.stdout).trim().slice(0, 2000) } : {}),
...(String(result.stderr || '').trim() ? { stderr: String(result.stderr).trim().slice(0, 2000) } : {}),
},
supportedFixes: [invalidReceipt
? `restore the ${stage} JSON receipt contract before retrying finalize`
: 'use the compact finalize summary to repair the named subject in place, then rerun finalize once'],
}];
}
function sidecarFile(directory, value) {
if (!value) return null;
return path.resolve(directory, value);
}
function browserEvidence(receipt, artifactPath) {
if (!receipt) return {};
const directory = receipt.sidecars?.directory
? path.resolve(receipt.sidecars.directory)
: path.dirname(path.resolve(artifactPath));
return {
...(receipt.sidecars?.receipt ? { browserCheckReceipt: sidecarFile(directory, receipt.sidecars.receipt) } : {}),
};
}
function defaultRunner({ cliPath, args, cwd, env }) {
return new Promise((resolve) => {
execFile(process.execPath, [cliPath, ...args], { cwd, env, encoding: 'utf8' }, (error, stdout, stderr) => {
resolve({
status: error ? (Number.isInteger(error.code) ? error.code : 1) : 0,
stdout,
stderr,
...(error ? { error, signal: error.signal } : {}),
});
});
});
}
function stageArguments({ stage, type, input, output, quality, repoRoot, outDir }) {
const qualityArgs = ['--quality', quality];
const repoArgs = repoRoot ? ['--repo-root', repoRoot] : [];
if (stage === 'validate') return ['validate', type, input, ...qualityArgs, ...repoArgs, '--json'];
if (stage === 'deliver') return ['deliver', type, input, output, ...qualityArgs, ...repoArgs, '--json'];
if (stage === 'check') return ['check', output, '--require-provenance'];
return [
'browser-check', output, '--json', '--require-provenance',
...(outDir ? ['--out-dir', outDir] : []),
];
}
export function defaultFinalizeReceiptPath(output, { outDir } = {}) {
// Reuse the physical artifact and cross-directory namespace used by browser evidence.
const browserReceipt = browserCheckSidecarPaths(output, { outDir }).receipt;
const stem = path.basename(browserReceipt).slice(0, -'.browser-check.json'.length);
return path.join(path.dirname(browserReceipt), `${boundedSidecarStem(stem, ['.finalize.json', '.finalize-summary.json'])}.finalize.json`);
}
export function defaultFinalizeSummaryPath(receiptPath) {
const receipt = path.resolve(receiptPath);
const component = path.basename(receipt);
const isDefault = /\.finalize\.json$/i.test(component);
const stem = component.replace(isDefault ? /\.finalize\.json$/i : /\.json$/i, '');
const suffix = isDefault ? '.finalize-summary.json' : '-summary.json';
// Already bounded default stems share the receipt namespace; avoid hashing twice.
const summaryStem = Buffer.byteLength(`${stem}${suffix}`) <= 255 && `${stem}${suffix}`.length <= 255
? stem : boundedSidecarStem(stem, [suffix]);
return path.join(path.dirname(receipt), `${summaryStem}${suffix}`);
}
function defaultDeliveryPaths(output) {
const artifact = canonicalFuturePath(output);
const delivery = artifact.replace(/\.html?$/i, '.delivery.json');
return {
provenance: delivery,
pending: delivery.replace(/\.json$/i, '-pending.json'),
lock: delivery.replace(/\.json$/i, '-lock.json'),
directoryLock: path.join(path.dirname(artifact), '.archify-delivery-lock.json'),
};
}
function reservedFinalizePaths({ input, output, outDir, deliveryPaths }) {
const browser = browserCheckSidecarPaths(output, { outDir });
return [path.resolve(input), path.resolve(output), ...Object.values(deliveryPaths(output)), browser.receipt];
}
// Node moves that remove the measured crossings and detours, most specific
// first. Only positions and sizes change; every relationship keeps its endpoints.
function placementHints({ crossings = [], detours = [], crowdedSides = [] }) {
const name = (relation) => `${relation.from} → ${relation.to}`;
const other = (relation, shared) => (relation.from === shared ? relation.to : relation.from);
const hints = [];
for (const side of crowdedSides) {
hints.push(`${side.node} has ${side.relationships} relationships facing its ${side.side} side, which fits ${Math.max(1, Math.floor((side.sidePx - 32) / 14) + 1)} ports: make that side at least ${side.neededPx}px, or move some of those neighbours so they face another side of ${side.node}.`);
}
for (const crossing of crossings) {
const shared = crossing.sharedNode;
hints.push(shared
? `${name(crossing.left)} and ${name(crossing.right)} cross next to ${shared}: move node ${other(crossing.left, shared)} or ${other(crossing.right, shared)} so the two reach ${shared} from different sides (for example one level with it, one directly above or below it).`
: `${name(crossing.left)} crosses ${name(crossing.right)}: move the node of whichever is a branch, return, or second entrance to the other side of the main path, so that relationship runs through an empty corridor.`);
}
for (const detour of detours) {
if (detour.directCorridorBlockers?.length) continue;
hints.push(`${name(detour.relationship)} needs ${detour.bends} bends: move node ${detour.relationship.from} or ${detour.relationship.to} so they share a row or column with matching centers, or sit diagonally with a clear corner.`);
}
return [...new Set(hints)].slice(0, 8);
}
export function compactFinalizeReceipt(receipt) {
const gates = {};
for (const stage of FINALIZE_STAGES) gates[stage] = receipt.stages?.[stage]?.status || 'not-run';
const allDiagnostics = receipt.diagnostics || [];
const diagnosticLimit = 8;
const selectedDiagnostics = [];
const selectedIndexes = new Set();
const seenCodes = new Set();
const seenSubjects = new Set();
const subjectKey = (entry) => {
const subject = entry?.subject;
if (!subject) return null;
if (typeof subject === 'string') return subject;
if (typeof subject !== 'object' || Array.isArray(subject)) return JSON.stringify(subject);
for (const key of ['id', 'edge', 'connection', 'relationship', 'component', 'node', 'path', 'stage']) {
if (subject[key] !== undefined) return `${key}:${JSON.stringify(subject[key])}`;
}
return JSON.stringify(subject);
};
const addDiagnostic = (entry, index) => {
if (selectedDiagnostics.length >= diagnosticLimit || selectedIndexes.has(index)) return;
selectedIndexes.add(index);
selectedDiagnostics.push({
code: entry.code,
severity: entry.severity || 'error',
message: entry.message,
...(entry.subject ? { subject: entry.subject } : {}),
...(entry.evidence && Object.keys(entry.evidence).length ? { evidence: entry.evidence } : {}),
...(Array.isArray(entry.supportedFixes) && entry.supportedFixes.length
? { supportedFixes: entry.supportedFixes.slice(0, 2) } : {}),
});
};
for (const [index, entry] of allDiagnostics.entries()) {
if (seenCodes.has(entry.code)) continue;
seenCodes.add(entry.code);
addDiagnostic(entry, index);
}
for (const [index, entry] of allDiagnostics.entries()) {
const key = subjectKey(entry);
if (!key || seenSubjects.has(key)) continue;
seenSubjects.add(key);
addDiagnostic(entry, index);
}
for (const [index, entry] of allDiagnostics.entries()) addDiagnostic(entry, index);
const compact = {
schemaVersion: 1,
ok: receipt.ok,
command: 'finalize',
status: receipt.status,
type: receipt.type,
quality: receipt.quality,
specification: receipt.specification,
artifact: receipt.artifact,
gates,
...(receipt.failedStage ? { failedStage: receipt.failedStage } : {}),
diagnostics: selectedDiagnostics,
diagnosticSummary: {
total: allDiagnostics.length,
shown: selectedDiagnostics.length,
truncated: allDiagnostics.length > selectedDiagnostics.length,
},
evidence: receipt.evidence,
...(receipt.update ? { update: receipt.update } : {}),
visualReview: receipt.visualReview || 'not-requested',
durationMs: receipt.durationMs,
};
const metrics = receipt.stages?.check?.receipt?.composition?.metrics;
const reviewSignals = Object.fromEntries([
'resolvedCrossovers', 'routesOverSuggestedBends', 'routesOverSuggestedStretch',
].filter((key) => Number.isFinite(metrics?.[key]) && metrics[key] > 0)
.map((key) => [key, metrics[key]]));
if (receipt.ok && Object.keys(reviewSignals).length) {
const routeReview = receipt.stages?.check?.receipt?.composition?.routeReview;
const hints = routeReview ? placementHints(routeReview) : [];
compact.visualReviewRecommendation = {
action: 'inspect-route-readability',
signals: reviewSignals,
reason: 'Automated gates passed, but crossings or detours still need perceptual review before claiming visual quality.',
...(routeReview ? {
affectedRoutes: {
crossings: routeReview.crossings.slice(0, 8),
detours: routeReview.detours.slice(0, 8),
truncated: routeReview.crossings.length > 8 || routeReview.detours.length > 8,
},
...(hints.length ? { hints } : {}),
repair: 'Trace these relationships at the desktop viewport. For Architecture, use references/architecture-layout-repair.md: reflow a blocked main path or tangled connected scene, and repair an isolated defect locally only when the surrounding composition is accepted. Preserve all semantic content and user-fixed geometry. Rerun finalize once after the edit.',
} : {}),
};
}
const leadingSpace = receipt.stages?.check?.receipt?.composition?.leadingSpace;
if (receipt.ok && leadingSpace?.reviewSuggested === true) {
compact.layoutReviewRecommendation = {
action: 'inspect-leading-space',
evidence: leadingSpace,
reason: 'Measured content, including routes and labels, leaves a large empty area above the diagram. This is a composition suggestion, not a failed gate.',
repair: 'Check whether that leading space is intentional. If not, reposition the connected scene nearer the canvas origin while retaining room for its actual boundaries, labels and return routes. Preserve all meaning and user-fixed geometry, then rerun finalize. No screenshot is required.',
};
}
const sequenceColumnSpace = receipt.stages?.check?.receipt?.composition?.sequenceColumnSpace;
if (receipt.ok && receipt.type === 'sequence' && sequenceColumnSpace?.reviewSuggested === true) {
compact.layoutReviewRecommendation = {
action: 'inspect-sequence-width',
evidence: sequenceColumnSpace,
reason: 'Fixed participant columns leave substantial unused space on the right, after accounting for message labels and notes. This is a layout suggestion, not a failed gate.',
repair: 'For a newly authored Sequence with omitted meta.column_fit and no user-fixed column geometry, set meta.column_fit to "spread" and rerun finalize once. Preserve participant order, every message, its y position, labels, notes and sources. Retain explicit fixed layouts and legacy inputs; report the suggestion instead of changing them automatically.',
};
}
if (!receipt.ok && receipt.status === 'fail' && receipt.failedStage === 'validate') {
compact.nextAction = {
action: 'edit-in-place',
candidate: receipt.specification?.path,
constraint: receipt.type === 'architecture'
? 'Preserve all semantics and user-fixed geometry. Use references/architecture-layout-repair.md to choose a local repair or connected-scene reflow; edit the existing candidate.'
: 'Preserve unaffected semantics and geometry; do not replace the whole candidate.',
then: 'finalize-once',
};
}
return compact;
}
export async function runFinalize({
cliPath,
type,
input,
output,
quality = 'showcase',
repoRoot,
candidateSha256,
outDir,
receiptPath,
deliveryPaths = defaultDeliveryPaths,
inspectDelivery,
cwd = process.cwd(),
env = process.env,
runCommand = defaultRunner,
runBrowserCheck,
startUpdateCheck = startDeliveryUpdateCheck,
resolveChrome = findChrome,
createBrowser = (chromePath, options) => new ChromeVisualBrowser(chromePath, options),
} = {}) {
if (!cliPath || !type || !input || !output) throw new Error('finalize requires cliPath, type, input, and output.');
const started = process.hrtime.bigint();
const startedAt = new Date().toISOString();
const resolvedInput = path.resolve(input);
const resolvedOutput = resolveOutputPath({ requestedOutput: output, inputPaths: [resolvedInput] }).outputPath;
const resolvedOutDir = outDir === undefined ? undefined : resolveNativeOutputDirectory(outDir);
const specification = identity(resolvedInput);
if (candidateSha256 && specification.sha256 !== candidateSha256) {
const error = new Error(`The candidate changed after validation: expected sha256 ${candidateSha256}, found ${specification.sha256 || 'unreadable'}.`);
error.finalizeCode = 'finalize/candidate-changed';
error.finalizeEvidence = {
candidate: resolvedInput,
expectedSha256: candidateSha256,
...(specification.sha256 ? { actualSha256: specification.sha256 } : {}),
};
throw error;
}
// Validate raw native CLI paths before creating directories or normalizing them.
if (receiptPath !== undefined) resolveOutputPath({ requestedOutput: receiptPath, inputPaths: [resolvedInput], requiredExtension: '.json' });
fs.mkdirSync(path.dirname(canonicalFuturePath(resolvedOutput)), { recursive: true });
const resolvedReceipt = resolveOutputPath({
requestedOutput: receiptPath ?? defaultFinalizeReceiptPath(resolvedOutput, { outDir: resolvedOutDir }),
inputPaths: [resolvedInput], requiredExtension: '.json',
}).outputPath;
const resolvedSummary = resolveOutputPath({
requestedOutput: defaultFinalizeSummaryPath(resolvedReceipt), inputPaths: [resolvedInput], requiredExtension: '.json',
}).outputPath;
const assertReceiptPaths = () => {
const reserved = reservedFinalizePaths({ input: resolvedInput, output: resolvedOutput, outDir: resolvedOutDir, deliveryPaths });
const receiptCollision = reserved.find((file) => pathsAlias(resolvedReceipt, file));
if (receiptCollision) throw new Error(`The finalize receipt must be distinct from the specification, artifact, and gate sidecars: "${receiptCollision}".`);
const summaryCollision = [resolvedReceipt, ...reserved].find((file) => pathsAlias(resolvedSummary, file));
if (summaryCollision) throw new Error(`The finalize summary must be distinct from the full receipt, specification, artifact, and gate sidecars: "${summaryCollision}".`);
};
assertReceiptPaths();
// Capture both paths before changing either: an unsafe summary cannot leave a new full receipt behind.
let receiptCapture = captureReceipt(resolvedReceipt);
let summaryCapture = captureReceipt(resolvedSummary);
const receipt = {
schemaVersion: 1,
ok: false,
command: 'finalize',
status: 'running',
type,
quality,
startedAt,
specification,
artifact: { path: resolvedOutput },
stages: {},
diagnostics: [],
evidence: { receipt: resolvedReceipt, summaryReceipt: resolvedSummary },
visualReview: 'not-requested',
};
const persistReceipts = () => {
assertReceiptPaths();
for (const [file, capture] of [[resolvedReceipt, receiptCapture], [resolvedSummary, summaryCapture]]) {
const current = verifyAtomicOutput(capture.snapshot);
if (current.status !== 'match') throw receiptWriteError(file, current);
}
receiptCapture = writeJsonAtomic(resolvedReceipt, receipt, receiptCapture, assertReceiptPaths);
summaryCapture = writeJsonAtomic(resolvedSummary, compactFinalizeReceipt(receipt), summaryCapture, assertReceiptPaths);
};
persistReceipts();
// The gates below usually take seconds, so a slower network can finish the
// update check in parallel instead of timing out on every delivery.
const updateCheck = startUpdateCheck({ env, deadlineMs: FINALIZE_UPDATE_DEADLINE_MS });
// Only launch/attach the blank browser here. The normal browser gate still
// verifies current delivery provenance before it consumes this one-shot factory.
const chromePath = runBrowserCheck ? resolveChrome({ env }) : null;
let browser;
let browserStartupError;
let browserTransferred = false;
if (chromePath) {
try {
browser = createBrowser(chromePath, { env });
// Deliver/check may fail before inspect() awaits startup. Handle the
// rejection now while retaining the same promise for the browser gate.
browser.sessionPromise.catch(() => {});
} catch (error) {
browserStartupError = error;
}
}
const browserFactory = () => {
if (browserStartupError) throw browserStartupError;
if (browserTransferred || !browser) throw new Error('The finalize browser is unavailable or already consumed.');
browserTransferred = true;
return browser;
};
try {
let exitCode = 0;
for (const stage of ['deliver', 'check', 'browser-check']) {
const stageStarted = process.hrtime.bigint();
const args = stageArguments({
stage,
type,
input: resolvedInput,
output: resolvedOutput,
quality,
repoRoot,
outDir: resolvedOutDir,
});
const inProcess = stage === 'browser-check' && runBrowserCheck;
let result;
if (inProcess) {
const checked = await runBrowserCheck({
artifactPath: resolvedOutput,
outDir: resolvedOutDir,
chromePath,
resolveChrome: () => chromePath,
browserFactory,
});
result = { status: checked.exitCode, stdout: JSON.stringify(checked.receipt) };
} else {
result = await runCommand({ stage, cliPath, args, cwd,
env: stage === 'deliver' ? { ...env, ARCHIFY_UPDATE_CHECK_DISABLED: '1' } : env });
}
const stageReceipt = parsedReceipt(result.stdout);
const code = result.status ?? 1;
let status = stageStatus(stage, code, stageReceipt, quality);
let stageDiagnostics;
const command = [process.execPath, cliPath, ...args];
const elapsed = durationMs(stageStarted);
if (status === 'pass') {
const deliveryReceipt = stage === 'deliver' ? stageReceipt : receipt.stages.deliver?.receipt;
const bindingDiagnostic = stageBindingDiagnostic({
stage,
receipt: stageReceipt,
expectedArtifact: deliveryReceipt?.artifact,
expectedReceiptId: deliveryReceipt?.receiptId,
output: resolvedOutput,
specification,
type,
deliveryValidation: deliveryReceipt?.validation,
});
if (bindingDiagnostic) stageDiagnostics = [bindingDiagnostic];
} else if (stage === 'deliver' && code === 0 && quality === 'showcase'
&& Number.isSafeInteger(stageReceipt?.validation?.warnings)
&& stageReceipt.validation.warnings > 0
&& validStageReceipt(stage, stageReceipt, quality, { allowShowcaseWarnings: true })) {
const bindingDiagnostic = stageBindingDiagnostic({
stage, receipt: stageReceipt, output: resolvedOutput, specification, type,
});
stageDiagnostics = bindingDiagnostic ? [bindingDiagnostic] : showcaseWarningDiagnostics(stageReceipt);
}
if (stageDiagnostics) {
status = 'fail';
}
const stageEntry = {
status,
exitCode: code,
durationMs: elapsed,
command,
...(inProcess ? { execution: 'in-process' } : {}),
...(stageReceipt ? { receipt: stageReceipt } : {}),
...(!stageReceipt && String(result.stdout || '').trim() ? { stdout: String(result.stdout).trim() } : {}),
...(String(result.stderr || '').trim() ? { stderr: String(result.stderr).trim() } : {}),
...(result.signal ? { signal: result.signal } : {}),
};
if (stage === 'deliver' && status === 'pass') {
receipt.stages.validate = {
status: 'pass',
exitCode: 0,
durationMs: null,
execution: 'embedded-in-deliver',
command,
receipt: {
schemaVersion: 1,
ok: true,
command: 'validate',
specification: stageReceipt.specification,
validation: stageReceipt.validation,
},
};
receipt.stages.deliver = stageEntry;
} else if (stage === 'deliver') {
const validationFailed = stageDiagnostics?.some((diagnostic) => diagnostic.code === 'finalize/candidate-changed-during-delivery')
|| ['input', 'render', 'check'].includes(stageReceipt?.stage);
receipt.stages.validate = validationFailed ? {
...stageEntry,
status: 'fail',
durationMs: null,
execution: 'embedded-in-deliver',
} : { status: 'not-run', execution: 'embedded-in-deliver' };
receipt.stages.deliver = validationFailed
? { status: 'not-run', execution: 'blocked-by-validate' }
: stageEntry;
} else {
receipt.stages[stage] = stageEntry;
}
if (stage === 'deliver' && stageReceipt?.artifact) receipt.artifact = {
path: resolvedOutput,
...stageReceipt.artifact,
};
if (stage === 'browser-check') {
receipt.evidence = {
receipt: resolvedReceipt,
summaryReceipt: resolvedSummary,
...browserEvidence(stageReceipt, resolvedOutput),
};
}
if (status !== 'pass') {
exitCode = status === 'skipped' ? 2 : (code || 1);
receipt.status = status;
receipt.diagnostics = stageDiagnostics || failureDiagnostics(stage, result, stageReceipt, quality);
receipt.failedStage = stage === 'deliver'
&& receipt.stages.validate.status === 'fail' ? 'validate' : stage;
break;
}
persistReceipts();
}
receipt.ok = exitCode === 0;
receipt.status = receipt.ok ? 'pass' : receipt.status === 'running' ? 'fail' : receipt.status;
if (receipt.ok) {
const deliveryReceipt = receipt.stages.deliver?.receipt;
const diagnostic = finalArtifactDiagnostic({
output: resolvedOutput,
expectedArtifact: deliveryReceipt?.artifact,
expectedReceiptId: deliveryReceipt?.receiptId,
type,
deliveryPath: deliveryPaths(resolvedOutput).provenance,
inspectDelivery,
});
if (diagnostic) {
exitCode = 1;
receipt.ok = false;
receipt.status = 'fail';
receipt.failedStage = 'finalize';
receipt.diagnostics = [diagnostic];
}
}
if (receipt.ok && !identitiesMatch(specification, identity(resolvedInput))) {
exitCode = 1;
receipt.ok = false;
receipt.status = 'fail';
receipt.failedStage = 'finalize';
receipt.diagnostics = [{
code: 'finalize/candidate-changed', severity: 'error',
message: 'The frozen candidate changed before the final handoff.',
subject: { candidate: resolvedInput },
evidence: { expected: specification, actual: identity(resolvedInput) },
supportedFixes: ['restore the frozen candidate, or finalize the edited candidate as a new attempt'],
}];
}
receipt.artifact = receipt.ok
? { path: resolvedOutput, ...receipt.stages.deliver.receipt.artifact }
: identity(resolvedOutput);
receipt.finishedAt = new Date().toISOString();
receipt.durationMs = durationMs(started);
receipt.update = await updateCheck;
persistReceipts();
return { exitCode, receipt, summary: compactFinalizeReceipt(receipt) };
} finally {
if (browser && !browserTransferred) await browser.close();
await updateCheck;
}
}
+146
View File
@@ -0,0 +1,146 @@
import { spawnSync } from 'node:child_process';
import path from 'node:path';
const OPENERS = {
darwin: {
command: 'open',
method: 'open',
timeoutMs: 5000,
args: (target) => [target],
},
linux: {
command: 'xdg-open',
method: 'xdg-open',
timeoutMs: 5000,
args: (target) => [target],
},
win32: {
command: 'powershell.exe',
method: 'powershell',
// PowerShell cold starts can approach five seconds on hosted Windows
// runners. Keep the launch bounded without treating normal startup as a
// timeout.
timeoutMs: 15000,
// Keep the command constant and pass the target through a child-only
// environment variable. Paths are never interpolated into executable source.
args: () => [
'-NoProfile',
'-NonInteractive',
'-Command',
'Start-Process -FilePath $env:ARCHIFY_OPEN_TARGET',
],
},
};
function failureDetails(result, opener, timeoutMs) {
const error = result?.error;
if (error?.code === 'ENOENT') {
return {
code: 'opener/unavailable',
reason: `Could not find ${opener.command}. Install or enable the platform opener, then open the target manually.`,
systemCode: 'ENOENT',
};
}
if (error?.code === 'ETIMEDOUT') {
return {
code: 'opener/timeout',
reason: `${opener.command} did not finish within ${timeoutMs}ms. Open the target manually or retry when the system is less busy.`,
systemCode: 'ETIMEDOUT',
timeoutMs,
};
}
if (error) {
return {
code: 'opener/spawn-failed',
reason: error.message || `${opener.command} could not be started. Open the target manually.`,
...(error.code ? { systemCode: String(error.code) } : {}),
};
}
if (result?.signal) {
return {
code: 'opener/signaled',
reason: `${opener.command} was terminated by ${result.signal}. Open the target manually.`,
signal: result.signal,
};
}
if (result?.status !== 0) {
return {
code: 'opener/nonzero-exit',
reason: `${opener.command} exited with status ${result?.status ?? 'unknown'}. Open the target manually.`,
exitCode: result?.status ?? null,
};
}
return null;
}
function launchTimeout(value, fallback) {
return Number.isSafeInteger(value) && value > 0 ? value : fallback;
}
function launchTarget(target, options = {}) {
const platform = options.platform || process.platform;
const opener = OPENERS[platform];
if (!opener) {
return {
requested: true,
status: 'unsupported',
target,
method: null,
};
}
const spawn = options.spawn || spawnSync;
const timeoutMs = launchTimeout(options.timeoutMs, opener.timeoutMs);
let result;
try {
const spawnOptions = {
encoding: 'utf8',
shell: false,
stdio: 'ignore',
timeout: timeoutMs,
windowsHide: true,
};
if (platform === 'win32') {
spawnOptions.env = {
...process.env,
ARCHIFY_OPEN_TARGET: target,
};
}
result = spawn(opener.command, opener.args(target), spawnOptions);
} catch (error) {
result = { error };
}
const failure = failureDetails(result, opener, timeoutMs);
let status = 'opened';
if (failure?.code === 'opener/unavailable') status = 'unsupported';
else if (failure) status = 'failed';
return {
requested: true,
status,
target,
method: opener.method,
...(failure ? { failure } : {}),
};
}
export function openArtifact(target, options = {}) {
return launchTarget(path.resolve(target), options);
}
export function openLoopbackUrl(target, options = {}) {
let url;
try {
url = new URL(target);
} catch {
throw new TypeError('Preview URL must be a valid loopback HTTP URL.');
}
if (url.protocol !== 'http:' || url.hostname !== '127.0.0.1' || !url.port) {
throw new TypeError('Preview URL must be a loopback URL using http://127.0.0.1:<port>.');
}
if (url.username || url.password || url.pathname !== '/' || url.search || url.hash) {
throw new TypeError('Preview URL must target the loopback preview root.');
}
return launchTarget(url.href, options);
}
+969
View File
@@ -0,0 +1,969 @@
import { spawn } from 'node:child_process';
import { createHash } from 'node:crypto';
import fs from 'node:fs';
import http from 'node:http';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { openLoopbackUrl } from './open-artifact.mjs';
import { resolveOutputPath } from '../renderers/shared/output-path.mjs';
import { sameLocation } from '../renderers/shared/path-semantics.mjs';
import {
captureAtomicOutput,
captureRegularFileBinding,
publishRegularFileBinding,
releaseRegularFileBinding,
removeEmptyDirectoryWithRetry,
removeOwnedRegularFile,
verifyAtomicOutput,
} from '../renderers/shared/atomic-output.mjs';
const here = path.dirname(fileURLToPath(import.meta.url));
const cliPath = path.join(here, 'archify.mjs');
const loopbackHost = '127.0.0.1';
const defaultDebounceMs = 400;
const defaultPollMs = 800;
const defaultStopGraceMs = 3000;
const defaultStopKillMs = 750;
const diagramTypes = new Set(['architecture', 'workflow', 'sequence', 'dataflow', 'lifecycle']);
let previewCommitSequence = 0;
function sha256(value) {
return createHash('sha256').update(value).digest('hex');
}
function sourceDigest(inputPath) {
try {
const bytes = fs.readFileSync(inputPath);
return { hash: sha256(bytes), bytes, missing: false };
} catch (error) {
return { hash: `unreadable:${error.code || 'unknown'}`, bytes: null, missing: true };
}
}
function initialAuthoredOutput(inputPath) {
try {
const source = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
if (typeof source?.meta?.output === 'string') {
return source.meta.output;
}
} catch {
// An invalid initial source still gets a status shell. Its output target is
// fixed to the same fallback that `deliver` would use after repair.
}
return undefined;
}
function previewPage() {
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Archify Live Preview</title>
<style>
:root { color-scheme: light dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
* { box-sizing: border-box; }
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; background: #0b111b; }
body { display: grid; grid-template-rows: auto minmax(0, 1fr); color: #e8edf5; }
header { position: relative; z-index: 2; display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 7px 12px; border-bottom: 1px solid #253248; background: rgba(11, 17, 27, .96); box-shadow: 0 8px 22px rgba(0,0,0,.18); }
.brand { font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #9cadc6; }
#status { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; min-height: 30px; padding: 5px 10px; border: 1px solid #33435d; border-radius: 999px; background: #111b2a; font-size: 12px; white-space: nowrap; }
#status::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: #6f819d; }
body[data-state="checking"] #status::before { background: #f3b44b; box-shadow: 0 0 0 4px rgba(243,180,75,.12); }
body[data-state="verified"] #status::before { background: #45d6a8; box-shadow: 0 0 0 4px rgba(69,214,168,.12); }
body[data-state="needs-fix"] #status::before { background: #ff6f78; box-shadow: 0 0 0 4px rgba(255,111,120,.12); }
details { max-width: min(62vw, 760px); }
summary { cursor: pointer; color: #ffbdc2; font-size: 12px; }
.diagnostic { position: absolute; top: 38px; right: 12px; width: min(760px, calc(100vw - 24px)); max-height: min(44vh, 360px); overflow: auto; padding: 14px; border: 1px solid #6a3440; border-radius: 10px; background: #17131b; box-shadow: 0 14px 48px rgba(0,0,0,.42); }
pre { margin: 0 0 10px; white-space: pre-wrap; overflow-wrap: anywhere; font: 11px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; color: #f2dfe2; }
button { min-height: 32px; padding: 5px 10px; border: 1px solid #4a5d79; border-radius: 7px; background: #1a273a; color: #eef4ff; cursor: pointer; }
main { position: relative; min-height: 0; }
iframe { display: none; width: 100%; height: 100%; border: 0; background: #fff; }
body[data-has-artifact="true"] iframe { display: block; }
#empty { position: absolute; inset: 0; display: grid; place-items: center; padding: 32px; color: #91a2bc; text-align: center; background: radial-gradient(circle at 50% 38%, #15233a 0, #0b111b 55%); }
body[data-has-artifact="true"] #empty { display: none; }
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; } }
</style>
</head>
<body data-state="checking" data-has-artifact="false">
<header>
<span class="brand">Archify Preview</span>
<details id="failure" hidden>
<summary role="button" aria-controls="diagnostic-panel">View diagnostic</summary>
<div class="diagnostic" id="diagnostic-panel"><pre id="diagnostic"></pre><button id="copy" type="button">Copy diagnostic</button></div>
</details>
<span id="status" role="status" aria-live="polite">Checking · generation 1</span>
</header>
<main>
<div id="empty">Waiting for the first verified diagram. Invalid input will stay here with an exact diagnostic.</div>
<iframe id="artifact" title="Verified Archify diagram"></iframe>
</main>
<script>
(function () {
'use strict';
var body = document.body;
var status = document.getElementById('status');
var failure = document.getElementById('failure');
var diagnostic = document.getElementById('diagnostic');
var artifact = document.getElementById('artifact');
var lastRevision = 0;
function render(state) {
body.dataset.state = state.status;
if (state.status === 'verified') {
status.textContent = 'Verified · rev ' + state.revision;
failure.hidden = true;
failure.open = false;
if (state.revision !== lastRevision) {
lastRevision = state.revision;
artifact.src = '/artifact.html?revision=' + encodeURIComponent(state.revision) + '&sha=' + encodeURIComponent(state.lastVerified.sha256.slice(0, 12));
body.dataset.hasArtifact = 'true';
}
} else if (state.status === 'needs-fix') {
status.textContent = 'Needs fix · ' + (state.revision ? 'showing rev ' + state.revision : 'no verified revision');
diagnostic.textContent = 'Generation ' + state.generation + ' · ' + state.failure.stage + '\\n\\n' + state.failure.message;
failure.hidden = false;
} else {
status.textContent = 'Checking · generation ' + state.generation;
failure.hidden = true;
}
}
document.getElementById('copy').addEventListener('click', function () {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(diagnostic.textContent).catch(function () {});
}
});
var events = new EventSource('/events');
events.addEventListener('state', function (event) {
try { render(JSON.parse(event.data)); } catch (_) {}
});
}());
</script>
</body>
</html>`;
}
function compactMessage(value) {
let text = String(value || 'Preview build failed without a diagnostic.').trim();
const lines = text.split(/\r?\n/);
const errorLine = lines.findIndex((line) => /^Error:\s/.test(line));
if (errorLine > 0) text = lines.slice(errorLine).join('\n');
const relevant = text.split(/\r?\n/);
const stackLine = relevant.findIndex((line, index) => index > 0 && /^\s*at\s/.test(line));
if (stackLine > 0) text = relevant.slice(0, stackLine).join('\n');
return text.length > 6000 ? `${text.slice(0, 6000)}\n… diagnostic truncated` : text;
}
function redactDiagnostic(value, paths) {
let text = compactMessage(value);
for (const [absolutePath, replacement] of paths) {
if (!absolutePath) continue;
text = text.split(absolutePath).join(replacement);
}
return text;
}
function safeJson(value) {
return JSON.stringify(value).replace(/</g, '\\u003c');
}
function responseHeaders(contentType) {
return {
'Cache-Control': 'no-store',
'Content-Type': contentType,
'Cross-Origin-Resource-Policy': 'same-origin',
'Referrer-Policy': 'no-referrer',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'SAMEORIGIN',
};
}
function parseReceipt(stdout) {
try {
return JSON.parse(stdout);
} catch {
return null;
}
}
function atomicOutputFailure(result) {
const code = result?.reason?.code || 'unclassified';
let diagnosticCode = 'output/target-indeterminate';
let message;
if (code === 'target-hardlinked' || code === 'candidate-hardlinked') {
diagnosticCode = 'output/target-hardlinked';
message = code === 'candidate-hardlinked'
? 'Preview commit candidate has multiple hard-link names and cannot be published safely.'
: 'Preview output has multiple hard-link names; atomic publication cannot update every name.';
} else if (code === 'target-not-regular-file' || code === 'candidate-not-regular-file') {
diagnosticCode = 'output/target-not-regular-file';
message = code === 'candidate-not-regular-file'
? 'Preview commit candidate is no longer a regular file.'
: 'Preview output already exists and is not a regular file.';
} else if (result?.status === 'different') {
diagnosticCode = 'output/target-changed';
message = `Preview output changed while the verified candidate was being prepared (${code}).`;
} else {
message = `Preview output stability could not be determined safely (${code}).`;
}
return {
code: diagnosticCode,
message,
evidence: { relation: result?.reason || { code } },
};
}
function atomicOutputError(result) {
const failure = atomicOutputFailure(result);
return Object.assign(new Error(failure.message), { previewFailure: failure });
}
function stagePreviewCommit(commitPath, artifact, mode) {
for (let attempt = 0; attempt < 100; attempt += 1) {
previewCommitSequence += 1;
const candidatePath = path.join(
path.dirname(commitPath),
`.archify-preview-commit-${process.pid}-${Date.now().toString(36)}-${previewCommitSequence}.tmp`,
);
let descriptor;
let identity;
try {
const noFollow = process.platform === 'win32' ? 0 : (fs.constants.O_NOFOLLOW || 0);
descriptor = fs.openSync(
candidatePath,
fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | noFollow,
mode ?? 0o666,
);
let metadata;
try {
metadata = fs.fstatSync(descriptor, { bigint: true });
} catch (error) {
try {
const retry = fs.fstatSync(descriptor, { bigint: true });
if (retry.isFile() && retry.ino !== 0n) {
identity = { device: retry.dev, inode: retry.ino };
}
} catch {}
throw error;
}
if (!metadata.isFile() || metadata.ino === 0n) {
throw new Error('Preview commit candidate identity could not be verified safely.');
}
identity = { device: metadata.dev, inode: metadata.ino };
fs.writeFileSync(descriptor, artifact);
if (mode !== null) fs.fchmodSync(descriptor, mode);
fs.closeSync(descriptor);
descriptor = undefined;
return { candidatePath, identity };
} catch (error) {
if (descriptor !== undefined) {
try { fs.closeSync(descriptor); } catch {}
}
if (error.code === 'EEXIST') continue;
if (identity) removeOwnedRegularFile(candidatePath, identity);
throw error;
}
}
throw Object.assign(new Error('Could not reserve a preview commit candidate beside the output.'), {
code: 'EEXIST',
errno: -17,
syscall: 'open',
});
}
function captureOwnedDirectory(directoryPath) {
const metadata = fs.lstatSync(directoryPath, { bigint: true });
if (!metadata.isDirectory() || metadata.ino === 0n) {
throw new Error('Preview staging directory identity could not be verified safely.');
}
return { device: metadata.dev, inode: metadata.ino };
}
function cleanupOwnedDirectory(directoryPath, identity) {
let current;
try {
current = fs.lstatSync(directoryPath, { bigint: true });
} catch (error) {
if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return;
throw error;
}
// Preserve a replacement rather than recursively deleting a directory that
// this preview instance did not create.
if (!current.isDirectory()
|| current.ino === 0n
|| current.dev !== identity.device
|| current.ino !== identity.inode) return;
try {
removeEmptyDirectoryWithRetry(directoryPath);
} catch (error) {
// An entry whose identity was never bound to this preview may be an
// external claimant. Preserve the private directory as recovery material
// instead of recursively deleting unknown contents.
if (error?.code !== 'ENOTEMPTY' && error?.code !== 'EEXIST') throw error;
}
}
function captureOwnedDeliverySidecars(candidatePath, snapshotPath, receipt) {
const suffixes = ['.delivery.json', '.delivery-pending.json'];
const capturedSidecars = [];
for (const suffix of suffixes) {
const sidecarPath = candidatePath.replace(/\.html$/iu, suffix);
const captured = captureRegularFileBinding(sidecarPath, {
subject: 'preview-delivery-sidecar',
expectedLinks: 1,
includeContent: true,
});
if (captured.status !== 'captured') continue;
try {
const sidecar = JSON.parse(captured.content.buffer.toString('utf8'));
const commonMatches = sidecar?.schemaVersion === 1
&& sidecar.command === 'deliver'
&& sameLocation(sidecar.output, candidatePath).status === 'match'
&& (!receipt?.receiptId || sidecar.receiptId === receipt.receiptId);
const currentMatches = suffix === '.delivery.json'
&& commonMatches
&& sidecar.status === 'current'
&& receipt?.artifact?.sha256
&& sidecar.artifact?.sha256 === receipt.artifact.sha256;
const pendingMatches = suffix === '.delivery-pending.json'
&& commonMatches
&& sidecar.status === 'pending'
&& sameLocation(sidecar.input, snapshotPath).status === 'match';
if (currentMatches || pendingMatches) {
capturedSidecars.push({ path: sidecarPath, identity: captured.identity });
}
} catch {
// Preserve malformed or claimant-controlled sidecars for recovery.
} finally {
releaseRegularFileBinding(captured.binding);
}
}
return capturedSidecars;
}
export async function startPreview(options) {
const type = options.type;
if (!diagramTypes.has(type)) throw new Error(`Unknown diagram type "${type}".`);
if (options.quality && !['standard', 'showcase'].includes(options.quality)) {
throw new Error(`Unknown quality profile "${options.quality}".`);
}
const inputPath = path.resolve(options.input);
const outputRequest = {
requestedOutput: options.output,
authoredOutput: initialAuthoredOutput(inputPath),
defaultOutput: `${type}.html`,
inputPaths: [inputPath],
inputDescription: 'its JSON input',
cwd: options.cwd || process.cwd(),
};
const { outputPath } = resolveOutputPath(outputRequest);
const outputDirectory = path.dirname(outputPath);
const debounceMs = Number.isFinite(options.debounceMs) ? options.debounceMs : defaultDebounceMs;
const pollMs = Number.isFinite(options.pollMs) ? options.pollMs : defaultPollMs;
const stopGraceMs = Number.isFinite(options.stopGraceMs) ? Math.max(0, options.stopGraceMs) : defaultStopGraceMs;
const stopKillMs = Number.isFinite(options.stopKillMs) ? Math.max(0, options.stopKillMs) : defaultStopKillMs;
const shouldOpen = options.open !== false;
fs.mkdirSync(outputDirectory, { recursive: true });
// Keep cleanup bound to the physical directory selected at startup. A
// symlink or junction in the requested output parent may be redirected while
// preview is running and must not redirect recursive cleanup to a claimant.
const physicalOutputDirectory = fs.realpathSync.native(outputDirectory);
const stagingDirectory = fs.mkdtempSync(path.toNamespacedPath(
path.join(physicalOutputDirectory, '.archify-preview-'),
));
let stagingIdentity;
try {
stagingIdentity = captureOwnedDirectory(stagingDirectory);
} catch (error) {
try { fs.rmdirSync(stagingDirectory); } catch {}
throw error;
}
let port = 0;
let watcher;
let debounceTimer;
let pollTimer;
let stopGraceTimer;
let stopKillTimer;
let child;
let stopping = false;
let forceStopping = false;
let stopped = false;
let serverClosing = false;
let serverClosed = false;
let queuedHash = null;
let activeHash = null;
let lastGoodSourceHash = null;
let sourceEpoch = 0;
let activeEpoch = 0;
let pendingBuild = false;
let artifactBuffer = null;
const clients = new Set();
const sockets = new Set();
const state = {
schemaVersion: 1,
status: 'checking',
generation: 0,
revision: 0,
lastVerified: null,
failure: null,
};
let resolveClosed;
const closed = new Promise((resolve) => { resolveClosed = resolve; });
function publicState() {
return JSON.parse(JSON.stringify(state));
}
function sendState(res) {
res.write(`event: state\ndata: ${safeJson(publicState())}\n\n`);
}
function broadcast() {
for (const res of clients) sendState(res);
}
const page = Buffer.from(previewPage());
const server = http.createServer((req, res) => {
const expectedHost = `${loopbackHost}:${port}`;
if (req.headers.host !== expectedHost) {
res.writeHead(403, responseHeaders('text/plain; charset=utf-8'));
res.end('Forbidden host');
return;
}
if (req.method !== 'GET' && req.method !== 'HEAD') {
res.writeHead(405, { ...responseHeaders('text/plain; charset=utf-8'), Allow: 'GET, HEAD' });
res.end('Method not allowed');
return;
}
let url;
try {
url = new URL(req.url, `http://${expectedHost}`);
} catch {
res.writeHead(400, responseHeaders('text/plain; charset=utf-8'));
res.end('Bad request');
return;
}
if (url.pathname === '/') {
res.writeHead(200, {
...responseHeaders('text/html; charset=utf-8'),
'Content-Security-Policy': "default-src 'none'; frame-src 'self'; connect-src 'self'; script-src 'unsafe-inline'; style-src 'unsafe-inline'",
'Content-Length': page.byteLength,
});
if (req.method === 'HEAD') res.end();
else res.end(page);
return;
}
if (url.pathname === '/state') {
const body = Buffer.from(`${safeJson(publicState())}\n`);
res.writeHead(200, { ...responseHeaders('application/json; charset=utf-8'), 'Content-Length': body.byteLength });
if (req.method === 'HEAD') res.end();
else res.end(body);
return;
}
if (url.pathname === '/artifact.html') {
if (!artifactBuffer) {
res.writeHead(404, responseHeaders('text/plain; charset=utf-8'));
res.end('No verified artifact yet');
return;
}
res.writeHead(200, { ...responseHeaders('text/html; charset=utf-8'), 'Content-Length': artifactBuffer.byteLength });
if (req.method === 'HEAD') res.end();
else res.end(artifactBuffer);
return;
}
if (url.pathname === '/events' && req.method === 'GET') {
res.writeHead(200, {
...responseHeaders('text/event-stream; charset=utf-8'),
Connection: 'keep-alive',
});
res.write('retry: 1000\n\n');
clients.add(res);
sendState(res);
req.on('close', () => clients.delete(res));
return;
}
res.writeHead(404, responseHeaders('text/plain; charset=utf-8'));
res.end('Not found');
});
server.on('connection', (socket) => {
sockets.add(socket);
socket.once('close', () => sockets.delete(socket));
// A connection event already queued when force-stop begins must not keep
// server.close() waiting after the current sockets have been destroyed.
if (forceStopping) socket.destroy();
});
try {
await new Promise((resolve, reject) => {
server.once('error', reject);
server.listen(0, loopbackHost, () => {
server.off('error', reject);
port = server.address().port;
resolve();
});
});
} catch (error) {
try { server.close(); } catch {}
cleanupOwnedDirectory(stagingDirectory, stagingIdentity);
throw error;
}
const url = `http://${loopbackHost}:${port}/`;
function finishStop() {
if (stopped || child || !serverClosed) return;
stopped = true;
clearTimeout(debounceTimer);
clearInterval(pollTimer);
clearTimeout(stopGraceTimer);
clearTimeout(stopKillTimer);
try {
cleanupOwnedDirectory(stagingDirectory, stagingIdentity);
} finally {
resolveClosed();
}
}
function signalActiveChild(signal) {
if (!child || child.exitCode !== null || child.signalCode !== null) return;
try {
if (process.platform !== 'win32' && child.pid) process.kill(-child.pid, signal);
else child.kill(signal);
} catch (error) {
if (error.code === 'ESRCH') return;
try { child.kill(signal); } catch {}
}
}
function closeServer() {
if (serverClosing) return;
serverClosing = true;
for (const res of clients) res.end();
clients.clear();
server.close(() => {
serverClosed = true;
finishStop();
});
server.closeIdleConnections?.();
}
function startBoundedChildDrain() {
if (!child || stopGraceTimer || stopKillTimer) return;
stopGraceTimer = setTimeout(() => {
stopGraceTimer = undefined;
if (!child) return finishStop();
signalActiveChild('SIGTERM');
stopKillTimer = setTimeout(() => {
stopKillTimer = undefined;
signalActiveChild('SIGKILL');
}, stopKillMs);
}, stopGraceMs);
}
async function stop({ force = false } = {}) {
if (force) forceStopping = true;
if (!stopping) {
stopping = true;
clearTimeout(debounceTimer);
clearInterval(pollTimer);
watcher?.close();
closeServer();
}
if (forceStopping) {
for (const socket of sockets) socket.destroy();
}
if (child && force) {
clearTimeout(stopGraceTimer);
clearTimeout(stopKillTimer);
stopGraceTimer = undefined;
stopKillTimer = undefined;
signalActiveChild('SIGKILL');
} else if (child) {
startBoundedChildDrain();
} else {
finishStop();
}
return closed;
}
function publishFailure(receipt, stdout, stderr, candidatePath, snapshotPath) {
const repairDetails = receipt?.diagnostics
?.slice(0, 12)
.map((entry) => {
const fix = entry.supportedFixes?.length ? `\nFix: ${entry.supportedFixes.join('; ')}` : '';
return `[${entry.code}] ${entry.message}${fix}`;
}) || [];
const checkerDetails = receipt?.checker?.checks
?.filter((check) => !check.ok)
.flatMap((check) => check.details || [])
.filter(Boolean)
.slice(0, 12) || [];
const diagnostic = [
receipt?.error,
...(repairDetails.length ? repairDetails : checkerDetails),
].filter(Boolean).join('\n') || stderr || stdout;
state.status = 'needs-fix';
state.failure = {
stage: receipt?.stage || 'render',
...(receipt?.code ? { code: receipt.code } : {}),
...(receipt?.evidence ? { evidence: receipt.evidence } : {}),
message: redactDiagnostic(
diagnostic,
[
[inputPath, '<input.json>'],
[outputPath, '<output.html>'],
[snapshotPath, '<input.json>'],
[candidatePath, '<candidate.html>'],
[stagingDirectory, '<preview-staging>'],
[path.resolve(here, '..'), '<archify-skill>'],
[path.resolve(options.cwd || process.cwd()), '<working-directory>'],
...(options.repoRoot ? [[path.resolve(options.repoRoot), '<repo-root>']] : []),
],
),
};
broadcast();
}
function commitCandidate(candidatePath, receipt, generationHash, outputCapture) {
let candidate;
let sourceCandidateBinding;
let sourceCandidateIdentity;
let commitCandidatePath;
let commitCandidateIdentity;
let commitCandidateBinding;
try {
const sourceCapture = captureRegularFileBinding(candidatePath, {
subject: 'candidate',
expectedSha256: receipt?.artifact?.sha256,
expectedBytes: receipt?.artifact?.bytes,
includeContent: true,
});
if (sourceCapture.status !== 'captured') throw atomicOutputError(sourceCapture);
sourceCandidateBinding = sourceCapture.binding;
sourceCandidateIdentity = sourceCapture.identity;
candidate = sourceCapture.content.buffer;
const digest = sourceCapture.content.sha256;
const releasedSource = releaseRegularFileBinding(sourceCandidateBinding);
sourceCandidateBinding = undefined;
if (releasedSource.status !== 'released') throw atomicOutputError(releasedSource);
resolveOutputPath(outputRequest);
const sameArtifact = state.lastVerified?.sha256 === digest;
const currentSource = sourceDigest(inputPath);
if (currentSource.hash !== generationHash) {
return {
committed: false,
supersededBy: currentSource,
candidateIdentity: sourceCandidateIdentity,
};
}
const beforeStage = verifyAtomicOutput(outputCapture.snapshot);
if (beforeStage.status !== 'match') throw atomicOutputError(beforeStage);
({
candidatePath: commitCandidatePath,
identity: commitCandidateIdentity,
} = stagePreviewCommit(outputCapture.commitPath, candidate, outputCapture.mode));
const candidateCapture = captureRegularFileBinding(commitCandidatePath, {
subject: 'candidate',
expectedSha256: digest,
expectedBytes: candidate.byteLength,
expectedIdentity: commitCandidateIdentity,
...(outputCapture.mode === null ? {} : { expectedMode: outputCapture.mode }),
});
if (candidateCapture.status !== 'captured') throw atomicOutputError(candidateCapture);
commitCandidateBinding = candidateCapture.binding;
const beforeCommit = verifyAtomicOutput(outputCapture.snapshot);
if (beforeCommit.status !== 'match') throw atomicOutputError(beforeCommit);
const publication = publishRegularFileBinding(
commitCandidateBinding,
commitCandidatePath,
outputCapture.snapshot,
{ subject: 'candidate' },
);
if (!['committed', 'committed-with-warning'].includes(publication.status)) {
throw atomicOutputError(publication);
}
const releasedCandidate = releaseRegularFileBinding(commitCandidateBinding);
commitCandidateBinding = undefined;
if (releasedCandidate.status !== 'released') throw atomicOutputError(releasedCandidate);
commitCandidatePath = undefined;
commitCandidateIdentity = undefined;
artifactBuffer = candidate;
lastGoodSourceHash = generationHash;
state.status = 'verified';
if (!sameArtifact) {
state.revision += 1;
state.lastVerified = {
sha256: digest,
bytes: candidate.byteLength,
checksPassed: receipt.validation.checksPassed,
checkCount: receipt.validation.checkCount,
compositionProfile: receipt.validation.compositionProfile,
compositionStatus: receipt.validation.compositionStatus,
};
}
state.failure = null;
broadcast();
return { committed: true, supersededBy: null, candidateIdentity: sourceCandidateIdentity };
} catch (error) {
publishFailure({
stage: 'commit',
error: `Could not publish the verified preview: ${error.message}`,
...(error.previewFailure || {}),
}, '', '', candidatePath);
return { committed: false, supersededBy: null, candidateIdentity: sourceCandidateIdentity };
} finally {
if (sourceCandidateBinding) releaseRegularFileBinding(sourceCandidateBinding);
if (commitCandidateBinding) releaseRegularFileBinding(commitCandidateBinding);
if (commitCandidatePath && commitCandidateIdentity) {
removeOwnedRegularFile(commitCandidatePath, commitCandidateIdentity);
}
}
}
function beginBuild(digest, epoch) {
if (stopping || child) return;
activeHash = digest.hash;
activeEpoch = epoch;
state.generation += 1;
state.status = 'checking';
state.failure = null;
broadcast();
const candidatePath = path.join(stagingDirectory, `generation-${state.generation}.html`);
const snapshotPath = path.join(stagingDirectory, `generation-${state.generation}.json`);
let snapshotIdentity;
const outputCapture = captureAtomicOutput(outputPath);
if (outputCapture.status !== 'captured') {
const failure = atomicOutputFailure(outputCapture);
publishFailure(
{ stage: 'prepare', error: failure.message, ...failure },
'',
'',
candidatePath,
snapshotPath,
);
return;
}
const beforeBuild = verifyAtomicOutput(outputCapture.snapshot);
if (beforeBuild.status !== 'match') {
const failure = atomicOutputFailure(beforeBuild);
publishFailure(
{ stage: 'prepare', error: failure.message, ...failure },
'',
'',
candidatePath,
snapshotPath,
);
return;
}
if (digest.bytes !== null) {
try {
fs.writeFileSync(snapshotPath, digest.bytes, { flag: 'wx', mode: 0o600 });
const metadata = fs.lstatSync(snapshotPath, { bigint: true });
if (metadata.isFile() && metadata.ino !== 0n) {
snapshotIdentity = { device: metadata.dev, inode: metadata.ino };
}
} catch (error) {
publishFailure(
{ stage: 'prepare', error: `Could not snapshot the observed input: ${error.message}` },
'',
'',
candidatePath,
snapshotPath,
);
return;
}
}
const args = [options.deliveryCli || cliPath, 'deliver', type, snapshotPath, candidatePath, '--json'];
if (options.quality) args.push('--quality', options.quality);
if (options.repoRoot) args.push('--repo-root', path.resolve(options.repoRoot));
let stdout = '';
let stderr = '';
child = spawn(process.execPath, args, {
cwd: options.cwd || process.cwd(),
env: process.env,
stdio: ['ignore', 'pipe', 'pipe'],
detached: process.platform !== 'win32',
});
child.stdout.setEncoding('utf8');
child.stderr.setEncoding('utf8');
child.stdout.on('data', (chunk) => { stdout += chunk; });
child.stderr.on('data', (chunk) => { stderr += chunk; });
child.on('error', (error) => { stderr += error.message; });
child.on('close', (code) => {
const receipt = parseReceipt(stdout);
const generationEpoch = activeEpoch;
const generationHash = activeHash;
const stale = generationEpoch !== sourceEpoch;
let supersededBy = null;
let candidateIdentity;
const deliverySidecars = captureOwnedDeliverySidecars(
candidatePath,
snapshotPath,
receipt,
);
child = null;
clearTimeout(stopGraceTimer);
clearTimeout(stopKillTimer);
stopGraceTimer = undefined;
stopKillTimer = undefined;
if (!stopping && !stale && code === 0 && receipt?.ok) {
({ supersededBy, candidateIdentity } = commitCandidate(
candidatePath,
receipt,
generationHash,
outputCapture,
));
} else if (code === 0 && receipt?.ok) {
const abandonedCandidate = captureRegularFileBinding(candidatePath, {
subject: 'abandoned-preview-candidate',
expectedSha256: receipt.artifact?.sha256,
expectedBytes: receipt.artifact?.bytes,
});
if (abandonedCandidate.status === 'captured') {
candidateIdentity = abandonedCandidate.identity;
releaseRegularFileBinding(abandonedCandidate.binding);
}
} else if (!stopping && !stale) {
publishFailure(receipt, stdout, stderr, candidatePath, snapshotPath);
}
if (candidateIdentity) removeOwnedRegularFile(candidatePath, candidateIdentity);
if (snapshotIdentity) removeOwnedRegularFile(snapshotPath, snapshotIdentity, { subject: 'snapshot' });
for (const deliverySidecar of deliverySidecars) {
removeOwnedRegularFile(deliverySidecar.path, deliverySidecar.identity, {
subject: 'preview-delivery-sidecar',
});
}
if (stopping) {
finishStop();
} else if (pendingBuild || stale || supersededBy) {
pendingBuild = false;
if (supersededBy && sourceEpoch === generationEpoch) sourceEpoch += 1;
const digest = supersededBy || sourceDigest(inputPath);
queueStableBuild(digest.hash, true);
}
});
}
function queueStableBuild(hash, immediate = false) {
queuedHash = hash;
clearTimeout(debounceTimer);
const launch = () => {
if (stopping) return;
const digest = sourceDigest(inputPath);
if (digest.hash !== queuedHash) {
queueStableBuild(digest.hash);
return;
}
if (digest.hash === lastGoodSourceHash) {
if (state.status !== 'verified' && state.lastVerified) {
state.status = 'verified';
state.failure = null;
broadcast();
}
return;
}
if (child) {
pendingBuild = true;
return;
}
beginBuild(digest, sourceEpoch);
};
debounceTimer = setTimeout(launch, immediate ? 0 : debounceMs);
}
function observeSource({ immediate = false } = {}) {
const digest = sourceDigest(inputPath);
if (!immediate && digest.hash === queuedHash) return;
sourceEpoch += 1;
queueStableBuild(digest.hash, immediate);
}
if (options.watch !== false) {
try {
// `path.resolve` keeps Windows 8.3 names intact. Canonicalize short names
// and junctions before libuv opens the directory so its callback path has
// the same prefix as the watched path.
const watchedDirectory = fs.realpathSync.native(path.dirname(inputPath));
watcher = fs.watch(watchedDirectory, (event, filename) => {
// On Windows the watcher hands us just the basename; on POSIX it can be
// null. Resolve named events against the directory libuv actually opened
// so file-level case, 8.3, and hard-link aliases still identify the input.
if (
!filename
|| sameLocation(path.join(watchedDirectory, filename.toString()), inputPath).status === 'match'
) {
observeSource();
}
});
watcher.on('error', () => {
const failedWatcher = watcher;
watcher = undefined;
failedWatcher?.close();
});
} catch (error) {
await stop();
throw new Error(`Could not watch the input directory: ${error.message}`);
}
}
pollTimer = setInterval(() => observeSource(), pollMs);
let opener = null;
if (shouldOpen) {
try {
opener = openLoopbackUrl(url);
} catch {
opener = { requested: true, status: 'unsupported', target: url, method: null };
}
}
observeSource({ immediate: true });
return {
url,
input: inputPath,
output: outputPath,
opener,
state: publicState,
stop,
closed,
};
}
export async function runPreview(options) {
const preview = await startPreview(options);
console.log(`preview ${preview.url}`);
console.log(`watching ${preview.input}`);
console.log(`output ${preview.output}`);
if (preview.opener && preview.opener.status !== 'opened') {
console.error(`Could not open the preview (${preview.opener.status}). ${preview.opener.failure?.reason || 'Open it manually.'} Target: ${preview.url}`);
}
let signalCount = 0;
const stop = () => {
signalCount += 1;
if (signalCount === 1) {
console.log('\nstopping preview…');
preview.stop();
} else {
console.log('\nforcing preview shutdown…');
preview.stop({ force: true });
}
};
process.on('SIGINT', stop);
process.on('SIGTERM', stop);
await preview.closed;
process.off('SIGINT', stop);
process.off('SIGTERM', stop);
}
+41
View File
@@ -0,0 +1,41 @@
import { recoverRetiredPublication } from '../renderers/shared/atomic-output.mjs';
function usage(stream = process.stderr) {
stream.write('Usage: node bin/recover-output.mjs <private-recovery-directory> [--json]\n');
}
const args = process.argv.slice(2);
if (args.length === 1 && ['--help', '-h'].includes(args[0])) {
usage(process.stdout);
} else {
let json = false;
let recoveryDirectory;
let invalid = false;
for (const argument of args) {
if (argument === '--json' && !json) {
json = true;
} else if (argument.startsWith('-') || recoveryDirectory !== undefined) {
invalid = true;
} else {
recoveryDirectory = argument;
}
}
if (invalid || recoveryDirectory === undefined) {
usage();
process.exitCode = 64;
} else {
const result = recoverRetiredPublication(recoveryDirectory);
if (json) {
process.stdout.write(`${JSON.stringify(result)}\n`);
} else {
const reason = result.reason?.code || 'publication-recovery-unknown';
process.stdout.write(`${result.status}: ${reason}\n`);
}
// A complete prior recovery is idempotent. A preserved target is a safe,
// deliberate non-action that needs the operator to inspect the claimant.
process.exitCode = ['recovered', 'absent'].includes(result.status)
? 0
: result.status === 'preserved' ? 2 : 1;
}
}
File diff suppressed because it is too large. Load diff
+31
View File
@@ -0,0 +1,31 @@
# Built-in brand marks
Archify ships a bounded catalogue of 107 commonly used brands for architecture,
workflow, sequence, data-flow, and lifecycle nodes. The mark is optional authored
identity: it never replaces the node's semantic `type`, color, label, or
relationships.
Unknown sites are handled by an explicit two-stage workflow. Run
`node bin/archify.mjs brands capture <url> --json`, then author the returned
digest-pinned `brand` value. Normal render and validate commands do not perform
an unpinned capture, and changed or unavailable content fails closed.
Most vector paths and brand metadata are generated from Simple Icons 16.28.0.
The OpenAI mark is traced to OpenAI's official brand guidelines. Every generated
entry records its source and, when available upstream, its guidelines and license
metadata in `renderers/shared/generated-brand-marks.mjs`.
Brand names and logos may be trademarks of their respective owners. Simple
Icons' CC0 license covers its collection work, not every underlying trademark or
artwork. Contributors must review the recorded source, current brand guidelines,
and intended referential use before adding or updating a mark. Archify does not
imply sponsorship, endorsement, or partnership.
Edit `catalog.json`, then regenerate the committed zero-runtime-dependency bundle:
```bash
npm run generate:brand-marks
npm run check:brand-marks
```
Do not hand-edit `renderers/shared/generated-brand-marks.mjs`.
+131
View File
@@ -0,0 +1,131 @@
{
"schemaVersion": 1,
"marks": [
{
"id": "openai",
"title": "OpenAI",
"category": "ai",
"aliases": ["chatgpt", "gpt", "codex"],
"domains": ["openai.com", "chatgpt.com"],
"custom": {
"viewBox": 20,
"hex": "000000",
"path": "M11.248 18.25q-.825 0-1.568-.314a4.3 4.3 0 0 1-1.32-.874 4 4 0 0 1-1.304.214 4 4 0 0 1-2.046-.544 4.27 4.27 0 0 1-1.518-1.485 4 4 0 0 1-.56-2.095q0-.48.131-1.04A4.4 4.4 0 0 1 2.04 10.71a4.07 4.07 0 0 1 .017-3.4 4.2 4.2 0 0 1 1.056-1.418 3.8 3.8 0 0 1 1.6-.842 3.9 3.9 0 0 1 .76-1.683q.593-.759 1.451-1.188a4.04 4.04 0 0 1 1.832-.429q.825 0 1.567.313.742.314 1.32.875a4 4 0 0 1 1.304-.215q1.106 0 2.046.545a4.14 4.14 0 0 1 1.501 1.485q.578.941.578 2.095 0 .48-.132 1.04.66.61 1.023 1.419.363.792.363 1.666 0 .892-.38 1.717a4.3 4.3 0 0 1-1.072 1.435 3.8 3.8 0 0 1-1.584.825 3.8 3.8 0 0 1-.775 1.683 4.06 4.06 0 0 1-1.436 1.188 4.04 4.04 0 0 1-1.832.429m-4.076-2.062q.825 0 1.435-.347l3.103-1.782a.36.36 0 0 0 .164-.313v-1.42L7.881 14.62a.67.67 0 0 1-.726 0l-3.118-1.798a.5.5 0 0 1-.017.115v.198q0 .841.396 1.551.413.693 1.139 1.089a3.2 3.2 0 0 0 1.617.412m.165-2.69a.4.4 0 0 0 .181.05q.083 0 .165-.05l1.238-.71-3.977-2.31a.7.7 0 0 1-.363-.643v-3.58q-.825.362-1.32 1.122a2.9 2.9 0 0 0-.495 1.65q0 .809.413 1.55.412.743 1.072 1.123zm3.91 3.663q.875 0 1.585-.396a2.96 2.96 0 0 0 1.534-2.64v-3.564a.32.32 0 0 0-.165-.297l-1.254-.726v4.604a.7.7 0 0 1-.363.643l-3.119 1.799a3 3 0 0 0 1.783.577m.627-6.039V8.878L10.01 7.822 8.129 8.878v2.244l1.881 1.056zM7.057 5.859a.7.7 0 0 1 .363-.644l3.119-1.798a3 3 0 0 0-1.782-.578q-.874 0-1.584.396A2.96 2.96 0 0 0 6.05 4.324a3.07 3.07 0 0 0-.396 1.551v3.547q0 .199.165.314l1.237.726zm8.383 7.887q.825-.364 1.303-1.123.495-.758.495-1.65a3.15 3.15 0 0 0-.412-1.55q-.413-.743-1.073-1.123l-3.086-1.782q-.099-.065-.181-.049a.3.3 0 0 0-.165.05l-1.238.692 3.993 2.327a.6.6 0 0 1 .264.264.64.64 0 0 1 .1.363zm-3.317-8.382a.63.63 0 0 1 .726 0l3.135 1.831v-.297q0-.792-.396-1.501a2.86 2.86 0 0 0-1.105-1.155q-.71-.43-1.65-.43-.825 0-1.436.347L8.294 5.941a.36.36 0 0 0-.165.314v1.418z",
"source": "https://openai.com/brand/",
"guidelines": "https://openai.com/brand/"
}
},
{ "id": "claude", "category": "ai", "simpleIcon": "claude", "aliases": ["claude-ai"], "domains": ["claude.ai"] },
{ "id": "anthropic", "category": "ai", "simpleIcon": "anthropic", "domains": ["anthropic.com"] },
{ "id": "google-gemini", "category": "ai", "simpleIcon": "googlegemini", "aliases": ["gemini"], "domains": ["gemini.google.com"] },
{ "id": "deepseek", "category": "ai", "simpleIcon": "deepseek", "domains": ["deepseek.com"] },
{ "id": "qwen", "category": "ai", "simpleIcon": "qwen", "domains": ["qwen.ai"] },
{ "id": "meta", "category": "ai", "simpleIcon": "meta", "aliases": ["llama"], "domains": ["meta.com"] },
{ "id": "mistral-ai", "category": "ai", "simpleIcon": "mistralai", "aliases": ["mistral"], "domains": ["mistral.ai"] },
{ "id": "hugging-face", "category": "ai", "simpleIcon": "huggingface", "aliases": ["huggingface"], "domains": ["huggingface.co"] },
{ "id": "ollama", "category": "ai", "simpleIcon": "ollama", "domains": ["ollama.com"] },
{ "id": "openrouter", "category": "ai", "simpleIcon": "openrouter", "aliases": ["open-router"], "domains": ["openrouter.ai"] },
{ "id": "perplexity", "category": "ai", "simpleIcon": "perplexity", "domains": ["perplexity.ai"] },
{ "id": "replicate", "category": "ai", "simpleIcon": "replicate", "domains": ["replicate.com"] },
{ "id": "google-cloud", "category": "cloud", "simpleIcon": "googlecloud", "aliases": ["gcp", "googlecloud"], "domains": ["cloud.google.com"] },
{ "id": "cloudflare", "category": "cloud", "simpleIcon": "cloudflare", "domains": ["cloudflare.com"] },
{ "id": "vercel", "category": "cloud", "simpleIcon": "vercel", "domains": ["vercel.com"] },
{ "id": "netlify", "category": "cloud", "simpleIcon": "netlify", "domains": ["netlify.com"] },
{ "id": "digitalocean", "category": "cloud", "simpleIcon": "digitalocean", "aliases": ["digital-ocean"], "domains": ["digitalocean.com"] },
{ "id": "render", "category": "cloud", "simpleIcon": "render", "domains": ["render.com"] },
{ "id": "railway", "category": "cloud", "simpleIcon": "railway", "domains": ["railway.com", "railway.app"] },
{ "id": "fly-io", "category": "cloud", "simpleIcon": "flydotio", "aliases": ["fly.io"], "domains": ["fly.io"] },
{ "id": "cloudinary", "category": "cloud", "simpleIcon": "cloudinary", "domains": ["cloudinary.com"] },
{ "id": "alibaba-cloud", "category": "cloud", "simpleIcon": "alibabacloud", "aliases": ["aliyun"], "domains": ["alibabacloud.com", "aliyun.com"] },
{ "id": "firebase", "category": "cloud", "simpleIcon": "firebase", "domains": ["firebase.google.com"] },
{ "id": "supabase", "category": "cloud", "simpleIcon": "supabase", "domains": ["supabase.com"] },
{ "id": "neon", "category": "cloud", "simpleIcon": "neon", "domains": ["neon.tech"] },
{ "id": "github", "category": "engineering", "simpleIcon": "github", "domains": ["github.com"] },
{ "id": "gitlab", "category": "engineering", "simpleIcon": "gitlab", "domains": ["gitlab.com"] },
{ "id": "bitbucket", "category": "engineering", "simpleIcon": "bitbucket", "domains": ["bitbucket.org"] },
{ "id": "docker", "category": "engineering", "simpleIcon": "docker", "domains": ["docker.com"] },
{ "id": "kubernetes", "category": "engineering", "simpleIcon": "kubernetes", "aliases": ["k8s"], "domains": ["kubernetes.io"] },
{ "id": "terraform", "category": "engineering", "simpleIcon": "terraform", "domains": ["terraform.io"] },
{ "id": "pulumi", "category": "engineering", "simpleIcon": "pulumi", "domains": ["pulumi.com"] },
{ "id": "ansible", "category": "engineering", "simpleIcon": "ansible", "domains": ["ansible.com"] },
{ "id": "jenkins", "category": "engineering", "simpleIcon": "jenkins", "domains": ["jenkins.io"] },
{ "id": "circleci", "category": "engineering", "simpleIcon": "circleci", "aliases": ["circle-ci"], "domains": ["circleci.com"] },
{ "id": "github-actions", "category": "engineering", "simpleIcon": "githubactions" },
{ "id": "argo", "category": "engineering", "simpleIcon": "argo", "aliases": ["argocd", "argo-cd"], "domains": ["argoproj.github.io"] },
{ "id": "helm", "category": "engineering", "simpleIcon": "helm", "domains": ["helm.sh"] },
{ "id": "grafana", "category": "engineering", "simpleIcon": "grafana", "domains": ["grafana.com"] },
{ "id": "prometheus", "category": "engineering", "simpleIcon": "prometheus", "domains": ["prometheus.io"] },
{ "id": "sentry", "category": "engineering", "simpleIcon": "sentry", "domains": ["sentry.io"] },
{ "id": "datadog", "category": "engineering", "simpleIcon": "datadog", "domains": ["datadoghq.com"] },
{ "id": "pagerduty", "category": "engineering", "simpleIcon": "pagerduty", "aliases": ["pager-duty"], "domains": ["pagerduty.com"] },
{ "id": "postgresql", "category": "data", "simpleIcon": "postgresql", "aliases": ["postgres"], "domains": ["postgresql.org"] },
{ "id": "mysql", "category": "data", "simpleIcon": "mysql", "domains": ["mysql.com"] },
{ "id": "mongodb", "category": "data", "simpleIcon": "mongodb", "aliases": ["mongo"], "domains": ["mongodb.com"] },
{ "id": "redis", "category": "data", "simpleIcon": "redis", "domains": ["redis.io"] },
{ "id": "apache-kafka", "category": "data", "simpleIcon": "apachekafka", "aliases": ["kafka"], "domains": ["kafka.apache.org"] },
{ "id": "rabbitmq", "category": "data", "simpleIcon": "rabbitmq", "aliases": ["rabbit-mq"], "domains": ["rabbitmq.com"] },
{ "id": "clickhouse", "category": "data", "simpleIcon": "clickhouse", "domains": ["clickhouse.com"] },
{ "id": "elasticsearch", "category": "data", "simpleIcon": "elasticsearch", "aliases": ["elastic"], "domains": ["elastic.co"] },
{ "id": "opensearch", "category": "data", "simpleIcon": "opensearch", "aliases": ["open-search"], "domains": ["opensearch.org"] },
{ "id": "snowflake", "category": "data", "simpleIcon": "snowflake", "domains": ["snowflake.com"] },
{ "id": "databricks", "category": "data", "simpleIcon": "databricks", "domains": ["databricks.com"] },
{ "id": "planetscale", "category": "data", "simpleIcon": "planetscale", "aliases": ["planet-scale"], "domains": ["planetscale.com"] },
{ "id": "prisma", "category": "data", "simpleIcon": "prisma", "domains": ["prisma.io"] },
{ "id": "sqlite", "category": "data", "simpleIcon": "sqlite", "domains": ["sqlite.org"] },
{ "id": "mariadb", "category": "data", "simpleIcon": "mariadb", "aliases": ["maria-db"], "domains": ["mariadb.org"] },
{ "id": "influxdb", "category": "data", "simpleIcon": "influxdb", "aliases": ["influx-db"], "domains": ["influxdata.com"] },
{ "id": "apache-airflow", "category": "data", "simpleIcon": "apacheairflow", "aliases": ["airflow"], "domains": ["airflow.apache.org"] },
{ "id": "notion", "category": "collaboration", "simpleIcon": "notion", "domains": ["notion.so"] },
{ "id": "figma", "category": "collaboration", "simpleIcon": "figma", "domains": ["figma.com"] },
{ "id": "jira", "category": "collaboration", "simpleIcon": "jira", "domains": ["atlassian.com"] },
{ "id": "linear", "category": "collaboration", "simpleIcon": "linear", "domains": ["linear.app"] },
{ "id": "discord", "category": "collaboration", "simpleIcon": "discord", "domains": ["discord.com"] },
{ "id": "zoom", "category": "collaboration", "simpleIcon": "zoom", "domains": ["zoom.us"] },
{ "id": "trello", "category": "collaboration", "simpleIcon": "trello", "domains": ["trello.com"] },
{ "id": "asana", "category": "collaboration", "simpleIcon": "asana", "domains": ["asana.com"] },
{ "id": "airtable", "category": "collaboration", "simpleIcon": "airtable", "domains": ["airtable.com"] },
{ "id": "miro", "category": "collaboration", "simpleIcon": "miro", "domains": ["miro.com"] },
{ "id": "stripe", "category": "business", "simpleIcon": "stripe", "domains": ["stripe.com"] },
{ "id": "shopify", "category": "business", "simpleIcon": "shopify", "domains": ["shopify.com"] },
{ "id": "hubspot", "category": "business", "simpleIcon": "hubspot", "domains": ["hubspot.com"] },
{ "id": "paypal", "category": "business", "simpleIcon": "paypal", "domains": ["paypal.com"] },
{ "id": "intercom", "category": "business", "simpleIcon": "intercom", "domains": ["intercom.com"] },
{ "id": "zendesk", "category": "business", "simpleIcon": "zendesk", "domains": ["zendesk.com"] },
{ "id": "wordpress", "category": "business", "simpleIcon": "wordpress", "domains": ["wordpress.org", "wordpress.com"] },
{ "id": "woocommerce", "category": "business", "simpleIcon": "woocommerce", "aliases": ["woo-commerce"], "domains": ["woocommerce.com"] },
{ "id": "wechat", "category": "channel", "simpleIcon": "wechat", "aliases": ["weixin", "微信"], "domains": ["weixin.qq.com"] },
{ "id": "youtube", "category": "channel", "simpleIcon": "youtube", "domains": ["youtube.com", "youtu.be"] },
{ "id": "tiktok", "category": "channel", "simpleIcon": "tiktok", "aliases": ["douyin", "抖音"], "domains": ["tiktok.com", "douyin.com"] },
{ "id": "x", "category": "channel", "simpleIcon": "x", "aliases": ["twitter"], "domains": ["x.com", "twitter.com"] },
{ "id": "instagram", "category": "channel", "simpleIcon": "instagram", "domains": ["instagram.com"] },
{ "id": "facebook", "category": "channel", "simpleIcon": "facebook", "domains": ["facebook.com"] },
{ "id": "reddit", "category": "channel", "simpleIcon": "reddit", "domains": ["reddit.com"] },
{ "id": "telegram", "category": "channel", "simpleIcon": "telegram", "domains": ["telegram.org", "t.me"] },
{ "id": "whatsapp", "category": "channel", "simpleIcon": "whatsapp", "domains": ["whatsapp.com"] },
{ "id": "pinterest", "category": "channel", "simpleIcon": "pinterest", "domains": ["pinterest.com"] },
{ "id": "python", "category": "language", "simpleIcon": "python", "domains": ["python.org"] },
{ "id": "typescript", "category": "language", "simpleIcon": "typescript", "aliases": ["ts"], "domains": ["typescriptlang.org"] },
{ "id": "javascript", "category": "language", "simpleIcon": "javascript", "aliases": ["js"] },
{ "id": "go", "category": "language", "simpleIcon": "go", "aliases": ["golang"], "domains": ["go.dev"] },
{ "id": "rust", "category": "language", "simpleIcon": "rust", "domains": ["rust-lang.org"] },
{ "id": "node-js", "category": "framework", "simpleIcon": "nodedotjs", "aliases": ["node", "nodejs"], "domains": ["nodejs.org"] },
{ "id": "react", "category": "framework", "simpleIcon": "react", "aliases": ["reactjs"], "domains": ["react.dev"] },
{ "id": "vue", "category": "framework", "simpleIcon": "vuedotjs", "aliases": ["vuejs", "vue.js"], "domains": ["vuejs.org"] },
{ "id": "next-js", "category": "framework", "simpleIcon": "nextdotjs", "aliases": ["nextjs", "next.js"], "domains": ["nextjs.org"] },
{ "id": "pytorch", "category": "framework", "simpleIcon": "pytorch", "domains": ["pytorch.org"] },
{ "id": "tensorflow", "category": "framework", "simpleIcon": "tensorflow", "domains": ["tensorflow.org"] },
{ "id": "angular", "category": "framework", "simpleIcon": "angular", "domains": ["angular.dev"] },
{ "id": "svelte", "category": "framework", "simpleIcon": "svelte", "domains": ["svelte.dev"] },
{ "id": "django", "category": "framework", "simpleIcon": "django", "domains": ["djangoproject.com"] },
{ "id": "flask", "category": "framework", "simpleIcon": "flask", "domains": ["palletsprojects.com"] },
{ "id": "fastapi", "category": "framework", "simpleIcon": "fastapi", "domains": ["fastapi.tiangolo.com"] },
{ "id": "spring", "category": "framework", "simpleIcon": "spring", "aliases": ["spring-boot"], "domains": ["spring.io"] },
{ "id": "dotnet", "category": "framework", "simpleIcon": "dotnet", "aliases": [".net"], "domains": ["dotnet.microsoft.com"] }
]
}
File diff suppressed because it is too large. Load diff
+55
View File
@@ -0,0 +1,55 @@
{
"schema_version": 1,
"diagram_type": "lifecycle",
"meta": {
"title": "Agent Run Lifecycle",
"output": "examples/lifecycle-agent-run.html",
"viewBox": [1030, 630],
"animation": "trace",
"quality_profile": "showcase"
},
"lanes": [
{ "id": "main", "label": "Lifecycle phases" },
{ "id": "waiting", "label": "Interruptions" },
{ "id": "exceptions", "label": "Recovery loop" },
{ "id": "terminal", "label": "Terminal exits" }
],
"states": [
{ "id": "queued", "type": "start", "label": "Queued", "sublabel": "request accepted", "lane": "main", "col": 0, "step": "01", "tag": "entry" },
{ "id": "planning", "type": "active", "label": "Planning", "sublabel": "build task graph", "lane": "main", "col": 1, "step": "02", "tag": "model" },
{ "id": "executing", "type": "active", "label": "Executing", "sublabel": "tool calls", "lane": "main", "col": 2, "step": "03", "tag": "work" },
{ "id": "reviewing", "type": "decision", "label": "Reviewing", "sublabel": "quality gate", "lane": "main", "col": 3, "step": "04", "tag": "check" },
{ "id": "completed", "type": "success", "label": "Completed", "sublabel": "final response", "lane": "main", "col": 4, "step": "05", "tag": "done" },
{ "id": "approval", "type": "waiting", "label": "Needs Approval", "sublabel": "human gate", "lane": "waiting", "col": 0, "tag": "pause" },
{ "id": "blocked", "type": "waiting", "label": "Blocked", "sublabel": "missing input", "lane": "waiting", "col": 1, "tag": "wait" },
{ "id": "failed", "type": "failure", "label": "Failed", "sublabel": "recoverable error", "lane": "exceptions", "col": 0, "yOffset": 78, "tag": "retryable" },
{ "id": "cancelled", "type": "failure", "label": "Cancelled", "sublabel": "user stopped", "lane": "terminal", "col": 0, "tag": "terminal" },
{ "id": "expired", "type": "failure", "label": "Expired", "sublabel": "timeout", "lane": "terminal", "col": 1, "tag": "terminal" }
],
"transitions": [
{ "id": "approval-needed", "from": "executing", "to": "approval", "variant": "security", "fromSide": "bottom", "toSide": "top", "route": "straight" },
{ "id": "review-blocked", "from": "reviewing", "to": "blocked", "variant": "default", "route": "drop" },
{ "id": "execution-failed", "from": "executing", "to": "failed", "variant": "security", "fromSide": "left", "toSide": "left", "via": [[320, 157], [320, 385]] },
{ "id": "failed-retry", "from": "failed", "to": "executing", "variant": "emphasis", "fromSide": "left", "toSide": "top", "via": [[20, 385], [20, 80], [402, 80]] },
{ "id": "block-expired", "from": "blocked", "to": "expired", "variant": "security", "fromSide": "bottom", "toSide": "top", "route": "straight" },
{ "id": "approval-cancelled", "from": "approval", "to": "cancelled", "variant": "security", "fromSide": "bottom", "toSide": "top", "via": [[480, 336], [480, 432], [402, 432]] }
],
"cards": [
{
"dot": "emerald",
"title": "Main Path + Waits",
"items": [
"The run has five ordered phases from queue to completion",
"Approval and missing input pause the run without ending it"
]
},
{
"dot": "rose",
"title": "Recovery + Terminal Exits",
"items": [
"Failed loops back while retry budget remains",
"Cancelled and Expired are terminal exits with no return path"
]
}
]
}
+74
View File
@@ -0,0 +1,74 @@
{
"schema_version": 2,
"diagram_type": "workflow",
"meta": {
"title": "Agent Tool Call Workflow",
"animation": "trace",
"visual_preset": "signal-flow",
"quality_profile": "showcase",
"output": "examples/workflow-agent-tool-call-rendered.html"
},
"lanes": [
{ "id": "ui", "label": "User Interface" },
{ "id": "agent", "label": "Agent Runtime" },
{ "id": "policy", "label": "Policy & Recovery", "variant": "exception" },
{ "id": "tools", "label": "Tool Execution & Evidence" }
],
"phases": [
{ "id": "intake", "label": "Intake", "fromCol": 0, "toCol": 1 },
{ "id": "reasoning", "label": "Plan + route", "fromCol": 2, "toCol": 3, "variant": "emphasis" },
{ "id": "execution", "label": "Execute + report", "fromCol": 4, "toCol": 5, "variant": "dashed" }
],
"groups": [
{ "id": "agent_loop", "label": "Planning loop", "lane": "agent", "fromCol": 2, "toCol": 3, "variant": "emphasis" },
{ "id": "exception_path", "label": "Human or policy stop", "lane": "policy", "fromCol": 3, "toCol": 5, "variant": "security" },
{ "id": "evidence_path", "label": "Evidence path", "lane": "tools", "fromCol": 1, "toCol": 2, "variant": "dashed" },
{ "id": "tool_work", "label": "Tool work", "lane": "tools", "fromCol": 4, "toCol": 5, "variant": "dashed" }
],
"mainPath": ["user", "chat", "planner", "router", "approval", "tool", "external", "final"],
"nodes": [
{ "id": "user", "lane": "ui", "col": 0, "type": "external", "label": "User", "sublabel": "asks for work", "width": 132 },
{ "id": "chat", "lane": "ui", "col": 1, "type": "frontend", "label": "Chat Surface", "sublabel": "thread + files", "width": 132 },
{ "id": "final", "lane": "ui", "col": 5, "type": "backend", "label": "Final Reply", "sublabel": "answer + changes", "width": 132 },
{ "id": "planner", "lane": "agent", "col": 2, "type": "backend", "label": "Agent Planner", "sublabel": "plan next step", "tag": "context aware", "width": 132 },
{ "id": "router", "lane": "agent", "col": 3, "type": "backend", "label": "Tool Router", "sublabel": "choose capability", "width": 132 },
{ "id": "approval", "lane": "policy", "col": 3, "type": "security", "label": "Approval Gate", "sublabel": "scope + consent", "tag": "block risky ops", "width": 132 },
{ "id": "blocked", "lane": "policy", "col": 4, "type": "security", "label": "Blocked", "sublabel": "wait or reject", "width": 132 },
{ "id": "retry", "lane": "policy", "col": 5, "type": "messagebus", "label": "Retry Path", "sublabel": "revise request", "width": 132 },
{ "id": "tool", "lane": "tools", "col": 4, "type": "messagebus", "label": "Tool Call", "sublabel": "shell / browser / MCP", "tag": "structured result", "width": 132 },
{ "id": "external", "lane": "tools", "col": 5, "type": "cloud", "label": "External API", "sublabel": "network service", "width": 132 },
{ "id": "store", "lane": "tools", "col": 1, "type": "database", "label": "Context Store", "sublabel": "repo + memory", "width": 132 },
{ "id": "trace", "lane": "tools", "col": 2, "type": "database", "label": "Trace Log", "sublabel": "events + output", "width": 132 }
],
"edges": [
{ "id": "request-chat", "from": "user", "to": "chat", "variant": "default" },
{ "id": "plan-request", "from": "chat", "to": "planner", "label": "plan", "variant": "emphasis" },
{ "id": "planner-route", "from": "planner", "to": "router", "variant": "default" },
{ "id": "approval-check", "from": "router", "to": "approval", "label": "needs approval?", "variant": "security" },
{ "id": "approved-tool", "from": "approval", "to": "tool", "variant": "emphasis" },
{ "id": "approval-denied", "from": "approval", "to": "blocked", "label": "denied", "variant": "security", "role": "error" },
{ "id": "retry-request", "from": "blocked", "to": "retry", "variant": "dashed", "role": "branch" },
{ "id": "tool-external-call", "from": "tool", "to": "external", "variant": "default" },
{ "id": "external-reply", "from": "external", "to": "final", "variant": "emphasis", "role": "return", "fromSide": "right", "toSide": "right", "route": "outside-right", "width": 1.2 },
{ "id": "record-result", "from": "external", "to": "trace", "label": "record result", "variant": "dashed", "fromSide": "bottom", "toSide": "bottom", "route": "bottom-channel", "labelSegment": 1 },
{ "id": "write-trace-memory", "from": "store", "to": "trace", "label": "trace + memory", "variant": "dashed" }
],
"cards": [
{
"dot": "cyan",
"title": "Compiler Contract",
"items": [
"Lanes and columns determine node placement",
"Labels reserve clearance; routes stay orthogonal"
]
},
{
"dot": "rose",
"title": "Runtime Semantics",
"items": [
"Approval gates risky work before tool execution",
"Evidence returns through isolated trace and memory"
]
}
]
}
@@ -0,0 +1,56 @@
{
"schema_version": 1,
"diagram_type": "sequence",
"meta": {
"title": "Async Job Roundtrip",
"output": "examples/async-job-roundtrip.html",
"viewBox": [820, 920],
"animation": "trace",
"visual_preset": "signal-flow",
"quality_profile": "showcase"
},
"participants": [
{ "id": "client", "type": "external", "label": "Client", "sublabel": "mobile app" },
{ "id": "api", "type": "backend", "label": "Jobs API", "sublabel": "request edge" },
{ "id": "queue", "type": "messagebus", "label": "Queue", "sublabel": "durable work" },
{ "id": "worker", "type": "backend", "label": "Worker", "sublabel": "background" },
{ "id": "provider", "type": "cloud", "label": "Provider", "sublabel": "external API" },
{ "id": "store", "type": "database", "label": "Job Store", "sublabel": "source of truth" },
{ "id": "notify", "type": "messagebus", "label": "Notifier", "sublabel": "webhook" }
],
"segments": [
{ "from": 150, "to": 288, "label": "Accept" },
{ "from": 306, "to": 538, "label": "Background work" },
{ "from": 554, "to": 800, "label": "Notify + reconcile" }
],
"messages": [
{ "from": "client", "to": "api", "y": 180, "label": "POST /jobs", "variant": "emphasis" },
{ "from": "api", "to": "queue", "y": 222, "label": "enqueue job", "variant": "emphasis" },
{ "from": "api", "to": "client", "y": 264, "label": "202 + job id", "variant": "return" },
{ "from": "queue", "to": "worker", "y": 326, "label": "deliver", "variant": "emphasis" },
{ "from": "worker", "to": "provider", "y": 368, "label": "perform work", "variant": "default" },
{ "from": "provider", "to": "worker", "y": 410, "label": "result / timeout", "variant": "return" },
{ "from": "worker", "to": "queue", "y": 452, "label": "retry if timeout", "variant": "dashed" },
{ "from": "worker", "to": "store", "y": 494, "label": "persist final state", "variant": "emphasis" },
{ "from": "worker", "to": "notify", "y": 566, "label": "job.completed", "variant": "dashed" },
{ "from": "notify", "to": "client", "y": 608, "label": "signed webhook", "variant": "dashed" },
{ "from": "client", "to": "api", "y": 650, "label": "GET /jobs/:id", "variant": "default" },
{ "from": "api", "to": "store", "y": 692, "label": "read status", "variant": "default" },
{ "from": "store", "to": "api", "y": 734, "label": "completed", "variant": "return" },
{ "from": "api", "to": "client", "y": 776, "label": "200 final result", "variant": "return" }
],
"activations": [
{ "participant": "api", "from": 174, "to": 272, "type": "backend" },
{ "participant": "queue", "from": 216, "to": 334, "type": "messagebus" },
{ "participant": "worker", "from": 320, "to": 574, "type": "backend" },
{ "participant": "provider", "from": 362, "to": 416, "type": "cloud" },
{ "participant": "store", "from": 488, "to": 742, "type": "database" },
{ "participant": "notify", "from": 560, "to": 616, "type": "messagebus" },
{ "participant": "api", "from": 644, "to": 784, "type": "backend" }
],
"cards": [
{ "dot": "cyan", "title": "Fast Acknowledgement", "items": ["The caller receives a durable job id before work begins", "Queue ownership is visible in the acceptance contract", "The original connection does not wait for provider latency"] },
{ "dot": "orange", "title": "Bounded Recovery", "items": ["Timeouts re-enter the queue with a retry policy", "Final state is persisted before notification", "The job store remains the source of truth"] },
{ "dot": "emerald", "title": "Two Observation Paths", "items": ["A signed webhook announces completion", "Status polling is a fallback, not a second workflow", "Both paths converge on the same final state"] }
]
}
@@ -0,0 +1,34 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Brand-aware AI delivery",
"output": "brand-aware-delivery.html",
"quality_profile": "showcase",
"viewBox": [1120, 640]
},
"components": [
{ "id": "request", "type": "external", "label": "Product request", "sublabel": "Owner brief", "pos": [38, 260], "size": [138, 68] },
{ "id": "claude", "type": "frontend", "label": "Claude", "sublabel": "Plan and author", "brand": "claude", "pos": [220, 260], "size": [138, 68] },
{ "id": "github", "type": "messagebus", "label": "GitHub", "sublabel": "Review and merge", "brand": "github", "pos": [402, 260], "size": [138, 68] },
{ "id": "container", "type": "backend", "label": "Docker service", "sublabel": "Build and run", "brand": "docker", "pos": [584, 260], "size": [138, 68] },
{ "id": "edge", "type": "cloud", "label": "Cloudflare", "sublabel": "Global delivery", "brand": "cloudflare", "pos": [766, 260], "size": [138, 68] },
{ "id": "customer", "type": "external", "label": "Customers", "sublabel": "Web and mobile", "pos": [948, 260], "size": [138, 68] },
{ "id": "database", "type": "database", "label": "PostgreSQL", "sublabel": "Durable state", "brand": "postgresql", "pos": [584, 420], "size": [138, 68] },
{ "id": "billing", "type": "external", "label": "Stripe", "sublabel": "Billing events", "brand": "stripe", "pos": [766, 420], "size": [138, 68] }
],
"connections": [
{ "id": "brief-to-claude", "from": "request", "to": "claude", "label": "brief", "variant": "emphasis" },
{ "id": "claude-to-github", "from": "claude", "to": "github", "label": "change set", "labelDy": -28 },
{ "id": "github-to-container", "from": "github", "to": "container", "label": "approved build", "labelDy": -28 },
{ "id": "container-to-edge", "from": "container", "to": "edge", "label": "deploy" },
{ "id": "edge-to-customer", "from": "edge", "to": "customer", "label": "HTTPS", "variant": "emphasis" },
{ "id": "container-to-database", "from": "container", "to": "database", "label": "SQL", "fromSide": "bottom", "toSide": "top", "labelAt": [625, 370] },
{ "id": "container-to-billing", "from": "container", "to": "billing", "label": "create charge", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "via": [[653, 374], [835, 374]] },
{ "id": "billing-to-database", "from": "billing", "to": "database", "label": "webhook", "variant": "dashed" }
],
"cards": [
{ "dot": "cyan", "title": "Identity at a glance", "items": ["Semantic color still explains technical role", "Brand badges identify the actual products"] },
{ "dot": "amber", "title": "Portable by default", "items": ["Preset marks ship inside Archify", "Every visual export keeps the same badge"] }
]
}
@@ -0,0 +1,77 @@
{
"schema_version": 1,
"diagram_type": "sequence",
"meta": {
"title": "Cache Miss Request Sequence",
"output": "examples/sequence-cache-miss-request.html",
"viewBox": [1080, 580],
"column_fit": "spread",
"animation": "trace",
"quality_profile": "showcase"
},
"participants": [
{ "id": "user", "type": "external", "label": "User", "sublabel": "browser session" },
{ "id": "web", "type": "frontend", "label": "Web App", "sublabel": "React UI" },
{ "id": "api", "type": "backend", "label": "API", "sublabel": "request handler" },
{ "id": "auth", "type": "security", "label": "Auth", "sublabel": "JWT verify" },
{ "id": "redis", "type": "database", "label": "Redis", "sublabel": "cache" },
{ "id": "db", "type": "database", "label": "Postgres", "sublabel": "source of truth" },
{ "id": "trace", "type": "messagebus", "label": "Trace", "sublabel": "async event" }
],
"segments": [
{ "from": 150, "to": 250, "label": "Request" },
{ "from": 260, "to": 370, "label": "Fallback" },
{ "from": 380, "to": 480, "label": "Response + trace" }
],
"messages": [
{ "id": "open-page", "from": "user", "to": "web", "y": 160, "label": "open page", "variant": "default" },
{ "id": "dashboard-request", "from": "web", "to": "api", "y": 185, "label": "GET /dashboard", "variant": "emphasis" },
{ "id": "verify-jwt", "from": "api", "to": "auth", "y": 210, "label": "verify JWT", "variant": "security" },
{ "id": "auth-claims", "from": "auth", "to": "api", "y": 238, "label": "claims ok", "variant": "return" },
{ "id": "cache-read", "from": "api", "to": "redis", "y": 270, "label": "read cache", "variant": "default" },
{ "id": "cache-miss", "from": "redis", "to": "api", "y": 298, "label": "miss", "variant": "return" },
{ "id": "profile-query", "from": "api", "to": "db", "y": 330, "label": "query profile + metrics", "variant": "emphasis" },
{ "id": "profile-rows", "from": "db", "to": "api", "y": 358, "label": "rows", "variant": "return" },
{ "id": "cache-write", "from": "api", "to": "redis", "y": 390, "label": "set cache", "variant": "dashed" },
{ "id": "trace-emit", "from": "api", "to": "trace", "y": 418, "label": "emit trace", "variant": "dashed" },
{ "id": "dashboard-response", "from": "api", "to": "web", "y": 443, "label": "200 JSON", "variant": "return" },
{ "id": "page-render", "from": "web", "to": "user", "y": 468, "label": "render", "variant": "return" }
],
"activations": [
{ "participant": "web", "from": 180, "to": 474, "type": "frontend" },
{ "participant": "api", "from": 185, "to": 450, "type": "backend" },
{ "participant": "auth", "from": 205, "to": 244, "type": "security" },
{ "participant": "redis", "from": 265, "to": 304, "type": "database" },
{ "participant": "db", "from": 325, "to": 364, "type": "database" },
{ "participant": "trace", "from": 413, "to": 449, "type": "messagebus" }
],
"cards": [
{
"dot": "emerald",
"title": "Happy Path",
"items": [
"The main request is Web App -> API -> data source -> response",
"Return messages are quieter than forward calls",
"Activation bars make ownership duration visible"
]
},
{
"dot": "rose",
"title": "Policy + Fallback",
"items": [
"JWT verification is colored as a security interaction",
"Cache miss is visible without overpowering the main path",
"Database access only appears after cache fallback"
]
},
{
"dot": "orange",
"title": "Async Trace",
"items": [
"Trace emission is dashed and secondary",
"It does not block the response path",
"The diagram separates user-facing latency from observability"
]
}
]
}
@@ -0,0 +1,32 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Checkout Platform — Baseline",
"output": "checkout-platform.base.html",
"visual_preset": "signal-flow"
},
"components": [
{ "id": "buyers", "type": "external", "label": "Buyers", "sublabel": "Web + mobile", "pos": [40, 250], "size": [120, 60] },
{ "id": "edge", "type": "cloud", "label": "Edge Gateway", "sublabel": "TLS + routing", "pos": [220, 250], "size": [130, 60] },
{ "id": "checkout", "type": "backend", "label": "Checkout API", "sublabel": "v1 service", "pos": [430, 250], "size": [130, 60] },
{ "id": "cache", "type": "database", "label": "Session Cache", "sublabel": "Redis", "pos": [430, 100], "size": [130, 60] },
{ "id": "orders", "type": "database", "label": "Orders", "sublabel": "PostgreSQL", "pos": [640, 250], "size": [130, 60] },
{ "id": "queue", "type": "messagebus", "label": "Order Events", "sublabel": "durable queue", "pos": [430, 400], "size": [130, 60] },
{ "id": "worker", "type": "backend", "label": "Fulfilment", "sublabel": "async worker", "pos": [640, 400], "size": [130, 60] },
{ "id": "payments", "type": "external", "label": "Payment Rail", "sublabel": "external", "pos": [850, 250], "size": [130, 60] }
],
"boundaries": [
{ "kind": "region", "label": "Production region", "wraps": ["edge", "checkout", "cache", "orders", "queue", "worker"] },
{ "kind": "security-group", "label": "Checkout trust zone", "wraps": ["checkout", "orders"] }
],
"connections": [
{ "id": "buyer-request", "from": "buyers", "to": "edge", "label": "HTTPS", "variant": "emphasis" },
{ "id": "edge-checkout", "from": "edge", "to": "checkout" },
{ "id": "session-read", "from": "checkout", "to": "cache", "label": "session", "fromSide": "top", "toSide": "bottom", "labelDy": -66 },
{ "id": "persist-order", "from": "checkout", "to": "orders", "label": "SQL" },
{ "id": "publish-order", "from": "checkout", "to": "queue", "label": "accepted", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 66 },
{ "id": "consume-order", "from": "queue", "to": "worker" },
{ "id": "authorize-payment", "from": "orders", "to": "payments", "label": "authorize", "variant": "security" }
]
}
@@ -0,0 +1,32 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Checkout Platform — Fraud Gate",
"output": "checkout-platform.head.html",
"visual_preset": "signal-flow"
},
"components": [
{ "id": "buyers", "type": "external", "label": "Buyers", "sublabel": "Web + mobile", "pos": [40, 250], "size": [120, 60] },
{ "id": "edge", "type": "cloud", "label": "Edge Gateway", "sublabel": "TLS + routing", "pos": [220, 250], "size": [130, 60] },
{ "id": "checkout", "type": "backend", "label": "Checkout API", "sublabel": "v2 idempotent", "pos": [430, 250], "size": [130, 60] },
{ "id": "fraud", "type": "security", "label": "Fraud Gate", "sublabel": "policy scoring", "pos": [640, 100], "size": [130, 60], "tag": "new owner" },
{ "id": "orders", "type": "database", "label": "Orders", "sublabel": "PostgreSQL", "pos": [640, 250], "size": [130, 60] },
{ "id": "queue", "type": "messagebus", "label": "Order Events", "sublabel": "durable queue", "pos": [430, 420], "size": [130, 60] },
{ "id": "worker", "type": "backend", "label": "Fulfilment", "sublabel": "async worker", "pos": [640, 400], "size": [130, 60] },
{ "id": "payments", "type": "external", "label": "Payment Rail", "sublabel": "external", "pos": [850, 250], "size": [130, 60] }
],
"boundaries": [
{ "kind": "region", "label": "Production region", "wraps": ["edge", "checkout", "fraud", "orders", "queue", "worker"] },
{ "kind": "security-group", "label": "Checkout trust zone", "wraps": ["checkout", "fraud", "orders"] }
],
"connections": [
{ "id": "buyer-request", "from": "buyers", "to": "edge", "label": "HTTPS", "variant": "emphasis" },
{ "id": "edge-checkout", "from": "edge", "to": "checkout" },
{ "id": "fraud-check", "from": "checkout", "to": "fraud", "label": "screen", "variant": "security", "fromSide": "top", "toSide": "bottom", "labelDy": -66 },
{ "id": "persist-order", "from": "checkout", "to": "orders", "label": "SQL tx" },
{ "id": "publish-order", "from": "checkout", "to": "queue", "label": "accepted", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 76 },
{ "id": "consume-order", "from": "queue", "to": "worker" },
{ "id": "authorize-payment", "from": "fraud", "to": "payments", "label": "authorize", "variant": "security", "fromSide": "right", "toSide": "top", "via": [[820, 130], [915, 130]] }
]
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,48 @@
{
"schema_version": 2,
"diagram_type": "lifecycle",
"meta": {
"title": "Deployment Release Lifecycle",
"output": "examples/deployment-release.html",
"animation": "trace",
"visual_preset": "signal-flow",
"quality_profile": "showcase"
},
"lanes": [
{ "id": "main", "label": "Release phases" },
{ "id": "waiting", "label": "Approval + health wait" },
{ "id": "recovery", "label": "Rollback controller" },
{ "id": "terminal", "label": "Terminal exits" }
],
"states": [
{ "id": "queued", "type": "start", "label": "Queued", "sublabel": "change accepted", "lane": "main", "col": 0, "step": "01", "tag": "pending" },
{ "id": "building", "type": "active", "label": "Building", "sublabel": "immutable image", "lane": "main", "col": 1, "step": "02", "tag": "running" },
{ "id": "verifying", "type": "decision", "label": "Verifying", "sublabel": "tests + policy", "lane": "main", "col": 2, "step": "03", "tag": "gate" },
{ "id": "ready", "type": "waiting", "label": "Ready", "sublabel": "promotion pending", "lane": "main", "col": 3, "step": "04", "tag": "approved" },
{ "id": "live", "type": "success", "label": "Live", "sublabel": "production healthy", "lane": "main", "col": 4, "step": "05", "tag": "success" },
{ "id": "approval", "type": "waiting", "label": "Needs Approval", "sublabel": "release owner", "lane": "waiting", "col": 2, "tag": "pause" },
{ "id": "rollback", "type": "active", "label": "Rolling Back", "sublabel": "last good image", "lane": "recovery", "col": 3, "tag": "automatic" },
{ "id": "paused", "type": "waiting", "label": "Health Paused", "sublabel": "SLO regression", "lane": "waiting", "col": 4, "tag": "observe" },
{ "id": "cancelled", "type": "failure", "label": "Cancelled", "sublabel": "approval denied", "lane": "terminal", "col": 2, "tag": "terminal" },
{ "id": "failed", "type": "failure", "label": "Failed", "sublabel": "rollback failed", "lane": "terminal", "col": 3, "tag": "terminal" },
{ "id": "rolled_back", "type": "success", "label": "Rolled Back", "sublabel": "service restored", "lane": "terminal", "col": 4, "tag": "terminal" }
],
"transitions": [
{ "from": "queued", "to": "building" },
{ "from": "building", "to": "verifying" },
{ "from": "verifying", "to": "ready" },
{ "from": "ready", "to": "live" },
{ "from": "verifying", "to": "approval", "variant": "security", "label": "needs sign-off" },
{ "from": "approval", "to": "ready", "label": "approved" },
{ "from": "approval", "to": "cancelled", "variant": "security" },
{ "from": "ready", "to": "rollback", "variant": "security" },
{ "from": "rollback", "to": "failed", "variant": "security" },
{ "from": "live", "to": "paused", "variant": "dashed" },
{ "from": "paused", "to": "rolled_back", "variant": "emphasis" }
],
"cards": [
{ "dot": "cyan", "title": "Promotion Rail", "items": ["The release object moves through five ordered phases", "Verification and approval remain distinct states", "Live means production health is currently proven"] },
{ "dot": "amber", "title": "Wait States", "items": ["Human approval can pause without consuming a worker", "A health regression pauses further rollout", "Every wait exposes the event required to continue"] },
{ "dot": "rose", "title": "Explicit Endings", "items": ["Denied approval ends as Cancelled", "Rollback controller failure ends as Failed", "Successful rollback is a terminal restored outcome"] }
]
}
+52
View File
@@ -0,0 +1,52 @@
{
"schema_version": 1,
"diagram_type": "dataflow",
"meta": {
"title": "Order Event-stream Topology",
"output": "examples/event-stream.html",
"viewBox": [1080, 780],
"animation": "trace",
"visual_preset": "signal-flow",
"quality_profile": "showcase"
},
"stages": [
{ "label": "Producers" },
{ "label": "Transit" },
{ "label": "Processors" },
{ "label": "State + recovery" },
{ "label": "Consumers" }
],
"nodes": [
{ "id": "checkout", "type": "frontend", "label": "Checkout API", "sublabel": "order producer", "stage": 0, "row": 0, "tag": "team commerce" },
{ "id": "billing", "type": "backend", "label": "Billing API", "sublabel": "payment producer", "stage": 0, "row": 2, "tag": "team money" },
{ "id": "orders", "type": "messagebus", "label": "orders.v1", "sublabel": "12 partitions", "stage": 1, "row": 0, "tag": "key: order_id" },
{ "id": "payments", "type": "messagebus", "label": "payments.v2", "sublabel": "8 partitions", "stage": 1, "row": 2, "tag": "key: order_id" },
{ "id": "validate", "type": "backend", "label": "Order Validate", "sublabel": "group fulfillment", "stage": 2, "row": 0, "tag": "ordered" },
{ "id": "enrich", "type": "backend", "label": "Payment Enrich", "sublabel": "group analytics", "stage": 2, "row": 2, "tag": "at-least-once" },
{ "id": "state", "type": "database", "label": "Order State", "sublabel": "materialized view", "stage": 3, "row": 1, "tag": "idempotent" },
{ "id": "dlq", "type": "messagebus", "label": "events.dlq", "sublabel": "poison events", "stage": 3, "row": 4, "tag": "7-day retention" },
{ "id": "fulfillment", "type": "backend", "label": "Fulfillment", "sublabel": "shipping workflow", "stage": 4, "row": 0, "tag": "consumer" },
{ "id": "analytics", "type": "database", "label": "Analytics", "sublabel": "streaming facts", "stage": 4, "row": 2, "tag": "consumer" },
{ "id": "replay", "type": "security", "label": "Replay Tool", "sublabel": "approved batch", "stage": 4, "row": 4, "tag": "operator gate" },
{ "id": "ops", "type": "external", "label": "On-call", "sublabel": "DLQ owner", "stage": 4, "row": 3, "yOffset": -18, "tag": "SRE" }
],
"flows": [
{ "from": "checkout", "to": "orders", "label": "OrderPlaced", "classification": "schema v1", "variant": "emphasis", "route": "straight" },
{ "from": "billing", "to": "payments", "label": "PaymentCaptured", "classification": "schema v2", "variant": "emphasis", "route": "straight" },
{ "from": "orders", "to": "validate", "label": "ordered orders", "classification": "consumer group", "variant": "emphasis", "route": "straight" },
{ "from": "payments", "to": "enrich", "label": "payment facts", "classification": "at-least-once", "variant": "emphasis", "route": "straight" },
{ "from": "validate", "to": "state", "label": "valid order", "classification": "idempotent", "variant": "emphasis", "route": "vertical-channel" },
{ "from": "enrich", "to": "state", "label": "enriched payment", "classification": "idempotent", "variant": "default", "route": "vertical-channel" },
{ "from": "state", "to": "fulfillment", "label": "ready orders", "classification": "read model", "variant": "emphasis", "route": "vertical-channel" },
{ "from": "state", "to": "analytics", "label": "order facts", "classification": "non-PII", "variant": "default", "route": "vertical-channel" },
{ "from": "validate", "to": "dlq", "label": "invalid event", "classification": "dead letter", "variant": "security", "fromSide": "top", "toSide": "top", "via": [[530, 80], [20, 80], [20, 550], [745, 550]], "labelAt": [300, 550] },
{ "from": "enrich", "to": "dlq", "label": "poison event", "classification": "dead letter", "variant": "security", "route": "bottom-channel", "labelDy": 30 },
{ "from": "dlq", "to": "ops", "label": "failure sample", "classification": "restricted", "variant": "security", "route": "vertical-channel" },
{ "from": "dlq", "to": "replay", "label": "approved replay", "classification": "audited batch", "variant": "dashed", "route": "straight", "labelDy": 30 }
],
"cards": [
{ "dot": "amber", "title": "Transit Contract", "items": ["Every event and topic is named", "Partition keys preserve per-order ordering", "Consumer groups expose processing ownership"] },
{ "dot": "emerald", "title": "State + Delivery", "items": ["Processors write an idempotent materialized view", "Fulfillment and analytics consume distinct assets", "At-least-once delivery never implies duplicate business effects"] },
{ "dot": "rose", "title": "Failure Ownership", "items": ["Poison events land in a retained dead-letter topic", "On-call inspects samples before replay", "Replay is gated, batched, and auditable"] }
]
}
+143
View File
@@ -0,0 +1,143 @@
{
"schema_version": 2,
"diagram_type": "workflow",
"meta": {
"title": "2026年剩余假期安排",
"locale": "zh-CN",
"quality_profile": "showcase",
"legend": {
"entries": {
"backend": {
"label": "假期"
},
"security": {
"label": "补班"
},
"external": {
"label": "时间节点"
}
}
},
"output": "holiday-planning.html"
},
"lanes": [
{
"id": "calendar",
"label": "时间线"
},
{
"id": "makeup",
"label": "调休"
}
],
"mainPath": [
"today",
"mid_autumn",
"national_day",
"year_end"
],
"nodes": [
{
"id": "today",
"lane": "calendar",
"col": 0,
"type": "external",
"label": "今天 9月22日",
"sublabel": "周二 · 距中秋3天",
"icon": "clock"
},
{
"id": "mid_autumn",
"lane": "calendar",
"col": 1,
"type": "backend",
"label": "中秋节",
"sublabel": "9月25日–27日 · 3天",
"tag": "无需调休",
"icon": "moon"
},
{
"id": "national_day",
"lane": "calendar",
"col": 3,
"type": "backend",
"label": "国庆节",
"sublabel": "10月1日–7日 · 7天",
"tag": "连休",
"icon": "calendar"
},
{
"id": "year_end",
"lane": "calendar",
"col": 5,
"type": "external",
"label": "此后至年底",
"sublabel": "无法定节假日",
"icon": "flag"
},
{
"id": "makeup_work",
"lane": "makeup",
"col": 3,
"type": "security",
"label": "调休上班",
"sublabel": "10月10日(周六)",
"tag": "补班",
"icon": "briefcase"
}
],
"edges": [
{
"id": "to_mid_autumn",
"from": "today",
"to": "mid_autumn",
"label": "3天后连休"
},
{
"id": "mid_to_national",
"from": "mid_autumn",
"to": "national_day",
"label": "节后上班3天"
},
{
"id": "to_year_end",
"from": "national_day",
"to": "year_end",
"label": "假期结束"
},
{
"id": "makeup_branch",
"from": "national_day",
"to": "makeup_work",
"label": "国庆调休",
"role": "branch",
"variant": "security"
}
],
"cards": [
{
"dot": "emerald",
"title": "中秋节",
"items": [
"9月25日(周五)至27日(周日)放假,共3天",
"与周末自然相连,无需额外调休上班"
]
},
{
"dot": "amber",
"title": "国庆节",
"items": [
"10月1日(周四)至7日(周三)放假调休,共7天",
"9月20日(周日)上班日已过",
"10月10日(周六)需补班"
]
},
{
"dot": "slate",
"title": "年底前",
"items": [
"国庆假期结束后,到2026年底不再有法定节假日安排"
]
}
]
}
+59
View File
@@ -0,0 +1,59 @@
{
"schema_version": 1,
"diagram_type": "workflow",
"meta": {
"title": "Incident Response Runbook",
"output": "examples/incident-response.html",
"animation": "trace",
"visual_preset": "signal-flow",
"quality_profile": "showcase"
},
"lanes": [
{ "id": "signals", "label": "Signals" },
{ "id": "responders", "label": "Incident Command" },
{ "id": "mitigation", "label": "Service Mitigation" },
{ "id": "recovery", "label": "Recovery Evidence" },
{ "id": "communication", "label": "Stakeholder Communication" },
{ "id": "exceptions", "label": "Escalation + Rollback", "variant": "exception" }
],
"phases": [
{ "id": "detect", "label": "Detect", "fromCol": 0, "toCol": 1 },
{ "id": "respond", "label": "Triage + mitigate", "fromCol": 2, "toCol": 3, "variant": "emphasis" },
{ "id": "recover", "label": "Verify + close", "fromCol": 4, "toCol": 5, "variant": "dashed" }
],
"groups": [
{ "id": "command", "label": "Incident command", "lane": "responders", "fromCol": 1, "toCol": 3, "variant": "emphasis" },
{ "id": "exception_actions", "label": "If impact persists", "lane": "exceptions", "fromCol": 3, "toCol": 5, "variant": "security" }
],
"mainPath": ["alert", "page", "triage", "contain", "recover", "verify", "close"],
"nodes": [
{ "id": "alert", "lane": "signals", "col": 0, "type": "messagebus", "label": "SLO Alert", "sublabel": "burn rate" },
{ "id": "page", "lane": "responders", "col": 1, "width": 76, "type": "external", "label": "Page On-call", "sublabel": "acknowledge" },
{ "id": "triage", "lane": "responders", "col": 2, "width": 64, "type": "backend", "label": "Triage", "sublabel": "scope impact" },
{ "id": "declare", "lane": "responders", "col": 3, "type": "security", "label": "Declare", "sublabel": "assign commander", "tag": "SEV-1/2" },
{ "id": "contain", "lane": "mitigation", "col": 3, "width": 72, "type": "backend", "label": "Contain", "sublabel": "stop growth" },
{ "id": "recover", "lane": "mitigation", "col": 4, "width": 52, "type": "cloud", "label": "Recover", "sublabel": "restore" },
{ "id": "verify", "lane": "recovery", "col": 5, "type": "database", "label": "Verify", "sublabel": "SLO + traces", "tag": "15 min stable" },
{ "id": "close", "lane": "communication", "col": 5, "type": "external", "label": "Resolve", "sublabel": "final update" },
{ "id": "update", "lane": "communication", "col": 3, "type": "frontend", "label": "Status Update", "sublabel": "impact + ETA" },
{ "id": "escalate", "lane": "exceptions", "col": 3, "width": 72, "type": "security", "label": "Escalate", "sublabel": "specialist" },
{ "id": "rollback", "lane": "exceptions", "col": 5, "type": "messagebus", "label": "Rollback", "sublabel": "last good" }
],
"edges": [
{ "from": "alert", "to": "page", "label": "page", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "page", "to": "triage", "route": "bottom-channel", "fromSide": "bottom", "toSide": "bottom" },
{ "from": "triage", "to": "contain", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "contain", "to": "recover", "route": "bottom-channel", "fromSide": "bottom", "toSide": "bottom" },
{ "from": "recover", "to": "verify", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "verify", "to": "close", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "triage", "to": "declare", "variant": "security" },
{ "from": "declare", "to": "update", "variant": "dashed", "fromSide": "top", "toSide": "left", "via": [[430, 16], [20, 16], [20, 615]] },
{ "from": "update", "to": "escalate", "variant": "security", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "verify", "to": "rollback", "variant": "security", "role": "error", "route": "outside-right", "fromSide": "right", "toSide": "right" }
],
"cards": [
{ "dot": "rose", "title": "Ownership First", "items": ["A page is not an incident until someone owns command", "Severity and scope are explicit before mitigation spreads", "Escalation names the missing expertise"] },
{ "dot": "emerald", "title": "Recovery Is Evidence", "items": ["Mitigation can reduce impact without proving recovery", "SLOs and traces must stay healthy for a fixed window", "The final update follows verification, not optimism"] },
{ "dot": "cyan", "title": "Communication Contract", "items": ["Stakeholders receive impact, action, and next update time", "Rollback remains visible as a deliberate response", "Every branch has an owner and observable exit"] }
]
}
File diff suppressed because it is too large. Load diff
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "{title} Diagramm",
"diagram.description.architecture": "Ein von Archify erstelltes Architekturdiagramm.",
"diagram.description.workflow": "Ein von Archify erstelltes Workflow-Diagramm.",
"diagram.description.sequence": "Ein von Archify erstelltes Sequenzdiagramm.",
"diagram.description.dataflow": "Ein von Archify erstelltes Datenfluss-Diagramm.",
"diagram.description.lifecycle": "Ein von Archify erstelltes Lifecycle-Diagramm.",
"node.focus": "{label} fokussieren",
"node.focus.detail": "{label} fokussieren, {detail}",
"node.context.architecture": "Architekturkomponente",
"node.context.workflow": "Workflow-Knoten",
"node.context.sequence": "Sequenzteilnehmer",
"node.context.dataflow": "Datenfluss-Knoten",
"node.context.lifecycle": "Lifecycle-Status",
"legend.title": "Legende",
"legend.architecture.frontend": "Frontend",
"legend.architecture.backend": "Backend",
"legend.architecture.database": "Datenbank",
"legend.architecture.cloud": "Cloud",
"legend.architecture.security": "Sicherheit",
"legend.architecture.messagebus": "Nachrichtenbus",
"legend.architecture.external": "Extern",
"legend.workflow.frontend": "Benutzeroberfläche",
"legend.workflow.backend": "Agentenlogik",
"legend.workflow.security": "Richtlinie",
"legend.workflow.messagebus": "Werkzeugaktion",
"legend.workflow.database": "Kontext / Trace",
"legend.workflow.cloud": "Cloud-Dienst",
"legend.workflow.external": "Externes System",
"legend.sequence.emphasis": "Hauptanfrage",
"legend.sequence.return": "Rückgabe",
"legend.sequence.security": "Sicherheit",
"legend.sequence.dashed": "asynchroner Trace",
"legend.sequence.default": "Nachricht",
"legend.dataflow.emphasis": "Primärdaten",
"legend.dataflow.security": "Richtlinie / PII",
"legend.dataflow.dashed": "asynchroner Batch",
"legend.dataflow.database": "Datenspeicher",
"legend.dataflow.default": "Datenfluss",
"legend.lifecycle.start": "Startzustand",
"legend.lifecycle.active": "aktiv",
"legend.lifecycle.waiting": "Wartend",
"legend.lifecycle.decision": "Entscheidung",
"legend.lifecycle.success": "Erfolg",
"legend.lifecycle.failure": "Fehler / Abbruch",
"legend.lifecycle.neutral": "neutral",
"legend.lifecycle.external": "extern",
"legend.lifecycle.final": "Endzustand",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.kind.database": "Datenbank",
"viewer.kind.cloud": "Cloud",
"viewer.kind.security": "Sicherheit",
"viewer.kind.messagebus": "Nachrichtenbus",
"viewer.kind.external": "Extern",
"viewer.kind.neutral": "Neutral",
"viewer.kind.node": "Knoten",
"viewer.kind.start": "Start",
"viewer.kind.active": "Aktiv",
"viewer.kind.waiting": "Wartend",
"viewer.kind.decision": "Entscheidung",
"viewer.kind.success": "Erfolg",
"viewer.kind.failure": "Fehler",
"viewer.toolbar.actions": "Diagrammaktionen",
"viewer.theme.toggle.title": "Design wechseln (T)",
"viewer.theme.toggle": "Farbdesign wechseln",
"viewer.theme.dark": "Dunkel",
"viewer.theme.light": "Hell",
"viewer.preset.choose.title": "Visuellen Stil wählen (S wechselt durch)",
"viewer.preset.choose": "Visuellen Stil wählen",
"viewer.preset.style": "Stil",
"viewer.preset.menu": "Visueller Stil",
"viewer.preset.identity": "Visuelle Identität",
"viewer.preset.cycles": "S wechselt durch",
"viewer.preset.classic": "Klassisch",
"viewer.preset.classic.short": "Klassisch",
"viewer.preset.classic.hint": "Stabiler technischer Standard",
"viewer.preset.flow": "Signalfluss",
"viewer.preset.flow.short": "Fluss",
"viewer.preset.flow.hint": "Bewegungsbetonte Präsentation",
"viewer.preset.blueprint": "Bauplan",
"viewer.preset.blueprint.hint": "Technisches Review",
"viewer.preset.editorial": "Editorial",
"viewer.preset.editorial.hint": "Für Veröffentlichungen und Launch-Notizen",
"viewer.preset.badge.signalFlow": "SIGNALFLUSS",
"viewer.preset.badge.blueprint": "BAUPLAN / REV. 01",
"viewer.preset.badge.editorial": "EDITORIAL / FELDNOTIZ",
"viewer.preset.badge.editorialPlate": "ARCHIFY / TAFEL 04",
"viewer.preset.current": "Visueller Stil: {style}. Visuellen Stil wählen",
"viewer.motion.live": "Live",
"viewer.motion.still": "Statisch",
"viewer.motion.pause": "Bewegung pausieren",
"viewer.motion.resume": "Bewegung fortsetzen",
"viewer.motion.reduced": "Bewegung wegen reduzierter Bewegungseinstellung pausiert",
"viewer.motion.hidden": "Bewegung pausiert, während diese Seite ausgeblendet ist",
"viewer.motion.yielding": "Bewegung pausieren; derzeit an {owner} abgegeben",
"viewer.motion.yielding.title": "Live-Vorschau aktiv · an {owner} abgegeben",
"viewer.owner.route": "Routensonde",
"viewer.owner.lens": "Semantische Linse",
"viewer.owner.relationship": "Beziehungsvorschau",
"viewer.owner.intent": "Intent Trace",
"viewer.owner.focus": "semantischer Fokus",
"viewer.owner.legend": "Legendenvorschau",
"viewer.owner.reader": "Leserinteraktion",
"viewer.present.enter": "Präsentationsmodus starten",
"viewer.present.enter.title": "Präsentationsmodus (F)",
"viewer.present.exit": "Präsentationsmodus beenden",
"viewer.present.exit.title": "Präsentationsmodus beenden (F oder Escape)",
"viewer.present.present": "Präsentieren",
"viewer.present.exit.label": "Beenden",
"viewer.export.button": "Exportieren",
"viewer.export.button.title": "Diagramm exportieren (E)",
"viewer.export.diagram": "Diagramm exportieren",
"viewer.export.menu": "Exportieren",
"viewer.export.subtitle": "Portable, saubere Ausgaben",
"viewer.export.share": "Teilen",
"viewer.export.shareCard": "Freigabekarte",
"viewer.export.routeShareCard": "Routen-Freigabekarte",
"viewer.export.reachShareCard": "Reichweiten-Freigabekarte",
"viewer.export.copyDiagram": "Diagramm kopieren",
"viewer.export.clipboardPng": "PNG in Zwischenablage",
"viewer.export.raster": "Rasterbilder",
"viewer.export.image": "Bild",
"viewer.export.lossless": "Verlustfreies Bild",
"viewer.export.compact": "Kompaktes Bild",
"viewer.export.modern": "Modernes Bild",
"viewer.export.vectorMotion": "Vektor und Bewegung",
"viewer.export.vectorMotion.heading": "Vektor & Bewegung",
"viewer.export.editable": "Editierbarer Vektor",
"viewer.export.motion6s": "6-Sekunden-Bewegung",
"viewer.export.unsupported": "Von diesem Browser nicht unterstützt",
"viewer.export.clipboardUnsupported": "Bild-Zwischenablage wird von diesem Browser nicht unterstützt",
"viewer.export.clipboardUnsupported.short": "Dieser Browser unterstützt keine Bild-Zwischenablage.",
"viewer.export.motionUnavailable": "Bewegungsaufnahme in diesem Browser nicht verfügbar",
"viewer.export.webmUnavailable": "WebM in diesem Browser nicht verfügbar",
"viewer.export.failed": "Export fehlgeschlagen: {message}",
"viewer.export.unknownVariant": "Unbekannter Freigabekarten-Typ: {variant}",
"viewer.export.routeRequired": "Route verfolgen, bevor eine Routen-Freigabekarte exportiert wird",
"viewer.export.reachRequired": "Verfasste Reichweite verfolgen, bevor eine Reichweiten-Freigabekarte exportiert wird",
"viewer.export.unknown": "unbekannt",
"viewer.export.routeFailed": "Export der Routen-Freigabekarte fehlgeschlagen: {message}",
"viewer.export.reachFailed": "Export der Reichweiten-Freigabekarte fehlgeschlagen: {message}",
"viewer.export.copyFailed": "Kopieren fehlgeschlagen: {message}",
"viewer.export.copiedPng": "PNG in Zwischenablage kopiert",
"viewer.export.downloadedRoute": "Routen-Freigabekarte heruntergeladen",
"viewer.export.downloadedReach": "Reichweiten-Freigabekarte heruntergeladen",
"viewer.export.downloadedWebm": "WebM heruntergeladen",
"viewer.export.recording": "6 Sekunden Bewegung werden aufgezeichnet …",
"viewer.export.card.routeSummary.one": "Route: {source} → {target} · {count} gerichteter Hop",
"viewer.export.card.routeSummary.other": "Route: {source} → {target} · {count} gerichtete Hops",
"viewer.export.card.reachSummary": "Verfasst {direction} von {origin} · {nodes} · {links} · max. {hops}",
"viewer.export.card.node.one": "{count} Knoten",
"viewer.export.card.node.other": "{count} Knoten",
"viewer.export.card.link.one": "{count} Verbindung",
"viewer.export.card.link.other": "{count} Verbindungen",
"viewer.export.card.hop.one": "{count} Hop",
"viewer.export.card.hop.other": "{count} Hops",
"viewer.export.card.routeBadge": "ARCHIFY · ROUTE · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · {direction} REICHWEITE",
"viewer.export.direction.upstream": "Vorgelagert",
"viewer.export.direction.downstream": "Nachgelagert",
"viewer.export.error.canvasUnavailable": "Canvas für {label} nicht verfügbar",
"viewer.export.error.contextUnavailable": "2D-Canvas-Kontext für {label} nicht verfügbar",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob für {label} nicht verfügbar",
"viewer.export.error.toBlobNull": "canvas.toBlob lieferte keine Daten für {label}",
"viewer.export.error.variantsCombined": "Freigabekarten-Typen können nicht kombiniert werden",
"viewer.export.error.viewerState": "Beim Export der Freigabekarte konnte der temporäre Viewer-Zustand nicht entfernt werden",
"viewer.export.error.routeState": "Beim Export der Routenkarte konnte die aufgelöste Route nicht sicher erhalten werden",
"viewer.export.error.reachState": "Beim Export der Reichweitenkarte konnte die verfasste Reichweite nicht sicher erhalten werden",
"viewer.export.error.webmRequirements": "Der WebM-Bewegungsexport erfordert eine Trace-Animation und Browser-Unterstützung für MediaRecorder",
"viewer.export.error.mediaRecorder": "MediaRecorder fehlgeschlagen",
"viewer.export.error.emptyWebm": "MediaRecorder hat ein leeres WebM erzeugt",
"viewer.export.error.webmBackground": "SVG-Hintergrund konnte für den WebM-Export nicht geladen werden",
"viewer.guide.eyebrow": "Diagrammleitfaden",
"viewer.guide.close": "Diagrammleitfaden schließen",
"viewer.guide.inspecting": "Kompilierte Semantik wird geprüft",
"viewer.guide.actions": "Diagramm-Erkundungsaktionen",
"viewer.guide.find": "Beliebigen Knoten finden",
"viewer.guide.find.hint": "Labels, Zuständigkeiten, Arten und stabile IDs durchsuchen.",
"viewer.guide.route": "Eine Route verfolgen",
"viewer.guide.route.aria": "Eine gerichtete Route verfolgen",
"viewer.guide.route.hint": "Herausfinden, wie zwei semantische Knoten in verfasster Richtung verbunden sind.",
"viewer.guide.map": "Das gesamte System sehen",
"viewer.guide.map.hint": "Semantisches Radar mit Live-Ansicht und stabilen Knoten öffnen.",
"viewer.guide.lens": "Semantische Arten vergleichen",
"viewer.guide.lens.hint": "Rollen zählen, ihren Verkehr sichtbar machen und direkte verfasste Verbindungen vergleichen.",
"viewer.guide.present": "Präsentationsmodus starten",
"viewer.guide.present.hint": "Dem Live-Diagramm die gesamte Ansicht geben, ohne den Export zu verändern.",
"viewer.guide.shortcuts": "Weitere Tastenkombinationen",
"viewer.guide.shortcut.export": "Exportieren",
"viewer.guide.shortcut.theme": "Design",
"viewer.guide.shortcut.style": "Stil",
"viewer.guide.shortcut.reset": "Zurücksetzen",
"viewer.guide.shortcut.zoomIn": "Vergrößern",
"viewer.guide.shortcut.zoomOut": "Verkleinern",
"viewer.guide.shortcut.close": "Schließen",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} semantischer Knoten",
"viewer.guide.fact.node.other": "{count} semantische Knoten",
"viewer.guide.fact.relationship.one": "{count} Beziehung",
"viewer.guide.fact.relationship.other": "{count} Beziehungen",
"viewer.guide.open": "Diagrammleitfaden öffnen",
"viewer.finder.title": "Knoten finden",
"viewer.finder.close": "Knotensuche schließen",
"viewer.finder.placeholder": "Labels oder IDs suchen",
"viewer.finder.search": "Diagrammknoten durchsuchen",
"viewer.finder.results": "Diagrammknoten",
"viewer.finder.empty": "Keine passenden Knoten",
"viewer.finder.result.focus": "{label} fokussieren",
"viewer.finder.result.routeStart": "{label} als Routenstart wählen",
"viewer.finder.result.routeTarget": "{label} als Routenziel wählen, {links}",
"viewer.finder.status.empty": "Keine passenden Knoten",
"viewer.finder.status.count.one": "{count} passender Knoten",
"viewer.finder.status.count.other": "{count} passende Knoten",
"viewer.finder.noun.nodes": "Knoten",
"viewer.finder.link.one": "{count} Verbindung",
"viewer.finder.link.other": "{count} Verbindungen",
"viewer.finder.result.focus.one": "{label} fokussieren, {count} zugehörige Verbindung",
"viewer.finder.result.focus.other": "{label} fokussieren, {count} zugehörige Verbindungen",
"viewer.finder.status.filtered": "{visible} von {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Semantischer Pass",
"viewer.passport.metadata": "Knoten-Metadaten",
"viewer.passport.evidence": "Verifizierter Quellnachweis",
"viewer.passport.verified": "Verifizierte Quelle",
"viewer.passport.verificationScope": "Verifiziert gegen lokales Git zur fixierten Revision. Remote-Zugriff wurde nicht geprüft.",
"viewer.passport.reach": "Verfasste Reichweite",
"viewer.passport.reach.trace": "Verfasste Erreichbarkeit verfolgen",
"viewer.passport.upstream": "Vorgelagert",
"viewer.passport.downstream": "Nachgelagert",
"viewer.passport.upstream.trace": "Vorgelagerte verfasste Erreichbarkeit verfolgen",
"viewer.passport.downstream.trace": "Nachgelagerte verfasste Erreichbarkeit verfolgen",
"viewer.passport.close": "Semantischen Pass schließen",
"viewer.passport.copy": "Link kopieren",
"viewer.passport.copy.focus": "Link zum fokussierten Knoten kopieren",
"viewer.passport.relations": "Beziehungen",
"viewer.passport.relations.show": "Verbundene Beziehungen anzeigen",
"viewer.passport.relations.hide": "Verbundene Beziehungen ausblenden",
"viewer.passport.relations.list": "Verbundene Beziehungen",
"viewer.passport.copyRelation": "Beziehung kopieren",
"viewer.passport.copyNode": "Knoten kopieren",
"viewer.passport.copyPinned": "Link zur angehefteten Beziehung kopieren",
"viewer.passport.copySource": "Link zum Quellknoten kopieren",
"viewer.passport.copy.focused.success": "Link des fokussierten Knotens kopiert",
"viewer.passport.copy.pinned.success": "Link der angehefteten Beziehung kopiert",
"viewer.passport.copy.focused.failed": "Link des fokussierten Knotens konnte nicht kopiert werden",
"viewer.passport.copy.pinned.failed": "Link der angehefteten Beziehung konnte nicht kopiert werden",
"viewer.passport.relationship.none": "Keine verbundenen Beziehungen",
"viewer.passport.relationship.count.one": "{count} Beziehung",
"viewer.passport.relationship.count.other": "{count} Beziehungen",
"viewer.passport.relationship.show.one": "{count} verbundene Beziehung anzeigen",
"viewer.passport.relationship.show.other": "{count} verbundene Beziehungen anzeigen",
"viewer.passport.relationship.summary": "{out} ausgehend · {in} eingehend{loops}",
"viewer.passport.relationship.loops": " · {count} Selbstschleife",
"viewer.passport.relationship.explorer": "Direkter Beziehungs-Explorer",
"viewer.passport.relationship.help": "Pfeiltasten zum Erkunden der Beziehungen verwenden. Enter oder Leertaste heftet Details an; Escape hebt die Auswahl auf.",
"viewer.passport.relationship.loopsBack": "schleift zurück",
"viewer.passport.relationship.connectsTo": "verbindet zu",
"viewer.passport.relationship.connectsFrom": "verbindet von",
"viewer.passport.relationship.pinned": "Angeheftete Beziehung · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Beziehung {index} von {total} prüfen: {from} zu {to}, {label}. Enter für Details drücken.",
"viewer.passport.relationship.group.out": "Ausgehend",
"viewer.passport.relationship.group.in": "Eingehend",
"viewer.passport.relationship.group.loop": "Selbstschleifen",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "AUS →",
"viewer.passport.relationship.direction.in": "← EIN",
"viewer.passport.relationship.direction.loop": "SCHLEIFE",
"viewer.passport.sourceCount.one": "{count} verifizierte Quellenreferenz",
"viewer.passport.sourceCount.other": "{count} verifizierte Quellenreferenzen",
"viewer.passport.sourceMarker": "QUELLE",
"viewer.passport.beacon.one": "{count} verifizierte Quelle; diesen Knoten fokussieren, um sie zu prüfen",
"viewer.passport.beacon.other": "{count} verifizierte Quellen; diesen Knoten fokussieren, um sie zu prüfen",
"viewer.passport.repository.open": "Verifizierte Repository-Revision {revision} öffnen",
"viewer.passport.source.open": "Verifizierte Quelle {path} bei Revision {revision} öffnen",
"viewer.passport.source.openLink": "Öffnen ↗",
"viewer.passport.reach.upstream.one": "{count} vorgelagerten verfassten Knoten verfolgen",
"viewer.passport.reach.upstream.other": "{count} vorgelagerte verfasste Knoten verfolgen",
"viewer.passport.reach.downstream.one": "{count} nachgelagerten verfassten Knoten verfolgen",
"viewer.passport.reach.downstream.other": "{count} nachgelagerte verfasste Knoten verfolgen",
"viewer.passport.reach.noUpstream": "Keine vorgelagerten verfassten Knoten",
"viewer.passport.reach.noDownstream": "Keine nachgelagerten verfassten Knoten",
"viewer.passport.reach.status": "{direction} · {nodes} Knoten · {links} Verbindungen · max. {hops} Hops",
"viewer.route.eyebrow": "Routensonde",
"viewer.route.start": "Startknoten wählen",
"viewer.route.start.find": "Start finden",
"viewer.route.start.find.aria": "Einen Routenstart finden",
"viewer.route.copy": "Link kopieren",
"viewer.route.copy.aria": "Link zur verfolgten Route kopieren",
"viewer.route.clear": "Löschen",
"viewer.route.clear.aria": "Routensonde löschen",
"viewer.route.traced": "Verfolgte Route",
"viewer.route.pickTwo": "Zwei semantische Knoten im Diagramm auswählen",
"viewer.route.pickOne": "Einen semantischen Knoten im Diagramm auswählen",
"viewer.route.controls": "Routen-Verlaufssteuerung",
"viewer.route.previous": "Vorherige Routenposition",
"viewer.route.play": "Routenverlauf abspielen",
"viewer.route.pause": "Routenverlauf pausieren",
"viewer.route.replay": "Routenverlauf erneut abspielen",
"viewer.route.next": "Nächste Routenposition",
"viewer.route.journey": "Verlauf",
"viewer.route.pause.label": "Pausieren",
"viewer.route.replay.label": "Erneut abspielen",
"viewer.route.overview": "Übersicht",
"viewer.route.overview.aria": "Vollständige Routenübersicht anzeigen",
"viewer.route.instructions": "Quelle wählen, dann das Ziel. Die Richtung ist entscheidend.",
"viewer.route.destination": "Ziel ausgehend von {label} wählen",
"viewer.route.destination.find": "Ziel finden",
"viewer.route.destination.find.aria": "Ein erreichbares Routenziel finden",
"viewer.route.differentDestination": "Anderes Ziel wählen",
"viewer.route.distinct": "Eine Route benötigt zwei unterschiedliche semantische Knoten.",
"viewer.route.unreachable": "Keine gerichtete Route zu {label}",
"viewer.route.unreachable.detail": "{target} ist von {source} aus nicht erreichbar. Ein hervorgehobenes Ziel wählen.",
"viewer.route.start.instructions": "Quelle auswählen. Im nächsten Schritt werden nur gerichtete Ziele angezeigt.",
"viewer.route.copy.success": "Link zur verfolgten Route kopiert",
"viewer.route.copy.failed": "Link zur verfolgten Route konnte nicht kopiert werden",
"viewer.route.position": "Routenposition {index} von {total}: {label}",
"viewer.route.step": "Schritt {index} von {total} · {phase} · {label}",
"viewer.route.motionRequired": "Automatischer Verlauf erfordert Live-Bewegung",
"viewer.route.trigger.clear": "Verfolgte Route löschen",
"viewer.route.overview.status": "{nodes} · {hops} · kürzeste verfasste Route",
"viewer.route.overview.node.one": "{count} Knoten",
"viewer.route.overview.node.other": "{count} Knoten",
"viewer.route.overview.hop.one": "{count} gerichteter Hop",
"viewer.route.overview.hop.other": "{count} gerichtete Hops",
"viewer.route.phase.playing": "Läuft",
"viewer.route.phase.complete": "Abgeschlossen",
"viewer.route.phase.inspecting": "Wird geprüft",
"viewer.route.destination.count.one": "{count} gerichtetes Ziel verfügbar. Einen hervorgehobenen Knoten wählen.",
"viewer.route.destination.count.other": "{count} gerichtete Ziele verfügbar. Einen hervorgehobenen Knoten wählen.",
"viewer.route.noOutgoing": "Hier startet keine ausgehende Route. Löschen und eine andere Quelle wählen.",
"viewer.route.result.title": "{source} nach {target}",
"viewer.route.finder.source.title": "Routenstart wählen",
"viewer.route.finder.source.placeholder": "Routenquellen suchen",
"viewer.route.finder.source.empty": "Keine passenden Routenquellen",
"viewer.route.finder.source.results": "Knoten, die eine Route starten können",
"viewer.route.finder.source.noun": "Routenquellen",
"viewer.route.finder.source.badge": "Start",
"viewer.route.finder.target.title": "Ziel ausgehend von {label}",
"viewer.route.finder.target.placeholder": "Erreichbare Ziele suchen",
"viewer.route.finder.target.empty": "Keine passenden erreichbaren Ziele",
"viewer.route.finder.target.results": "Erreichbare Routenziele",
"viewer.route.finder.target.noun": "erreichbare Ziele",
"viewer.route.hop.one": "{count} Hop",
"viewer.route.hop.other": "{count} Hops",
"viewer.lens.eyebrow": "Semantische Linse",
"viewer.lens.title": "Systemrollen vergleichen",
"viewer.lens.close": "Semantische Linse schließen",
"viewer.lens.instruction": "Bis zu zwei semantische Arten wählen. Eine zeigt ihren echten Verkehr; zwei vergleichen nur direkte verfasste Beziehungen.",
"viewer.lens.kinds": "Semantische Arten",
"viewer.lens.choose": "Eine Art wählen, um ihre Knoten und berührenden Beziehungen zu prüfen.",
"viewer.lens.copy": "Link zur semantischen Linse kopieren",
"viewer.lens.clear": "Semantische Linse löschen",
"viewer.lens.open": "Semantische Linse öffnen",
"viewer.lens.openActive": "Aktive semantische Linse öffnen",
"viewer.lens.legend": "Semantische Legende",
"viewer.lens.legend.inspect.one": "{label} prüfen, {count} Knoten",
"viewer.lens.legend.inspect.other": "{label} prüfen, {count} Knoten",
"viewer.lens.kind.count.one": "{label}, {count} Knoten",
"viewer.lens.kind.count.other": "{label}, {count} Knoten",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} direkte Beziehung",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} direkte Beziehungen",
"viewer.lens.single": "{nodes} · {relationships} · verbundene Nachbarn bleiben sichtbar",
"viewer.lens.node.one": "{count} {label}-Knoten",
"viewer.lens.node.other": "{count} {label}-Knoten",
"viewer.lens.relationship.one": "{count} berührende Beziehung",
"viewer.lens.relationship.other": "{count} berührende Beziehungen",
"viewer.radar.title": "Semantisches Radar",
"viewer.radar.building": "Übersicht wird erstellt",
"viewer.radar.openFull": "Vollständiges semantisches Radar öffnen",
"viewer.radar.open": "Radar öffnen",
"viewer.radar.close": "Semantisches Radar schließen",
"viewer.radar.surface": "Diagrammübersicht. Auf einen Knoten klicken, um ihn zu fokussieren, oder mit den Pfeiltasten schwenken.",
"viewer.radar.click": "Knoten anklicken",
"viewer.radar.drag": "Ziehen zum Schwenken",
"viewer.radar.space": "Semantisches Radar benötigt mehr MAP-Platz.",
"viewer.radar.nodes": "Semantische Diagramm-Radarknoten",
"viewer.radar.focus": "{label} vom semantischen Radar fokussieren",
"viewer.radar.status": "{count} Knoten · {viewport}",
"viewer.radar.fullMap": "{count} Knoten · vollständige Karte",
"viewer.radar.compacted": "Radar verkleinert, um den semantischen Pass oder die MAP-Steuerung nicht zu verdecken.",
"viewer.radar.cancelWaiting": "Wartendes semantisches Radar auf mehr MAP-Platz abbrechen",
"viewer.radar.needsSpace": "Semantisches Radar benötigt mehr sichtbaren MAP-Platz",
"viewer.radar.viewport.full": "vollständige Karte",
"viewer.radar.viewport.width": "{percent}% Breite",
"viewer.radar.viewport.scale": "{percent}% Ansicht",
"viewer.nav.controls": "Diagrammansicht-Steuerung",
"viewer.nav.route": "Eine gerichtete Route verfolgen",
"viewer.nav.route.title": "Route verfolgen (R)",
"viewer.nav.route.short": "PFAD",
"viewer.nav.radar": "Semantisches Radar öffnen",
"viewer.nav.radar.title": "Semantisches Radar (M)",
"viewer.nav.radar.short": "KARTE",
"viewer.nav.lens": "Semantische Linse öffnen",
"viewer.nav.lens.title": "Semantische Linse (L)",
"viewer.nav.lens.short": "LINSE",
"viewer.nav.find": "Knoten finden",
"viewer.nav.find.title": "Knoten finden (/)",
"viewer.nav.guide": "Diagrammleitfaden öffnen",
"viewer.nav.guide.title": "Diagrammleitfaden (?)",
"viewer.nav.zoomOut": "Verkleinern",
"viewer.nav.zoomOut.title": "Verkleinern (-)",
"viewer.nav.reset": "Diagrammansicht zurücksetzen",
"viewer.nav.reset.title": "Ansicht zurücksetzen (0)",
"viewer.nav.read": "LESEN",
"viewer.nav.zoomIn": "Vergrößern",
"viewer.nav.zoomIn.title": "Vergrößern (+)",
"viewer.nav.camera": "{hint}. Diagrammansicht zurücksetzen",
"viewer.nav.camera.title": "{semantic}{hint} · Ansicht zurücksetzen (0)",
"viewer.nav.camera.semantic": "Semantische Kamera aktiv · ",
"viewer.nav.level.map": "KARTE",
"viewer.nav.level.read": "LESEN",
"viewer.nav.level.full": "VOLL",
"viewer.nav.level.auto": "AUTO",
"viewer.nav.detail.map": "Vergrößern, um Beziehungslabels und Knotenkontext anzuzeigen",
"viewer.nav.detail.read": "Erneut vergrößern, um Tags und Anmerkungen anzuzeigen",
"viewer.nav.detail.full": "Vollständige Diagrammdetails",
"viewer.intent.summary": "{label}. {out} ausgehend, {in} eingehend{loops}. {total} Verbindungen. Enter für Details drücken.",
"viewer.intent.loops": ", {count} Selbstschleife",
"viewer.common.copied": "Kopiert",
"viewer.common.copyFailed": "Kopieren fehlgeschlagen",
"viewer.common.copyLink": "Link kopieren",
"viewer.common.clear": "Löschen",
"viewer.common.close": "Schließen"
}
+440
View File
@@ -0,0 +1,440 @@
{
"page.title": "{title} · Diagrama",
"diagram.description.architecture": "Un diagrama de arquitectura generado por Archify.",
"diagram.description.workflow": "Un diagrama de flujo de trabajo generado por Archify.",
"diagram.description.sequence": "Un diagrama de secuencia generado por Archify.",
"diagram.description.dataflow": "Un diagrama de flujo de datos generado por Archify.",
"diagram.description.lifecycle": "Un diagrama de ciclo de vida generado por Archify.",
"node.focus": "Enfocar {label}",
"node.focus.detail": "Enfocar {label}, {detail}",
"node.context.architecture": "Componente de arquitectura",
"node.context.workflow": "Nodo de flujo de trabajo",
"node.context.sequence": "Participante de secuencia",
"node.context.dataflow": "Nodo de flujo de datos",
"node.context.lifecycle": "Estado del ciclo de vida",
"legend.title": "Leyenda",
"legend.architecture.frontend": "Frontend",
"legend.architecture.backend": "Backend",
"legend.architecture.database": "Base de datos",
"legend.architecture.cloud": "Nube",
"legend.architecture.security": "Seguridad",
"legend.architecture.messagebus": "Bus de mensajes",
"legend.architecture.external": "Externo",
"legend.workflow.frontend": "Interfaz de usuario",
"legend.workflow.backend": "Lógica del agente",
"legend.workflow.security": "Política",
"legend.workflow.messagebus": "Acción de herramienta",
"legend.workflow.database": "Contexto / traza",
"legend.workflow.cloud": "Servicio en la nube",
"legend.workflow.external": "Sistema externo",
"legend.sequence.emphasis": "solicitud principal",
"legend.sequence.return": "respuesta",
"legend.sequence.security": "seguridad",
"legend.sequence.dashed": "traza asíncrona",
"legend.sequence.default": "mensaje",
"legend.dataflow.emphasis": "datos primarios",
"legend.dataflow.security": "política / PII",
"legend.dataflow.dashed": "lote asíncrono",
"legend.dataflow.database": "almacén de datos",
"legend.dataflow.default": "flujo de datos",
"legend.lifecycle.start": "estado inicial",
"legend.lifecycle.active": "activo",
"legend.lifecycle.waiting": "en espera",
"legend.lifecycle.decision": "decisión",
"legend.lifecycle.success": "éxito",
"legend.lifecycle.failure": "fallo / salida",
"legend.lifecycle.neutral": "neutral",
"legend.lifecycle.external": "externo",
"legend.lifecycle.final": "estado final",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.kind.database": "Base de datos",
"viewer.kind.cloud": "Nube",
"viewer.kind.security": "Seguridad",
"viewer.kind.messagebus": "Bus de mensajes",
"viewer.kind.external": "Externo",
"viewer.kind.neutral": "Neutral",
"viewer.kind.node": "Nodo",
"viewer.kind.start": "Inicio",
"viewer.kind.active": "Activo",
"viewer.kind.waiting": "En espera",
"viewer.kind.decision": "Decisión",
"viewer.kind.success": "Éxito",
"viewer.kind.failure": "Fallo",
"viewer.toolbar.actions": "Acciones del diagrama",
"viewer.theme.toggle.title": "Cambiar tema (T)",
"viewer.theme.toggle": "Cambiar tema de color",
"viewer.theme.dark": "Oscuro",
"viewer.theme.light": "Claro",
"viewer.preset.choose.title": "Elegir estilo visual (S alterna)",
"viewer.preset.choose": "Elegir estilo visual",
"viewer.preset.style": "Estilo",
"viewer.preset.menu": "Estilo visual",
"viewer.preset.identity": "Identidad visual",
"viewer.preset.cycles": "alterna",
"viewer.preset.classic": "Clásico",
"viewer.preset.classic.short": "Clásico",
"viewer.preset.classic.hint": "Predeterminado técnico estable",
"viewer.preset.flow": "Flujo de señal",
"viewer.preset.flow.short": "Flujo",
"viewer.preset.flow.hint": "Presentación con énfasis en el movimiento",
"viewer.preset.blueprint": "Plano",
"viewer.preset.blueprint.hint": "Revisión de ingeniería",
"viewer.preset.editorial": "Editorial",
"viewer.preset.editorial.hint": "Notas de publicación y lanzamiento",
"viewer.preset.badge.signalFlow": "FLUJO DE SEÑAL",
"viewer.preset.badge.blueprint": "PLANO / REV 01",
"viewer.preset.badge.editorial": "EDITORIAL / NOTA DE CAMPO",
"viewer.preset.badge.editorialPlate": "ARCHIFY / LÁMINA 04",
"viewer.preset.current": "Estilo visual: {style}. Elegir estilo visual",
"viewer.motion.live": "Activa",
"viewer.motion.still": "Fija",
"viewer.motion.pause": "Pausar animación",
"viewer.motion.resume": "Reanudar animación",
"viewer.motion.reduced": "Animación pausada por la preferencia de movimiento reducido",
"viewer.motion.hidden": "Animación pausada mientras esta página está oculta",
"viewer.motion.yielding": "Pausar animación; cediendo a {owner}",
"viewer.motion.yielding.title": "Vista previa en vivo activada · cediendo a {owner}",
"viewer.owner.route": "Sonda de ruta",
"viewer.owner.lens": "Lente semántico",
"viewer.owner.relationship": "Vista previa de relación",
"viewer.owner.intent": "Traza de intención",
"viewer.owner.focus": "enfoque semántico",
"viewer.owner.legend": "vista previa de la leyenda",
"viewer.owner.reader": "interacción del lector",
"viewer.present.enter": "Entrar al modo presentación",
"viewer.present.enter.title": "Modo presentación (F)",
"viewer.present.exit": "Salir del modo presentación",
"viewer.present.exit.title": "Salir del modo presentación (F o Escape)",
"viewer.present.present": "Presentar",
"viewer.present.exit.label": "Salir",
"viewer.export.button": "Exportar",
"viewer.export.button.title": "Exportar diagrama (E)",
"viewer.export.diagram": "Exportar diagrama",
"viewer.export.menu": "Exportar",
"viewer.export.subtitle": "Salidas portables y limpias",
"viewer.export.share": "Compartir",
"viewer.export.shareCard": "Tarjeta para compartir",
"viewer.export.routeShareCard": "Tarjeta de ruta para compartir",
"viewer.export.reachShareCard": "Tarjeta de alcance para compartir",
"viewer.export.copyDiagram": "Copiar diagrama",
"viewer.export.clipboardPng": "PNG al portapapeles",
"viewer.export.raster": "Imágenes rasterizadas",
"viewer.export.image": "Imagen",
"viewer.export.lossless": "Imagen sin pérdida",
"viewer.export.compact": "Imagen compacta",
"viewer.export.modern": "Imagen moderna",
"viewer.export.vectorMotion": "Vector y movimiento",
"viewer.export.vectorMotion.heading": "Vector y movimiento",
"viewer.export.editable": "Vector editable",
"viewer.export.svg.auto": "SVG · Auto",
"viewer.export.svg.auto.hint": "Coincide con el tema del host",
"viewer.export.svg.light": "SVG · Claro",
"viewer.export.svg.light.hint": "Siempre claro",
"viewer.export.svg.dark": "SVG · Oscuro",
"viewer.export.svg.dark.hint": "Siempre oscuro",
"viewer.export.motion6s": "Movimiento de 6 s",
"viewer.export.unsupported": "No compatible con este navegador",
"viewer.export.clipboardUnsupported": "Este navegador no admite escribir imágenes en el portapapeles",
"viewer.export.clipboardUnsupported.short": "Escritura de imágenes en el portapapeles no admitida en este navegador.",
"viewer.export.motionUnavailable": "Captura de movimiento no disponible en este navegador",
"viewer.export.webmUnavailable": "WebM no disponible en este navegador",
"viewer.export.failed": "Falló la exportación: {message}",
"viewer.export.unknownVariant": "Variante de tarjeta para compartir desconocida: {variant}",
"viewer.export.routeRequired": "Traza una ruta antes de exportar una tarjeta de ruta para compartir",
"viewer.export.reachRequired": "Traza el alcance definido antes de exportar una tarjeta de alcance para compartir",
"viewer.export.unknown": "desconocido",
"viewer.export.routeFailed": "Falló la exportación de la tarjeta de ruta para compartir: {message}",
"viewer.export.reachFailed": "Falló la exportación de la tarjeta de alcance para compartir: {message}",
"viewer.export.copyFailed": "Error al copiar: {message}",
"viewer.export.copiedPng": "PNG copiado al portapapeles",
"viewer.export.downloadedRoute": "Tarjeta de ruta para compartir descargada",
"viewer.export.downloadedReach": "Tarjeta de alcance para compartir descargada",
"viewer.export.downloadedWebm": "WebM descargado",
"viewer.export.recording": "Grabando 6 segundos de movimiento…",
"viewer.export.card.routeSummary.one": "Ruta: {source} → {target} · {count} paso",
"viewer.export.card.routeSummary.other": "Ruta: {source} → {target} · {count} pasos",
"viewer.export.card.reachSummary": "Alcance definido {direction} desde {origin} · {nodes} · {links} · máx {hops}",
"viewer.export.card.node.one": "{count} nodo",
"viewer.export.card.node.other": "{count} nodos",
"viewer.export.card.link.one": "{count} enlace",
"viewer.export.card.link.other": "{count} enlaces",
"viewer.export.card.hop.one": "{count} salto",
"viewer.export.card.hop.other": "{count} saltos",
"viewer.export.card.routeBadge": "ARCHIFY · RUTA · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · ALCANCE {direction}",
"viewer.export.direction.upstream": "Ascendente",
"viewer.export.direction.downstream": "Descendente",
"viewer.export.error.canvasUnavailable": "Lienzo no disponible para {label}",
"viewer.export.error.contextUnavailable": "Contexto de lienzo 2D no disponible para {label}",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob no disponible para {label}",
"viewer.export.error.toBlobNull": "canvas.toBlob no devolvió datos para {label}",
"viewer.export.error.variantsCombined": "Las variantes de tarjeta para compartir no se pueden combinar",
"viewer.export.error.viewerState": "La exportación de la tarjeta para compartir no pudo quitar el estado temporal del visor",
"viewer.export.error.routeState": "La exportación de la tarjeta de ruta no pudo preservar la ruta resuelta de forma segura",
"viewer.export.error.reachState": "La exportación de la tarjeta de alcance no pudo preservar el alcance definido de forma segura",
"viewer.export.error.webmRequirements": "La exportación de movimiento WebM requiere una animación de traza y compatibilidad del navegador con MediaRecorder",
"viewer.export.error.mediaRecorder": "MediaRecorder falló",
"viewer.export.error.emptyWebm": "MediaRecorder produjo un WebM vacío",
"viewer.export.error.webmBackground": "No se pudo cargar el fondo SVG para la exportación WebM",
"viewer.guide.title": "Explorar este sistema",
"viewer.focus.selectedNodes": "{count} nodos seleccionados",
"viewer.guide.eyebrow": "Guía del diagrama",
"viewer.guide.close": "Cerrar la guía del diagrama",
"viewer.guide.inspecting": "Inspeccionando la semántica compilada",
"viewer.guide.actions": "Acciones de exploración del diagrama",
"viewer.guide.find": "Buscar cualquier nodo",
"viewer.guide.find.hint": "Busca por etiquetas, responsabilidades, tipos e IDs estables.",
"viewer.guide.route": "Trazar una ruta",
"viewer.guide.route.aria": "Trazar una ruta dirigida",
"viewer.guide.route.hint": "Pregunta cómo se conectan dos nodos semánticos en el sentido definido.",
"viewer.guide.map": "Ver todo el sistema",
"viewer.guide.map.hint": "Abre el Radar semántico con un viewport en vivo y nodos estables.",
"viewer.guide.lens": "Comparar tipos semánticos",
"viewer.guide.lens.hint": "Cuenta roles, revela su tráfico y compara enlaces directos definidos.",
"viewer.guide.present": "Entrar al modo presentación",
"viewer.guide.present.hint": "Dale al diagrama en vivo todo el viewport sin cambiar la exportación.",
"viewer.guide.shortcuts": "Atajos de teclado adicionales",
"viewer.guide.shortcut.export": "Exportar",
"viewer.guide.shortcut.theme": "Tema",
"viewer.guide.shortcut.style": "Estilo",
"viewer.guide.shortcut.reset": "Restablecer",
"viewer.guide.shortcut.zoomIn": "Acercar",
"viewer.guide.shortcut.zoomOut": "Alejar",
"viewer.guide.shortcut.close": "Cerrar",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} nodo semántico",
"viewer.guide.fact.node.other": "{count} nodos semánticos",
"viewer.guide.fact.relationship.one": "{count} relación",
"viewer.guide.fact.relationship.other": "{count} relaciones",
"viewer.guide.open": "Abrir la guía del diagrama",
"viewer.finder.title": "Buscar un nodo",
"viewer.finder.close": "Cerrar el buscador de nodos",
"viewer.finder.placeholder": "Buscar etiquetas o IDs",
"viewer.finder.search": "Buscar nodos del diagrama",
"viewer.finder.results": "Nodos del diagrama",
"viewer.finder.empty": "No hay nodos coincidentes",
"viewer.finder.result.focus": "Enfocar {label}",
"viewer.finder.result.routeStart": "Elegir {label} como inicio de la ruta",
"viewer.finder.result.routeTarget": "Elegir {label} como destino de la ruta, {links}",
"viewer.finder.status.empty": "No hay nodos coincidentes",
"viewer.finder.status.count.one": "{count} nodo coincidente",
"viewer.finder.status.count.other": "{count} nodos coincidentes",
"viewer.finder.noun.nodes": "nodos",
"viewer.finder.link.one": "{count} enlace",
"viewer.finder.link.other": "{count} enlaces",
"viewer.finder.result.focus.one": "Enfocar {label}, {count} conexión relacionada",
"viewer.finder.result.focus.other": "Enfocar {label}, {count} conexiones relacionadas",
"viewer.finder.status.filtered": "{visible} de {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Pasaporte semántico",
"viewer.passport.metadata": "Metadatos del nodo",
"viewer.passport.evidence": "Evidencia de fuente verificada",
"viewer.passport.verified": "Fuente verificada",
"viewer.passport.verificationScope": "Verificado contra el Git local en la revisión fijada. No se ha comprobado el acceso remoto.",
"viewer.passport.reach": "Alcance definido",
"viewer.passport.reach.trace": "Trazar la alcanzabilidad definida",
"viewer.passport.upstream": "Ascendente",
"viewer.passport.downstream": "Descendente",
"viewer.passport.upstream.trace": "Trazar la alcanzabilidad definida ascendente",
"viewer.passport.downstream.trace": "Trazar la alcanzabilidad definida descendente",
"viewer.passport.close": "Cerrar el pasaporte semántico",
"viewer.passport.move": "Mover el pasaporte semántico. Arrastra, usa las flechas o pulsa Inicio para restablecer.",
"viewer.passport.copy": "Copiar enlace",
"viewer.passport.copy.focus": "Copiar enlace al nodo enfocado",
"viewer.passport.relations": "Relaciones",
"viewer.passport.relations.show": "Mostrar relaciones conectadas",
"viewer.passport.relations.hide": "Ocultar relaciones conectadas",
"viewer.passport.relations.list": "Relaciones conectadas",
"viewer.passport.copyRelation": "Copiar relación",
"viewer.passport.copyNode": "Copiar nodo",
"viewer.passport.copyPinned": "Copiar enlace a la relación fijada",
"viewer.passport.copySource": "Copiar enlace al nodo de origen",
"viewer.passport.copy.focused.success": "Enlace del nodo enfocado copiado",
"viewer.passport.copy.pinned.success": "Enlace de la relación fijada copiado",
"viewer.passport.copy.focused.failed": "No se pudo copiar el enlace del nodo enfocado",
"viewer.passport.copy.pinned.failed": "No se pudo copiar el enlace de la relación fijada",
"viewer.passport.relationship.none": "No hay relaciones conectadas",
"viewer.passport.relationship.count.one": "{count} relación",
"viewer.passport.relationship.count.other": "{count} relaciones",
"viewer.passport.relationship.show.one": "Mostrar {count} relación conectada",
"viewer.passport.relationship.show.other": "Mostrar {count} relaciones conectadas",
"viewer.passport.relationship.summary": "{out} salientes · {in} entrantes{loops}",
"viewer.passport.relationship.loops": " · {count} bucle",
"viewer.passport.relationship.explorer": "Explorador de relaciones directas",
"viewer.passport.relationship.help": "Usa las flechas para explorar las relaciones. Pulsa Enter o Espacio para fijar detalles; Escape las borra.",
"viewer.passport.relationship.loopsBack": "vuelve sobre sí mismo",
"viewer.passport.relationship.connectsTo": "conecta a",
"viewer.passport.relationship.connectsFrom": "conecta desde",
"viewer.passport.relationship.pinned": "Relación fijada · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Inspeccionar relación {index} de {total}: {from} a {to}, {label}. Pulsa Enter para ver detalles.",
"viewer.passport.relationship.group.out": "Salientes",
"viewer.passport.relationship.group.in": "Entrantes",
"viewer.passport.relationship.group.loop": "Bucles propios",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "SAL →",
"viewer.passport.relationship.direction.in": "← ENT",
"viewer.passport.relationship.direction.loop": "BUCLE",
"viewer.passport.sourceCount.one": "{count} referencia de fuente verificada",
"viewer.passport.sourceCount.other": "{count} referencias de fuente verificadas",
"viewer.passport.sourceMarker": "SRC",
"viewer.passport.beacon.one": "{count} fuente verificada; enfoca este nodo para inspeccionar",
"viewer.passport.beacon.other": "{count} fuentes verificadas; enfoca este nodo para inspeccionar",
"viewer.passport.repository.open": "Abrir la revisión verificada del repositorio {revision}",
"viewer.passport.source.open": "Abrir la fuente verificada {path} en la revisión {revision}",
"viewer.passport.source.openLink": "Abrir ↗",
"viewer.passport.reach.upstream.one": "Trazar {count} nodo definido ascendente",
"viewer.passport.reach.upstream.other": "Trazar {count} nodos definidos ascendentes",
"viewer.passport.reach.downstream.one": "Trazar {count} nodo definido descendente",
"viewer.passport.reach.downstream.other": "Trazar {count} nodos definidos descendentes",
"viewer.passport.reach.noUpstream": "No hay nodos definidos ascendentes",
"viewer.passport.reach.noDownstream": "No hay nodos definidos descendentes",
"viewer.passport.reach.status": "{direction} · {nodes} nodos · {links} enlaces · máx {hops} saltos",
"viewer.route.eyebrow": "Ruta",
"viewer.route.start": "Haz clic donde empieza la ruta",
"viewer.route.start.find": "Buscar inicio",
"viewer.route.start.find.aria": "Buscar un inicio de ruta",
"viewer.route.copy": "Copiar enlace",
"viewer.route.copy.aria": "Copiar enlace a la ruta trazada",
"viewer.route.clear": "Borrar",
"viewer.route.clear.aria": "Borrar la sonda de ruta",
"viewer.route.traced": "Ruta",
"viewer.route.pickTwo": "Haz clic en dos nodos del diagrama",
"viewer.route.pickOne": "Haz clic en un nodo del diagrama",
"viewer.route.controls": "Controles del recorrido de la ruta",
"viewer.route.previous": "Posición anterior de la ruta",
"viewer.route.play": "Reproducir el recorrido de la ruta",
"viewer.route.pause": "Pausar el recorrido de la ruta",
"viewer.route.replay": "Repetir el recorrido de la ruta",
"viewer.route.next": "Posición siguiente de la ruta",
"viewer.route.journey": "Recorrido",
"viewer.route.pause.label": "Pausar",
"viewer.route.replay.label": "Repetir",
"viewer.route.overview": "Vista general",
"viewer.route.overview.aria": "Mostrar la vista general completa de la ruta",
"viewer.route.instructions": "Haz clic en un inicio y luego en un final. La ruta sigue las flechas.",
"viewer.route.destination": "¿Dónde termina la ruta desde {label}?",
"viewer.route.destination.find": "Buscar destino",
"viewer.route.destination.find.aria": "Buscar un destino de ruta alcanzable",
"viewer.route.differentDestination": "Elige un destino diferente",
"viewer.route.distinct": "Elige dos nodos distintos.",
"viewer.route.unreachable": "No se llega a {label} desde aquí",
"viewer.route.unreachable.detail": "No se llega a {target} desde {source} siguiendo las flechas. Elige un nodo resaltado.",
"viewer.route.start.instructions": "Luego elige el final. Solo se iluminan los nodos alcanzables.",
"viewer.route.copy.success": "Enlace de la ruta trazada copiado",
"viewer.route.copy.failed": "No se pudo copiar el enlace de la ruta trazada",
"viewer.route.position": "Posición de la ruta {index} de {total}: {label}",
"viewer.route.step": "Paso {index} de {total} · {phase} · {label}",
"viewer.route.motionRequired": "El recorrido automático requiere la animación Activa",
"viewer.route.trigger.clear": "Borrar la ruta trazada",
"viewer.route.overview.status": "{nodes} · {hops} · ruta más corta",
"viewer.route.overview.node.one": "{count} nodo",
"viewer.route.overview.node.other": "{count} nodos",
"viewer.route.overview.hop.one": "{count} paso",
"viewer.route.overview.hop.other": "{count} pasos",
"viewer.route.phase.playing": "Reproduciendo",
"viewer.route.phase.complete": "Completo",
"viewer.route.phase.inspecting": "Inspeccionando",
"viewer.route.destination.count.one": "{count} nodo alcanzable está resaltado. Haz clic en él.",
"viewer.route.destination.count.other": "{count} nodos alcanzables están resaltados. Haz clic en uno.",
"viewer.route.noOutgoing": "Ninguna flecha sale de este nodo. Bórralo y elige otro inicio.",
"viewer.route.result.title": "{source} a {target}",
"viewer.route.finder.source.title": "Elegir el inicio de la ruta",
"viewer.route.finder.source.placeholder": "Buscar orígenes de ruta",
"viewer.route.finder.source.empty": "No hay orígenes de ruta coincidentes",
"viewer.route.finder.source.results": "Nodos que pueden iniciar una ruta",
"viewer.route.finder.source.noun": "orígenes de ruta",
"viewer.route.finder.source.badge": "inicio",
"viewer.route.finder.target.title": "Destino desde {label}",
"viewer.route.finder.target.placeholder": "Buscar destinos alcanzables",
"viewer.route.finder.target.empty": "No hay destinos alcanzables coincidentes",
"viewer.route.finder.target.results": "Destinos de ruta alcanzables",
"viewer.route.finder.target.noun": "destinos alcanzables",
"viewer.route.hop.one": "{count} salto",
"viewer.route.hop.other": "{count} saltos",
"viewer.lens.eyebrow": "Lente semántico",
"viewer.lens.title": "Comparar roles del sistema",
"viewer.lens.close": "Cerrar el lente semántico",
"viewer.lens.instruction": "Elige hasta dos tipos semánticos. Uno revela su tráfico real; dos comparan solo las relaciones directas definidas.",
"viewer.lens.kinds": "Tipos semánticos",
"viewer.lens.choose": "Elige un tipo para inspeccionar sus nodos y las relaciones que lo tocan.",
"viewer.lens.copy": "Copiar enlace al lente semántico",
"viewer.lens.clear": "Borrar el lente semántico",
"viewer.lens.open": "Abrir el lente semántico",
"viewer.lens.openActive": "Abrir el lente semántico activo",
"viewer.lens.legend": "Leyenda semántica",
"viewer.lens.legend.inspect.one": "Inspeccionar {label}, {count} nodo",
"viewer.lens.legend.inspect.other": "Inspeccionar {label}, {count} nodos",
"viewer.lens.kind.count.one": "{label}, {count} nodo",
"viewer.lens.kind.count.other": "{label}, {count} nodos",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relación directa",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relaciones directas",
"viewer.lens.single": "{nodes} · {relationships} · los pares conectados permanecen visibles",
"viewer.lens.node.one": "{count} nodo {label}",
"viewer.lens.node.other": "{count} nodos {label}",
"viewer.lens.relationship.one": "{count} relación en contacto",
"viewer.lens.relationship.other": "{count} relaciones en contacto",
"viewer.radar.title": "Radar semántico",
"viewer.radar.building": "Construyendo la vista general",
"viewer.radar.openFull": "Abrir el radar semántico completo",
"viewer.radar.open": "Abrir el radar",
"viewer.radar.close": "Cerrar el radar semántico",
"viewer.radar.surface": "Vista general del diagrama. Haz clic en un nodo para enfocarlo, o usa las flechas para desplazarte.",
"viewer.radar.click": "Clic en el nodo",
"viewer.radar.drag": "Arrastra para desplazar",
"viewer.radar.space": "El radar semántico necesita más espacio de MAPA.",
"viewer.radar.nodes": "Nodos del radar del diagrama semántico",
"viewer.radar.focus": "Enfocar {label} desde el Radar semántico",
"viewer.radar.status": "{count} nodos · {viewport}",
"viewer.radar.fullMap": "{count} nodos · mapa completo",
"viewer.radar.compacted": "Radar compactado para no tapar el Pasaporte semántico ni los controles de MAPA.",
"viewer.radar.cancelWaiting": "Cancelar la espera del radar semántico por más espacio de MAPA",
"viewer.radar.needsSpace": "El radar semántico necesita más espacio de MAPA visible",
"viewer.radar.viewport.full": "mapa completo",
"viewer.radar.viewport.width": "{percent}% de ancho",
"viewer.radar.viewport.scale": "{percent}% del viewport",
"viewer.nav.controls": "Controles de vista del diagrama",
"viewer.nav.route": "Trazar una ruta dirigida",
"viewer.nav.route.title": "Trazar ruta (R)",
"viewer.nav.route.short": "RUTA",
"viewer.nav.radar": "Abrir el radar semántico",
"viewer.nav.radar.title": "Radar semántico (M)",
"viewer.nav.radar.short": "MAPA",
"viewer.nav.lens": "Abrir el lente semántico",
"viewer.nav.lens.title": "Lente semántico (L)",
"viewer.nav.lens.short": "LENTE",
"viewer.nav.find": "Buscar un nodo",
"viewer.nav.find.title": "Buscar un nodo (/)",
"viewer.outline.title": "Índice de nodos",
"viewer.rail.show": "Notas e índice",
"viewer.rail.controls": "Panel lateral",
"viewer.rail.collapse": "Contraer panel lateral",
"viewer.rail.bottom": "Mover el panel debajo",
"viewer.rail.right": "Mover el panel al lado",
"viewer.nav.guide": "Abrir la guía del diagrama",
"viewer.nav.guide.title": "Guía del diagrama (?)",
"viewer.nav.zoomOut": "Alejar",
"viewer.nav.zoomOut.title": "Alejar (-)",
"viewer.nav.reset": "Restablecer la vista del diagrama",
"viewer.nav.reset.title": "Restablecer vista (0)",
"viewer.nav.read": "LEER",
"viewer.nav.zoomIn": "Acercar",
"viewer.nav.zoomIn.title": "Acercar (+)",
"viewer.nav.camera": "{hint}. Restablecer la vista del diagrama",
"viewer.nav.camera.title": "{semantic}{hint} · restablecer vista (0)",
"viewer.nav.camera.semantic": "Cámara semántica activa · ",
"viewer.nav.level.map": "MAPA",
"viewer.nav.level.read": "LEER",
"viewer.nav.level.full": "COMPLETO",
"viewer.nav.level.auto": "AUTO",
"viewer.nav.detail.map": "Acerca para revelar las etiquetas de relación y el contexto de los nodos",
"viewer.nav.detail.read": "Acerca de nuevo para revelar etiquetas y anotaciones",
"viewer.nav.detail.full": "Detalle completo del diagrama",
"viewer.intent.summary": "{label}. {out} salientes, {in} entrantes{loops}. {total} conexiones. Pulsa Enter para ver detalles.",
"viewer.intent.loops": ", {count} bucle propio",
"viewer.common.copied": "Copiado",
"viewer.common.copyFailed": "Error al copiar",
"viewer.common.copyLink": "Copiar enlace",
"viewer.common.clear": "Borrar",
"viewer.common.close": "Cerrar"
}
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "Diagramme {title}",
"diagram.description.architecture": "Un diagramme d'architecture généré par Archify.",
"diagram.description.workflow": "Un diagramme de workflow généré par Archify.",
"diagram.description.sequence": "Un diagramme de séquence généré par Archify.",
"diagram.description.dataflow": "Un diagramme de flux de données généré par Archify.",
"diagram.description.lifecycle": "Un diagramme de cycle de vie généré par Archify.",
"node.focus": "Centrer sur {label}",
"node.focus.detail": "Centrer sur {label}, {detail}",
"node.context.architecture": "Composant d'architecture",
"node.context.workflow": "Nœud de workflow",
"node.context.sequence": "Participant de séquence",
"node.context.dataflow": "Nœud de flux de données",
"node.context.lifecycle": "État de cycle de vie",
"legend.title": "Légende",
"legend.architecture.frontend": "Frontend",
"legend.architecture.backend": "Backend",
"legend.architecture.database": "Base de données",
"legend.architecture.cloud": "Cloud",
"legend.architecture.security": "Sécurité",
"legend.architecture.messagebus": "Bus de messages",
"legend.architecture.external": "Externe",
"legend.workflow.frontend": "Interface utilisateur",
"legend.workflow.backend": "Logique de l'agent",
"legend.workflow.security": "Politique",
"legend.workflow.messagebus": "Action d'outil",
"legend.workflow.database": "Contexte / trace",
"legend.workflow.cloud": "Service cloud",
"legend.workflow.external": "Système externe",
"legend.sequence.emphasis": "requête principale",
"legend.sequence.return": "retour",
"legend.sequence.security": "sécurité",
"legend.sequence.dashed": "trace asynchrone",
"legend.sequence.default": "message",
"legend.dataflow.emphasis": "donnée principale",
"legend.dataflow.security": "politique / PII",
"legend.dataflow.dashed": "lot asynchrone",
"legend.dataflow.database": "entrepôt de données",
"legend.dataflow.default": "flux de données",
"legend.lifecycle.start": "état initial",
"legend.lifecycle.active": "actif",
"legend.lifecycle.waiting": "attente",
"legend.lifecycle.decision": "décision",
"legend.lifecycle.success": "succès",
"legend.lifecycle.failure": "échec / sortie",
"legend.lifecycle.neutral": "neutre",
"legend.lifecycle.external": "externe",
"legend.lifecycle.final": "état final",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.kind.database": "Base de données",
"viewer.kind.cloud": "Cloud",
"viewer.kind.security": "Sécurité",
"viewer.kind.messagebus": "Bus de messages",
"viewer.kind.external": "Externe",
"viewer.kind.neutral": "Neutre",
"viewer.kind.node": "Nœud",
"viewer.kind.start": "Début",
"viewer.kind.active": "Actif",
"viewer.kind.waiting": "En attente",
"viewer.kind.decision": "Décision",
"viewer.kind.success": "Succès",
"viewer.kind.failure": "Échec",
"viewer.toolbar.actions": "Actions du diagramme",
"viewer.theme.toggle.title": "Changer de thème (T)",
"viewer.theme.toggle": "Changer le thème de couleur",
"viewer.theme.dark": "Sombre",
"viewer.theme.light": "Clair",
"viewer.preset.choose.title": "Choisir le style visuel (S pour changer)",
"viewer.preset.choose": "Choisir le style visuel",
"viewer.preset.style": "Style",
"viewer.preset.menu": "Style visuel",
"viewer.preset.identity": "Identité visuelle",
"viewer.preset.cycles": "S pour changer",
"viewer.preset.classic": "Classic",
"viewer.preset.classic.short": "Classic",
"viewer.preset.classic.hint": "Style technique stable par défaut",
"viewer.preset.flow": "Flux de signal",
"viewer.preset.flow.short": "Flux",
"viewer.preset.flow.hint": "Présentation animée et dynamique",
"viewer.preset.blueprint": "Plan",
"viewer.preset.blueprint.hint": "Revue technique",
"viewer.preset.editorial": "Éditorial",
"viewer.preset.editorial.hint": "Notes de publication et de lancement",
"viewer.preset.badge.signalFlow": "FLUX DE SIGNAL",
"viewer.preset.badge.blueprint": "PLAN / RÉV. 01",
"viewer.preset.badge.editorial": "ÉDITORIAL / NOTE DE TERRAIN",
"viewer.preset.badge.editorialPlate": "ARCHIFY / PLANCHE 04",
"viewer.preset.current": "Style visuel : {style}. Choisir le style visuel",
"viewer.motion.live": "Animé",
"viewer.motion.still": "Statique",
"viewer.motion.pause": "Mettre l'animation en pause",
"viewer.motion.resume": "Reprendre l'animation",
"viewer.motion.reduced": "Animation suspendue en raison de la préférence de mouvement réduit",
"viewer.motion.hidden": "Animation suspendue pendant que la page est masquée",
"viewer.motion.yielding": "Mettre l'animation en pause ; cède actuellement la place à {owner}",
"viewer.motion.yielding.title": "Aperçu en direct activé · cède la place à {owner}",
"viewer.owner.route": "Sonde d'itinéraire",
"viewer.owner.lens": "Lentille sémantique",
"viewer.owner.relationship": "Aperçu de relation",
"viewer.owner.intent": "Trace d'intention",
"viewer.owner.focus": "le focus sémantique",
"viewer.owner.legend": "l'aperçu de la légende",
"viewer.owner.reader": "l'interaction du lecteur",
"viewer.present.enter": "Entrer en mode présentation",
"viewer.present.enter.title": "Mode présentation (F)",
"viewer.present.exit": "Quitter le mode présentation",
"viewer.present.exit.title": "Quitter le mode présentation (F ou Échap)",
"viewer.present.present": "Présenter",
"viewer.present.exit.label": "Quitter",
"viewer.export.button": "Exporter",
"viewer.export.button.title": "Exporter le diagramme (E)",
"viewer.export.diagram": "Exporter le diagramme",
"viewer.export.menu": "Exporter",
"viewer.export.subtitle": "Des exports portables et soignés",
"viewer.export.share": "Partager",
"viewer.export.shareCard": "Carte de partage",
"viewer.export.routeShareCard": "Carte de partage d'itinéraire",
"viewer.export.reachShareCard": "Carte de partage de portée",
"viewer.export.copyDiagram": "Copier le diagramme",
"viewer.export.clipboardPng": "PNG vers le presse-papiers",
"viewer.export.raster": "Images matricielles",
"viewer.export.image": "Image",
"viewer.export.lossless": "Image sans perte",
"viewer.export.compact": "Image compacte",
"viewer.export.modern": "Image au format moderne",
"viewer.export.vectorMotion": "Vecteur et animation",
"viewer.export.vectorMotion.heading": "Vecteur et animation",
"viewer.export.editable": "Vecteur modifiable",
"viewer.export.motion6s": "Animation de 6 s",
"viewer.export.unsupported": "Non pris en charge par ce navigateur",
"viewer.export.clipboardUnsupported": "L'écriture d'image dans le presse-papiers n'est pas prise en charge par ce navigateur",
"viewer.export.clipboardUnsupported.short": "L'écriture d'image dans le presse-papiers n'est pas prise en charge par ce navigateur.",
"viewer.export.motionUnavailable": "Capture d'animation indisponible dans ce navigateur",
"viewer.export.webmUnavailable": "WebM indisponible dans ce navigateur",
"viewer.export.failed": "Échec de l'export : {message}",
"viewer.export.unknownVariant": "Variante de carte de partage inconnue : {variant}",
"viewer.export.routeRequired": "Tracez un itinéraire avant d'exporter une carte de partage d'itinéraire",
"viewer.export.reachRequired": "Tracez une portée éditée avant d'exporter une carte de partage de portée",
"viewer.export.unknown": "inconnu",
"viewer.export.routeFailed": "Échec de l'export de la carte de partage d'itinéraire : {message}",
"viewer.export.reachFailed": "Échec de l'export de la carte de partage de portée : {message}",
"viewer.export.copyFailed": "Échec de la copie : {message}",
"viewer.export.copiedPng": "PNG copié dans le presse-papiers",
"viewer.export.downloadedRoute": "Carte de partage d'itinéraire téléchargée",
"viewer.export.downloadedReach": "Carte de partage de portée téléchargée",
"viewer.export.downloadedWebm": "WebM téléchargé",
"viewer.export.recording": "Enregistrement de 6 secondes d'animation…",
"viewer.export.card.routeSummary.one": "Itinéraire : {source} → {target} · {count} saut orienté",
"viewer.export.card.routeSummary.other": "Itinéraire : {source} → {target} · {count} sauts orientés",
"viewer.export.card.reachSummary": "Portée {direction} éditée depuis {origin} · {nodes} · {links} · max {hops}",
"viewer.export.card.node.one": "{count} nœud",
"viewer.export.card.node.other": "{count} nœuds",
"viewer.export.card.link.one": "{count} lien",
"viewer.export.card.link.other": "{count} liens",
"viewer.export.card.hop.one": "{count} saut",
"viewer.export.card.hop.other": "{count} sauts",
"viewer.export.card.routeBadge": "ARCHIFY · ITINÉRAIRE · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · PORTÉE {direction}",
"viewer.export.direction.upstream": "Amont",
"viewer.export.direction.downstream": "Aval",
"viewer.export.error.canvasUnavailable": "Canevas indisponible pour {label}",
"viewer.export.error.contextUnavailable": "Contexte de canevas 2D indisponible pour {label}",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob indisponible pour {label}",
"viewer.export.error.toBlobNull": "canvas.toBlob n'a renvoyé aucune donnée pour {label}",
"viewer.export.error.variantsCombined": "Les variantes de carte de partage ne peuvent pas être combinées",
"viewer.export.error.viewerState": "L'export de la carte de partage n'a pas pu supprimer l'état temporaire du visualiseur",
"viewer.export.error.routeState": "L'export de la carte d'itinéraire n'a pas pu préserver l'itinéraire résolu en toute sécurité",
"viewer.export.error.reachState": "L'export de la carte de portée n'a pas pu préserver la portée éditée en toute sécurité",
"viewer.export.error.webmRequirements": "L'export d'animation WebM nécessite une animation de trace et la prise en charge de MediaRecorder par le navigateur",
"viewer.export.error.mediaRecorder": "Échec de MediaRecorder",
"viewer.export.error.emptyWebm": "MediaRecorder a produit un fichier WebM vide",
"viewer.export.error.webmBackground": "L'arrière-plan SVG n'a pas pu être chargé pour l'export WebM",
"viewer.guide.eyebrow": "Guide du diagramme",
"viewer.guide.close": "Fermer le guide du diagramme",
"viewer.guide.inspecting": "Inspection de la sémantique compilée",
"viewer.guide.actions": "Actions d'exploration du diagramme",
"viewer.guide.find": "Trouver un nœud",
"viewer.guide.find.hint": "Rechercher par libellés, responsabilités, types et identifiants stables.",
"viewer.guide.route": "Tracer un itinéraire",
"viewer.guide.route.aria": "Tracer un itinéraire orienté",
"viewer.guide.route.hint": "Découvrez comment deux nœuds sémantiques sont reliés dans le sens édité.",
"viewer.guide.map": "Voir tout le système",
"viewer.guide.map.hint": "Ouvrir le radar sémantique avec un viewport en direct et des nœuds stables.",
"viewer.guide.lens": "Comparer les types sémantiques",
"viewer.guide.lens.hint": "Comptez les rôles, révélez leur trafic et comparez les liens édités directs.",
"viewer.guide.present": "Entrer en mode présentation",
"viewer.guide.present.hint": "Donnez tout le viewport au diagramme en direct sans modifier l'export.",
"viewer.guide.shortcuts": "Raccourcis clavier supplémentaires",
"viewer.guide.shortcut.export": "Exporter",
"viewer.guide.shortcut.theme": "Thème",
"viewer.guide.shortcut.style": "Style",
"viewer.guide.shortcut.reset": "Réinitialiser",
"viewer.guide.shortcut.zoomIn": "Zoom avant",
"viewer.guide.shortcut.zoomOut": "Zoom arrière",
"viewer.guide.shortcut.close": "Fermer",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} nœud sémantique",
"viewer.guide.fact.node.other": "{count} nœuds sémantiques",
"viewer.guide.fact.relationship.one": "{count} relation",
"viewer.guide.fact.relationship.other": "{count} relations",
"viewer.guide.open": "Ouvrir le guide du diagramme",
"viewer.finder.title": "Trouver un nœud",
"viewer.finder.close": "Fermer le chercheur de nœuds",
"viewer.finder.placeholder": "Rechercher par libellé ou identifiant",
"viewer.finder.search": "Rechercher les nœuds du diagramme",
"viewer.finder.results": "Nœuds du diagramme",
"viewer.finder.empty": "Aucun nœud correspondant",
"viewer.finder.result.focus": "Centrer sur {label}",
"viewer.finder.result.routeStart": "Choisir {label} comme point de départ de l'itinéraire",
"viewer.finder.result.routeTarget": "Choisir {label} comme destination de l'itinéraire, {links}",
"viewer.finder.status.empty": "Aucun nœud correspondant",
"viewer.finder.status.count.one": "{count} nœud correspondant",
"viewer.finder.status.count.other": "{count} nœuds correspondants",
"viewer.finder.noun.nodes": "nœuds",
"viewer.finder.link.one": "{count} lien",
"viewer.finder.link.other": "{count} liens",
"viewer.finder.result.focus.one": "Centrer sur {label}, {count} connexion associée",
"viewer.finder.result.focus.other": "Centrer sur {label}, {count} connexions associées",
"viewer.finder.status.filtered": "{visible} sur {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Passeport sémantique",
"viewer.passport.metadata": "Métadonnées du nœud",
"viewer.passport.evidence": "Preuve de source vérifiée",
"viewer.passport.verified": "Source vérifiée",
"viewer.passport.verificationScope": "Vérifié par rapport au Git local à la révision épinglée. L'accès distant n'a pas été vérifié.",
"viewer.passport.reach": "Portée éditée",
"viewer.passport.reach.trace": "Tracer l'accessibilité éditée",
"viewer.passport.upstream": "Amont",
"viewer.passport.downstream": "Aval",
"viewer.passport.upstream.trace": "Tracer l'accessibilité éditée en amont",
"viewer.passport.downstream.trace": "Tracer l'accessibilité éditée en aval",
"viewer.passport.close": "Fermer le passeport sémantique",
"viewer.passport.copy": "Copier le lien",
"viewer.passport.copy.focus": "Copier le lien du nœud centré",
"viewer.passport.relations": "Relations",
"viewer.passport.relations.show": "Afficher les relations connectées",
"viewer.passport.relations.hide": "Masquer les relations connectées",
"viewer.passport.relations.list": "Relations connectées",
"viewer.passport.copyRelation": "Copier la relation",
"viewer.passport.copyNode": "Copier le nœud",
"viewer.passport.copyPinned": "Copier le lien de la relation épinglée",
"viewer.passport.copySource": "Copier le lien du nœud source",
"viewer.passport.copy.focused.success": "Lien du nœud centré copié",
"viewer.passport.copy.pinned.success": "Lien de la relation épinglée copié",
"viewer.passport.copy.focused.failed": "Impossible de copier le lien du nœud centré",
"viewer.passport.copy.pinned.failed": "Impossible de copier le lien de la relation épinglée",
"viewer.passport.relationship.none": "Aucune relation connectée",
"viewer.passport.relationship.count.one": "{count} relation",
"viewer.passport.relationship.count.other": "{count} relations",
"viewer.passport.relationship.show.one": "Afficher {count} relation connectée",
"viewer.passport.relationship.show.other": "Afficher {count} relations connectées",
"viewer.passport.relationship.summary": "{out} sortantes · {in} entrantes{loops}",
"viewer.passport.relationship.loops": " · {count} boucle",
"viewer.passport.relationship.explorer": "Explorateur de relations directes",
"viewer.passport.relationship.help": "Utilisez les flèches pour explorer les relations. Appuyez sur Entrée ou Espace pour épingler les détails ; Échap efface.",
"viewer.passport.relationship.loopsBack": "boucle vers",
"viewer.passport.relationship.connectsTo": "se connecte à",
"viewer.passport.relationship.connectsFrom": "se connecte depuis",
"viewer.passport.relationship.pinned": "Relation épinglée · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Inspecter la relation {index} sur {total} : {from} vers {to}, {label}. Appuyez sur Entrée pour les détails.",
"viewer.passport.relationship.group.out": "Sortantes",
"viewer.passport.relationship.group.in": "Entrantes",
"viewer.passport.relationship.group.loop": "Boucles propres",
"viewer.passport.relationship.row": "{group} : {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "SORT →",
"viewer.passport.relationship.direction.in": "← ENTRE",
"viewer.passport.relationship.direction.loop": "BOUCLE",
"viewer.passport.sourceCount.one": "{count} référence de source vérifiée",
"viewer.passport.sourceCount.other": "{count} références de source vérifiées",
"viewer.passport.sourceMarker": "SOURCE",
"viewer.passport.beacon.one": "{count} source vérifiée ; centrez ce nœud pour l'inspecter",
"viewer.passport.beacon.other": "{count} sources vérifiées ; centrez ce nœud pour l'inspecter",
"viewer.passport.repository.open": "Ouvrir la révision {revision} du dépôt vérifié",
"viewer.passport.source.open": "Ouvrir la source vérifiée {path} à la révision {revision}",
"viewer.passport.source.openLink": "Ouvrir ↗",
"viewer.passport.reach.upstream.one": "Tracer {count} nœud édité en amont",
"viewer.passport.reach.upstream.other": "Tracer {count} nœuds édités en amont",
"viewer.passport.reach.downstream.one": "Tracer {count} nœud édité en aval",
"viewer.passport.reach.downstream.other": "Tracer {count} nœuds édités en aval",
"viewer.passport.reach.noUpstream": "Aucun nœud édité en amont",
"viewer.passport.reach.noDownstream": "Aucun nœud édité en aval",
"viewer.passport.reach.status": "{direction} · {nodes} nœuds · {links} liens · max {hops} sauts",
"viewer.route.eyebrow": "Sonde d'itinéraire",
"viewer.route.start": "Choisir un nœud de départ",
"viewer.route.start.find": "Trouver le départ",
"viewer.route.start.find.aria": "Trouver un départ d'itinéraire",
"viewer.route.copy": "Copier le lien",
"viewer.route.copy.aria": "Copier le lien de l'itinéraire tracé",
"viewer.route.clear": "Effacer",
"viewer.route.clear.aria": "Effacer la sonde d'itinéraire",
"viewer.route.traced": "Itinéraire tracé",
"viewer.route.pickTwo": "Choisissez deux nœuds sémantiques sur le diagramme",
"viewer.route.pickOne": "Choisissez un nœud sémantique sur le diagramme",
"viewer.route.controls": "Contrôles du parcours d'itinéraire",
"viewer.route.previous": "Position précédente de l'itinéraire",
"viewer.route.play": "Lire le parcours de l'itinéraire",
"viewer.route.pause": "Mettre le parcours de l'itinéraire en pause",
"viewer.route.replay": "Rejouer le parcours de l'itinéraire",
"viewer.route.next": "Position suivante de l'itinéraire",
"viewer.route.journey": "Parcours",
"viewer.route.pause.label": "Pause",
"viewer.route.replay.label": "Rejouer",
"viewer.route.overview": "Vue d'ensemble",
"viewer.route.overview.aria": "Afficher la vue d'ensemble complète de l'itinéraire",
"viewer.route.instructions": "Choisissez la source, puis la destination. Le sens compte.",
"viewer.route.destination": "Choisir une destination depuis {label}",
"viewer.route.destination.find": "Trouver une cible",
"viewer.route.destination.find.aria": "Trouver une destination d'itinéraire accessible",
"viewer.route.differentDestination": "Choisir une autre destination",
"viewer.route.distinct": "Un itinéraire nécessite deux nœuds sémantiques distincts.",
"viewer.route.unreachable": "Aucun itinéraire orienté vers {label}",
"viewer.route.unreachable.detail": "{target} n'est pas accessible depuis {source}. Choisissez une destination surlignée.",
"viewer.route.start.instructions": "Sélectionnez la source. L'étape suivante ne révélera que les destinations orientées.",
"viewer.route.copy.success": "Lien de l'itinéraire tracé copié",
"viewer.route.copy.failed": "Impossible de copier le lien de l'itinéraire tracé",
"viewer.route.position": "Position {index} sur {total} de l'itinéraire : {label}",
"viewer.route.step": "Étape {index} sur {total} · {phase} · {label}",
"viewer.route.motionRequired": "Le parcours automatique nécessite le mode Animé",
"viewer.route.trigger.clear": "Effacer l'itinéraire tracé",
"viewer.route.overview.status": "{nodes} · {hops} · itinéraire édité le plus court",
"viewer.route.overview.node.one": "{count} nœud",
"viewer.route.overview.node.other": "{count} nœuds",
"viewer.route.overview.hop.one": "{count} saut orienté",
"viewer.route.overview.hop.other": "{count} sauts orientés",
"viewer.route.phase.playing": "En lecture",
"viewer.route.phase.complete": "Terminé",
"viewer.route.phase.inspecting": "Inspection",
"viewer.route.destination.count.one": "{count} destination orientée disponible. Choisissez un nœud surligné.",
"viewer.route.destination.count.other": "{count} destinations orientées disponibles. Choisissez un nœud surligné.",
"viewer.route.noOutgoing": "Aucun itinéraire sortant ne débute ici. Effacez et choisissez une autre source.",
"viewer.route.result.title": "{source} vers {target}",
"viewer.route.finder.source.title": "Choisir le départ de l'itinéraire",
"viewer.route.finder.source.placeholder": "Rechercher des sources d'itinéraire",
"viewer.route.finder.source.empty": "Aucune source d'itinéraire correspondante",
"viewer.route.finder.source.results": "Nœuds pouvant débuter un itinéraire",
"viewer.route.finder.source.noun": "sources d'itinéraire",
"viewer.route.finder.source.badge": "départ",
"viewer.route.finder.target.title": "Destination depuis {label}",
"viewer.route.finder.target.placeholder": "Rechercher des destinations accessibles",
"viewer.route.finder.target.empty": "Aucune destination accessible correspondante",
"viewer.route.finder.target.results": "Destinations d'itinéraire accessibles",
"viewer.route.finder.target.noun": "destinations accessibles",
"viewer.route.hop.one": "{count} saut",
"viewer.route.hop.other": "{count} sauts",
"viewer.lens.eyebrow": "Lentille sémantique",
"viewer.lens.title": "Comparer les rôles du système",
"viewer.lens.close": "Fermer la lentille sémantique",
"viewer.lens.instruction": "Choisissez jusqu'à deux types sémantiques. Un seul révèle son trafic réel ; deux comparent uniquement les relations éditées directes.",
"viewer.lens.kinds": "Types sémantiques",
"viewer.lens.choose": "Choisissez un type pour inspecter ses nœuds et relations associées.",
"viewer.lens.copy": "Copier le lien de la lentille sémantique",
"viewer.lens.clear": "Effacer la lentille sémantique",
"viewer.lens.open": "Ouvrir la lentille sémantique",
"viewer.lens.openActive": "Ouvrir la lentille sémantique active",
"viewer.lens.legend": "Légende sémantique",
"viewer.lens.legend.inspect.one": "Inspecter {label}, {count} nœud",
"viewer.lens.legend.inspect.other": "Inspecter {label}, {count} nœuds",
"viewer.lens.kind.count.one": "{label}, {count} nœud",
"viewer.lens.kind.count.other": "{label}, {count} nœuds",
"viewer.lens.compare.one": "{first} → {second} : {forward} · {second} → {first} : {reverse} · {count} relation directe",
"viewer.lens.compare.other": "{first} → {second} : {forward} · {second} → {first} : {reverse} · {count} relations directes",
"viewer.lens.single": "{nodes} · {relationships} · les nœuds connectés restent visibles",
"viewer.lens.node.one": "{count} nœud {label}",
"viewer.lens.node.other": "{count} nœuds {label}",
"viewer.lens.relationship.one": "{count} relation associée",
"viewer.lens.relationship.other": "{count} relations associées",
"viewer.radar.title": "Radar sémantique",
"viewer.radar.building": "Construction de la vue d'ensemble",
"viewer.radar.openFull": "Ouvrir le radar sémantique complet",
"viewer.radar.open": "Ouvrir le radar",
"viewer.radar.close": "Fermer le radar sémantique",
"viewer.radar.surface": "Vue d'ensemble du diagramme. Cliquez sur un nœud pour le centrer, ou utilisez les flèches pour vous déplacer.",
"viewer.radar.click": "Cliquer sur le nœud",
"viewer.radar.drag": "Glisser pour se déplacer",
"viewer.radar.space": "Le radar sémantique a besoin de plus d'espace MAP.",
"viewer.radar.nodes": "Nœuds du radar du diagramme sémantique",
"viewer.radar.focus": "Centrer sur {label} depuis le radar sémantique",
"viewer.radar.status": "{count} nœuds · {viewport}",
"viewer.radar.fullMap": "{count} nœuds · carte complète",
"viewer.radar.compacted": "Radar compacté pour éviter de masquer le passeport sémantique ou les contrôles MAP.",
"viewer.radar.cancelWaiting": "Annuler l'attente du radar sémantique pour plus d'espace MAP",
"viewer.radar.needsSpace": "Le radar sémantique a besoin de plus d'espace MAP visible",
"viewer.radar.viewport.full": "carte complète",
"viewer.radar.viewport.width": "{percent} % de largeur",
"viewer.radar.viewport.scale": "{percent} % du viewport",
"viewer.nav.controls": "Contrôles de la vue du diagramme",
"viewer.nav.route": "Tracer un itinéraire orienté",
"viewer.nav.route.title": "Tracer l'itinéraire (R)",
"viewer.nav.route.short": "ROUTE",
"viewer.nav.radar": "Ouvrir le radar sémantique",
"viewer.nav.radar.title": "Radar sémantique (M)",
"viewer.nav.radar.short": "CARTE",
"viewer.nav.lens": "Ouvrir la lentille sémantique",
"viewer.nav.lens.title": "Lentille sémantique (L)",
"viewer.nav.lens.short": "LENTILLE",
"viewer.nav.find": "Trouver un nœud",
"viewer.nav.find.title": "Trouver un nœud (/)",
"viewer.nav.guide": "Ouvrir le guide du diagramme",
"viewer.nav.guide.title": "Guide du diagramme (?)",
"viewer.nav.zoomOut": "Zoom arrière",
"viewer.nav.zoomOut.title": "Zoom arrière (-)",
"viewer.nav.reset": "Réinitialiser la vue du diagramme",
"viewer.nav.reset.title": "Réinitialiser la vue (0)",
"viewer.nav.read": "LIRE",
"viewer.nav.zoomIn": "Zoom avant",
"viewer.nav.zoomIn.title": "Zoom avant (+)",
"viewer.nav.camera": "{hint}. Réinitialiser la vue du diagramme",
"viewer.nav.camera.title": "{semantic}{hint} · réinitialiser la vue (0)",
"viewer.nav.camera.semantic": "Caméra sémantique active · ",
"viewer.nav.level.map": "CARTE",
"viewer.nav.level.read": "LIRE",
"viewer.nav.level.full": "COMPLET",
"viewer.nav.level.auto": "AUTO",
"viewer.nav.detail.map": "Zoomez pour révéler les libellés des relations et le contexte des nœuds",
"viewer.nav.detail.read": "Zoomez à nouveau pour révéler les étiquettes et annotations",
"viewer.nav.detail.full": "Détail complet du diagramme",
"viewer.intent.summary": "{label}. {out} sortantes, {in} entrantes{loops}. {total} connexions. Appuyez sur Entrée pour les détails.",
"viewer.intent.loops": ", {count} boucle propre",
"viewer.common.copied": "Copié",
"viewer.common.copyFailed": "Échec de la copie",
"viewer.common.copyLink": "Copier le lien",
"viewer.common.clear": "Effacer",
"viewer.common.close": "Fermer"
}
+22
View File
@@ -0,0 +1,22 @@
{
"page.title": "Diagramme {title}",
"diagram.description.architecture": "Un diagramme d'architecture généré par Archify.",
"legend.title": "Légende",
"node.focus": "Focus {label}",
"node.context.architecture": "Composant d'architecture",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.export.button": "Exporter",
"viewer.export.button.title": "Exporter le diagramme (E)",
"viewer.export.diagram": "Exporter le diagramme",
"viewer.finder.title": "Rechercher un nœud",
"viewer.route.eyebrow": "Sonde d'itinéraire",
"viewer.route.overview.node.one": "{count} nœud",
"viewer.route.overview.node.other": "{count} nœuds",
"viewer.guide.eyebrow": "Guide du diagramme",
"viewer.nav.controls": "Contrôles de vue du diagramme",
"viewer.theme.toggle": "Changer de thème",
"viewer.preset.classic": "Classique",
"viewer.common.close": "Fermer",
"viewer.common.copyLink": "Copier le lien"
}
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "{title} · Diagramma",
"diagram.description.architecture": "Un diagramma di architettura generato da Archify.",
"diagram.description.workflow": "Un diagramma di flusso di lavoro generato da Archify.",
"diagram.description.sequence": "Un diagramma di sequenza generato da Archify.",
"diagram.description.dataflow": "Un diagramma di flusso dati generato da Archify.",
"diagram.description.lifecycle": "Un diagramma di ciclo di vita generato da Archify.",
"node.focus": "Metti a fuoco {label}",
"node.focus.detail": "Metti a fuoco {label}, {detail}",
"node.context.architecture": "Componente di architettura",
"node.context.workflow": "Nodo del flusso di lavoro",
"node.context.sequence": "Partecipante alla sequenza",
"node.context.dataflow": "Nodo del flusso dati",
"node.context.lifecycle": "Stato del ciclo di vita",
"legend.title": "Legenda",
"legend.architecture.frontend": "Frontend",
"legend.architecture.backend": "Backend",
"legend.architecture.database": "Database",
"legend.architecture.cloud": "Cloud",
"legend.architecture.security": "Sicurezza",
"legend.architecture.messagebus": "Bus di messaggi",
"legend.architecture.external": "Esterno",
"legend.workflow.frontend": "Interfaccia utente",
"legend.workflow.backend": "Logica dell'agente",
"legend.workflow.security": "Criterio",
"legend.workflow.messagebus": "Azione dello strumento",
"legend.workflow.database": "Contesto / traccia",
"legend.workflow.cloud": "Servizio cloud",
"legend.workflow.external": "Sistema esterno",
"legend.sequence.emphasis": "richiesta principale",
"legend.sequence.return": "risposta",
"legend.sequence.security": "sicurezza",
"legend.sequence.dashed": "traccia asincrona",
"legend.sequence.default": "messaggio",
"legend.dataflow.emphasis": "dati primari",
"legend.dataflow.security": "criterio / PII",
"legend.dataflow.dashed": "batch asincrono",
"legend.dataflow.database": "archivio dati",
"legend.dataflow.default": "flusso dati",
"legend.lifecycle.start": "stato iniziale",
"legend.lifecycle.active": "attivo",
"legend.lifecycle.waiting": "attesa",
"legend.lifecycle.decision": "decisione",
"legend.lifecycle.success": "successo",
"legend.lifecycle.failure": "errore / uscita",
"legend.lifecycle.neutral": "neutro",
"legend.lifecycle.external": "esterno",
"legend.lifecycle.final": "stato finale",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.kind.database": "Database",
"viewer.kind.cloud": "Cloud",
"viewer.kind.security": "Sicurezza",
"viewer.kind.messagebus": "Bus di messaggi",
"viewer.kind.external": "Esterno",
"viewer.kind.neutral": "Neutro",
"viewer.kind.node": "Nodo",
"viewer.kind.start": "Inizio",
"viewer.kind.active": "Attivo",
"viewer.kind.waiting": "In attesa",
"viewer.kind.decision": "Decisione",
"viewer.kind.success": "Successo",
"viewer.kind.failure": "Errore",
"viewer.toolbar.actions": "Azioni del diagramma",
"viewer.theme.toggle.title": "Cambia tema (T)",
"viewer.theme.toggle": "Cambia tema colore",
"viewer.theme.dark": "Scuro",
"viewer.theme.light": "Chiaro",
"viewer.preset.choose.title": "Scegli lo stile visivo (S per scorrere)",
"viewer.preset.choose": "Scegli lo stile visivo",
"viewer.preset.style": "Stile",
"viewer.preset.menu": "Stile visivo",
"viewer.preset.identity": "Identità visiva",
"viewer.preset.cycles": "S per scorrere",
"viewer.preset.classic": "Classico",
"viewer.preset.classic.short": "Classico",
"viewer.preset.classic.hint": "Predefinito tecnico stabile",
"viewer.preset.flow": "Flusso di segnale",
"viewer.preset.flow.short": "Flusso",
"viewer.preset.flow.hint": "Presentazione dinamica",
"viewer.preset.blueprint": "Progetto",
"viewer.preset.blueprint.hint": "Revisione tecnica",
"viewer.preset.editorial": "Editoriale",
"viewer.preset.editorial.hint": "Note di pubblicazione e lancio",
"viewer.preset.badge.signalFlow": "FLUSSO DI SEGNALE",
"viewer.preset.badge.blueprint": "PROGETTO / REV. 01",
"viewer.preset.badge.editorial": "EDITORIALE / NOTA SUL CAMPO",
"viewer.preset.badge.editorialPlate": "ARCHIFY / TAVOLA 04",
"viewer.preset.current": "Stile visivo: {style}. Scegli lo stile visivo",
"viewer.motion.live": "Attiva",
"viewer.motion.still": "Statica",
"viewer.motion.pause": "Metti in pausa l'animazione",
"viewer.motion.resume": "Riprendi l'animazione",
"viewer.motion.reduced": "Animazione in pausa per preferenza di movimento ridotto",
"viewer.motion.hidden": "Animazione in pausa mentre la pagina è nascosta",
"viewer.motion.yielding": "Metti in pausa l'animazione; attualmente cede il posto a {owner}",
"viewer.motion.yielding.title": "Anteprima dinamica attiva · cede il posto a {owner}",
"viewer.owner.route": "Esplora percorso",
"viewer.owner.lens": "Lente semantica",
"viewer.owner.relationship": "Anteprima relazione",
"viewer.owner.intent": "Traccia intento",
"viewer.owner.focus": "focus semantico",
"viewer.owner.legend": "anteprima legenda",
"viewer.owner.reader": "interazione del lettore",
"viewer.present.enter": "Entra in modalità presentazione",
"viewer.present.enter.title": "Modalità presentazione (F)",
"viewer.present.exit": "Esci dalla modalità presentazione",
"viewer.present.exit.title": "Esci dalla modalità presentazione (F o Esc)",
"viewer.present.present": "Presenta",
"viewer.present.exit.label": "Esci",
"viewer.export.button": "Esporta",
"viewer.export.button.title": "Esporta diagramma (E)",
"viewer.export.diagram": "Esporta diagramma",
"viewer.export.menu": "Esporta",
"viewer.export.subtitle": "Output portatili e puliti",
"viewer.export.share": "Condividi",
"viewer.export.shareCard": "Scheda di condivisione",
"viewer.export.routeShareCard": "Scheda di condivisione del percorso",
"viewer.export.reachShareCard": "Scheda di condivisione della raggiungibilità",
"viewer.export.copyDiagram": "Copia diagramma",
"viewer.export.clipboardPng": "PNG negli appunti",
"viewer.export.raster": "Immagini raster",
"viewer.export.image": "Immagine",
"viewer.export.lossless": "Immagine senza perdita",
"viewer.export.compact": "Immagine compatta",
"viewer.export.modern": "Immagine moderna",
"viewer.export.vectorMotion": "Vettoriale e animazione",
"viewer.export.vectorMotion.heading": "Vettoriale e animazione",
"viewer.export.editable": "Vettoriale modificabile",
"viewer.export.motion6s": "Animazione di 6s",
"viewer.export.unsupported": "Non supportato da questo browser",
"viewer.export.clipboardUnsupported": "La scrittura di immagini negli appunti non è supportata da questo browser",
"viewer.export.clipboardUnsupported.short": "La scrittura di immagini negli appunti non è supportata in questo browser.",
"viewer.export.motionUnavailable": "Cattura dell'animazione non disponibile in questo browser",
"viewer.export.webmUnavailable": "WebM non disponibile in questo browser",
"viewer.export.failed": "Esportazione non riuscita: {message}",
"viewer.export.unknownVariant": "Variante scheda di condivisione sconosciuta: {variant}",
"viewer.export.routeRequired": "Traccia un percorso prima di esportare una scheda di condivisione del percorso",
"viewer.export.reachRequired": "Traccia la raggiungibilità prima di esportare una scheda di condivisione della raggiungibilità",
"viewer.export.unknown": "sconosciuto",
"viewer.export.routeFailed": "Esportazione della scheda di condivisione del percorso non riuscita: {message}",
"viewer.export.reachFailed": "Esportazione della scheda di condivisione della raggiungibilità non riuscita: {message}",
"viewer.export.copyFailed": "Copia non riuscita: {message}",
"viewer.export.copiedPng": "PNG copiato negli appunti",
"viewer.export.downloadedRoute": "Scheda di condivisione del percorso scaricata",
"viewer.export.downloadedReach": "Scheda di condivisione della raggiungibilità scaricata",
"viewer.export.downloadedWebm": "WebM scaricato",
"viewer.export.recording": "Registrazione di 6 secondi di animazione…",
"viewer.export.card.routeSummary.one": "Percorso: {source} → {target} · {count} salto diretto",
"viewer.export.card.routeSummary.other": "Percorso: {source} → {target} · {count} salti diretti",
"viewer.export.card.reachSummary": "{direction} generata da {origin} · {nodes} · {links} · max {hops}",
"viewer.export.card.node.one": "{count} nodo",
"viewer.export.card.node.other": "{count} nodi",
"viewer.export.card.link.one": "{count} collegamento",
"viewer.export.card.link.other": "{count} collegamenti",
"viewer.export.card.hop.one": "{count} salto",
"viewer.export.card.hop.other": "{count} salti",
"viewer.export.card.routeBadge": "ARCHIFY · PERCORSO · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · RAGGIUNGIBILITÀ {direction}",
"viewer.export.direction.upstream": "A monte",
"viewer.export.direction.downstream": "A valle",
"viewer.export.error.canvasUnavailable": "Canvas non disponibile per {label}",
"viewer.export.error.contextUnavailable": "Contesto canvas 2D non disponibile per {label}",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob non disponibile per {label}",
"viewer.export.error.toBlobNull": "canvas.toBlob non ha restituito dati per {label}",
"viewer.export.error.variantsCombined": "Le varianti della scheda di condivisione non possono essere combinate",
"viewer.export.error.viewerState": "L'esportazione della scheda di condivisione non è riuscita a rimuovere lo stato temporaneo del visualizzatore",
"viewer.export.error.routeState": "L'esportazione della scheda del percorso non è riuscita a preservare in modo sicuro il percorso risolto",
"viewer.export.error.reachState": "L'esportazione della scheda di raggiungibilità non è riuscita a preservare in modo sicuro la raggiungibilità generata",
"viewer.export.error.webmRequirements": "L'esportazione dell'animazione WebM richiede un'animazione di traccia e il supporto MediaRecorder del browser",
"viewer.export.error.mediaRecorder": "MediaRecorder non riuscito",
"viewer.export.error.emptyWebm": "MediaRecorder ha prodotto un WebM vuoto",
"viewer.export.error.webmBackground": "Impossibile caricare lo sfondo SVG per l'esportazione WebM",
"viewer.guide.eyebrow": "Guida al diagramma",
"viewer.guide.close": "Chiudi la guida al diagramma",
"viewer.guide.inspecting": "Analisi della semantica compilata",
"viewer.guide.actions": "Azioni di esplorazione del diagramma",
"viewer.guide.find": "Trova un nodo qualsiasi",
"viewer.guide.find.hint": "Cerca etichette, responsabilità, tipi e ID stabili.",
"viewer.guide.route": "Traccia un percorso",
"viewer.guide.route.aria": "Traccia un percorso diretto",
"viewer.guide.route.hint": "Chiedi come si collegano due nodi semantici nella direzione generata.",
"viewer.guide.map": "Guarda l'intero sistema",
"viewer.guide.map.hint": "Apri il Radar semantico con un viewport dal vivo e nodi stabili.",
"viewer.guide.lens": "Confronta i tipi semantici",
"viewer.guide.lens.hint": "Conta i ruoli, mostra il loro traffico e confronta i collegamenti diretti generati.",
"viewer.guide.present": "Entra in modalità presentazione",
"viewer.guide.present.hint": "Assegna il viewport al diagramma dal vivo senza modificare l'esportazione.",
"viewer.guide.shortcuts": "Altre scorciatoie da tastiera",
"viewer.guide.shortcut.export": "Esporta",
"viewer.guide.shortcut.theme": "Tema",
"viewer.guide.shortcut.style": "Stile",
"viewer.guide.shortcut.reset": "Ripristina",
"viewer.guide.shortcut.zoomIn": "Zoom avanti",
"viewer.guide.shortcut.zoomOut": "Zoom indietro",
"viewer.guide.shortcut.close": "Chiudi",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} nodo semantico",
"viewer.guide.fact.node.other": "{count} nodi semantici",
"viewer.guide.fact.relationship.one": "{count} relazione",
"viewer.guide.fact.relationship.other": "{count} relazioni",
"viewer.guide.open": "Apri la guida al diagramma",
"viewer.finder.title": "Trova un nodo",
"viewer.finder.close": "Chiudi il cercatore di nodi",
"viewer.finder.placeholder": "Cerca etichette o ID",
"viewer.finder.search": "Cerca nodi del diagramma",
"viewer.finder.results": "Nodi del diagramma",
"viewer.finder.empty": "Nessun nodo corrispondente",
"viewer.finder.result.focus": "Metti a fuoco {label}",
"viewer.finder.result.routeStart": "Scegli {label} come inizio del percorso",
"viewer.finder.result.routeTarget": "Scegli {label} come destinazione del percorso, {links}",
"viewer.finder.status.empty": "Nessun nodo corrispondente",
"viewer.finder.status.count.one": "{count} nodo corrispondente",
"viewer.finder.status.count.other": "{count} nodi corrispondenti",
"viewer.finder.noun.nodes": "nodi",
"viewer.finder.link.one": "{count} collegamento",
"viewer.finder.link.other": "{count} collegamenti",
"viewer.finder.result.focus.one": "Metti a fuoco {label}, {count} connessione correlata",
"viewer.finder.result.focus.other": "Metti a fuoco {label}, {count} connessioni correlate",
"viewer.finder.status.filtered": "{visible} di {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Passaporto semantico",
"viewer.passport.metadata": "Metadati del nodo",
"viewer.passport.evidence": "Fonte verificata",
"viewer.passport.verified": "Fonte verificata",
"viewer.passport.verificationScope": "Verificato rispetto al Git locale alla revisione fissata. L'accesso remoto non è stato verificato.",
"viewer.passport.reach": "Raggiungibilità generata",
"viewer.passport.reach.trace": "Traccia la raggiungibilità generata",
"viewer.passport.upstream": "A monte",
"viewer.passport.downstream": "A valle",
"viewer.passport.upstream.trace": "Traccia la raggiungibilità generata a monte",
"viewer.passport.downstream.trace": "Traccia la raggiungibilità generata a valle",
"viewer.passport.close": "Chiudi il passaporto semantico",
"viewer.passport.copy": "Copia link",
"viewer.passport.copy.focus": "Copia il link al nodo in focus",
"viewer.passport.relations": "Relazioni",
"viewer.passport.relations.show": "Mostra le relazioni collegate",
"viewer.passport.relations.hide": "Nascondi le relazioni collegate",
"viewer.passport.relations.list": "Relazioni collegate",
"viewer.passport.copyRelation": "Copia relazione",
"viewer.passport.copyNode": "Copia nodo",
"viewer.passport.copyPinned": "Copia il link alla relazione fissata",
"viewer.passport.copySource": "Copia il link al nodo di origine",
"viewer.passport.copy.focused.success": "Link del nodo in focus copiato",
"viewer.passport.copy.pinned.success": "Link della relazione fissata copiato",
"viewer.passport.copy.focused.failed": "Impossibile copiare il link del nodo in focus",
"viewer.passport.copy.pinned.failed": "Impossibile copiare il link della relazione fissata",
"viewer.passport.relationship.none": "Nessuna relazione collegata",
"viewer.passport.relationship.count.one": "{count} relazione",
"viewer.passport.relationship.count.other": "{count} relazioni",
"viewer.passport.relationship.show.one": "Mostra {count} relazione collegata",
"viewer.passport.relationship.show.other": "Mostra {count} relazioni collegate",
"viewer.passport.relationship.summary": "{out} in uscita · {in} in entrata{loops}",
"viewer.passport.relationship.loops": " · {count} autoanello",
"viewer.passport.relationship.explorer": "Esploratore delle relazioni dirette",
"viewer.passport.relationship.help": "Usa i tasti freccia per esplorare le relazioni. Premi Invio o Spazio per fissare i dettagli; Esc per annullare.",
"viewer.passport.relationship.loopsBack": "torna su se stesso",
"viewer.passport.relationship.connectsTo": "si collega a",
"viewer.passport.relationship.connectsFrom": "si collega da",
"viewer.passport.relationship.pinned": "Relazione fissata · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Analizza la relazione {index} di {total}: da {from} a {to}, {label}. Premi Invio per i dettagli.",
"viewer.passport.relationship.group.out": "In uscita",
"viewer.passport.relationship.group.in": "In entrata",
"viewer.passport.relationship.group.loop": "Autoanelli",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "USCITA →",
"viewer.passport.relationship.direction.in": "← ENTRATA",
"viewer.passport.relationship.direction.loop": "ANELLO",
"viewer.passport.sourceCount.one": "{count} riferimento a fonte verificata",
"viewer.passport.sourceCount.other": "{count} riferimenti a fonti verificate",
"viewer.passport.sourceMarker": "FONTE",
"viewer.passport.beacon.one": "{count} fonte verificata; metti a fuoco questo nodo per analizzarla",
"viewer.passport.beacon.other": "{count} fonti verificate; metti a fuoco questo nodo per analizzarle",
"viewer.passport.repository.open": "Apri la revisione {revision} del repository verificato",
"viewer.passport.source.open": "Apri la fonte verificata {path} alla revisione {revision}",
"viewer.passport.source.openLink": "Apri ↗",
"viewer.passport.reach.upstream.one": "Traccia {count} nodo generato a monte",
"viewer.passport.reach.upstream.other": "Traccia {count} nodi generati a monte",
"viewer.passport.reach.downstream.one": "Traccia {count} nodo generato a valle",
"viewer.passport.reach.downstream.other": "Traccia {count} nodi generati a valle",
"viewer.passport.reach.noUpstream": "Nessun nodo generato a monte",
"viewer.passport.reach.noDownstream": "Nessun nodo generato a valle",
"viewer.passport.reach.status": "{direction} · {nodes} nodi · {links} collegamenti · max {hops} salti",
"viewer.route.eyebrow": "Esplora percorso",
"viewer.route.start": "Scegli un nodo di partenza",
"viewer.route.start.find": "Trova partenza",
"viewer.route.start.find.aria": "Trova una partenza del percorso",
"viewer.route.copy": "Copia link",
"viewer.route.copy.aria": "Copia il link al percorso tracciato",
"viewer.route.clear": "Cancella",
"viewer.route.clear.aria": "Cancella l'esplorazione del percorso",
"viewer.route.traced": "Percorso tracciato",
"viewer.route.pickTwo": "Scegli due nodi semantici sul diagramma",
"viewer.route.pickOne": "Scegli un nodo semantico sul diagramma",
"viewer.route.controls": "Controlli del tragitto del percorso",
"viewer.route.previous": "Posizione precedente nel percorso",
"viewer.route.play": "Riproduci il tragitto del percorso",
"viewer.route.pause": "Metti in pausa il tragitto del percorso",
"viewer.route.replay": "Riavvia il tragitto del percorso",
"viewer.route.next": "Posizione successiva nel percorso",
"viewer.route.journey": "Tragitto",
"viewer.route.pause.label": "Pausa",
"viewer.route.replay.label": "Riavvia",
"viewer.route.overview": "Panoramica",
"viewer.route.overview.aria": "Mostra la panoramica completa del percorso",
"viewer.route.instructions": "Scegli prima l'origine, poi la destinazione. La direzione è importante.",
"viewer.route.destination": "Scegli una destinazione da {label}",
"viewer.route.destination.find": "Trova destinazione",
"viewer.route.destination.find.aria": "Trova una destinazione raggiungibile del percorso",
"viewer.route.differentDestination": "Scegli una destinazione diversa",
"viewer.route.distinct": "Un percorso richiede due nodi semantici distinti.",
"viewer.route.unreachable": "Nessun percorso diretto verso {label}",
"viewer.route.unreachable.detail": "{target} non è raggiungibile da {source}. Scegli una destinazione evidenziata.",
"viewer.route.start.instructions": "Seleziona l'origine. Il passo successivo mostrerà solo le destinazioni dirette.",
"viewer.route.copy.success": "Link del percorso tracciato copiato",
"viewer.route.copy.failed": "Impossibile copiare il link del percorso tracciato",
"viewer.route.position": "Posizione {index} di {total} nel percorso: {label}",
"viewer.route.step": "Passo {index} di {total} · {phase} · {label}",
"viewer.route.motionRequired": "Il tragitto automatico richiede l'animazione attiva",
"viewer.route.trigger.clear": "Cancella il percorso tracciato",
"viewer.route.overview.status": "{nodes} · {hops} · percorso generato più breve",
"viewer.route.overview.node.one": "{count} nodo",
"viewer.route.overview.node.other": "{count} nodi",
"viewer.route.overview.hop.one": "{count} salto diretto",
"viewer.route.overview.hop.other": "{count} salti diretti",
"viewer.route.phase.playing": "In riproduzione",
"viewer.route.phase.complete": "Completato",
"viewer.route.phase.inspecting": "Analisi in corso",
"viewer.route.destination.count.one": "{count} destinazione diretta disponibile. Scegli un nodo evidenziato.",
"viewer.route.destination.count.other": "{count} destinazioni dirette disponibili. Scegli un nodo evidenziato.",
"viewer.route.noOutgoing": "Nessun percorso in uscita da qui. Cancella e scegli un'altra origine.",
"viewer.route.result.title": "{source} verso {target}",
"viewer.route.finder.source.title": "Scegli l'inizio del percorso",
"viewer.route.finder.source.placeholder": "Cerca origini del percorso",
"viewer.route.finder.source.empty": "Nessuna origine del percorso corrispondente",
"viewer.route.finder.source.results": "Nodi che possono avviare un percorso",
"viewer.route.finder.source.noun": "origini del percorso",
"viewer.route.finder.source.badge": "inizio",
"viewer.route.finder.target.title": "Destinazione da {label}",
"viewer.route.finder.target.placeholder": "Cerca destinazioni raggiungibili",
"viewer.route.finder.target.empty": "Nessuna destinazione raggiungibile corrispondente",
"viewer.route.finder.target.results": "Destinazioni raggiungibili del percorso",
"viewer.route.finder.target.noun": "destinazioni raggiungibili",
"viewer.route.hop.one": "{count} salto",
"viewer.route.hop.other": "{count} salti",
"viewer.lens.eyebrow": "Lente semantica",
"viewer.lens.title": "Confronta i ruoli del sistema",
"viewer.lens.close": "Chiudi la lente semantica",
"viewer.lens.instruction": "Scegli fino a due tipi semantici. Uno mostra il traffico reale; due confrontano solo le relazioni dirette generate.",
"viewer.lens.kinds": "Tipi semantici",
"viewer.lens.choose": "Scegli un tipo per analizzarne i nodi e le relazioni collegate.",
"viewer.lens.copy": "Copia il link alla lente semantica",
"viewer.lens.clear": "Cancella la lente semantica",
"viewer.lens.open": "Apri la lente semantica",
"viewer.lens.openActive": "Apri la lente semantica attiva",
"viewer.lens.legend": "Legenda semantica",
"viewer.lens.legend.inspect.one": "Analizza {label}, {count} nodo",
"viewer.lens.legend.inspect.other": "Analizza {label}, {count} nodi",
"viewer.lens.kind.count.one": "{label}, {count} nodo",
"viewer.lens.kind.count.other": "{label}, {count} nodi",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relazione diretta",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relazioni dirette",
"viewer.lens.single": "{nodes} · {relationships} · i nodi collegati restano visibili",
"viewer.lens.node.one": "{count} nodo {label}",
"viewer.lens.node.other": "{count} nodi {label}",
"viewer.lens.relationship.one": "{count} relazione a contatto",
"viewer.lens.relationship.other": "{count} relazioni a contatto",
"viewer.radar.title": "Radar semantico",
"viewer.radar.building": "Costruzione della panoramica",
"viewer.radar.openFull": "Apri il radar semantico completo",
"viewer.radar.open": "Apri radar",
"viewer.radar.close": "Chiudi il radar semantico",
"viewer.radar.surface": "Panoramica del diagramma. Clicca su un nodo per metterlo a fuoco, oppure usa i tasti freccia per scorrere.",
"viewer.radar.click": "Clicca sul nodo",
"viewer.radar.drag": "Trascina per scorrere",
"viewer.radar.space": "Il radar semantico richiede più spazio nella MAPPA.",
"viewer.radar.nodes": "Nodi del radar semantico del diagramma",
"viewer.radar.focus": "Metti a fuoco {label} dal radar semantico",
"viewer.radar.status": "{count} nodi · {viewport}",
"viewer.radar.fullMap": "{count} nodi · mappa completa",
"viewer.radar.compacted": "Radar compattato per evitare di coprire il passaporto semantico o i controlli della MAPPA.",
"viewer.radar.cancelWaiting": "Annulla l'attesa del radar semantico per più spazio nella MAPPA",
"viewer.radar.needsSpace": "Il radar semantico richiede più spazio visibile nella MAPPA",
"viewer.radar.viewport.full": "mappa completa",
"viewer.radar.viewport.width": "{percent}% di larghezza",
"viewer.radar.viewport.scale": "{percent}% del viewport",
"viewer.nav.controls": "Controlli della vista del diagramma",
"viewer.nav.route": "Traccia un percorso diretto",
"viewer.nav.route.title": "Traccia percorso (R)",
"viewer.nav.route.short": "PERCORSO",
"viewer.nav.radar": "Apri il radar semantico",
"viewer.nav.radar.title": "Radar semantico (M)",
"viewer.nav.radar.short": "MAPPA",
"viewer.nav.lens": "Apri la lente semantica",
"viewer.nav.lens.title": "Lente semantica (L)",
"viewer.nav.lens.short": "LENTE",
"viewer.nav.find": "Trova un nodo",
"viewer.nav.find.title": "Trova un nodo (/)",
"viewer.nav.guide": "Apri la guida al diagramma",
"viewer.nav.guide.title": "Guida al diagramma (?)",
"viewer.nav.zoomOut": "Zoom indietro",
"viewer.nav.zoomOut.title": "Zoom indietro (-)",
"viewer.nav.reset": "Ripristina la vista del diagramma",
"viewer.nav.reset.title": "Ripristina vista (0)",
"viewer.nav.read": "LEGGI",
"viewer.nav.zoomIn": "Zoom avanti",
"viewer.nav.zoomIn.title": "Zoom avanti (+)",
"viewer.nav.camera": "{hint}. Ripristina la vista del diagramma",
"viewer.nav.camera.title": "{semantic}{hint} · ripristina vista (0)",
"viewer.nav.camera.semantic": "Camera semantica attiva · ",
"viewer.nav.level.map": "MAPPA",
"viewer.nav.level.read": "LEGGI",
"viewer.nav.level.full": "COMPLETO",
"viewer.nav.level.auto": "AUTO",
"viewer.nav.detail.map": "Ingrandisci per mostrare le etichette delle relazioni e il contesto dei nodi",
"viewer.nav.detail.read": "Ingrandisci ancora per mostrare tag e annotazioni",
"viewer.nav.detail.full": "Dettaglio completo del diagramma",
"viewer.intent.summary": "{label}. {out} in uscita, {in} in entrata{loops}. {total} connessioni. Premi Invio per i dettagli.",
"viewer.intent.loops": ", {count} autoanello",
"viewer.common.copied": "Copiato",
"viewer.common.copyFailed": "Copia non riuscita",
"viewer.common.copyLink": "Copia link",
"viewer.common.clear": "Cancella",
"viewer.common.close": "Chiudi"
}
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "{title} ダイアグラム",
"diagram.description.architecture": "Archifyで生成されたアーキテクチャ図です。",
"diagram.description.workflow": "Archifyで生成されたワークフロー図です。",
"diagram.description.sequence": "Archifyで生成されたシーケンス図です。",
"diagram.description.dataflow": "Archifyで生成されたデータフロー図です。",
"diagram.description.lifecycle": "Archifyで生成されたライフサイクル図です。",
"node.focus": "{label} にフォーカス",
"node.focus.detail": "{label} にフォーカス、{detail}",
"node.context.architecture": "アーキテクチャコンポーネント",
"node.context.workflow": "ワークフローノード",
"node.context.sequence": "シーケンス参加者",
"node.context.dataflow": "データフローノード",
"node.context.lifecycle": "ライフサイクル状態",
"legend.title": "凡例",
"legend.architecture.frontend": "フロントエンド",
"legend.architecture.backend": "バックエンド",
"legend.architecture.database": "データベース",
"legend.architecture.cloud": "クラウド",
"legend.architecture.security": "セキュリティ",
"legend.architecture.messagebus": "メッセージバス",
"legend.architecture.external": "外部",
"legend.workflow.frontend": "ユーザーUI",
"legend.workflow.backend": "エージェントロジック",
"legend.workflow.security": "ポリシー",
"legend.workflow.messagebus": "ツール実行",
"legend.workflow.database": "コンテキスト / トレース",
"legend.workflow.cloud": "クラウドサービス",
"legend.workflow.external": "外部システム",
"legend.sequence.emphasis": "主要リクエスト",
"legend.sequence.return": "戻り値",
"legend.sequence.security": "セキュリティ",
"legend.sequence.dashed": "非同期トレース",
"legend.sequence.default": "メッセージ",
"legend.dataflow.emphasis": "主要データ",
"legend.dataflow.security": "ポリシー / PII",
"legend.dataflow.dashed": "非同期バッチ",
"legend.dataflow.database": "データストア",
"legend.dataflow.default": "データフロー",
"legend.lifecycle.start": "初期状態",
"legend.lifecycle.active": "アクティブ",
"legend.lifecycle.waiting": "待機",
"legend.lifecycle.decision": "判断",
"legend.lifecycle.success": "成功",
"legend.lifecycle.failure": "失敗 / 終了",
"legend.lifecycle.neutral": "中立",
"legend.lifecycle.external": "外部",
"legend.lifecycle.final": "終了状態",
"viewer.kind.frontend": "フロントエンド",
"viewer.kind.backend": "バックエンド",
"viewer.kind.database": "データベース",
"viewer.kind.cloud": "クラウド",
"viewer.kind.security": "セキュリティ",
"viewer.kind.messagebus": "メッセージバス",
"viewer.kind.external": "外部",
"viewer.kind.neutral": "中立",
"viewer.kind.node": "ノード",
"viewer.kind.start": "開始",
"viewer.kind.active": "アクティブ",
"viewer.kind.waiting": "待機",
"viewer.kind.decision": "判断",
"viewer.kind.success": "成功",
"viewer.kind.failure": "失敗",
"viewer.toolbar.actions": "ダイアグラム操作",
"viewer.theme.toggle.title": "テーマ切替 (T)",
"viewer.theme.toggle": "カラーテーマを切替",
"viewer.theme.dark": "ダーク",
"viewer.theme.light": "ライト",
"viewer.preset.choose.title": "ビジュアルスタイルを選択 (Sで循環)",
"viewer.preset.choose": "ビジュアルスタイルを選択",
"viewer.preset.style": "スタイル",
"viewer.preset.menu": "ビジュアルスタイル",
"viewer.preset.identity": "ビジュアルアイデンティティ",
"viewer.preset.cycles": "Sで循環",
"viewer.preset.classic": "クラシック",
"viewer.preset.classic.short": "クラシック",
"viewer.preset.classic.hint": "安定した技術標準",
"viewer.preset.flow": "シグナルフロー",
"viewer.preset.flow.short": "フロー",
"viewer.preset.flow.hint": "動きを強調した表現",
"viewer.preset.blueprint": "ブループリント",
"viewer.preset.blueprint.hint": "エンジニアリングレビュー向け",
"viewer.preset.editorial": "エディトリアル",
"viewer.preset.editorial.hint": "発行・公開ノート向け",
"viewer.preset.badge.signalFlow": "シグナルフロー",
"viewer.preset.badge.blueprint": "ブループリント / 改訂01",
"viewer.preset.badge.editorial": "エディトリアル / フィールドノート",
"viewer.preset.badge.editorialPlate": "ARCHIFY / プレート04",
"viewer.preset.current": "ビジュアルスタイル: {style}。ビジュアルスタイルを選択",
"viewer.motion.live": "ライブ",
"viewer.motion.still": "静止",
"viewer.motion.pause": "動きを一時停止",
"viewer.motion.resume": "動きを再開",
"viewer.motion.reduced": "「視差効果を減らす」設定により動きを一時停止しました",
"viewer.motion.hidden": "このページが非表示のため動きを一時停止しました",
"viewer.motion.yielding": "動きを一時停止、現在 {owner} に譲っています",
"viewer.motion.yielding.title": "ライブプレビュー有効 · {owner} に譲っています",
"viewer.owner.route": "ルートプローブ",
"viewer.owner.lens": "セマンティックレンズ",
"viewer.owner.relationship": "関係性プレビュー",
"viewer.owner.intent": "インテントトレース",
"viewer.owner.focus": "セマンティックフォーカス",
"viewer.owner.legend": "凡例プレビュー",
"viewer.owner.reader": "読者の操作",
"viewer.present.enter": "プレゼンテーションステージに入る",
"viewer.present.enter.title": "プレゼンテーションステージ (F)",
"viewer.present.exit": "プレゼンテーションステージを終了",
"viewer.present.exit.title": "プレゼンテーションステージを終了 (F または Escape)",
"viewer.present.present": "プレゼンテーション",
"viewer.present.exit.label": "終了",
"viewer.export.button": "エクスポート",
"viewer.export.button.title": "ダイアグラムをエクスポート (E)",
"viewer.export.diagram": "ダイアグラムをエクスポート",
"viewer.export.menu": "エクスポート",
"viewer.export.subtitle": "持ち運びやすくクリーンな出力",
"viewer.export.share": "共有",
"viewer.export.shareCard": "シェアカード",
"viewer.export.routeShareCard": "ルートシェアカード",
"viewer.export.reachShareCard": "到達範囲シェアカード",
"viewer.export.copyDiagram": "ダイアグラムをコピー",
"viewer.export.clipboardPng": "PNGをクリップボードへ",
"viewer.export.raster": "ラスター画像",
"viewer.export.image": "画像",
"viewer.export.lossless": "無劣化画像",
"viewer.export.compact": "コンパクト画像",
"viewer.export.modern": "モダン画像",
"viewer.export.vectorMotion": "ベクターとモーション",
"viewer.export.vectorMotion.heading": "ベクター & モーション",
"viewer.export.editable": "編集可能なベクター",
"viewer.export.motion6s": "6秒のモーション",
"viewer.export.unsupported": "このブラウザではサポートされていません",
"viewer.export.clipboardUnsupported": "このブラウザはクリップボードへの画像書き込みに対応していません",
"viewer.export.clipboardUnsupported.short": "このブラウザはクリップボードへの画像書き込みに対応していません。",
"viewer.export.motionUnavailable": "このブラウザではモーションキャプチャを利用できません",
"viewer.export.webmUnavailable": "このブラウザではWebMを利用できません",
"viewer.export.failed": "エクスポートに失敗しました: {message}",
"viewer.export.unknownVariant": "不明なシェアカードの種類です: {variant}",
"viewer.export.routeRequired": "ルートシェアカードをエクスポートする前にルートをトレースしてください",
"viewer.export.reachRequired": "到達範囲シェアカードをエクスポートする前に編集済みの到達範囲をトレースしてください",
"viewer.export.unknown": "不明",
"viewer.export.routeFailed": "ルートシェアカードのエクスポートに失敗しました: {message}",
"viewer.export.reachFailed": "到達範囲シェアカードのエクスポートに失敗しました: {message}",
"viewer.export.copyFailed": "コピーに失敗しました: {message}",
"viewer.export.copiedPng": "PNGをクリップボードにコピーしました",
"viewer.export.downloadedRoute": "ルートシェアカードをダウンロードしました",
"viewer.export.downloadedReach": "到達範囲シェアカードをダウンロードしました",
"viewer.export.downloadedWebm": "WebMをダウンロードしました",
"viewer.export.recording": "6秒間のモーションを録画中…",
"viewer.export.card.routeSummary.one": "ルート: {source} → {target} · {count} 個の有向ホップ",
"viewer.export.card.routeSummary.other": "ルート: {source} → {target} · {count} 個の有向ホップ",
"viewer.export.card.reachSummary": "{origin} からの編集済み{direction} · {nodes} · {links} · 最大{hops}",
"viewer.export.card.node.one": "{count} 個のノード",
"viewer.export.card.node.other": "{count} 個のノード",
"viewer.export.card.link.one": "{count} 個の接続",
"viewer.export.card.link.other": "{count} 個の接続",
"viewer.export.card.hop.one": "{count} ホップ",
"viewer.export.card.hop.other": "{count} ホップ",
"viewer.export.card.routeBadge": "ARCHIFY · ROUTE · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · {direction} REACH",
"viewer.export.direction.upstream": "アップストリーム",
"viewer.export.direction.downstream": "ダウンストリーム",
"viewer.export.error.canvasUnavailable": "{label} のキャンバスを利用できません",
"viewer.export.error.contextUnavailable": "{label} の2Dキャンバスコンテキストを利用できません",
"viewer.export.error.toBlobUnavailable": "{label} でcanvas.toBlobを利用できません",
"viewer.export.error.toBlobNull": "{label} のcanvas.toBlobがデータを返しませんでした",
"viewer.export.error.variantsCombined": "シェアカードの種類は組み合わせられません",
"viewer.export.error.viewerState": "シェアカードのエクスポートで一時的なビューアー状態を削除できませんでした",
"viewer.export.error.routeState": "ルートカードのエクスポートで解決済みルートを安全に保持できませんでした",
"viewer.export.error.reachState": "到達範囲カードのエクスポートで編集済み到達範囲を安全に保持できませんでした",
"viewer.export.error.webmRequirements": "WebMモーションのエクスポートにはトレースアニメーションとブラウザのMediaRecorder対応が必要です",
"viewer.export.error.mediaRecorder": "MediaRecorderが失敗しました",
"viewer.export.error.emptyWebm": "MediaRecorderが空のWebMを生成しました",
"viewer.export.error.webmBackground": "WebMエクスポート用のSVG背景を読み込めませんでした",
"viewer.guide.eyebrow": "ダイアグラムガイド",
"viewer.guide.close": "ダイアグラムガイドを閉じる",
"viewer.guide.inspecting": "コンパイル済みセマンティクスを検査中",
"viewer.guide.actions": "ダイアグラム探索アクション",
"viewer.guide.find": "任意のノードを検索",
"viewer.guide.find.hint": "ラベル、役割、種類、固定IDで検索します。",
"viewer.guide.route": "ルートをトレース",
"viewer.guide.route.aria": "有向ルートをトレース",
"viewer.guide.route.hint": "2つのセマンティックノードが編集済みの方向でどう接続されているかを確認します。",
"viewer.guide.map": "システム全体を表示",
"viewer.guide.map.hint": "ライブビューポートと固定ノードを持つセマンティックレーダーを開きます。",
"viewer.guide.lens": "セマンティックな種類を比較",
"viewer.guide.lens.hint": "役割を数え、そのトラフィックを表示し、直接編集されたリンクを比較します。",
"viewer.guide.present": "プレゼンテーションステージに入る",
"viewer.guide.present.hint": "エクスポートを変更せずに、ライブダイアグラムにビューポート全体を割り当てます。",
"viewer.guide.shortcuts": "その他のキーボードショートカット",
"viewer.guide.shortcut.export": "エクスポート",
"viewer.guide.shortcut.theme": "テーマ",
"viewer.guide.shortcut.style": "スタイル",
"viewer.guide.shortcut.reset": "リセット",
"viewer.guide.shortcut.zoomIn": "拡大",
"viewer.guide.shortcut.zoomOut": "縮小",
"viewer.guide.shortcut.close": "閉じる",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} 個のセマンティックノード",
"viewer.guide.fact.node.other": "{count} 個のセマンティックノード",
"viewer.guide.fact.relationship.one": "{count} 件の関係",
"viewer.guide.fact.relationship.other": "{count} 件の関係",
"viewer.guide.open": "ダイアグラムガイドを開く",
"viewer.finder.title": "ノードを検索",
"viewer.finder.close": "ノード検索を閉じる",
"viewer.finder.placeholder": "ラベルまたはIDで検索",
"viewer.finder.search": "ダイアグラムノードを検索",
"viewer.finder.results": "ダイアグラムノード",
"viewer.finder.empty": "一致するノードがありません",
"viewer.finder.result.focus": "{label} にフォーカス",
"viewer.finder.result.routeStart": "{label} をルートの起点として選択",
"viewer.finder.result.routeTarget": "{label} をルートの終点として選択、{links}",
"viewer.finder.status.empty": "一致するノードがありません",
"viewer.finder.status.count.one": "一致するノード {count} 個",
"viewer.finder.status.count.other": "一致するノード {count} 個",
"viewer.finder.noun.nodes": "ノード",
"viewer.finder.link.one": "{count} 件の接続",
"viewer.finder.link.other": "{count} 件の接続",
"viewer.finder.result.focus.one": "{label} にフォーカス、関連する接続 {count} 件",
"viewer.finder.result.focus.other": "{label} にフォーカス、関連する接続 {count} 件",
"viewer.finder.status.filtered": "{available} 件の{noun}中 {visible} 件",
"viewer.finder.status.all": "{available} 件の{noun}",
"viewer.passport.eyebrow": "セマンティックパスポート",
"viewer.passport.metadata": "ノードメタデータ",
"viewer.passport.evidence": "検証済みソースの根拠",
"viewer.passport.verified": "検証済みソース",
"viewer.passport.verificationScope": "固定されたリビジョン時点のローカルGitに対して検証済みです。リモートアクセスは確認していません。",
"viewer.passport.reach": "編集済みの到達範囲",
"viewer.passport.reach.trace": "編集済みの到達可能性をトレース",
"viewer.passport.upstream": "アップストリーム",
"viewer.passport.downstream": "ダウンストリーム",
"viewer.passport.upstream.trace": "アップストリームの編集済み到達可能性をトレース",
"viewer.passport.downstream.trace": "ダウンストリームの編集済み到達可能性をトレース",
"viewer.passport.close": "セマンティックパスポートを閉じる",
"viewer.passport.copy": "リンクをコピー",
"viewer.passport.copy.focus": "フォーカス中のノードへのリンクをコピー",
"viewer.passport.relations": "関係",
"viewer.passport.relations.show": "関連する関係を表示",
"viewer.passport.relations.hide": "関連する関係を非表示",
"viewer.passport.relations.list": "関連する関係",
"viewer.passport.copyRelation": "関係をコピー",
"viewer.passport.copyNode": "ノードをコピー",
"viewer.passport.copyPinned": "固定された関係へのリンクをコピー",
"viewer.passport.copySource": "ソースノードへのリンクをコピー",
"viewer.passport.copy.focused.success": "フォーカス中のノードのリンクをコピーしました",
"viewer.passport.copy.pinned.success": "固定された関係のリンクをコピーしました",
"viewer.passport.copy.focused.failed": "フォーカス中のノードのリンクをコピーできませんでした",
"viewer.passport.copy.pinned.failed": "固定された関係のリンクをコピーできませんでした",
"viewer.passport.relationship.none": "関連する関係がありません",
"viewer.passport.relationship.count.one": "{count} 件の関係",
"viewer.passport.relationship.count.other": "{count} 件の関係",
"viewer.passport.relationship.show.one": "関連する関係 {count} 件を表示",
"viewer.passport.relationship.show.other": "関連する関係 {count} 件を表示",
"viewer.passport.relationship.summary": "出向 {out} · 入向 {in}{loops}",
"viewer.passport.relationship.loops": " · 自己ループ {count} 件",
"viewer.passport.relationship.explorer": "直接関係エクスプローラー",
"viewer.passport.relationship.help": "矢印キーで関係を探索します。EnterまたはSpaceで詳細を固定し、Escapeで解除します。",
"viewer.passport.relationship.loopsBack": "ループして戻る",
"viewer.passport.relationship.connectsTo": "接続先",
"viewer.passport.relationship.connectsFrom": "接続元",
"viewer.passport.relationship.pinned": "固定された関係 · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "第 {index}/{total} 件の関係を検査: {from} から {to} へ、{label}。詳細はEnterを押してください。",
"viewer.passport.relationship.group.out": "出向",
"viewer.passport.relationship.group.in": "入向",
"viewer.passport.relationship.group.loop": "自己ループ",
"viewer.passport.relationship.row": "{group}: {relationship}、{neighbor}",
"viewer.passport.relationship.direction.out": "出 →",
"viewer.passport.relationship.direction.in": "← 入",
"viewer.passport.relationship.direction.loop": "ループ",
"viewer.passport.sourceCount.one": "検証済みソース参照 {count} 件",
"viewer.passport.sourceCount.other": "検証済みソース参照 {count} 件",
"viewer.passport.sourceMarker": "出典",
"viewer.passport.beacon.one": "検証済みソース {count} 件。このノードにフォーカスして確認",
"viewer.passport.beacon.other": "検証済みソース {count} 件。このノードにフォーカスして確認",
"viewer.passport.repository.open": "検証済みリポジトリのリビジョン {revision} を開く",
"viewer.passport.source.open": "リビジョン {revision} の検証済みソース {path} を開く",
"viewer.passport.source.openLink": "開く ↗",
"viewer.passport.reach.upstream.one": "アップストリームの編集済みノード {count} 件をトレース",
"viewer.passport.reach.upstream.other": "アップストリームの編集済みノード {count} 件をトレース",
"viewer.passport.reach.downstream.one": "ダウンストリームの編集済みノード {count} 件をトレース",
"viewer.passport.reach.downstream.other": "ダウンストリームの編集済みノード {count} 件をトレース",
"viewer.passport.reach.noUpstream": "アップストリームの編集済みノードがありません",
"viewer.passport.reach.noDownstream": "ダウンストリームの編集済みノードがありません",
"viewer.passport.reach.status": "{direction} · ノード {nodes} 件 · 接続 {links} 件 · 最大 {hops} ホップ",
"viewer.route.eyebrow": "ルートプローブ",
"viewer.route.start": "開始ノードを選択",
"viewer.route.start.find": "開始点を検索",
"viewer.route.start.find.aria": "ルートの開始点を検索",
"viewer.route.copy": "リンクをコピー",
"viewer.route.copy.aria": "トレース済みルートへのリンクをコピー",
"viewer.route.clear": "クリア",
"viewer.route.clear.aria": "ルートプローブをクリア",
"viewer.route.traced": "トレース済みルート",
"viewer.route.pickTwo": "ダイアグラム上で2つのセマンティックノードを選択してください",
"viewer.route.pickOne": "ダイアグラム上でセマンティックノードを1つ選択してください",
"viewer.route.controls": "ルートジャーニーのコントロール",
"viewer.route.previous": "前のルート位置",
"viewer.route.play": "ルートジャーニーを再生",
"viewer.route.pause": "ルートジャーニーを一時停止",
"viewer.route.replay": "ルートジャーニーをリプレイ",
"viewer.route.next": "次のルート位置",
"viewer.route.journey": "ジャーニー",
"viewer.route.pause.label": "一時停止",
"viewer.route.replay.label": "リプレイ",
"viewer.route.overview": "概要",
"viewer.route.overview.aria": "ルート全体の概要を表示",
"viewer.route.instructions": "起点を選択してから終点を選択してください。方向が重要です。",
"viewer.route.destination": "{label} からの終点を選択",
"viewer.route.destination.find": "終点を検索",
"viewer.route.destination.find.aria": "到達可能なルートの終点を検索",
"viewer.route.differentDestination": "別の終点を選択",
"viewer.route.distinct": "ルートには2つの異なるセマンティックノードが必要です。",
"viewer.route.unreachable": "{label} への有向ルートがありません",
"viewer.route.unreachable.detail": "{source} から {target} には到達できません。ハイライトされた終点を選択してください。",
"viewer.route.start.instructions": "起点を選択してください。次のステップでは有向の終点のみが表示されます。",
"viewer.route.copy.success": "トレース済みルートのリンクをコピーしました",
"viewer.route.copy.failed": "ルートのリンクをコピーできませんでした",
"viewer.route.position": "ルート位置 {index}/{total}: {label}",
"viewer.route.step": "ステップ {index}/{total} · {phase} · {label}",
"viewer.route.motionRequired": "自動ジャーニーにはライブモーションが必要です",
"viewer.route.trigger.clear": "トレース済みルートをクリア",
"viewer.route.overview.status": "{nodes} · {hops} · 最短の編集済みルート",
"viewer.route.overview.node.one": "{count} 個のノード",
"viewer.route.overview.node.other": "{count} 個のノード",
"viewer.route.overview.hop.one": "{count} 有向ホップ",
"viewer.route.overview.hop.other": "{count} 有向ホップ",
"viewer.route.phase.playing": "再生中",
"viewer.route.phase.complete": "完了",
"viewer.route.phase.inspecting": "検査中",
"viewer.route.destination.count.one": "利用可能な有向終点 {count} 件。ハイライトされたノードを選択してください。",
"viewer.route.destination.count.other": "利用可能な有向終点 {count} 件。ハイライトされたノードを選択してください。",
"viewer.route.noOutgoing": "ここから始まる出向ルートはありません。クリアして別の起点を選択してください。",
"viewer.route.result.title": "{source} から {target}",
"viewer.route.finder.source.title": "ルートの開始点を選択",
"viewer.route.finder.source.placeholder": "ルートの起点を検索",
"viewer.route.finder.source.empty": "一致するルートの起点がありません",
"viewer.route.finder.source.results": "ルートを開始できるノード",
"viewer.route.finder.source.noun": "ルートの起点",
"viewer.route.finder.source.badge": "起点",
"viewer.route.finder.target.title": "{label} からの終点",
"viewer.route.finder.target.placeholder": "到達可能な終点を検索",
"viewer.route.finder.target.empty": "一致する到達可能な終点がありません",
"viewer.route.finder.target.results": "到達可能なルートの終点",
"viewer.route.finder.target.noun": "到達可能な終点",
"viewer.route.hop.one": "{count} ホップ",
"viewer.route.hop.other": "{count} ホップ",
"viewer.lens.eyebrow": "セマンティックレンズ",
"viewer.lens.title": "システムの役割を比較",
"viewer.lens.close": "セマンティックレンズを閉じる",
"viewer.lens.instruction": "セマンティックな種類を最大2つ選択してください。1つ選ぶと実際のトラフィックが表示され、2つ選ぶと直接の編集済み関係のみを比較します。",
"viewer.lens.kinds": "セマンティックな種類",
"viewer.lens.choose": "種類を選択すると、そのノードと接する関係を検査できます。",
"viewer.lens.copy": "セマンティックレンズへのリンクをコピー",
"viewer.lens.clear": "セマンティックレンズをクリア",
"viewer.lens.open": "セマンティックレンズを開く",
"viewer.lens.openActive": "アクティブなセマンティックレンズを開く",
"viewer.lens.legend": "セマンティック凡例",
"viewer.lens.legend.inspect.one": "{label} を検査、{count} 個のノード",
"viewer.lens.legend.inspect.other": "{label} を検査、{count} 個のノード",
"viewer.lens.kind.count.one": "{label}、{count} 個のノード",
"viewer.lens.kind.count.other": "{label}、{count} 個のノード",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · 直接関係 {count} 件",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · 直接関係 {count} 件",
"viewer.lens.single": "{nodes} · {relationships} · 接続された周辺ノードは表示されたままです",
"viewer.lens.node.one": "{label} ノード {count} 個",
"viewer.lens.node.other": "{label} ノード {count} 個",
"viewer.lens.relationship.one": "接する関係 {count} 件",
"viewer.lens.relationship.other": "接する関係 {count} 件",
"viewer.radar.title": "セマンティックレーダー",
"viewer.radar.building": "概要を構築中",
"viewer.radar.openFull": "完全なセマンティックレーダーを開く",
"viewer.radar.open": "レーダーを開く",
"viewer.radar.close": "セマンティックレーダーを閉じる",
"viewer.radar.surface": "ダイアグラムの概要です。ノードをクリックしてフォーカスするか、矢印キーでパンできます。",
"viewer.radar.click": "ノードをクリック",
"viewer.radar.drag": "ドラッグしてパン",
"viewer.radar.space": "セマンティックレーダーにはさらにMAPスペースが必要です。",
"viewer.radar.nodes": "セマンティックダイアグラムのレーダーノード",
"viewer.radar.focus": "セマンティックレーダーから {label} にフォーカス",
"viewer.radar.status": "{count} 個のノード · {viewport}",
"viewer.radar.fullMap": "{count} 個のノード · 全体マップ",
"viewer.radar.compacted": "セマンティックパスポートやMAPコントロールを覆わないよう、レーダーを縮小しました。",
"viewer.radar.cancelWaiting": "MAPスペースの確保待ちのセマンティックレーダーをキャンセル",
"viewer.radar.needsSpace": "セマンティックレーダーには表示可能なMAPスペースがさらに必要です",
"viewer.radar.viewport.full": "全体マップ",
"viewer.radar.viewport.width": "幅 {percent}%",
"viewer.radar.viewport.scale": "ビューポート {percent}%",
"viewer.nav.controls": "ダイアグラム表示コントロール",
"viewer.nav.route": "有向ルートをトレース",
"viewer.nav.route.title": "ルートをトレース (R)",
"viewer.nav.route.short": "パス",
"viewer.nav.radar": "セマンティックレーダーを開く",
"viewer.nav.radar.title": "セマンティックレーダー (M)",
"viewer.nav.radar.short": "マップ",
"viewer.nav.lens": "セマンティックレンズを開く",
"viewer.nav.lens.title": "セマンティックレンズ (L)",
"viewer.nav.lens.short": "レンズ",
"viewer.nav.find": "ノードを検索",
"viewer.nav.find.title": "ノードを検索 (/)",
"viewer.nav.guide": "ダイアグラムガイドを開く",
"viewer.nav.guide.title": "ダイアグラムガイド (?)",
"viewer.nav.zoomOut": "縮小",
"viewer.nav.zoomOut.title": "縮小 (-)",
"viewer.nav.reset": "ダイアグラム表示をリセット",
"viewer.nav.reset.title": "表示をリセット (0)",
"viewer.nav.read": "閲覧",
"viewer.nav.zoomIn": "拡大",
"viewer.nav.zoomIn.title": "拡大 (+)",
"viewer.nav.camera": "{hint}。ダイアグラム表示をリセット",
"viewer.nav.camera.title": "{semantic}{hint} · 表示をリセット (0)",
"viewer.nav.camera.semantic": "セマンティックカメラ有効 · ",
"viewer.nav.level.map": "マップ",
"viewer.nav.level.read": "閲覧",
"viewer.nav.level.full": "全体",
"viewer.nav.level.auto": "自動",
"viewer.nav.detail.map": "拡大すると関係ラベルとノードのコンテキストが表示されます",
"viewer.nav.detail.read": "さらに拡大するとタグと注釈が表示されます",
"viewer.nav.detail.full": "ダイアグラムの完全な詳細",
"viewer.intent.summary": "{label}。出向 {out} 件、入向 {in} 件{loops}。合計 {total} 件の接続。詳細はEnterを押してください。",
"viewer.intent.loops": "、自己ループ {count} 件",
"viewer.common.copied": "コピーしました",
"viewer.common.copyFailed": "コピーに失敗しました",
"viewer.common.copyLink": "リンクをコピー",
"viewer.common.clear": "クリア",
"viewer.common.close": "閉じる"
}
+440
View File
@@ -0,0 +1,440 @@
{
"page.title": "{title} 다이어그램",
"diagram.description.architecture": "Archify로 생성한 아키텍처 다이어그램입니다.",
"diagram.description.workflow": "Archify로 생성한 워크플로우 다이어그램입니다.",
"diagram.description.sequence": "Archify로 생성한 시퀀스 다이어그램입니다.",
"diagram.description.dataflow": "Archify로 생성한 데이터플로우 다이어그램입니다.",
"diagram.description.lifecycle": "Archify로 생성한 라이프사이클 다이어그램입니다.",
"node.focus": "{label} 포커스",
"node.focus.detail": "{label} 포커스, {detail}",
"node.context.architecture": "아키텍처 구성요소",
"node.context.workflow": "워크플로우 노드",
"node.context.sequence": "시퀀스 참가자",
"node.context.dataflow": "데이터플로우 노드",
"node.context.lifecycle": "라이프사이클 상태",
"legend.title": "범례",
"legend.architecture.frontend": "프런트엔드",
"legend.architecture.backend": "백엔드",
"legend.architecture.database": "데이터베이스",
"legend.architecture.cloud": "클라우드",
"legend.architecture.security": "보안",
"legend.architecture.messagebus": "메시지 버스",
"legend.architecture.external": "외부",
"legend.workflow.frontend": "사용자 UI",
"legend.workflow.backend": "Agent 로직",
"legend.workflow.security": "정책",
"legend.workflow.messagebus": "도구 실행",
"legend.workflow.database": "컨텍스트 / 추적",
"legend.workflow.cloud": "클라우드 서비스",
"legend.workflow.external": "외부 시스템",
"legend.sequence.emphasis": "주요 요청",
"legend.sequence.return": "반환",
"legend.sequence.security": "보안",
"legend.sequence.dashed": "비동기 추적",
"legend.sequence.default": "메시지",
"legend.dataflow.emphasis": "주요 데이터",
"legend.dataflow.security": "정책 / PII",
"legend.dataflow.dashed": "비동기 배치",
"legend.dataflow.database": "데이터 저장소",
"legend.dataflow.default": "데이터 흐름",
"legend.lifecycle.start": "초기 상태",
"legend.lifecycle.active": "활성",
"legend.lifecycle.waiting": "대기",
"legend.lifecycle.decision": "판단",
"legend.lifecycle.success": "성공",
"legend.lifecycle.failure": "실패 / 종료",
"legend.lifecycle.neutral": "중립 상태",
"legend.lifecycle.external": "외부 상태",
"legend.lifecycle.final": "종료 상태",
"viewer.kind.frontend": "프런트엔드",
"viewer.kind.backend": "백엔드",
"viewer.kind.database": "데이터베이스",
"viewer.kind.cloud": "클라우드",
"viewer.kind.security": "보안",
"viewer.kind.messagebus": "메시지 버스",
"viewer.kind.external": "외부",
"viewer.kind.neutral": "중립",
"viewer.kind.node": "노드",
"viewer.kind.start": "시작",
"viewer.kind.active": "활성",
"viewer.kind.waiting": "대기",
"viewer.kind.decision": "판단",
"viewer.kind.success": "성공",
"viewer.kind.failure": "실패",
"viewer.toolbar.actions": "다이어그램 동작",
"viewer.theme.toggle.title": "테마 전환 (T)",
"viewer.theme.toggle": "색상 테마 전환",
"viewer.theme.dark": "다크",
"viewer.theme.light": "라이트",
"viewer.preset.choose.title": "시각 스타일 선택 (S로 순환)",
"viewer.preset.choose": "시각 스타일 선택",
"viewer.preset.style": "스타일",
"viewer.preset.menu": "시각 스타일",
"viewer.preset.identity": "시각적 정체성",
"viewer.preset.cycles": "S로 순환",
"viewer.preset.classic": "클래식",
"viewer.preset.classic.short": "클래식",
"viewer.preset.classic.hint": "안정적인 기술 기본 스타일",
"viewer.preset.flow": "시그널 플로우",
"viewer.preset.flow.short": "플로우",
"viewer.preset.flow.hint": "동적 흐름을 강조하는 프레젠테이션",
"viewer.preset.blueprint": "블루프린트",
"viewer.preset.blueprint.hint": "엔지니어링 리뷰용",
"viewer.preset.editorial": "에디토리얼",
"viewer.preset.editorial.hint": "발행 및 출시 노트에 적합",
"viewer.preset.badge.signalFlow": "시그널 플로우",
"viewer.preset.badge.blueprint": "블루프린트 / 개정 01",
"viewer.preset.badge.editorial": "에디토리얼 / 현장 노트",
"viewer.preset.badge.editorialPlate": "ARCHIFY / 도판 04",
"viewer.preset.current": "현재 시각 스타일: {style}. 시각 스타일 선택",
"viewer.motion.live": "동적",
"viewer.motion.still": "정적",
"viewer.motion.pause": "동작 일시정지",
"viewer.motion.resume": "동작 재개",
"viewer.motion.reduced": "동작 최소화 설정으로 인해 동작이 일시정지되었습니다",
"viewer.motion.hidden": "페이지가 보이지 않는 동안 동작이 일시정지되었습니다",
"viewer.motion.yielding": "동작 일시정지; 현재 {owner}에 양보 중",
"viewer.motion.yielding.title": "실시간 미리보기 활성화 · {owner}에 양보 중",
"viewer.owner.route": "경로 탐색",
"viewer.owner.lens": "시맨틱 렌즈",
"viewer.owner.relationship": "관계 미리보기",
"viewer.owner.intent": "의도 추적",
"viewer.owner.focus": "시맨틱 포커스",
"viewer.owner.legend": "범례 미리보기",
"viewer.owner.reader": "읽는 이의 상호작용",
"viewer.present.enter": "프레젠테이션 모드 진입",
"viewer.present.enter.title": "프레젠테이션 모드 (F)",
"viewer.present.exit": "프레젠테이션 모드 종료",
"viewer.present.exit.title": "프레젠테이션 모드 종료 (F 또는 Escape)",
"viewer.present.present": "프레젠테이션",
"viewer.present.exit.label": "종료",
"viewer.export.button": "내보내기",
"viewer.export.button.title": "다이어그램 내보내기 (E)",
"viewer.export.diagram": "다이어그램 내보내기",
"viewer.export.menu": "내보내기",
"viewer.export.subtitle": "가볍고 깔끔한 출력물",
"viewer.export.share": "공유",
"viewer.export.shareCard": "공유 카드",
"viewer.export.routeShareCard": "경로 공유 카드",
"viewer.export.reachShareCard": "도달범위 공유 카드",
"viewer.export.copyDiagram": "다이어그램 복사",
"viewer.export.clipboardPng": "PNG를 클립보드로",
"viewer.export.raster": "래스터 이미지",
"viewer.export.image": "이미지",
"viewer.export.lossless": "무손실 이미지",
"viewer.export.compact": "압축 이미지",
"viewer.export.modern": "최신 이미지 포맷",
"viewer.export.vectorMotion": "벡터 및 동작",
"viewer.export.vectorMotion.heading": "벡터 및 동작",
"viewer.export.editable": "편집 가능한 벡터",
"viewer.export.motion6s": "6초 동작",
"viewer.export.unsupported": "이 브라우저에서 지원하지 않습니다",
"viewer.export.clipboardUnsupported": "이 브라우저는 이미지 클립보드 쓰기를 지원하지 않습니다",
"viewer.export.clipboardUnsupported.short": "이 브라우저는 이미지 클립보드 쓰기를 지원하지 않습니다.",
"viewer.export.motionUnavailable": "이 브라우저에서는 동작을 캡처할 수 없습니다",
"viewer.export.webmUnavailable": "이 브라우저에서는 WebM을 사용할 수 없습니다",
"viewer.export.failed": "내보내기 실패: {message}",
"viewer.export.unknownVariant": "알 수 없는 공유 카드 유형: {variant}",
"viewer.export.routeRequired": "경로 공유 카드를 내보내려면 먼저 경로를 추적하세요",
"viewer.export.reachRequired": "도달범위 공유 카드를 내보내려면 먼저 편집된 도달범위를 추적하세요",
"viewer.export.unknown": "알 수 없음",
"viewer.export.routeFailed": "경로 공유 카드 내보내기 실패: {message}",
"viewer.export.reachFailed": "도달범위 공유 카드 내보내기 실패: {message}",
"viewer.export.copyFailed": "복사 실패: {message}",
"viewer.export.copiedPng": "PNG를 클립보드에 복사했습니다",
"viewer.export.downloadedRoute": "경로 공유 카드를 다운로드했습니다",
"viewer.export.downloadedReach": "도달범위 공유 카드를 다운로드했습니다",
"viewer.export.downloadedWebm": "WebM을 다운로드했습니다",
"viewer.export.recording": "6초 분량 동작을 녹화 중…",
"viewer.export.card.routeSummary.one": "경로: {source} → {target} · 방향성 홉 {count}개",
"viewer.export.card.routeSummary.other": "경로: {source} → {target} · 방향성 홉 {count}개",
"viewer.export.card.reachSummary": "{origin}에서 시작한 편집된 {direction} · {nodes} · {links} · 최대 {hops}",
"viewer.export.card.node.one": "노드 {count}개",
"viewer.export.card.node.other": "노드 {count}개",
"viewer.export.card.link.one": "연결 {count}개",
"viewer.export.card.link.other": "연결 {count}개",
"viewer.export.card.hop.one": "홉 {count}개",
"viewer.export.card.hop.other": "홉 {count}개",
"viewer.export.card.routeBadge": "ARCHIFY · 경로 · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · {direction} 도달범위",
"viewer.export.direction.upstream": "업스트림",
"viewer.export.direction.downstream": "다운스트림",
"viewer.export.error.canvasUnavailable": "{label}에 캔버스를 사용할 수 없습니다",
"viewer.export.error.contextUnavailable": "{label}에 2D 캔버스 컨텍스트를 만들 수 없습니다",
"viewer.export.error.toBlobUnavailable": "{label}에서 canvas.toBlob을 사용할 수 없습니다",
"viewer.export.error.toBlobNull": "{label}의 canvas.toBlob이 데이터를 반환하지 않았습니다",
"viewer.export.error.variantsCombined": "공유 카드 유형을 함께 조합할 수 없습니다",
"viewer.export.error.viewerState": "공유 카드 내보내기가 임시 뷰어 상태를 제거하지 못했습니다",
"viewer.export.error.routeState": "경로 카드 내보내기가 해석된 경로를 안전하게 보존하지 못했습니다",
"viewer.export.error.reachState": "도달범위 카드 내보내기가 편집된 도달범위를 안전하게 보존하지 못했습니다",
"viewer.export.error.webmRequirements": "WebM 동작 내보내기는 추적 애니메이션과 브라우저의 MediaRecorder 지원이 필요합니다",
"viewer.export.error.mediaRecorder": "MediaRecorder 실패",
"viewer.export.error.emptyWebm": "MediaRecorder가 빈 WebM을 생성했습니다",
"viewer.export.error.webmBackground": "WebM 내보내기용 SVG 배경을 불러오지 못했습니다",
"viewer.guide.eyebrow": "다이어그램 가이드",
"viewer.guide.close": "다이어그램 가이드 닫기",
"viewer.guide.inspecting": "컴파일된 시맨틱 구조 검사 중",
"viewer.guide.actions": "다이어그램 탐색 동작",
"viewer.guide.find": "아무 노드나 찾기",
"viewer.guide.find.hint": "라벨, 역할, 유형, 고정 ID로 검색합니다.",
"viewer.guide.route": "경로 추적",
"viewer.guide.route.aria": "방향성 경로 추적",
"viewer.guide.route.hint": "두 시맨틱 노드가 편집된 방향으로 어떻게 연결되는지 확인합니다.",
"viewer.guide.map": "전체 시스템 보기",
"viewer.guide.map.hint": "실시간 뷰포트와 고정 노드가 있는 시맨틱 레이더를 엽니다.",
"viewer.guide.lens": "시맨틱 유형 비교",
"viewer.guide.lens.hint": "역할 수를 세고, 트래픽을 표시하고, 직접 편집된 연결을 비교합니다.",
"viewer.guide.present": "프레젠테이션 모드 진입",
"viewer.guide.present.hint": "내보내기는 그대로 두고 실시간 다이어그램에 뷰포트 전체를 할애합니다.",
"viewer.guide.shortcuts": "추가 키보드 단축키",
"viewer.guide.shortcut.export": "내보내기",
"viewer.guide.shortcut.theme": "테마",
"viewer.guide.shortcut.style": "스타일",
"viewer.guide.shortcut.reset": "초기화",
"viewer.guide.shortcut.zoomIn": "확대",
"viewer.guide.shortcut.zoomOut": "축소",
"viewer.guide.shortcut.close": "닫기",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "시맨틱 노드 {count}개",
"viewer.guide.fact.node.other": "시맨틱 노드 {count}개",
"viewer.guide.fact.relationship.one": "관계 {count}개",
"viewer.guide.fact.relationship.other": "관계 {count}개",
"viewer.guide.open": "다이어그램 가이드 열기",
"viewer.finder.title": "노드 찾기",
"viewer.finder.close": "노드 찾기 닫기",
"viewer.finder.placeholder": "라벨 또는 ID로 검색",
"viewer.finder.search": "다이어그램 노드 검색",
"viewer.finder.results": "다이어그램 노드",
"viewer.finder.empty": "일치하는 노드가 없습니다",
"viewer.finder.result.focus": "{label} 포커스",
"viewer.finder.result.routeStart": "{label}을(를) 경로 시작점으로 선택",
"viewer.finder.result.routeTarget": "{label}을(를) 경로 도착점으로 선택, {links}",
"viewer.finder.status.empty": "일치하는 노드가 없습니다",
"viewer.finder.status.count.one": "일치하는 노드 {count}개",
"viewer.finder.status.count.other": "일치하는 노드 {count}개",
"viewer.finder.noun.nodes": "노드",
"viewer.finder.link.one": "연결 {count}개",
"viewer.finder.link.other": "연결 {count}개",
"viewer.finder.result.focus.one": "{label} 포커스, 관련 연결 {count}개",
"viewer.finder.result.focus.other": "{label} 포커스, 관련 연결 {count}개",
"viewer.finder.status.filtered": "{noun} {available}개 중 {visible}개",
"viewer.finder.status.all": "{noun} {available}개",
"viewer.passport.eyebrow": "시맨틱 패스포트",
"viewer.passport.metadata": "노드 메타데이터",
"viewer.passport.evidence": "검증된 소스 근거",
"viewer.passport.verified": "검증된 소스",
"viewer.passport.verificationScope": "고정된 리비전 기준 로컬 Git으로 검증했습니다. 원격 접근 권한은 확인하지 않았습니다.",
"viewer.passport.reach": "편집된 도달범위",
"viewer.passport.reach.trace": "편집된 도달가능성 추적",
"viewer.passport.upstream": "업스트림",
"viewer.passport.downstream": "다운스트림",
"viewer.passport.upstream.trace": "업스트림 편집 도달가능성 추적",
"viewer.passport.downstream.trace": "다운스트림 편집 도달가능성 추적",
"viewer.passport.close": "시맨틱 패스포트 닫기",
"viewer.passport.copy": "링크 복사",
"viewer.passport.copy.focus": "포커스된 노드의 링크 복사",
"viewer.passport.relations": "관계",
"viewer.passport.relations.show": "연결된 관계 표시",
"viewer.passport.relations.hide": "연결된 관계 숨기기",
"viewer.passport.relations.list": "연결된 관계",
"viewer.passport.copyRelation": "관계 복사",
"viewer.passport.copyNode": "노드 복사",
"viewer.passport.copyPinned": "고정된 관계의 링크 복사",
"viewer.passport.copySource": "소스 노드의 링크 복사",
"viewer.passport.copy.focused.success": "포커스된 노드 링크를 복사했습니다",
"viewer.passport.copy.pinned.success": "고정된 관계 링크를 복사했습니다",
"viewer.passport.copy.focused.failed": "포커스된 노드 링크를 복사할 수 없습니다",
"viewer.passport.copy.pinned.failed": "고정된 관계 링크를 복사할 수 없습니다",
"viewer.passport.relationship.none": "연결된 관계가 없습니다",
"viewer.passport.relationship.count.one": "관계 {count}개",
"viewer.passport.relationship.count.other": "관계 {count}개",
"viewer.passport.relationship.show.one": "연결된 관계 {count}개 표시",
"viewer.passport.relationship.show.other": "연결된 관계 {count}개 표시",
"viewer.passport.relationship.summary": "출향 {out}개 · 입향 {in}개{loops}",
"viewer.passport.relationship.loops": " · 자기순환 {count}개",
"viewer.passport.relationship.explorer": "직접 관계 탐색기",
"viewer.passport.relationship.help": "방향키로 관계를 탐색하세요. Enter 또는 Space로 상세정보를 고정하고, Escape로 지웁니다.",
"viewer.passport.relationship.loopsBack": "되돌아옴",
"viewer.passport.relationship.connectsTo": "연결 대상",
"viewer.passport.relationship.connectsFrom": "연결 출처",
"viewer.passport.relationship.pinned": "고정된 관계 · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "{total}개 중 {index}번 관계 검사: {from}에서 {to}로, {label}. Enter로 상세정보 확인.",
"viewer.passport.relationship.group.out": "출향",
"viewer.passport.relationship.group.in": "입향",
"viewer.passport.relationship.group.loop": "자기순환",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "출 →",
"viewer.passport.relationship.direction.in": "← 입",
"viewer.passport.relationship.direction.loop": "순환",
"viewer.passport.sourceCount.one": "검증된 소스 참조 {count}개",
"viewer.passport.sourceCount.other": "검증된 소스 참조 {count}개",
"viewer.passport.sourceMarker": "출처",
"viewer.passport.beacon.one": "검증된 소스 {count}개; 이 노드를 포커스하여 확인",
"viewer.passport.beacon.other": "검증된 소스 {count}개; 이 노드를 포커스하여 확인",
"viewer.passport.repository.open": "검증된 저장소 리비전 {revision} 열기",
"viewer.passport.source.open": "리비전 {revision}의 검증된 소스 {path} 열기",
"viewer.passport.source.openLink": "열기 ↗",
"viewer.passport.reach.upstream.one": "업스트림 편집 노드 {count}개 추적",
"viewer.passport.reach.upstream.other": "업스트림 편집 노드 {count}개 추적",
"viewer.passport.reach.downstream.one": "다운스트림 편집 노드 {count}개 추적",
"viewer.passport.reach.downstream.other": "다운스트림 편집 노드 {count}개 추적",
"viewer.passport.reach.noUpstream": "업스트림 편집 노드가 없습니다",
"viewer.passport.reach.noDownstream": "다운스트림 편집 노드가 없습니다",
"viewer.passport.reach.status": "{direction} · 노드 {nodes}개 · 연결 {links}개 · 최대 {hops}홉",
"viewer.route.eyebrow": "경로 탐색",
"viewer.route.start": "시작 노드 선택",
"viewer.route.start.find": "시작점 찾기",
"viewer.route.start.find.aria": "경로 시작점 찾기",
"viewer.route.copy": "링크 복사",
"viewer.route.copy.aria": "추적된 경로의 링크 복사",
"viewer.route.clear": "지우기",
"viewer.route.clear.aria": "경로 탐색 지우기",
"viewer.route.traced": "추적된 경로",
"viewer.route.pickTwo": "다이어그램에서 시맨틱 노드 두 개를 선택하세요",
"viewer.route.pickOne": "다이어그램에서 시맨틱 노드 하나를 선택하세요",
"viewer.route.controls": "경로 이동 제어",
"viewer.route.previous": "이전 경로 위치",
"viewer.route.play": "경로 이동 재생",
"viewer.route.pause": "경로 이동 일시정지",
"viewer.route.replay": "경로 이동 다시 재생",
"viewer.route.next": "다음 경로 위치",
"viewer.route.journey": "이동 경로",
"viewer.route.pause.label": "일시정지",
"viewer.route.replay.label": "다시 재생",
"viewer.route.overview": "개요",
"viewer.route.overview.aria": "전체 경로 개요 표시",
"viewer.route.instructions": "출발지를 먼저 선택한 뒤 도착지를 선택하세요. 방향이 중요합니다.",
"viewer.route.destination": "{label}에서 출발할 도착지 선택",
"viewer.route.destination.find": "도착지 찾기",
"viewer.route.destination.find.aria": "도달 가능한 경로 도착지 찾기",
"viewer.route.differentDestination": "다른 도착지 선택",
"viewer.route.distinct": "경로에는 서로 다른 시맨틱 노드 두 개가 필요합니다.",
"viewer.route.unreachable": "{label}(으)로 가는 방향성 경로가 없습니다",
"viewer.route.unreachable.detail": "{source}에서 {target}에 도달할 수 없습니다. 강조 표시된 도착지를 선택하세요.",
"viewer.route.start.instructions": "출발지를 선택하세요. 다음 단계에서는 방향성으로 도달 가능한 도착지만 표시됩니다.",
"viewer.route.copy.success": "추적된 경로 링크를 복사했습니다",
"viewer.route.copy.failed": "경로 링크를 복사할 수 없습니다",
"viewer.route.position": "경로 위치 {total}개 중 {index}번: {label}",
"viewer.route.step": "{total}단계 중 {index}단계 · {phase} · {label}",
"viewer.route.motionRequired": "자동 이동에는 동적 모드가 필요합니다",
"viewer.route.trigger.clear": "추적된 경로 지우기",
"viewer.route.overview.status": "{nodes} · {hops} · 최단 편집 경로",
"viewer.route.overview.node.one": "노드 {count}개",
"viewer.route.overview.node.other": "노드 {count}개",
"viewer.route.overview.hop.one": "방향성 홉 {count}개",
"viewer.route.overview.hop.other": "방향성 홉 {count}개",
"viewer.route.phase.playing": "재생 중",
"viewer.route.phase.complete": "완료됨",
"viewer.route.phase.inspecting": "검사 중",
"viewer.route.destination.count.one": "방향성 도착지 {count}개 사용 가능. 강조 표시된 노드를 선택하세요.",
"viewer.route.destination.count.other": "방향성 도착지 {count}개 사용 가능. 강조 표시된 노드를 선택하세요.",
"viewer.route.noOutgoing": "여기서 시작하는 출향 경로가 없습니다. 지우고 다른 출발지를 선택하세요.",
"viewer.route.result.title": "{source}에서 {target}까지",
"viewer.route.finder.source.title": "경로 시작점 선택",
"viewer.route.finder.source.placeholder": "경로 출발지 검색",
"viewer.route.finder.source.empty": "일치하는 경로 출발지가 없습니다",
"viewer.route.finder.source.results": "경로를 시작할 수 있는 노드",
"viewer.route.finder.source.noun": "경로 출발지",
"viewer.route.finder.source.badge": "시작",
"viewer.route.finder.target.title": "{label}에서 출발할 도착지",
"viewer.route.finder.target.placeholder": "도달 가능한 도착지 검색",
"viewer.route.finder.target.empty": "일치하는 도달 가능한 도착지가 없습니다",
"viewer.route.finder.target.results": "도달 가능한 경로 도착지",
"viewer.route.finder.target.noun": "도달 가능한 도착지",
"viewer.route.hop.one": "홉 {count}개",
"viewer.route.hop.other": "홉 {count}개",
"viewer.lens.eyebrow": "시맨틱 렌즈",
"viewer.lens.title": "시스템 역할 비교",
"viewer.lens.close": "시맨틱 렌즈 닫기",
"viewer.lens.instruction": "시맨틱 유형을 최대 두 개까지 선택하세요. 하나를 선택하면 실제 트래픽을 표시하고, 두 개를 선택하면 직접 편집된 관계만 비교합니다.",
"viewer.lens.kinds": "시맨틱 유형",
"viewer.lens.choose": "유형을 선택해 해당 노드와 연결된 관계를 확인하세요.",
"viewer.lens.copy": "시맨틱 렌즈 링크 복사",
"viewer.lens.clear": "시맨틱 렌즈 지우기",
"viewer.lens.open": "시맨틱 렌즈 열기",
"viewer.lens.openActive": "활성 시맨틱 렌즈 열기",
"viewer.lens.legend": "시맨틱 범례",
"viewer.lens.legend.inspect.one": "{label} 검사, 노드 {count}개",
"viewer.lens.legend.inspect.other": "{label} 검사, 노드 {count}개",
"viewer.lens.kind.count.one": "{label}, 노드 {count}개",
"viewer.lens.kind.count.other": "{label}, 노드 {count}개",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · 직접 관계 {count}개",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · 직접 관계 {count}개",
"viewer.lens.single": "{nodes} · {relationships} · 연결된 주변 노드는 계속 표시됩니다",
"viewer.lens.node.one": "{label} 노드 {count}개",
"viewer.lens.node.other": "{label} 노드 {count}개",
"viewer.lens.relationship.one": "연결된 관계 {count}개",
"viewer.lens.relationship.other": "연결된 관계 {count}개",
"viewer.radar.title": "시맨틱 레이더",
"viewer.radar.building": "개요 구성 중",
"viewer.radar.openFull": "전체 시맨틱 레이더 열기",
"viewer.radar.open": "레이더 열기",
"viewer.radar.close": "시맨틱 레이더 닫기",
"viewer.radar.surface": "다이어그램 개요입니다. 노드를 클릭해 포커스하거나 방향키로 이동하세요.",
"viewer.radar.click": "노드 클릭",
"viewer.radar.drag": "드래그로 이동",
"viewer.radar.space": "시맨틱 레이더를 표시하려면 지도 공간이 더 필요합니다.",
"viewer.radar.nodes": "시맨틱 다이어그램 레이더 노드",
"viewer.radar.focus": "시맨틱 레이더에서 {label} 포커스",
"viewer.radar.status": "노드 {count}개 · {viewport}",
"viewer.radar.fullMap": "노드 {count}개 · 전체 지도",
"viewer.radar.compacted": "시맨틱 패스포트나 지도 컨트롤을 가리지 않도록 레이더를 축소했습니다.",
"viewer.radar.cancelWaiting": "지도 공간 확보를 기다리는 시맨틱 레이더 취소",
"viewer.radar.needsSpace": "시맨틱 레이더를 표시하려면 지도 공간이 더 필요합니다",
"viewer.radar.viewport.full": "전체 지도",
"viewer.radar.viewport.width": "너비 {percent}%",
"viewer.radar.viewport.scale": "뷰포트 {percent}%",
"viewer.nav.controls": "다이어그램 보기 제어",
"viewer.nav.route": "방향성 경로 추적",
"viewer.nav.route.title": "경로 추적 (R)",
"viewer.nav.route.short": "경로",
"viewer.nav.radar": "시맨틱 레이더 열기",
"viewer.nav.radar.title": "시맨틱 레이더 (M)",
"viewer.nav.radar.short": "지도",
"viewer.nav.lens": "시맨틱 렌즈 열기",
"viewer.nav.lens.title": "시맨틱 렌즈 (L)",
"viewer.nav.lens.short": "렌즈",
"viewer.nav.find": "노드 찾기",
"viewer.nav.find.title": "노드 찾기 (/)",
"viewer.nav.guide": "다이어그램 가이드 열기",
"viewer.nav.guide.title": "다이어그램 가이드 (?)",
"viewer.nav.zoomOut": "축소",
"viewer.nav.zoomOut.title": "축소 (-)",
"viewer.nav.reset": "다이어그램 보기 초기화",
"viewer.nav.reset.title": "보기 초기화 (0)",
"viewer.nav.read": "읽기",
"viewer.nav.zoomIn": "확대",
"viewer.nav.zoomIn.title": "확대 (+)",
"viewer.nav.camera": "{hint}. 다이어그램 보기 초기화",
"viewer.nav.camera.title": "{semantic}{hint} · 보기 초기화 (0)",
"viewer.nav.camera.semantic": "시맨틱 카메라 활성 · ",
"viewer.nav.level.map": "개요",
"viewer.nav.level.read": "읽기",
"viewer.nav.level.full": "전체",
"viewer.nav.level.auto": "자동",
"viewer.nav.detail.map": "확대하면 관계 라벨과 노드 컨텍스트가 표시됩니다",
"viewer.nav.detail.read": "다시 확대하면 태그와 주석이 표시됩니다",
"viewer.nav.detail.full": "다이어그램 전체 상세정보",
"viewer.intent.summary": "{label}. 출향 {out}개, 입향 {in}개{loops}. 연결 총 {total}개. Enter로 상세정보 확인.",
"viewer.intent.loops": ", 자기순환 {count}개",
"viewer.common.copied": "복사됨",
"viewer.common.copyFailed": "복사 실패",
"viewer.common.copyLink": "링크 복사",
"viewer.common.clear": "지우기",
"viewer.common.close": "닫기",
"viewer.export.svg.auto": "SVG · 자동",
"viewer.export.svg.auto.hint": "호스트 테마에 맞춤",
"viewer.export.svg.light": "SVG · 라이트",
"viewer.export.svg.light.hint": "항상 라이트 테마",
"viewer.export.svg.dark": "SVG · 다크",
"viewer.export.svg.dark.hint": "항상 다크 테마",
"viewer.guide.title": "이 시스템 살펴보기",
"viewer.focus.selectedNodes": "선택한 노드 {count}개",
"viewer.passport.move": "의미 정보 패널 이동. 드래그하거나 화살표 키를 사용하고, Home 키로 초기화하세요.",
"viewer.outline.title": "노드 목록",
"viewer.rail.show": "설명 및 목록",
"viewer.rail.controls": "측면 패널",
"viewer.rail.collapse": "측면 패널 접기",
"viewer.rail.bottom": "패널을 다이어그램 아래로 이동",
"viewer.rail.right": "패널을 다이어그램 옆으로 이동"
}
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "Diagrama {title}",
"diagram.description.architecture": "Um diagrama de arquitetura gerado pelo Archify.",
"diagram.description.workflow": "Um diagrama de fluxo de trabalho gerado pelo Archify.",
"diagram.description.sequence": "Um diagrama de sequência gerado pelo Archify.",
"diagram.description.dataflow": "Um diagrama de fluxo de dados gerado pelo Archify.",
"diagram.description.lifecycle": "Um diagrama de ciclo de vida gerado pelo Archify.",
"node.focus": "Focar {label}",
"node.focus.detail": "Focar {label}, {detail}",
"node.context.architecture": "Componente de arquitetura",
"node.context.workflow": "Nó de fluxo de trabalho",
"node.context.sequence": "Participante da sequência",
"node.context.dataflow": "Nó de fluxo de dados",
"node.context.lifecycle": "Estado do ciclo de vida",
"legend.title": "Legenda",
"legend.architecture.frontend": "Frontend",
"legend.architecture.backend": "Backend",
"legend.architecture.database": "Banco de dados",
"legend.architecture.cloud": "Nuvem",
"legend.architecture.security": "Segurança",
"legend.architecture.messagebus": "Barramento de mensagens",
"legend.architecture.external": "Externo",
"legend.workflow.frontend": "UI do usuário",
"legend.workflow.backend": "Lógica do agente",
"legend.workflow.security": "Política",
"legend.workflow.messagebus": "Ação de ferramenta",
"legend.workflow.database": "Contexto / rastreamento",
"legend.workflow.cloud": "Serviço de nuvem",
"legend.workflow.external": "Sistema externo",
"legend.sequence.emphasis": "solicitação principal",
"legend.sequence.return": "retorno",
"legend.sequence.security": "segurança",
"legend.sequence.dashed": "rastreamento assíncrono",
"legend.sequence.default": "mensagem",
"legend.dataflow.emphasis": "dado principal",
"legend.dataflow.security": "política / PII",
"legend.dataflow.dashed": "lote assíncrono",
"legend.dataflow.database": "armazenamento de dados",
"legend.dataflow.default": "fluxo de dados",
"legend.lifecycle.start": "estado inicial",
"legend.lifecycle.active": "ativo",
"legend.lifecycle.waiting": "aguardando",
"legend.lifecycle.decision": "decisão",
"legend.lifecycle.success": "sucesso",
"legend.lifecycle.failure": "falha / saída",
"legend.lifecycle.neutral": "neutro",
"legend.lifecycle.external": "externo",
"legend.lifecycle.final": "estado final",
"viewer.kind.frontend": "Frontend",
"viewer.kind.backend": "Backend",
"viewer.kind.database": "Banco de dados",
"viewer.kind.cloud": "Nuvem",
"viewer.kind.security": "Segurança",
"viewer.kind.messagebus": "Barramento de mensagens",
"viewer.kind.external": "Externo",
"viewer.kind.neutral": "Neutro",
"viewer.kind.node": "Nó",
"viewer.kind.start": "Início",
"viewer.kind.active": "Ativo",
"viewer.kind.waiting": "Aguardando",
"viewer.kind.decision": "Decisão",
"viewer.kind.success": "Sucesso",
"viewer.kind.failure": "Falha",
"viewer.toolbar.actions": "Ações do diagrama",
"viewer.theme.toggle.title": "Alternar tema (T)",
"viewer.theme.toggle": "Alternar tema de cores",
"viewer.theme.dark": "Escuro",
"viewer.theme.light": "Claro",
"viewer.preset.choose.title": "Escolher estilo visual (S alterna)",
"viewer.preset.choose": "Escolher estilo visual",
"viewer.preset.style": "Estilo",
"viewer.preset.menu": "Estilo visual",
"viewer.preset.identity": "Identidade visual",
"viewer.preset.cycles": "S alterna",
"viewer.preset.classic": "Clássico",
"viewer.preset.classic.short": "Clássico",
"viewer.preset.classic.hint": "Padrão técnico estável",
"viewer.preset.flow": "Fluxo de Sinal",
"viewer.preset.flow.short": "Fluxo",
"viewer.preset.flow.hint": "Apresentação com foco em movimento",
"viewer.preset.blueprint": "Planta",
"viewer.preset.blueprint.hint": "Revisão de engenharia",
"viewer.preset.editorial": "Editorial",
"viewer.preset.editorial.hint": "Notas de publicação e lançamento",
"viewer.preset.badge.signalFlow": "FLUXO DE SINAL",
"viewer.preset.badge.blueprint": "PLANTA / REV 01",
"viewer.preset.badge.editorial": "EDITORIAL / NOTA DE CAMPO",
"viewer.preset.badge.editorialPlate": "ARCHIFY / PLACA 04",
"viewer.preset.current": "Estilo visual: {style}. Escolher estilo visual",
"viewer.motion.live": "Ao vivo",
"viewer.motion.still": "Estático",
"viewer.motion.pause": "Pausar movimento",
"viewer.motion.resume": "Retomar movimento",
"viewer.motion.reduced": "Movimento pausado pela preferência de movimento reduzido",
"viewer.motion.hidden": "Movimento pausado enquanto esta página está oculta",
"viewer.motion.yielding": "Pausar movimento; cedendo atualmente a {owner}",
"viewer.motion.yielding.title": "Pré-visualização ao vivo ativada · cedendo a {owner}",
"viewer.owner.route": "Sonda de Rota",
"viewer.owner.lens": "Lente Semântica",
"viewer.owner.relationship": "Pré-visualização de Relação",
"viewer.owner.intent": "Rastreamento de Intenção",
"viewer.owner.focus": "foco semântico",
"viewer.owner.legend": "pré-visualização da legenda",
"viewer.owner.reader": "interação do leitor",
"viewer.present.enter": "Entrar no palco de apresentação",
"viewer.present.enter.title": "Palco de apresentação (F)",
"viewer.present.exit": "Sair do palco de apresentação",
"viewer.present.exit.title": "Sair do palco de apresentação (F ou Escape)",
"viewer.present.present": "Apresentar",
"viewer.present.exit.label": "Sair",
"viewer.export.button": "Exportar",
"viewer.export.button.title": "Exportar diagrama (E)",
"viewer.export.diagram": "Exportar diagrama",
"viewer.export.menu": "Exportar",
"viewer.export.subtitle": "Saídas portáteis e limpas",
"viewer.export.share": "Compartilhar",
"viewer.export.shareCard": "Cartão de Compartilhamento",
"viewer.export.routeShareCard": "Cartão de Compartilhamento de Rota",
"viewer.export.reachShareCard": "Cartão de Compartilhamento de Alcance",
"viewer.export.copyDiagram": "Copiar diagrama",
"viewer.export.clipboardPng": "PNG para a área de transferência",
"viewer.export.raster": "Imagens raster",
"viewer.export.image": "Imagem",
"viewer.export.lossless": "Imagem sem perdas",
"viewer.export.compact": "Imagem compacta",
"viewer.export.modern": "Imagem moderna",
"viewer.export.vectorMotion": "Vetor e movimento",
"viewer.export.vectorMotion.heading": "Vetor e movimento",
"viewer.export.editable": "Vetor editável",
"viewer.export.motion6s": "Movimento de 6s",
"viewer.export.unsupported": "Não suportado por este navegador",
"viewer.export.clipboardUnsupported": "Este navegador não suporta gravação de imagem na área de transferência",
"viewer.export.clipboardUnsupported.short": "Gravação de imagem na área de transferência não suportada neste navegador.",
"viewer.export.motionUnavailable": "Captura de movimento indisponível neste navegador",
"viewer.export.webmUnavailable": "WebM indisponível neste navegador",
"viewer.export.failed": "Falha na exportação: {message}",
"viewer.export.unknownVariant": "Variante de Cartão de Compartilhamento desconhecida: {variant}",
"viewer.export.routeRequired": "Trace uma rota antes de exportar um Cartão de Compartilhamento de Rota",
"viewer.export.reachRequired": "Trace o alcance definido antes de exportar um Cartão de Compartilhamento de Alcance",
"viewer.export.unknown": "desconhecido",
"viewer.export.routeFailed": "Falha ao exportar o Cartão de Compartilhamento de Rota: {message}",
"viewer.export.reachFailed": "Falha ao exportar o Cartão de Compartilhamento de Alcance: {message}",
"viewer.export.copyFailed": "Falha ao copiar: {message}",
"viewer.export.copiedPng": "PNG copiado para a área de transferência",
"viewer.export.downloadedRoute": "Cartão de Compartilhamento de Rota baixado",
"viewer.export.downloadedReach": "Cartão de Compartilhamento de Alcance baixado",
"viewer.export.downloadedWebm": "WebM baixado",
"viewer.export.recording": "Gravando 6 segundos de movimento…",
"viewer.export.card.routeSummary.one": "Rota: {source} → {target} · {count} salto direcionado",
"viewer.export.card.routeSummary.other": "Rota: {source} → {target} · {count} saltos direcionados",
"viewer.export.card.reachSummary": "{direction} definido a partir de {origin} · {nodes} · {links} · máx {hops}",
"viewer.export.card.node.one": "{count} nó",
"viewer.export.card.node.other": "{count} nós",
"viewer.export.card.link.one": "{count} conexão",
"viewer.export.card.link.other": "{count} conexões",
"viewer.export.card.hop.one": "{count} salto",
"viewer.export.card.hop.other": "{count} saltos",
"viewer.export.card.routeBadge": "ARCHIFY · ROTA · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · ALCANCE {direction}",
"viewer.export.direction.upstream": "Upstream",
"viewer.export.direction.downstream": "Downstream",
"viewer.export.error.canvasUnavailable": "Canvas indisponível para {label}",
"viewer.export.error.contextUnavailable": "Contexto de canvas 2D indisponível para {label}",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob indisponível para {label}",
"viewer.export.error.toBlobNull": "canvas.toBlob não retornou dados para {label}",
"viewer.export.error.variantsCombined": "As variantes do Cartão de Compartilhamento não podem ser combinadas",
"viewer.export.error.viewerState": "A exportação do Cartão de Compartilhamento não conseguiu remover o estado temporário do visualizador",
"viewer.export.error.routeState": "A exportação do Cartão de Rota não conseguiu preservar a rota resolvida com segurança",
"viewer.export.error.reachState": "A exportação do Cartão de Alcance não conseguiu preservar o alcance definido com segurança",
"viewer.export.error.webmRequirements": "A exportação de movimento em WebM requer uma animação de rastreamento e suporte a MediaRecorder no navegador",
"viewer.export.error.mediaRecorder": "Falha no MediaRecorder",
"viewer.export.error.emptyWebm": "O MediaRecorder produziu um WebM vazio",
"viewer.export.error.webmBackground": "Não foi possível carregar o plano de fundo SVG para a exportação em WebM",
"viewer.guide.eyebrow": "Guia do diagrama",
"viewer.guide.close": "Fechar guia do diagrama",
"viewer.guide.inspecting": "Inspecionando semântica compilada",
"viewer.guide.actions": "Ações de exploração do diagrama",
"viewer.guide.find": "Encontrar qualquer nó",
"viewer.guide.find.hint": "Pesquise rótulos, responsabilidades, tipos e IDs estáveis.",
"viewer.guide.route": "Traçar uma rota",
"viewer.guide.route.aria": "Traçar uma rota direcionada",
"viewer.guide.route.hint": "Descubra como dois nós semânticos se conectam na direção definida.",
"viewer.guide.map": "Ver o sistema inteiro",
"viewer.guide.map.hint": "Abra o Radar Semântico com uma janela de visualização ativa e nós estáveis.",
"viewer.guide.lens": "Comparar tipos semânticos",
"viewer.guide.lens.hint": "Conte papéis, revele seu tráfego e compare links diretos definidos.",
"viewer.guide.present": "Entrar no Palco de Apresentação",
"viewer.guide.present.hint": "Dê ao diagrama ao vivo a janela de visualização inteira sem alterar a exportação.",
"viewer.guide.shortcuts": "Atalhos de teclado adicionais",
"viewer.guide.shortcut.export": "Exportar",
"viewer.guide.shortcut.theme": "Tema",
"viewer.guide.shortcut.style": "Estilo",
"viewer.guide.shortcut.reset": "Redefinir",
"viewer.guide.shortcut.zoomIn": "Ampliar",
"viewer.guide.shortcut.zoomOut": "Reduzir",
"viewer.guide.shortcut.close": "Fechar",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} nó semântico",
"viewer.guide.fact.node.other": "{count} nós semânticos",
"viewer.guide.fact.relationship.one": "{count} relação",
"viewer.guide.fact.relationship.other": "{count} relações",
"viewer.guide.open": "Abrir guia do diagrama",
"viewer.finder.title": "Encontrar um nó",
"viewer.finder.close": "Fechar buscador de nós",
"viewer.finder.placeholder": "Pesquisar rótulos ou IDs",
"viewer.finder.search": "Pesquisar nós do diagrama",
"viewer.finder.results": "Nós do diagrama",
"viewer.finder.empty": "Nenhum nó correspondente",
"viewer.finder.result.focus": "Focar {label}",
"viewer.finder.result.routeStart": "Escolher {label} como início da rota",
"viewer.finder.result.routeTarget": "Escolher {label} como destino da rota, {links}",
"viewer.finder.status.empty": "Nenhum nó correspondente",
"viewer.finder.status.count.one": "{count} nó correspondente",
"viewer.finder.status.count.other": "{count} nós correspondentes",
"viewer.finder.noun.nodes": "nós",
"viewer.finder.link.one": "{count} conexão",
"viewer.finder.link.other": "{count} conexões",
"viewer.finder.result.focus.one": "Focar {label}, {count} conexão relacionada",
"viewer.finder.result.focus.other": "Focar {label}, {count} conexões relacionadas",
"viewer.finder.status.filtered": "{visible} de {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Passaporte semântico",
"viewer.passport.metadata": "Metadados do nó",
"viewer.passport.evidence": "Evidência de origem verificada",
"viewer.passport.verified": "Origem verificada",
"viewer.passport.verificationScope": "Verificado com o Git local na revisão fixada. O acesso remoto não foi verificado.",
"viewer.passport.reach": "Alcance definido",
"viewer.passport.reach.trace": "Traçar alcançabilidade definida",
"viewer.passport.upstream": "Upstream",
"viewer.passport.downstream": "Downstream",
"viewer.passport.upstream.trace": "Traçar alcançabilidade definida upstream",
"viewer.passport.downstream.trace": "Traçar alcançabilidade definida downstream",
"viewer.passport.close": "Fechar passaporte semântico",
"viewer.passport.copy": "Copiar link",
"viewer.passport.copy.focus": "Copiar link do nó em foco",
"viewer.passport.relations": "Relações",
"viewer.passport.relations.show": "Mostrar relações conectadas",
"viewer.passport.relations.hide": "Ocultar relações conectadas",
"viewer.passport.relations.list": "Relações conectadas",
"viewer.passport.copyRelation": "Copiar relação",
"viewer.passport.copyNode": "Copiar nó",
"viewer.passport.copyPinned": "Copiar link da relação fixada",
"viewer.passport.copySource": "Copiar link do nó de origem",
"viewer.passport.copy.focused.success": "Link do nó em foco copiado",
"viewer.passport.copy.pinned.success": "Link da relação fixada copiado",
"viewer.passport.copy.focused.failed": "Não foi possível copiar o link do nó em foco",
"viewer.passport.copy.pinned.failed": "Não foi possível copiar o link da relação fixada",
"viewer.passport.relationship.none": "Nenhuma relação conectada",
"viewer.passport.relationship.count.one": "{count} relação",
"viewer.passport.relationship.count.other": "{count} relações",
"viewer.passport.relationship.show.one": "Mostrar {count} relação conectada",
"viewer.passport.relationship.show.other": "Mostrar {count} relações conectadas",
"viewer.passport.relationship.summary": "{out} de saída · {in} de entrada{loops}",
"viewer.passport.relationship.loops": " · {count} laço",
"viewer.passport.relationship.explorer": "Explorador de relações diretas",
"viewer.passport.relationship.help": "Use as setas para explorar relações. Pressione Enter ou Espaço para fixar detalhes; Escape limpa.",
"viewer.passport.relationship.loopsBack": "retorna em laço",
"viewer.passport.relationship.connectsTo": "conecta-se a",
"viewer.passport.relationship.connectsFrom": "conecta-se de",
"viewer.passport.relationship.pinned": "Relação fixada · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Inspecionar relação {index} de {total}: {from} para {to}, {label}. Pressione Enter para detalhes.",
"viewer.passport.relationship.group.out": "Saída",
"viewer.passport.relationship.group.in": "Entrada",
"viewer.passport.relationship.group.loop": "Laços próprios",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "SAÍDA →",
"viewer.passport.relationship.direction.in": "← ENTRADA",
"viewer.passport.relationship.direction.loop": "LAÇO",
"viewer.passport.sourceCount.one": "{count} referência de origem verificada",
"viewer.passport.sourceCount.other": "{count} referências de origem verificadas",
"viewer.passport.sourceMarker": "ORIG",
"viewer.passport.beacon.one": "{count} origem verificada; foque este nó para inspecionar",
"viewer.passport.beacon.other": "{count} origens verificadas; foque este nó para inspecionar",
"viewer.passport.repository.open": "Abrir revisão verificada do repositório {revision}",
"viewer.passport.source.open": "Abrir origem verificada {path} na revisão {revision}",
"viewer.passport.source.openLink": "Abrir ↗",
"viewer.passport.reach.upstream.one": "Traçar {count} nó definido upstream",
"viewer.passport.reach.upstream.other": "Traçar {count} nós definidos upstream",
"viewer.passport.reach.downstream.one": "Traçar {count} nó definido downstream",
"viewer.passport.reach.downstream.other": "Traçar {count} nós definidos downstream",
"viewer.passport.reach.noUpstream": "Nenhum nó definido upstream",
"viewer.passport.reach.noDownstream": "Nenhum nó definido downstream",
"viewer.passport.reach.status": "{direction} · {nodes} nós · {links} conexões · máx {hops} saltos",
"viewer.route.eyebrow": "Sonda de rota",
"viewer.route.start": "Escolher um nó inicial",
"viewer.route.start.find": "Encontrar início",
"viewer.route.start.find.aria": "Encontrar um início de rota",
"viewer.route.copy": "Copiar link",
"viewer.route.copy.aria": "Copiar link da rota traçada",
"viewer.route.clear": "Limpar",
"viewer.route.clear.aria": "Limpar sonda de rota",
"viewer.route.traced": "Rota traçada",
"viewer.route.pickTwo": "Escolha dois nós semânticos no diagrama",
"viewer.route.pickOne": "Escolha um nó semântico no diagrama",
"viewer.route.controls": "Controles da jornada de rota",
"viewer.route.previous": "Posição anterior da rota",
"viewer.route.play": "Reproduzir jornada de rota",
"viewer.route.pause": "Pausar jornada de rota",
"viewer.route.replay": "Reproduzir jornada de rota novamente",
"viewer.route.next": "Próxima posição da rota",
"viewer.route.journey": "Jornada",
"viewer.route.pause.label": "Pausar",
"viewer.route.replay.label": "Repetir",
"viewer.route.overview": "Visão geral",
"viewer.route.overview.aria": "Mostrar visão geral completa da rota",
"viewer.route.instructions": "Escolha a origem e depois o destino. A direção importa.",
"viewer.route.destination": "Escolher um destino a partir de {label}",
"viewer.route.destination.find": "Encontrar destino",
"viewer.route.destination.find.aria": "Encontrar um destino de rota alcançável",
"viewer.route.differentDestination": "Escolher um destino diferente",
"viewer.route.distinct": "Uma rota precisa de dois nós semânticos distintos.",
"viewer.route.unreachable": "Nenhuma rota direcionada para {label}",
"viewer.route.unreachable.detail": "{target} não é alcançável a partir de {source}. Escolha um destino destacado.",
"viewer.route.start.instructions": "Selecione a origem. O próximo passo revelará apenas destinos direcionados.",
"viewer.route.copy.success": "Link da rota traçada copiado",
"viewer.route.copy.failed": "Não foi possível copiar o link da rota traçada",
"viewer.route.position": "Posição {index} de {total} na rota: {label}",
"viewer.route.step": "Passo {index} de {total} · {phase} · {label}",
"viewer.route.motionRequired": "A jornada automática requer movimento Ao vivo",
"viewer.route.trigger.clear": "Limpar rota traçada",
"viewer.route.overview.status": "{nodes} · {hops} · rota mais curta definida",
"viewer.route.overview.node.one": "{count} nó",
"viewer.route.overview.node.other": "{count} nós",
"viewer.route.overview.hop.one": "{count} salto direcionado",
"viewer.route.overview.hop.other": "{count} saltos direcionados",
"viewer.route.phase.playing": "Reproduzindo",
"viewer.route.phase.complete": "Concluído",
"viewer.route.phase.inspecting": "Inspecionando",
"viewer.route.destination.count.one": "{count} destino direcionado disponível. Escolha um nó destacado.",
"viewer.route.destination.count.other": "{count} destinos direcionados disponíveis. Escolha um nó destacado.",
"viewer.route.noOutgoing": "Nenhuma rota de saída começa aqui. Limpe e escolha outra origem.",
"viewer.route.result.title": "{source} até {target}",
"viewer.route.finder.source.title": "Escolher início da rota",
"viewer.route.finder.source.placeholder": "Pesquisar origens de rota",
"viewer.route.finder.source.empty": "Nenhuma origem de rota correspondente",
"viewer.route.finder.source.results": "Nós que podem iniciar uma rota",
"viewer.route.finder.source.noun": "origens de rota",
"viewer.route.finder.source.badge": "início",
"viewer.route.finder.target.title": "Destino a partir de {label}",
"viewer.route.finder.target.placeholder": "Pesquisar destinos alcançáveis",
"viewer.route.finder.target.empty": "Nenhum destino alcançável correspondente",
"viewer.route.finder.target.results": "Destinos de rota alcançáveis",
"viewer.route.finder.target.noun": "destinos alcançáveis",
"viewer.route.hop.one": "{count} salto",
"viewer.route.hop.other": "{count} saltos",
"viewer.lens.eyebrow": "Lente semântica",
"viewer.lens.title": "Comparar papéis do sistema",
"viewer.lens.close": "Fechar lente semântica",
"viewer.lens.instruction": "Escolha até dois tipos semânticos. Um revela seu tráfego real; dois comparam apenas relações diretas definidas.",
"viewer.lens.kinds": "Tipos semânticos",
"viewer.lens.choose": "Escolha um tipo para inspecionar seus nós e relações que o tocam.",
"viewer.lens.copy": "Copiar link da lente semântica",
"viewer.lens.clear": "Limpar lente semântica",
"viewer.lens.open": "Abrir lente semântica",
"viewer.lens.openActive": "Abrir lente semântica ativa",
"viewer.lens.legend": "Legenda semântica",
"viewer.lens.legend.inspect.one": "Inspecionar {label}, {count} nó",
"viewer.lens.legend.inspect.other": "Inspecionar {label}, {count} nós",
"viewer.lens.kind.count.one": "{label}, {count} nó",
"viewer.lens.kind.count.other": "{label}, {count} nós",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relação direta",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} relações diretas",
"viewer.lens.single": "{nodes} · {relationships} · nós conectados permanecem visíveis",
"viewer.lens.node.one": "{count} nó {label}",
"viewer.lens.node.other": "{count} nós {label}",
"viewer.lens.relationship.one": "{count} relação que toca",
"viewer.lens.relationship.other": "{count} relações que tocam",
"viewer.radar.title": "Radar semântico",
"viewer.radar.building": "Construindo visão geral",
"viewer.radar.openFull": "Abrir radar semântico completo",
"viewer.radar.open": "Abrir radar",
"viewer.radar.close": "Fechar radar semântico",
"viewer.radar.surface": "Visão geral do diagrama. Clique em um nó para focá-lo, ou use as setas para navegar.",
"viewer.radar.click": "Clicar no nó",
"viewer.radar.drag": "Arrastar para navegar",
"viewer.radar.space": "O radar semântico precisa de mais espaço no MAPA.",
"viewer.radar.nodes": "Nós do radar do diagrama semântico",
"viewer.radar.focus": "Focar {label} a partir do Radar Semântico",
"viewer.radar.status": "{count} nós · {viewport}",
"viewer.radar.fullMap": "{count} nós · mapa completo",
"viewer.radar.compacted": "Radar compactado para não cobrir o Passaporte Semântico ou os controles do MAPA.",
"viewer.radar.cancelWaiting": "Cancelar radar semântico aguardando mais espaço no MAPA",
"viewer.radar.needsSpace": "O radar semântico precisa de mais espaço visível no MAPA",
"viewer.radar.viewport.full": "mapa completo",
"viewer.radar.viewport.width": "{percent}% de largura",
"viewer.radar.viewport.scale": "{percent}% da janela de visualização",
"viewer.nav.controls": "Controles de visualização do diagrama",
"viewer.nav.route": "Traçar uma rota direcionada",
"viewer.nav.route.title": "Traçar rota (R)",
"viewer.nav.route.short": "ROTA",
"viewer.nav.radar": "Abrir radar semântico",
"viewer.nav.radar.title": "Radar semântico (M)",
"viewer.nav.radar.short": "MAPA",
"viewer.nav.lens": "Abrir lente semântica",
"viewer.nav.lens.title": "Lente semântica (L)",
"viewer.nav.lens.short": "LENTE",
"viewer.nav.find": "Encontrar um nó",
"viewer.nav.find.title": "Encontrar um nó (/)",
"viewer.nav.guide": "Abrir guia do diagrama",
"viewer.nav.guide.title": "Guia do diagrama (?)",
"viewer.nav.zoomOut": "Reduzir",
"viewer.nav.zoomOut.title": "Reduzir (-)",
"viewer.nav.reset": "Redefinir visualização do diagrama",
"viewer.nav.reset.title": "Redefinir visualização (0)",
"viewer.nav.read": "LEITURA",
"viewer.nav.zoomIn": "Ampliar",
"viewer.nav.zoomIn.title": "Ampliar (+)",
"viewer.nav.camera": "{hint}. Redefinir visualização do diagrama",
"viewer.nav.camera.title": "{semantic}{hint} · redefinir visualização (0)",
"viewer.nav.camera.semantic": "Câmera semântica ativa · ",
"viewer.nav.level.map": "MAPA",
"viewer.nav.level.read": "LEITURA",
"viewer.nav.level.full": "COMPLETO",
"viewer.nav.level.auto": "AUTO",
"viewer.nav.detail.map": "Amplie para revelar rótulos de relações e contexto do nó",
"viewer.nav.detail.read": "Amplie novamente para revelar tags e anotações",
"viewer.nav.detail.full": "Detalhe completo do diagrama",
"viewer.intent.summary": "{label}. {out} de saída, {in} de entrada{loops}. {total} conexões. Pressione Enter para detalhes.",
"viewer.intent.loops": ", {count} laço próprio",
"viewer.common.copied": "Copiado",
"viewer.common.copyFailed": "Falha ao copiar",
"viewer.common.copyLink": "Copiar link",
"viewer.common.clear": "Limpar",
"viewer.common.close": "Fechar"
}
+425
View File
@@ -0,0 +1,425 @@
{
"page.title": "{title} — диаграмма",
"diagram.description.architecture": "Диаграмма архитектуры, созданная в Archify.",
"diagram.description.workflow": "Диаграмма рабочего процесса, созданная в Archify.",
"diagram.description.sequence": "Диаграмма последовательности, созданная в Archify.",
"diagram.description.dataflow": "Диаграмма потока данных, созданная в Archify.",
"diagram.description.lifecycle": "Диаграмма жизненного цикла, созданная в Archify.",
"node.focus": "Фокус: {label}",
"node.focus.detail": "Фокус: {label}, {detail}",
"node.context.architecture": "Компонент архитектуры",
"node.context.workflow": "Узел рабочего процесса",
"node.context.sequence": "Участник последовательности",
"node.context.dataflow": "Узел потока данных",
"node.context.lifecycle": "Состояние жизненного цикла",
"legend.title": "Легенда",
"legend.architecture.frontend": "Фронтенд",
"legend.architecture.backend": "Бэкенд",
"legend.architecture.database": "База данных",
"legend.architecture.cloud": "Облако",
"legend.architecture.security": "Безопасность",
"legend.architecture.messagebus": "Шина сообщений",
"legend.architecture.external": "Внешний",
"legend.workflow.frontend": "Интерфейс пользователя",
"legend.workflow.backend": "Логика агента",
"legend.workflow.security": "Политика",
"legend.workflow.messagebus": "Действие инструмента",
"legend.workflow.database": "Контекст / трассировка",
"legend.workflow.cloud": "Облачный сервис",
"legend.workflow.external": "Внешняя система",
"legend.sequence.emphasis": "основной запрос",
"legend.sequence.return": "возврат",
"legend.sequence.security": "безопасность",
"legend.sequence.dashed": "асинхронная трассировка",
"legend.sequence.default": "сообщение",
"legend.dataflow.emphasis": "основные данные",
"legend.dataflow.security": "политика / ПДн",
"legend.dataflow.dashed": "асинхронный пакет",
"legend.dataflow.database": "хранилище данных",
"legend.dataflow.default": "поток данных",
"legend.lifecycle.start": "начальное состояние",
"legend.lifecycle.active": "активно",
"legend.lifecycle.waiting": "ожидание",
"legend.lifecycle.decision": "решение",
"legend.lifecycle.success": "успех",
"legend.lifecycle.failure": "ошибка / выход",
"legend.lifecycle.neutral": "нейтральное",
"legend.lifecycle.external": "внешнее",
"legend.lifecycle.final": "конечное состояние",
"viewer.kind.frontend": "Фронтенд",
"viewer.kind.backend": "Бэкенд",
"viewer.kind.database": "База данных",
"viewer.kind.cloud": "Облако",
"viewer.kind.security": "Безопасность",
"viewer.kind.messagebus": "Шина сообщений",
"viewer.kind.external": "Внешний",
"viewer.kind.neutral": "Нейтральный",
"viewer.kind.node": "Узел",
"viewer.kind.start": "Начало",
"viewer.kind.active": "Активно",
"viewer.kind.waiting": "Ожидание",
"viewer.kind.decision": "Решение",
"viewer.kind.success": "Успех",
"viewer.kind.failure": "Ошибка",
"viewer.toolbar.actions": "Действия с диаграммой",
"viewer.theme.toggle.title": "Переключить тему (T)",
"viewer.theme.toggle": "Переключить цветовую тему",
"viewer.theme.dark": "Тёмная",
"viewer.theme.light": "Светлая",
"viewer.preset.choose.title": "Выбрать визуальный стиль (S — переключение)",
"viewer.preset.choose": "Выбрать визуальный стиль",
"viewer.preset.style": "Стиль",
"viewer.preset.menu": "Визуальный стиль",
"viewer.preset.identity": "Визуальная идентичность",
"viewer.preset.cycles": "S — переключение",
"viewer.preset.classic": "Классический",
"viewer.preset.classic.short": "Классика",
"viewer.preset.classic.hint": "Стабильный технический стиль по умолчанию",
"viewer.preset.flow": "Поток сигналов",
"viewer.preset.flow.short": "Поток",
"viewer.preset.flow.hint": "Презентация с акцентом на анимацию",
"viewer.preset.blueprint": "Чертёж",
"viewer.preset.blueprint.hint": "Для инженерного обзора",
"viewer.preset.editorial": "Редакционный",
"viewer.preset.editorial.hint": "Для публикаций и анонсов",
"viewer.preset.badge.signalFlow": "ПОТОК СИГНАЛОВ",
"viewer.preset.badge.blueprint": "ЧЕРТЁЖ / РЕД. 01",
"viewer.preset.badge.editorial": "РЕДАКЦИЯ / ЗАМЕТКА",
"viewer.preset.badge.editorialPlate": "ARCHIFY / ПЛАСТИНА 04",
"viewer.preset.current": "Визуальный стиль: {style}. Выбрать визуальный стиль",
"viewer.motion.live": "Динамика",
"viewer.motion.still": "Статика",
"viewer.motion.pause": "Приостановить анимацию",
"viewer.motion.resume": "Возобновить анимацию",
"viewer.motion.reduced": "Анимация приостановлена из-за настройки сниженного движения",
"viewer.motion.hidden": "Анимация приостановлена, пока страница скрыта",
"viewer.motion.yielding": "Приостановить анимацию; сейчас уступает место: {owner}",
"viewer.motion.yielding.title": "Динамический просмотр включён · уступает место: {owner}",
"viewer.owner.route": "Зонд маршрута",
"viewer.owner.lens": "Семантическая линза",
"viewer.owner.relationship": "Предпросмотр связи",
"viewer.owner.intent": "Трассировка намерения",
"viewer.owner.focus": "семантический фокус",
"viewer.owner.legend": "предпросмотр легенды",
"viewer.owner.reader": "взаимодействие читателя",
"viewer.present.enter": "Войти в режим презентации",
"viewer.present.enter.title": "Режим презентации (F)",
"viewer.present.exit": "Выйти из режима презентации",
"viewer.present.exit.title": "Выйти из режима презентации (F или Escape)",
"viewer.present.present": "Презентация",
"viewer.present.exit.label": "Выход",
"viewer.export.button": "Экспорт",
"viewer.export.button.title": "Экспортировать диаграмму (E)",
"viewer.export.diagram": "Экспортировать диаграмму",
"viewer.export.menu": "Экспорт",
"viewer.export.subtitle": "Портативные, аккуратные файлы",
"viewer.export.share": "Поделиться",
"viewer.export.shareCard": "Карточка для публикации",
"viewer.export.routeShareCard": "Карточка маршрута",
"viewer.export.reachShareCard": "Карточка достижимости",
"viewer.export.copyDiagram": "Копировать диаграмму",
"viewer.export.clipboardPng": "PNG в буфер обмена",
"viewer.export.raster": "Растровые изображения",
"viewer.export.image": "Изображение",
"viewer.export.lossless": "Изображение без потерь",
"viewer.export.compact": "Компактное изображение",
"viewer.export.modern": "Современный формат изображения",
"viewer.export.vectorMotion": "Вектор и анимация",
"viewer.export.vectorMotion.heading": "Вектор и анимация",
"viewer.export.editable": "Редактируемый вектор",
"viewer.export.motion6s": "Анимация 6 с",
"viewer.export.unsupported": "Не поддерживается этим браузером",
"viewer.export.clipboardUnsupported": "Этот браузер не поддерживает запись изображений в буфер обмена",
"viewer.export.clipboardUnsupported.short": "Запись изображений в буфер обмена не поддерживается в этом браузере.",
"viewer.export.motionUnavailable": "Запись анимации недоступна в этом браузере",
"viewer.export.webmUnavailable": "WebM недоступен в этом браузере",
"viewer.export.failed": "Ошибка экспорта: {message}",
"viewer.export.unknownVariant": "Неизвестный тип карточки: {variant}",
"viewer.export.routeRequired": "Постройте маршрут перед экспортом карточки маршрута",
"viewer.export.reachRequired": "Постройте заданную достижимость перед экспортом карточки достижимости",
"viewer.export.unknown": "неизвестно",
"viewer.export.routeFailed": "Не удалось экспортировать карточку маршрута: {message}",
"viewer.export.reachFailed": "Не удалось экспортировать карточку достижимости: {message}",
"viewer.export.copyFailed": "Не удалось скопировать: {message}",
"viewer.export.copiedPng": "PNG скопирован в буфер обмена",
"viewer.export.downloadedRoute": "Карточка маршрута загружена",
"viewer.export.downloadedReach": "Карточка достижимости загружена",
"viewer.export.downloadedWebm": "WebM загружен",
"viewer.export.recording": "Запись анимации, 6 секунд…",
"viewer.export.card.routeSummary.one": "Маршрут: {source} → {target} · {count} направленный переход",
"viewer.export.card.routeSummary.other": "Маршрут: {source} → {target} · {count} направленных переходов",
"viewer.export.card.reachSummary": "Заданная достижимость ({direction}) от {origin} · {nodes} · {links} · макс. {hops}",
"viewer.export.card.node.one": "{count} узел",
"viewer.export.card.node.other": "{count} узлов",
"viewer.export.card.link.one": "{count} связь",
"viewer.export.card.link.other": "{count} связей",
"viewer.export.card.hop.one": "{count} переход",
"viewer.export.card.hop.other": "{count} переходов",
"viewer.export.card.routeBadge": "ARCHIFY · МАРШРУТ · {hops}",
"viewer.export.card.reachBadge": "ARCHIFY · ДОСТИЖИМОСТЬ ({direction})",
"viewer.export.direction.upstream": "Восходящий",
"viewer.export.direction.downstream": "Нисходящий",
"viewer.export.error.canvasUnavailable": "Canvas недоступен для {label}",
"viewer.export.error.contextUnavailable": "2D-контекст canvas недоступен для {label}",
"viewer.export.error.toBlobUnavailable": "canvas.toBlob недоступен для {label}",
"viewer.export.error.toBlobNull": "canvas.toBlob не вернул данные для {label}",
"viewer.export.error.variantsCombined": "Типы карточек нельзя комбинировать",
"viewer.export.error.viewerState": "Не удалось удалить временное состояние просмотра при экспорте карточки",
"viewer.export.error.routeState": "Не удалось безопасно сохранить построенный маршрут при экспорте карточки",
"viewer.export.error.reachState": "Не удалось безопасно сохранить заданную достижимость при экспорте карточки",
"viewer.export.error.webmRequirements": "Экспорт анимации в WebM требует анимации трассировки и поддержки MediaRecorder в браузере",
"viewer.export.error.mediaRecorder": "Ошибка MediaRecorder",
"viewer.export.error.emptyWebm": "MediaRecorder создал пустой файл WebM",
"viewer.export.error.webmBackground": "Не удалось загрузить фон SVG для экспорта WebM",
"viewer.guide.eyebrow": "Гид по диаграмме",
"viewer.guide.close": "Закрыть гид по диаграмме",
"viewer.guide.inspecting": "Анализ скомпилированной семантики",
"viewer.guide.actions": "Действия для изучения диаграммы",
"viewer.guide.find": "Найти любой узел",
"viewer.guide.find.hint": "Поиск по меткам, ролям, типам и постоянным ID.",
"viewer.guide.route": "Построить маршрут",
"viewer.guide.route.aria": "Построить направленный маршрут",
"viewer.guide.route.hint": "Узнайте, как два семантических узла связаны в заданном направлении.",
"viewer.guide.map": "Просмотреть всю систему",
"viewer.guide.map.hint": "Откройте семантический радар с живым видом и постоянными узлами.",
"viewer.guide.lens": "Сравнить семантические типы",
"viewer.guide.lens.hint": "Подсчитайте роли, покажите их связи и сравните прямые заданные связи.",
"viewer.guide.present": "Войти в режим презентации",
"viewer.guide.present.hint": "Отдать весь экран живой диаграмме без изменения экспорта.",
"viewer.guide.shortcuts": "Дополнительные горячие клавиши",
"viewer.guide.shortcut.export": "Экспорт",
"viewer.guide.shortcut.theme": "Тема",
"viewer.guide.shortcut.style": "Стиль",
"viewer.guide.shortcut.reset": "Сброс",
"viewer.guide.shortcut.zoomIn": "Увеличить",
"viewer.guide.shortcut.zoomOut": "Уменьшить",
"viewer.guide.shortcut.close": "Закрыть",
"viewer.guide.facts": "{nodes} · {relationships}",
"viewer.guide.fact.node.one": "{count} семантический узел",
"viewer.guide.fact.node.other": "{count} семантических узлов",
"viewer.guide.fact.relationship.one": "{count} связь",
"viewer.guide.fact.relationship.other": "{count} связей",
"viewer.guide.open": "Открыть гид по диаграмме",
"viewer.finder.title": "Найти узел",
"viewer.finder.close": "Закрыть поиск узлов",
"viewer.finder.placeholder": "Поиск по меткам или ID",
"viewer.finder.search": "Поиск узлов диаграммы",
"viewer.finder.results": "Узлы диаграммы",
"viewer.finder.empty": "Совпадений не найдено",
"viewer.finder.result.focus": "Фокус: {label}",
"viewer.finder.result.routeStart": "Выбрать {label} как начало маршрута",
"viewer.finder.result.routeTarget": "Выбрать {label} как конец маршрута, {links}",
"viewer.finder.status.empty": "Совпадений не найдено",
"viewer.finder.status.count.one": "{count} совпадающий узел",
"viewer.finder.status.count.other": "{count} совпадающих узлов",
"viewer.finder.noun.nodes": "узлов",
"viewer.finder.link.one": "{count} связь",
"viewer.finder.link.other": "{count} связей",
"viewer.finder.result.focus.one": "Фокус: {label}, {count} связанное соединение",
"viewer.finder.result.focus.other": "Фокус: {label}, {count} связанных соединений",
"viewer.finder.status.filtered": "{visible} из {available} {noun}",
"viewer.finder.status.all": "{available} {noun}",
"viewer.passport.eyebrow": "Семантический паспорт",
"viewer.passport.metadata": "Метаданные узла",
"viewer.passport.evidence": "Проверенные источники",
"viewer.passport.verified": "Проверенный источник",
"viewer.passport.verificationScope": "Проверено по локальному Git на фиксированной ревизии. Удалённый доступ не проверялся.",
"viewer.passport.reach": "Заданная достижимость",
"viewer.passport.reach.trace": "Отследить заданную достижимость",
"viewer.passport.upstream": "Восходящий",
"viewer.passport.downstream": "Нисходящий",
"viewer.passport.upstream.trace": "Отследить восходящую заданную достижимость",
"viewer.passport.downstream.trace": "Отследить нисходящую заданную достижимость",
"viewer.passport.close": "Закрыть семантический паспорт",
"viewer.passport.copy": "Скопировать ссылку",
"viewer.passport.copy.focus": "Скопировать ссылку на выделенный узел",
"viewer.passport.relations": "Связи",
"viewer.passport.relations.show": "Показать связанные отношения",
"viewer.passport.relations.hide": "Скрыть связанные отношения",
"viewer.passport.relations.list": "Связанные отношения",
"viewer.passport.copyRelation": "Копировать связь",
"viewer.passport.copyNode": "Копировать узел",
"viewer.passport.copyPinned": "Скопировать ссылку на закреплённую связь",
"viewer.passport.copySource": "Скопировать ссылку на исходный узел",
"viewer.passport.copy.focused.success": "Ссылка на выделенный узел скопирована",
"viewer.passport.copy.pinned.success": "Ссылка на закреплённую связь скопирована",
"viewer.passport.copy.focused.failed": "Не удалось скопировать ссылку на выделенный узел",
"viewer.passport.copy.pinned.failed": "Не удалось скопировать ссылку на закреплённую связь",
"viewer.passport.relationship.none": "Нет связанных отношений",
"viewer.passport.relationship.count.one": "{count} связь",
"viewer.passport.relationship.count.other": "{count} связей",
"viewer.passport.relationship.show.one": "Показать {count} связанное отношение",
"viewer.passport.relationship.show.other": "Показать {count} связанных отношений",
"viewer.passport.relationship.summary": "{out} исходящих · {in} входящих{loops}",
"viewer.passport.relationship.loops": " · {count} петля",
"viewer.passport.relationship.explorer": "Обзор прямых связей",
"viewer.passport.relationship.help": "Используйте стрелки для просмотра связей. Enter или пробел — закрепить детали; Escape — очистить.",
"viewer.passport.relationship.loopsBack": "замыкается",
"viewer.passport.relationship.connectsTo": "соединяется с",
"viewer.passport.relationship.connectsFrom": "соединяется от",
"viewer.passport.relationship.pinned": "Закреплённая связь · {from} → {to} · {label}",
"viewer.passport.relationship.inspect": "Просмотр связи {index} из {total}: {from} → {to}, {label}. Enter — подробности.",
"viewer.passport.relationship.group.out": "Исходящие",
"viewer.passport.relationship.group.in": "Входящие",
"viewer.passport.relationship.group.loop": "Петли",
"viewer.passport.relationship.row": "{group}: {relationship}, {neighbor}",
"viewer.passport.relationship.direction.out": "ИСХ →",
"viewer.passport.relationship.direction.in": "← ВХ",
"viewer.passport.relationship.direction.loop": "ПЕТЛЯ",
"viewer.passport.sourceCount.one": "{count} проверенная ссылка на источник",
"viewer.passport.sourceCount.other": "{count} проверенных ссылок на источники",
"viewer.passport.sourceMarker": "ИСТ",
"viewer.passport.beacon.one": "{count} проверенный источник; выделите узел для просмотра",
"viewer.passport.beacon.other": "{count} проверенных источников; выделите узел для просмотра",
"viewer.passport.repository.open": "Открыть проверенную ревизию репозитория {revision}",
"viewer.passport.source.open": "Открыть проверенный источник {path} на ревизии {revision}",
"viewer.passport.source.openLink": "Открыть ↗",
"viewer.passport.reach.upstream.one": "Отследить {count} восходящий заданный узел",
"viewer.passport.reach.upstream.other": "Отследить {count} восходящих заданных узлов",
"viewer.passport.reach.downstream.one": "Отследить {count} нисходящий заданный узел",
"viewer.passport.reach.downstream.other": "Отследить {count} нисходящих заданных узлов",
"viewer.passport.reach.noUpstream": "Нет восходящих заданных узлов",
"viewer.passport.reach.noDownstream": "Нет нисходящих заданных узлов",
"viewer.passport.reach.status": "{direction} · узлов: {nodes} · связей: {links} · макс. переходов: {hops}",
"viewer.route.eyebrow": "Зонд маршрута",
"viewer.route.start": "Выбрать начальный узел",
"viewer.route.start.find": "Найти начало",
"viewer.route.start.find.aria": "Найти начало маршрута",
"viewer.route.copy": "Скопировать ссылку",
"viewer.route.copy.aria": "Скопировать ссылку на построенный маршрут",
"viewer.route.clear": "Очистить",
"viewer.route.clear.aria": "Очистить зонд маршрута",
"viewer.route.traced": "Построенный маршрут",
"viewer.route.pickTwo": "Выберите два семантических узла на диаграмме",
"viewer.route.pickOne": "Выберите семантический узел на диаграмме",
"viewer.route.controls": "Управление воспроизведением маршрута",
"viewer.route.previous": "Предыдущая позиция маршрута",
"viewer.route.play": "Воспроизвести маршрут",
"viewer.route.pause": "Приостановить маршрут",
"viewer.route.replay": "Повторить маршрут",
"viewer.route.next": "Следующая позиция маршрута",
"viewer.route.journey": "Путь",
"viewer.route.pause.label": "Пауза",
"viewer.route.replay.label": "Повтор",
"viewer.route.overview": "Обзор",
"viewer.route.overview.aria": "Показать полный обзор маршрута",
"viewer.route.instructions": "Сначала выберите источник, затем цель. Направление важно.",
"viewer.route.destination": "Выбрать цель от {label}",
"viewer.route.destination.find": "Найти цель",
"viewer.route.destination.find.aria": "Найти достижимую цель маршрута",
"viewer.route.differentDestination": "Выбрать другую цель",
"viewer.route.distinct": "Для маршрута нужны два разных семантических узла.",
"viewer.route.unreachable": "Нет направленного маршрута к {label}",
"viewer.route.unreachable.detail": "{target} недостижим из {source}. Выберите подсвеченную цель.",
"viewer.route.start.instructions": "Выберите источник. На следующем шаге будут показаны только направленно достижимые цели.",
"viewer.route.copy.success": "Ссылка на построенный маршрут скопирована",
"viewer.route.copy.failed": "Не удалось скопировать ссылку на маршрут",
"viewer.route.position": "Позиция маршрута {index} из {total}: {label}",
"viewer.route.step": "Шаг {index} из {total} · {phase} · {label}",
"viewer.route.motionRequired": "Автоматическое воспроизведение требует режима «Динамика»",
"viewer.route.trigger.clear": "Очистить построенный маршрут",
"viewer.route.overview.status": "{nodes} · {hops} · кратчайший заданный маршрут",
"viewer.route.overview.node.one": "{count} узел",
"viewer.route.overview.node.other": "{count} узлов",
"viewer.route.overview.hop.one": "{count} направленный переход",
"viewer.route.overview.hop.other": "{count} направленных переходов",
"viewer.route.phase.playing": "Воспроизведение",
"viewer.route.phase.complete": "Завершено",
"viewer.route.phase.inspecting": "Просмотр",
"viewer.route.destination.count.one": "Доступна {count} направленная цель. Выберите подсвеченный узел.",
"viewer.route.destination.count.other": "Доступно {count} направленных целей. Выберите подсвеченный узел.",
"viewer.route.noOutgoing": "Отсюда не начинается ни один исходящий маршрут. Очистите и выберите другой источник.",
"viewer.route.result.title": "{source} → {target}",
"viewer.route.finder.source.title": "Выбрать начало маршрута",
"viewer.route.finder.source.placeholder": "Поиск источников маршрута",
"viewer.route.finder.source.empty": "Совпадающих источников маршрута не найдено",
"viewer.route.finder.source.results": "Узлы, с которых можно начать маршрут",
"viewer.route.finder.source.noun": "источников маршрута",
"viewer.route.finder.source.badge": "начало",
"viewer.route.finder.target.title": "Цель от {label}",
"viewer.route.finder.target.placeholder": "Поиск достижимых целей",
"viewer.route.finder.target.empty": "Совпадающих достижимых целей не найдено",
"viewer.route.finder.target.results": "Достижимые цели маршрута",
"viewer.route.finder.target.noun": "достижимых целей",
"viewer.route.hop.one": "{count} переход",
"viewer.route.hop.other": "{count} переходов",
"viewer.lens.eyebrow": "Семантическая линза",
"viewer.lens.title": "Сравнить роли системы",
"viewer.lens.close": "Закрыть семантическую линзу",
"viewer.lens.instruction": "Выберите до двух семантических типов. Один тип показывает реальные связи; два — сравнивают только прямые заданные связи.",
"viewer.lens.kinds": "Семантические типы",
"viewer.lens.choose": "Выберите тип, чтобы просмотреть его узлы и смежные связи.",
"viewer.lens.copy": "Скопировать ссылку на семантическую линзу",
"viewer.lens.clear": "Очистить семантическую линзу",
"viewer.lens.open": "Открыть семантическую линзу",
"viewer.lens.openActive": "Открыть активную семантическую линзу",
"viewer.lens.legend": "Семантическая легенда",
"viewer.lens.legend.inspect.one": "Просмотр {label}, {count} узел",
"viewer.lens.legend.inspect.other": "Просмотр {label}, {count} узлов",
"viewer.lens.kind.count.one": "{label}, {count} узел",
"viewer.lens.kind.count.other": "{label}, {count} узлов",
"viewer.lens.compare.one": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} прямая связь",
"viewer.lens.compare.other": "{first} → {second}: {forward} · {second} → {first}: {reverse} · {count} прямых связей",
"viewer.lens.single": "{nodes} · {relationships} · связанные узлы остаются видимыми",
"viewer.lens.node.one": "{count} узел типа {label}",
"viewer.lens.node.other": "{count} узлов типа {label}",
"viewer.lens.relationship.one": "{count} смежная связь",
"viewer.lens.relationship.other": "{count} смежных связей",
"viewer.radar.title": "Семантический радар",
"viewer.radar.building": "Построение обзора",
"viewer.radar.openFull": "Открыть полный семантический радар",
"viewer.radar.open": "Открыть радар",
"viewer.radar.close": "Закрыть семантический радар",
"viewer.radar.surface": "Обзор диаграммы. Щёлкните узел, чтобы выделить его, или используйте стрелки для панорамирования.",
"viewer.radar.click": "Щёлкнуть узел",
"viewer.radar.drag": "Перетащите для панорамирования",
"viewer.radar.space": "Семантическому радару нужно больше места на карте.",
"viewer.radar.nodes": "Узлы семантического радара диаграммы",
"viewer.radar.focus": "Фокус: {label} (из семантического радара)",
"viewer.radar.status": "Узлов: {count} · {viewport}",
"viewer.radar.fullMap": "Узлов: {count} · полная карта",
"viewer.radar.compacted": "Радар уменьшен, чтобы не перекрывать семантический паспорт или элементы управления картой.",
"viewer.radar.cancelWaiting": "Отменить ожидание семантическим радаром дополнительного места",
"viewer.radar.needsSpace": "Семантическому радару нужно больше видимого места на карте",
"viewer.radar.viewport.full": "полная карта",
"viewer.radar.viewport.width": "ширина {percent}%",
"viewer.radar.viewport.scale": "масштаб {percent}%",
"viewer.nav.controls": "Управление видом диаграммы",
"viewer.nav.route": "Построить направленный маршрут",
"viewer.nav.route.title": "Построить маршрут (R)",
"viewer.nav.route.short": "ПУТЬ",
"viewer.nav.radar": "Открыть семантический радар",
"viewer.nav.radar.title": "Семантический радар (M)",
"viewer.nav.radar.short": "КАРТА",
"viewer.nav.lens": "Открыть семантическую линзу",
"viewer.nav.lens.title": "Семантическая линза (L)",
"viewer.nav.lens.short": "ЛИНЗА",
"viewer.nav.find": "Найти узел",
"viewer.nav.find.title": "Найти узел (/)",
"viewer.nav.guide": "Открыть гид по диаграмме",
"viewer.nav.guide.title": "Гид по диаграмме (?)",
"viewer.nav.zoomOut": "Уменьшить",
"viewer.nav.zoomOut.title": "Уменьшить (-)",
"viewer.nav.reset": "Сбросить вид диаграммы",
"viewer.nav.reset.title": "Сбросить вид (0)",
"viewer.nav.read": "ЧТЕНИЕ",
"viewer.nav.zoomIn": "Увеличить",
"viewer.nav.zoomIn.title": "Увеличить (+)",
"viewer.nav.camera": "{hint}. Сбросить вид диаграммы",
"viewer.nav.camera.title": "{semantic}{hint} · сбросить вид (0)",
"viewer.nav.camera.semantic": "Семантическая камера активна · ",
"viewer.nav.level.map": "КАРТА",
"viewer.nav.level.read": "ЧТЕНИЕ",
"viewer.nav.level.full": "ПОЛНЫЙ",
"viewer.nav.level.auto": "АВТО",
"viewer.nav.detail.map": "Увеличьте, чтобы увидеть подписи связей и контекст узлов",
"viewer.nav.detail.read": "Увеличьте ещё, чтобы увидеть теги и аннотации",
"viewer.nav.detail.full": "Полная детализация диаграммы",
"viewer.intent.summary": "{label}. {out} исходящих, {in} входящих{loops}. Всего связей: {total}. Enter — подробности.",
"viewer.intent.loops": ", {count} петля",
"viewer.common.copied": "Скопировано",
"viewer.common.copyFailed": "Не удалось скопировать",
"viewer.common.copyLink": "Скопировать ссылку",
"viewer.common.clear": "Очистить",
"viewer.common.close": "Закрыть"
}
+71
View File
@@ -0,0 +1,71 @@
{
"schema_version": 1,
"diagram_type": "dataflow",
"meta": {
"title": "Product Analytics Data Flow",
"output": "examples/dataflow-product-analytics.html",
"viewBox": [1080, 520],
"animation": "trace",
"quality_profile": "showcase"
},
"stages": [
{ "label": "Sources" },
{ "label": "Ingest" },
{ "label": "Process" },
{ "label": "Store" },
{ "label": "Consume" }
],
"nodes": [
{ "id": "web", "type": "frontend", "label": "Web App", "sublabel": "browser SDK", "stage": 0, "row": 0, "tag": "events" },
{ "id": "mobile", "type": "frontend", "label": "Mobile", "sublabel": "iOS / Android", "stage": 0, "row": 2, "tag": "events" },
{ "id": "edge", "type": "cloud", "label": "Edge API", "sublabel": "collector", "stage": 1, "row": 1, "tag": "TLS" },
{ "id": "consent", "type": "security", "label": "Consent Gate", "sublabel": "policy filter", "stage": 2, "row": 0, "tag": "PII guard" },
{ "id": "stream", "type": "messagebus", "label": "Event Stream", "sublabel": "Kafka topic", "stage": 2, "row": 2, "tag": "ordered" },
{ "id": "pii", "type": "security", "label": "PII Vault", "sublabel": "encrypted", "stage": 3, "row": 0, "tag": "restricted" },
{ "id": "warehouse", "type": "database", "label": "Warehouse", "sublabel": "analytics tables", "stage": 3, "row": 1, "tag": "curated" },
{ "id": "features", "type": "database", "label": "Feature Store", "sublabel": "daily batch", "stage": 3, "row": 2, "tag": "derived" },
{ "id": "dashboard", "type": "backend", "label": "Dashboards", "sublabel": "product metrics", "stage": 4, "row": 0, "tag": "SQL" },
{ "id": "model", "type": "backend", "label": "ML Model", "sublabel": "ranking job", "stage": 4, "row": 2, "tag": "features" }
],
"flows": [
{ "id": "web-clickstream", "from": "web", "to": "edge", "label": "clickstream", "classification": "user events", "variant": "emphasis", "fromSide": "right", "toSide": "left", "via": [[205, 157], [205, 271]], "labelAt": [204, 190] },
{ "id": "mobile-events", "from": "mobile", "to": "edge", "label": "app events", "classification": "device events", "variant": "default", "fromSide": "right", "toSide": "left", "via": [[222, 385], [222, 271]], "labelAt": [220, 342] },
{ "id": "consent-enrichment", "from": "edge", "to": "consent", "label": "identity + consent", "classification": "PII touch", "variant": "security", "fromSide": "top", "toSide": "left", "via": [[315, 112], [450, 112], [450, 157]], "labelAt": [382, 100] },
{ "id": "accepted-events", "from": "edge", "to": "stream", "label": "accepted events", "classification": "append-only", "variant": "emphasis", "fromSide": "right", "toSide": "left", "via": [[420, 271], [420, 385]], "labelAt": [438, 324] },
{ "id": "identity-map", "from": "consent", "to": "pii", "label": "identity map", "classification": "encrypted PII", "variant": "security", "route": "straight", "labelAt": [638, 144] },
{ "id": "normalized-facts", "from": "stream", "to": "warehouse", "label": "normalized facts", "classification": "non-PII", "variant": "emphasis", "fromSide": "right", "toSide": "left", "via": [[638, 385], [638, 271]], "labelAt": [638, 326] },
{ "id": "daily-aggregates", "from": "warehouse", "to": "features", "label": "daily aggregates", "classification": "batch", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "route": "straight", "labelAt": [745, 326] },
{ "id": "metrics-query", "from": "warehouse", "to": "dashboard", "label": "metrics SQL", "classification": "read-only", "variant": "default", "fromSide": "right", "toSide": "bottom", "via": [[852, 271], [960, 271]], "labelAt": [876, 258] },
{ "id": "feature-vectors", "from": "features", "to": "model", "label": "feature vectors", "classification": "derived", "variant": "dashed", "route": "straight", "labelAt": [852, 372] },
{ "id": "restricted-join", "from": "pii", "to": "dashboard", "label": "restricted join", "classification": "approved only", "variant": "security", "route": "straight", "labelAt": [852, 144] }
],
"cards": [
{
"dot": "emerald",
"title": "Primary Data Path",
"items": [
"Events move left to right through source, ingest, process, store, and consume stages",
"The hot path stays visually clear even with secondary batch flows",
"Labels name data assets instead of generic API verbs"
]
},
{
"dot": "rose",
"title": "Sensitive Boundary",
"items": [
"Consent and PII paths are styled as security flows",
"PII lands in a restricted vault, separate from the analytics warehouse",
"Restricted joins are visible without implying default access"
]
},
{
"dot": "orange",
"title": "Derived Consumers",
"items": [
"Dashboards read curated facts from the warehouse",
"Feature vectors are derived by batch from analytics tables",
"Consumption paths stay distinct from collection and consent handling"
]
}
]
}
@@ -0,0 +1,51 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Production Deployment Ownership",
"output": "examples/production-deployment.html",
"visual_preset": "blueprint",
"animation": "trace",
"quality_profile": "showcase",
"engineering_profile": "deployment-ownership"
},
"components": [
{ "id": "clients", "type": "external", "label": "Customers", "sublabel": "web + mobile", "pos": [38, 300], "size": [122, 60] },
{ "id": "edge", "type": "cloud", "label": "Global Edge", "sublabel": "CDN + WAF", "pos": [230, 300], "size": [126, 60], "tag": "edge team" },
{ "id": "gateway", "type": "security", "label": "API Gateway", "sublabel": "public :443", "pos": [430, 300], "size": [128, 60], "tag": "platform" },
{ "id": "api_a", "type": "backend", "label": "API Pods / AZ-a", "sublabel": "private subnet", "pos": [630, 195], "size": [136, 62], "tag": "app team" },
{ "id": "api_b", "type": "backend", "label": "API Pods / AZ-b", "sublabel": "private subnet", "pos": [630, 405], "size": [136, 62], "tag": "app team" },
{ "id": "redis", "type": "database", "label": "Redis", "sublabel": "multi-AZ cache", "pos": [840, 195], "size": [126, 62], "tag": "platform" },
{ "id": "postgres", "type": "database", "label": "PostgreSQL", "sublabel": "primary / encrypted", "pos": [840, 405], "size": [126, 62], "tag": "data team" },
{ "id": "events", "type": "messagebus", "label": "Event Bus", "sublabel": "orders.v1", "pos": [1040, 300], "size": [126, 60], "tag": "platform" },
{ "id": "worker", "type": "backend", "label": "Workers", "sublabel": "private workload", "pos": [1190, 300], "size": [126, 60], "tag": "app team" },
{ "id": "replica", "type": "database", "label": "DR Replica", "sublabel": "eu-west-1", "pos": [1040, 578], "size": [126, 62], "tag": "data team" },
{ "id": "audit", "type": "cloud", "label": "Audit Archive", "sublabel": "immutable objects", "pos": [1190, 450], "size": [126, 62], "tag": "security" },
{ "id": "observability", "type": "external", "label": "Observability", "sublabel": "metrics + traces", "pos": [1190, 85], "size": [126, 62], "tag": "SRE" }
],
"boundaries": [
{ "kind": "region", "label": "AWS us-east-1 / production", "wraps": ["edge", "gateway", "api_a", "api_b", "redis", "postgres", "events", "worker", "audit"], "pad": 20 },
{ "kind": "security-group", "label": "private application network", "wraps": ["api_a", "api_b", "redis", "postgres", "events", "worker"], "pad": 14 },
{ "kind": "region", "label": "AWS eu-west-1 / disaster recovery", "wraps": ["replica"] },
{ "kind": "security-group", "label": "DR private subnet", "wraps": ["replica"], "pad": 14 }
],
"connections": [
{ "from": "clients", "to": "edge", "label": "HTTPS", "variant": "emphasis" },
{ "from": "edge", "to": "gateway", "label": "mTLS", "variant": "security" },
{ "from": "gateway", "to": "api_a", "label": "VPC route", "variant": "emphasis", "route": "orthogonal-h", "labelAt": [594, 275] },
{ "from": "gateway", "to": "api_b", "label": "VPC route", "variant": "emphasis", "route": "orthogonal-h", "labelAt": [594, 385] },
{ "from": "api_a", "to": "redis", "label": "cache", "route": "straight" },
{ "from": "api_b", "to": "postgres", "label": "SQL", "route": "straight" },
{ "from": "api_a", "to": "events", "label": "publish", "variant": "dashed", "fromSide": "top", "toSide": "top", "via": [[698, 170], [1103, 170]] },
{ "from": "api_b", "to": "events", "variant": "dashed", "fromSide": "top", "toSide": "bottom", "via": [[698, 380], [1103, 380]] },
{ "from": "events", "to": "worker", "variant": "emphasis" },
{ "from": "postgres", "to": "replica", "label": "cross-region WAL", "variant": "security", "route": "orthogonal-v", "labelAt": [1003, 529] },
{ "from": "worker", "to": "audit", "label": "evidence", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 58 },
{ "from": "worker", "to": "observability", "label": "OTLP", "variant": "dashed", "route": "orthogonal-v" }
],
"cards": [
{ "dot": "cyan", "title": "Runtime Ownership", "items": ["Platform owns the edge, gateway, cache, and event bus", "Application teams own API pods and workers", "Data owns primary and disaster-recovery state"] },
{ "dot": "rose", "title": "Named Crossings", "items": ["Public HTTPS terminates at the managed edge", "mTLS crosses into the application network", "Cross-region WAL is explicit and encrypted"] },
{ "dot": "emerald", "title": "Operational Evidence", "items": ["Workers emit traces to SRE-owned observability", "Audit evidence lands in immutable storage", "Unknown placement should remain marked, never invented"] }
]
}
+57
View File
@@ -0,0 +1,57 @@
{
"schema_version": 1,
"diagram_type": "workflow",
"meta": {
"title": "Release Delivery Workflow",
"output": "examples/release-delivery.html",
"animation": "trace",
"quality_profile": "showcase"
},
"lanes": [
{ "id": "dev", "label": "Developer" },
{ "id": "ci", "label": "Continuous Integration" },
{ "id": "approval", "label": "Release Governance" },
{ "id": "environment", "label": "Production Environment" },
{ "id": "communication", "label": "Release Communication" },
{ "id": "exceptions", "label": "Failure + Rollback", "variant": "exception" }
],
"phases": [
{ "id": "change", "label": "Change", "fromCol": 0, "toCol": 1 },
{ "id": "verify", "label": "Build + verify", "fromCol": 2, "toCol": 3, "variant": "emphasis" },
{ "id": "promote", "label": "Promote + observe", "fromCol": 4, "toCol": 5, "variant": "dashed" }
],
"groups": [
{ "id": "blocking_checks", "label": "Blocking checks", "lane": "ci", "fromCol": 2, "toCol": 3, "variant": "emphasis" },
{ "id": "rollback_work", "label": "Recovery path", "lane": "exceptions", "fromCol": 3, "toCol": 5, "variant": "security" }
],
"mainPath": ["commit", "pull_request", "build", "checks", "approval", "deploy", "verify_prod", "announce"],
"nodes": [
{ "id": "commit", "lane": "dev", "col": 0, "type": "frontend", "label": "Commit", "sublabel": "signed change" },
{ "id": "pull_request", "lane": "dev", "col": 1, "type": "frontend", "label": "Pull Request", "sublabel": "reviewed diff" },
{ "id": "build", "lane": "ci", "col": 2, "type": "backend", "label": "Build", "sublabel": "locked inputs", "tag": "reproducible" },
{ "id": "checks", "lane": "ci", "col": 3, "type": "security", "label": "Quality Gates", "sublabel": "test + scan", "tag": "blocking" },
{ "id": "approval", "lane": "approval", "col": 4, "type": "security", "label": "Approve", "sublabel": "release owner", "tag": "human gate" },
{ "id": "deploy", "lane": "environment", "col": 4, "type": "cloud", "label": "Deploy", "sublabel": "canary 10%", "tag": "production" },
{ "id": "verify_prod", "lane": "environment", "col": 5, "type": "backend", "label": "Verify", "sublabel": "smoke + SLO" },
{ "id": "announce", "lane": "communication", "col": 5, "type": "external", "label": "Announce", "sublabel": "status + notes" },
{ "id": "failed", "lane": "exceptions", "col": 2, "type": "security", "label": "Stop Release", "sublabel": "gate failed" },
{ "id": "rollback", "lane": "exceptions", "col": 4, "width": 64, "type": "messagebus", "label": "Rollback", "sublabel": "last good image", "tag": "owner: on-call" }
],
"edges": [
{ "from": "commit", "to": "pull_request" },
{ "from": "pull_request", "to": "build", "label": "merge", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "build", "to": "checks" },
{ "from": "checks", "to": "approval", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "approval", "to": "deploy", "variant": "security", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "deploy", "to": "verify_prod" },
{ "from": "verify_prod", "to": "announce", "label": "healthy", "variant": "emphasis", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "checks", "to": "failed", "label": "red", "variant": "security", "role": "error", "route": "drop", "fromSide": "bottom", "toSide": "top" },
{ "from": "verify_prod", "to": "rollback", "variant": "security", "role": "error", "route": "outside-right", "fromSide": "right", "toSide": "right" },
{ "from": "rollback", "to": "deploy", "label": "restore", "variant": "dashed", "role": "return", "route": "return-left", "fromSide": "left", "toSide": "left" }
],
"cards": [
{ "dot": "emerald", "title": "One Happy Path", "items": ["Every change is reviewed before a reproducible build", "Blocking checks must be green before human approval", "Production is complete only after smoke and SLO verification"] },
{ "dot": "rose", "title": "Stop Conditions", "items": ["Test or security failure stops promotion", "Production health can reverse a release", "Rollback ownership is visible before an incident"] },
{ "dot": "cyan", "title": "Release Evidence", "items": ["Approval, immutable image, and check results are retained", "The release announcement follows verification", "The main path remains readable without hiding failure"] }
]
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+41
View File
@@ -0,0 +1,41 @@
{
"schema_version": 1,
"diagram_type": "architecture",
"meta": {
"title": "Sample Web App",
"output": "web-app-rendered.html",
"quality_profile": "showcase"
},
"components": [
{ "id": "users", "type": "external", "label": "Users", "sublabel": "Browser / Mobile", "pos": [40, 300], "size": [120, 60] },
{ "id": "auth", "type": "security", "label": "Auth Provider", "sublabel": "OAuth 2.0", "pos": [40, 110], "size": [120, 64], "tag": "JWT + PKCE" },
{ "id": "cdn", "type": "cloud", "label": "CloudFront", "sublabel": "CDN", "pos": [250, 300], "size": [130, 60] },
{ "id": "lb", "type": "cloud", "label": "Load Balancer", "sublabel": "HTTPS :443", "pos": [460, 300], "size": [130, 60] },
{ "id": "api", "type": "backend", "label": "API Server", "sublabel": "FastAPI :8000", "pos": [670, 300], "size": [130, 60] },
{ "id": "cache", "type": "database", "label": "Redis", "sublabel": "cache :6379", "pos": [670, 150], "size": [130, 60] },
{ "id": "db", "type": "database", "label": "PostgreSQL", "sublabel": "primary :5432", "pos": [880, 300], "size": [130, 60] },
{ "id": "s3", "type": "cloud", "label": "S3", "sublabel": "static assets", "pos": [250, 440], "size": [130, 60], "tag": "OAI protected" },
{ "id": "queue", "type": "messagebus", "label": "SQS", "sublabel": "job queue", "pos": [670, 440], "size": [130, 60] },
{ "id": "worker", "type": "backend", "label": "Worker", "sublabel": "async jobs", "pos": [880, 440], "size": [130, 60] }
],
"boundaries": [
{ "kind": "region", "label": "AWS Region: us-west-2", "wraps": ["cdn", "lb", "api", "cache", "db", "s3", "queue", "worker"] },
{ "kind": "security-group", "label": "sg-api :443/:8000", "wraps": ["lb", "api"] }
],
"connections": [
{ "id": "users-to-cdn", "from": "users", "to": "cdn", "label": "HTTPS", "variant": "emphasis" },
{ "id": "jwt-verification", "from": "auth", "to": "api", "label": "verify JWT", "variant": "security", "fromSide": "right", "toSide": "top", "via": [[620, 142], [620, 246], [735, 246]] },
{ "id": "cdn-to-lb", "from": "cdn", "to": "lb" },
{ "id": "static-assets", "from": "cdn", "to": "s3", "label": "static", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 58 },
{ "id": "lb-to-api", "from": "lb", "to": "api" },
{ "id": "cache-read-through", "from": "api", "to": "cache", "label": "read-through", "fromSide": "top", "toSide": "bottom", "labelDy": -68 },
{ "id": "api-sql", "from": "api", "to": "db", "label": "SQL" },
{ "id": "enqueue-job", "from": "api", "to": "queue", "label": "enqueue", "variant": "dashed", "fromSide": "bottom", "toSide": "top", "labelDy": 58 },
{ "id": "queue-to-worker", "from": "queue", "to": "worker" }
],
"cards": [
{ "dot": "cyan", "title": "Edge", "items": ["CloudFront CDN fronts all traffic", "S3 serves static assets via OAI"] },
{ "dot": "emerald", "title": "Application", "items": ["FastAPI behind an HTTPS load balancer", "Redis read-through cache", "Async work drained from SQS by a worker"] },
{ "dot": "rose", "title": "Security", "items": ["OAuth 2.0 with JWT + PKCE", "API + LB isolated in a security group"] }
]
}
File diff suppressed because it is too large. Load diff
+280
View File
@@ -0,0 +1,280 @@
import { compileWorkflow } from '../renderers/workflow/workflow-compiler.mjs';
import {
createMappedWorkflowCandidate,
intrinsicWorkflow,
planningWorkflow,
} from '../renderers/workflow/workflow-migration-geometry.mjs';
import { validateSchema } from '../renderers/shared/validator.mjs';
export { createHorizontalRankMapper } from '../renderers/workflow/workflow-migration-geometry.mjs';
const TARGET_SCHEMA_VERSION = 2;
function clone(value) {
return JSON.parse(JSON.stringify(value));
}
function diagnostic({ code, message, subject = {}, evidence = {}, supportedFixes = [] }) {
return {
code,
severity: 'error',
message,
subject,
evidence,
supportedFixes,
};
}
function schemaDiagnostics(workflow) {
try {
validateSchema('workflow', workflow);
return [];
} catch (error) {
if (Array.isArray(error?.archifyDiagnostics)) {
return error.archifyDiagnostics.map((entry) => ({ ...entry }));
}
throw error;
}
}
function legacyLayoutProbe(workflow, qualityProfile) {
// The probe discovers fixed-v1 rank centers, not authored canvas capacity.
// Omitting viewBox lets a capacity-only legacy failure reach the v2 compiler,
// which can measure and monotonically expand the real migrated document.
const probe = {
schema_version: 1,
diagram_type: 'workflow',
meta: {
title: workflow.meta.title,
output: workflow.meta.output,
...(workflow.meta.locale ? { locale: workflow.meta.locale } : {}),
legend: { mode: 'hidden' },
},
lanes: clone(workflow.lanes),
nodes: [{
id: 'migration_probe',
lane: workflow.lanes[0].id,
col: 0,
type: 'backend',
label: 'Probe',
}],
edges: [],
};
return compileWorkflow({ workflow: probe, qualityProfile });
}
function legacyRequirementProbe(workflow, qualityProfile) {
// Measure the complete fixed-v1 document without treating an authored
// viewBox as its intrinsic requirement. The authored viewBox remains a
// migration capacity and is preserved separately on the migrated document.
const probe = clone(workflow);
delete probe.meta.viewBox;
return compileWorkflow({ workflow: probe, qualityProfile });
}
function requiredViewBoxFrom(result) {
if (Array.isArray(result?.receipt?.requiredViewBox)) {
return [...result.receipt.requiredViewBox];
}
const required = result?.diagnostics
?.map((entry) => entry?.evidence?.requiredViewBox)
.find((candidate) => Array.isArray(candidate) && candidate.length === 2);
return required ? [...required] : null;
}
function expandableViewBox(result) {
if (result.ok || !result.diagnostics?.length) return null;
if (!result.diagnostics.every((entry) => entry.code === 'workflow/viewbox-capacity')) return null;
return requiredViewBoxFrom(result);
}
function result({
ok,
document,
fromSchemaVersion = 1,
preExistingDiagnostics = [],
migrationDiagnostics = [],
newSchemaDiagnostics = [],
changedCoordinates = [],
oldRequiredViewBox = null,
newRequiredViewBox = null,
}) {
return {
ok,
...(document ? { document } : {}),
fromSchemaVersion,
toSchemaVersion: TARGET_SCHEMA_VERSION,
preExistingDiagnostics,
migrationDiagnostics,
newSchemaDiagnostics,
changedCoordinates,
oldRequiredViewBox,
newRequiredViewBox,
};
}
export function migrateWorkflowDocument(inputWorkflow) {
if (!inputWorkflow || typeof inputWorkflow !== 'object' || Array.isArray(inputWorkflow)) {
return result({
ok: false,
migrationDiagnostics: [diagnostic({
code: 'migration/source-document',
message: 'Workflow migration requires one parsed JSON object.',
supportedFixes: ['provide one workflow schema v1 JSON document'],
})],
});
}
// Migration has no quality override: the authored policy (or effective
// standard default) must validate the document after it leaves this process.
const qualityProfile = inputWorkflow.meta?.quality_profile || 'standard';
const workflow = clone(inputWorkflow);
const preExistingDiagnostics = schemaDiagnostics(workflow);
if (preExistingDiagnostics.length) {
return result({
ok: false,
fromSchemaVersion: workflow.schema_version,
preExistingDiagnostics,
});
}
if (workflow.schema_version === TARGET_SCHEMA_VERSION) {
const compiled = compileWorkflow({ workflow: clone(workflow), qualityProfile });
const requiredViewBox = requiredViewBoxFrom(compiled);
if (!compiled.ok) {
return result({
ok: false,
fromSchemaVersion: TARGET_SCHEMA_VERSION,
preExistingDiagnostics: compiled.diagnostics,
oldRequiredViewBox: requiredViewBox,
newRequiredViewBox: requiredViewBox,
});
}
return result({
ok: true,
document: workflow,
fromSchemaVersion: TARGET_SCHEMA_VERSION,
oldRequiredViewBox: requiredViewBox,
newRequiredViewBox: requiredViewBox,
});
}
if (workflow.schema_version !== 1) {
return result({
ok: false,
fromSchemaVersion: workflow.schema_version,
migrationDiagnostics: [diagnostic({
code: 'migration/source-schema-version',
message: 'Workflow migration to schema v2 requires a schema v1 or v2 source.',
subject: { path: '/schema_version' },
evidence: { actual: workflow.schema_version, expected: [1, 2] },
supportedFixes: ['use an unchanged schema v1 workflow or an already migrated schema v2 workflow as the source'],
})],
});
}
const legacy = compileWorkflow({ workflow: clone(workflow), qualityProfile });
const legacyProbe = legacyLayoutProbe(workflow, qualityProfile);
if (!legacyProbe.ok) {
return result({
ok: false,
preExistingDiagnostics: legacy.ok ? [] : legacy.diagnostics,
migrationDiagnostics: legacyProbe.diagnostics,
});
}
const legacyRequirement = legacyRequirementProbe(workflow, qualityProfile);
const oldRequiredViewBox = requiredViewBoxFrom(legacyRequirement)
|| requiredViewBoxFrom(legacyProbe)
|| requiredViewBoxFrom(legacy);
const preExistingLayoutDiagnostics = legacy.ok ? [] : legacy.diagnostics;
let planned = compileWorkflow({ workflow: intrinsicWorkflow(workflow), qualityProfile });
if (!planned.ok) {
// Old absolute pins can be invalid at the new rank centers before their X
// coordinates are mapped. Obtain the same rank plan from an automatic-route
// projection, then validate every authored pin again after mapping.
planned = compileWorkflow({ workflow: planningWorkflow(workflow), qualityProfile });
}
if (!planned.ok) {
return result({
ok: false,
preExistingDiagnostics: preExistingLayoutDiagnostics,
newSchemaDiagnostics: planned.diagnostics,
oldRequiredViewBox,
newRequiredViewBox: requiredViewBoxFrom(planned),
});
}
let mappedCandidate;
try {
mappedCandidate = createMappedWorkflowCandidate(
workflow,
legacyProbe.receipt.columns,
planned.receipt.columns,
);
} catch (error) {
return result({
ok: false,
preExistingDiagnostics: preExistingLayoutDiagnostics,
migrationDiagnostics: [diagnostic({
code: 'migration/rank-mapping',
message: 'Could not construct a stable horizontal rank mapping.',
evidence: { reason: error.message },
supportedFixes: ['report the workflow and compiler receipts to the Archify maintainers'],
})],
oldRequiredViewBox,
newRequiredViewBox: requiredViewBoxFrom(planned),
});
}
const { document: migrated, changedCoordinates } = mappedCandidate;
let compiled = compileWorkflow({ workflow: migrated, qualityProfile });
const requiredExpansion = migrated.meta.viewBox ? expandableViewBox(compiled) : null;
if (requiredExpansion) {
const current = migrated.meta.viewBox;
const expanded = [
Math.max(current[0], requiredExpansion[0]),
Math.max(current[1], requiredExpansion[1]),
];
if (expanded[0] > current[0] || expanded[1] > current[1]) {
migrated.meta.viewBox = expanded;
compiled = compileWorkflow({ workflow: migrated, qualityProfile });
}
}
const newRequiredViewBox = requiredViewBoxFrom(compiled) || requiredViewBoxFrom(planned);
if (!compiled.ok) {
return result({
ok: false,
preExistingDiagnostics: preExistingLayoutDiagnostics,
newSchemaDiagnostics: compiled.diagnostics,
changedCoordinates,
oldRequiredViewBox,
newRequiredViewBox,
});
}
const migratedSchemaDiagnostics = schemaDiagnostics(migrated);
if (migratedSchemaDiagnostics.length) {
return result({
ok: false,
preExistingDiagnostics: preExistingLayoutDiagnostics,
newSchemaDiagnostics: migratedSchemaDiagnostics,
changedCoordinates,
oldRequiredViewBox,
newRequiredViewBox,
});
}
return result({
ok: true,
document: migrated,
preExistingDiagnostics: preExistingLayoutDiagnostics,
changedCoordinates,
oldRequiredViewBox,
newRequiredViewBox,
});
}
export function serializeMigratedWorkflow(workflow) {
return `${JSON.stringify(workflow, null, 2)}\n`;
}
+40
View File
@@ -0,0 +1,40 @@
{
"name": "archify",
"version": "3.0.1",
"private": true,
"type": "module",
"description": "JSON-IR diagram renderers (architecture / workflow / sequence / dataflow / lifecycle).",
"license": "MIT",
"bin": {
"archify": "./bin/archify.mjs"
},
"engines": {
"node": ">=18"
},
"scripts": {
"generate:viewer": "node ../scripts/generate-viewer.mjs",
"check:viewer": "node ../scripts/generate-viewer.mjs --check",
"generate:brand-marks": "node scripts/generate-brand-marks.mjs",
"check:brand-marks": "node scripts/generate-brand-marks.mjs --check",
"generate:validators": "node scripts/generate-validators.mjs",
"check:validators": "node scripts/generate-validators.mjs --check",
"check:release-identity": "node ../scripts/check-release-identity.mjs",
"build:gallery": "node ../scripts/build-gallery.mjs ../docs",
"build:guide": "node ../scripts/build-guide.mjs ../docs/guide.html",
"build:start": "node ../scripts/build-start.mjs ../docs/start.html",
"build:readme-showcase": "node ../scripts/build-readme-showcase.mjs",
"test:webm": "node test/webm-artifact.smoke.mjs && node --test test/site-language-integration.mjs",
"test:browser": "node ../scripts/run-browser-tests.mjs",
"test": "npm run check:viewer && npm run check:brand-marks && npm run check:validators && npm run check:release-identity && node test/golden.mjs && node ../scripts/run-tests.mjs",
"render:examples": "node scripts/render-examples.mjs ../examples"
},
"devDependencies": {
"ajv": "^8.17.1",
"parse5": "7.3.0",
"saxes": "6.0.0",
"simple-icons": "16.28.0"
},
"overrides": {
"fast-uri": "^3.1.7"
}
}
+430
View File
@@ -0,0 +1,430 @@
const RAW_RECIPES = [
{
id: 'system-overview', type: 'architecture', proof: 'web-app',
presentation: { preset: 'classic', motion: 'static' },
start: {
en: { descriptionPrompt: 'Use Archify to turn this plain-language system description into a high-level architecture diagram: [describe the users, core components, primary path, external dependencies, and boundaries]. No repository is required. Ask only for missing facts that would materially change the diagram, mark any remaining unknowns instead of inventing them, and keep one obvious primary path across 8–12 core components.' },
zh: { descriptionPrompt: '用 Archify 把下面这段自然语言系统描述画成高层架构图:[在这里描述用户、核心组件、主要路径、外部依赖和边界]。不需要代码库。只追问会实质影响图的缺失信息,其余不确定内容要标明而不是编造;保留 8–12 个核心组件和一条一眼可见的主路径。' },
},
signals: [['system overview', 12], ['architecture', 10], ['components', 6], ['services', 4], ['repository', 5], ['trust boundary', 8], ['架构', 10], ['系统总览', 12], ['组件', 6], ['服务', 4], ['仓库', 5], ['信任边界', 8]],
en: {
title: 'System overview', question: 'What exists, who owns it, and how is it connected?',
summary: 'A bounded map of core components, external dependencies, primary paths, and trust boundaries.',
useWhen: 'Onboarding, design reviews, repository orientation, or explaining a service landscape.',
avoidWhen: 'The audience needs exact call order, state transitions, or row-level data lineage.',
include: ['8–12 core components', 'one primary path', 'external dependencies', 'trust boundaries'],
prompt: 'Analyze this repository, then use Archify to create a high-level architecture diagram. Show 8–12 core runtime components, one primary request or data path, external dependencies, ownership or trust boundaries, and put supporting detail in cards instead of adding more edges.',
},
zh: {
title: '系统总览', question: '系统里有什么、归谁负责、彼此如何连接?',
summary: '用一张有边界的图展示核心组件、外部依赖、主路径和信任边界。',
useWhen: '适合新人上手、方案评审、仓库梳理和服务全景说明。',
avoidWhen: '如果重点是精确调用顺序、状态流转或字段级血缘,请换其他配方。',
include: ['8–12 个核心组件', '一条主路径', '外部依赖', '归属或信任边界'],
prompt: '分析这个仓库,然后用 Archify 生成高层系统架构图。展示 8–12 个核心运行时组件、一条主要请求或数据路径、外部依赖、归属或信任边界;支持性细节放进卡片,不要继续堆连线。',
},
},
{
id: 'deployment-ownership', type: 'architecture', proof: 'deployment-ownership',
presentation: { preset: 'blueprint', motion: 'trace' },
signals: [['deployment topology', 14], ['region', 7], ['vpc', 9], ['cluster', 6], ['availability zone', 8], ['ownership', 7], ['cloud deployment', 12], ['部署拓扑', 14], ['区域', 6], ['集群', 6], ['可用区', 8], ['资源归属', 9], ['跨区', 8]],
en: {
title: 'Deployment ownership', question: 'Where does each workload run, and what crosses a boundary?',
summary: 'A deployment-focused map of regions, networks, clusters, workloads, stores, and cross-boundary mechanisms.',
useWhen: 'Cloud reviews, production readiness, multi-region planning, or infrastructure ownership handoffs.',
avoidWhen: 'Deployment facts are unknown or the real question is application behavior rather than placement.',
include: ['regions and networks', 'workload ownership', 'stateful services', 'named boundary crossings'],
prompt: 'Use Archify to draw the production deployment topology. Group resources by region, network, cluster, and owner; show workloads and stateful services; label every cross-boundary mechanism. Do not invent deployment facts—mark unknown areas explicitly. If the user wants a fail-closed deployment review, ask before setting meta.engineering_profile to deployment-ownership; otherwise leave the engineering profile unset.',
},
zh: {
title: '部署与归属', question: '每个工作负载运行在哪里,哪些连接跨越了边界?',
summary: '围绕 Region、网络、集群、工作负载、存储和跨边界机制组织部署图。',
useWhen: '适合云上评审、生产就绪、多区域规划和基础设施交接。',
avoidWhen: '部署事实不清楚,或真正问题是应用行为而不是资源位置时不要使用。',
include: ['区域与网络', '工作负载归属', '有状态服务', '明确的跨边界机制'],
prompt: '用 Archify 绘制生产部署拓扑。按区域、网络、集群和负责人分组,展示工作负载与有状态服务,并标注每一种跨边界机制。不要编造部署事实,不确定的区域要明确标出。如果用户需要失败即阻断的部署评审,先征得确认,再把 meta.engineering_profile 设为 deployment-ownership;否则不要启用工程画像。',
},
},
{
id: 'agent-tool-call', type: 'workflow', proof: 'agent-tool-call',
presentation: { preset: 'signal-flow', motion: 'trace' },
start: {
en: { descriptionPrompt: 'Use Archify workflow mode to turn this description into a diagram: [paste the actors, main steps, decisions, approvals, and exception paths]. Use lanes for distinct owners, keep one unmistakable happy path, and mark missing ownership or unresolved branches instead of inventing them.' },
zh: { descriptionPrompt: '用 Archify 工作流模式把下面的描述画成图:[粘贴参与者、主要步骤、决策、审批和异常路径]。不同负责方使用独立泳道,保留一条明确的成功主路径,缺失的负责人或未定分支要标明而不是编造。' },
},
signals: [['agent tool call', 16], ['tool call', 12], ['approval gate', 10], ['human in the loop', 9], ['mcp', 7], ['planner', 6], ['agent loop', 10], ['智能体工具调用', 16], ['工具调用', 12], ['审批门', 10], ['人在回路', 9], ['规划器', 6], ['智能体循环', 10]],
en: {
title: 'Agent tool-call loop', question: 'How does an agent plan, get permission, act, recover, and report?',
summary: 'A lane-based agent loop with policy gates, tool execution, exception recovery, evidence, and final response.',
useWhen: 'Explaining agent runtimes, MCP/tool orchestration, approvals, retries, or observability.',
avoidWhen: 'The goal is only to show static agent components or exact API message timing.',
include: ['request and planning', 'policy or approval gate', 'tool execution', 'exception and evidence paths'],
prompt: 'Use Archify workflow mode to explain this agent tool-call loop. Separate user surface, agent runtime, policy boundary, exception handling, tool execution, and observability into lanes. Make the successful path primary and show approval, retry, blocked, and evidence paths explicitly.',
},
zh: {
title: '智能体工具调用', question: '智能体如何规划、获批、执行、恢复并汇报?',
summary: '用泳道表达策略门、工具执行、异常恢复、证据和最终回复。',
useWhen: '适合解释 Agent Runtime、MCP/工具编排、审批、重试和可观测性。',
avoidWhen: '如果只想看静态组件,或重点是精确 API 消息时序,请换其他配方。',
include: ['请求与规划', '策略或审批门', '工具执行', '异常与证据路径'],
prompt: '用 Archify 工作流模式解释这段智能体工具调用。把用户界面、Agent Runtime、策略边界、异常处理、工具执行和可观测性分成泳道;突出成功主路径,并明确展示审批、重试、阻塞和证据路径。',
},
},
{
id: 'delivery-workflow', type: 'workflow', proof: 'delivery-workflow',
presentation: { preset: 'classic', motion: 'trace' },
signals: [['ci/cd', 14], ['release workflow', 14], ['deployment pipeline', 11], ['pull request', 7], ['staging', 7], ['rollback', 8], ['发布流程', 14], ['流水线', 9], ['上线', 7], ['预发', 7], ['回滚', 8], ['审批发布', 10]],
en: {
title: 'Delivery workflow', question: 'How does a change move safely from commit to production?',
summary: 'A delivery flow with build, checks, environments, approvals, smoke tests, rollback, and ownership lanes.',
useWhen: 'CI/CD design, release reviews, deployment governance, or onboarding developers to delivery.',
avoidWhen: 'The question is where infrastructure runs or what states a deployment object can occupy.',
include: ['trigger and build', 'blocking checks', 'approval and environments', 'rollback and verification'],
prompt: 'Use Archify workflow mode to draw this delivery process from commit to production. Separate developer, CI, approval, environment, and exception lanes; mark blocking checks, smoke tests, ownership, and the rollback path. Keep one unmistakable happy path.',
},
zh: {
title: '研发交付流程', question: '一次变更如何安全地从提交走到生产?',
summary: '展示构建、检查、环境、审批、冒烟、回滚和负责人泳道。',
useWhen: '适合 CI/CD 设计、发布评审、部署治理和研发新人上手。',
avoidWhen: '如果重点是基础设施位置或部署对象的状态集合,请换架构图或生命周期图。',
include: ['触发与构建', '阻断检查', '审批与环境', '回滚与验证'],
prompt: '用 Archify 工作流模式绘制从代码提交到生产发布的流程。拆分开发者、CI、审批、环境和异常泳道;标出阻断检查、冒烟测试、负责人和回滚路径,并保留一条一眼可见的成功主路径。',
},
},
{
id: 'incident-runbook', type: 'workflow', proof: 'incident-runbook',
presentation: { preset: 'signal-flow', motion: 'trace' },
signals: [['incident response', 15], ['runbook', 12], ['outage', 9], ['triage', 8], ['mitigation', 8], ['escalation', 7], ['事故处置', 15], ['故障', 9], ['应急预案', 12], ['排障', 9], ['缓解', 7], ['升级响应', 8]],
en: {
title: 'Incident runbook', question: 'How do responders detect, triage, mitigate, verify, and escalate?',
summary: 'An operational workflow that separates signals, responders, mitigation, communications, and recovery proof.',
useWhen: 'Incident playbooks, on-call handoffs, reliability reviews, and tabletop exercises.',
avoidWhen: 'The audience needs live metrics or a post-incident component topology instead of response actions.',
include: ['detection signal', 'triage owner', 'mitigation and rollback', 'verification and communication'],
prompt: 'Use Archify workflow mode to turn this incident runbook into responder lanes. Show detection, triage, mitigation, escalation, communication, rollback, and recovery verification. Separate decision gates from actions and make missing ownership visible.',
},
zh: {
title: '事故处置 Runbook', question: '响应者如何发现、分诊、缓解、验证并升级?',
summary: '把信号、响应者、缓解动作、沟通和恢复证据拆成可执行流程。',
useWhen: '适合故障预案、On-call 交接、稳定性评审和桌面演练。',
avoidWhen: '如果受众需要实时指标仪表盘或事故后的组件拓扑,而不是响应动作,请换其他视图。',
include: ['发现信号', '分诊负责人', '缓解与回滚', '恢复验证与沟通'],
prompt: '用 Archify 工作流模式把事故处置预案画成响应者泳道。展示发现、分诊、缓解、升级、沟通、回滚和恢复验证;把决策门与操作分开,并让缺失的负责人清晰可见。',
},
},
{
id: 'api-request', type: 'sequence', proof: 'cache-miss',
presentation: { preset: 'classic', motion: 'trace' },
start: {
en: { descriptionPrompt: 'Use Archify sequence mode to draw this interaction: [paste the participants, calls, returns, fallback, and asynchronous side effects]. Keep message order unambiguous, labels short, and unknown behavior explicit. No repository is required.' },
zh: { descriptionPrompt: '用 Archify 时序模式绘制下面的交互:[粘贴参与者、调用、返回、回退和异步副作用]。确保消息顺序无歧义、标签简短,并明确标注未知行为。不需要代码库。' },
},
signals: [['api request', 14], ['request response', 12], ['call chain', 11], ['cache miss', 13], ['jwt', 8], ['who calls whom', 12], ['api 请求', 14], ['请求响应', 12], ['调用链', 11], ['缓存未命中', 13], ['谁调用谁', 12], ['鉴权链路', 9]],
en: {
title: 'API request chain', question: 'Who calls whom, in what order, and what returns?',
summary: 'A time-ordered request path with authentication, cache fallback, persistence, return traffic, and async trace.',
useWhen: 'API documentation, debugging request latency, auth reviews, or explaining cache fallback.',
avoidWhen: 'Order is unimportant and the audience only needs the stable service topology.',
include: ['callers and callees', 'request and return messages', 'fallback or error path', 'async side effects'],
prompt: 'Use Archify sequence mode to show this request from caller to final response. Include authentication, cache hit or miss, persistence fallback, return messages, and asynchronous trace or event emission. Keep message labels short and order unambiguous.',
},
zh: {
title: 'API 请求链', question: '谁调用谁、顺序如何、最终返回什么?',
summary: '按时间展示鉴权、缓存回退、持久化、返回流量和异步追踪。',
useWhen: '适合 API 文档、请求耗时排查、鉴权评审和缓存回退说明。',
avoidWhen: '如果顺序不重要,受众只需要稳定的服务拓扑,请用架构图。',
include: ['调用方与被调用方', '请求与返回消息', '回退或错误路径', '异步副作用'],
prompt: '用 Archify 时序模式展示从调用方到最终响应的完整请求。包含鉴权、缓存命中或未命中、持久化回退、返回消息,以及异步 Trace 或事件上报;消息标签保持简短,顺序必须明确。',
},
},
{
id: 'async-roundtrip', type: 'sequence', proof: 'async-roundtrip',
presentation: { preset: 'signal-flow', motion: 'trace' },
signals: [['async roundtrip', 14], ['webhook', 10], ['callback', 10], ['acknowledgement', 8], ['timeout', 7], ['retry message', 8], ['异步回调', 14], ['回调', 10], ['确认消息', 8], ['超时', 7], ['消息重试', 9], ['webhook', 10]],
en: {
title: 'Async roundtrip', question: 'What happens after the initial request returns?',
summary: 'A sequence view of enqueue, acknowledgement, background work, callbacks, retries, timeout, and final consistency.',
useWhen: 'Webhooks, jobs, queues, payment callbacks, eventual consistency, or async API contracts.',
avoidWhen: 'The primary question is topic topology and consumer ownership rather than time order.',
include: ['initial acknowledgement', 'queue or scheduler', 'background work', 'callback, retry, and timeout'],
prompt: 'Use Archify sequence mode to explain this asynchronous roundtrip. Show the initial acknowledgement, enqueue or scheduling step, background processing, callback or polling, retry and timeout behavior, and the point where the caller can observe final consistency.',
},
zh: {
title: '异步往返链路', question: '初始请求返回之后,后台还会发生什么?',
summary: '按时间展示入队、确认、后台处理、回调、重试、超时和最终一致。',
useWhen: '适合 Webhook、后台任务、队列、支付回调、最终一致和异步 API 契约。',
avoidWhen: '如果重点是 Topic 拓扑和消费者归属,而不是时间顺序,请用事件数据流配方。',
include: ['初始确认', '队列或调度器', '后台处理', '回调、重试与超时'],
prompt: '用 Archify 时序模式解释这段异步往返链路。展示初始确认、入队或调度、后台处理、回调或轮询、重试与超时,以及调用方何时能观察到最终一致结果。',
},
},
{
id: 'data-lineage', type: 'dataflow', proof: 'product-analytics',
presentation: { preset: 'classic', motion: 'trace' },
signals: [['data lineage', 15], ['etl', 12], ['warehouse', 9], ['pii', 11], ['governance', 9], ['analytics pipeline', 12], ['数据血缘', 15], ['数据管道', 11], ['数仓', 9], ['治理', 9], ['隐私数据', 10], ['用户同意', 9]],
en: {
title: 'Data lineage', question: 'Where does data come from, how does it change, and who consumes it?',
summary: 'A governed path from sources through consent, transforms, sensitive stores, warehouse, and consumers.',
useWhen: 'Analytics architecture, ETL/ELT review, PII assessment, warehouse design, or model feature lineage.',
avoidWhen: 'The audience needs request timing or operational task ownership rather than data assets.',
include: ['sources and assets', 'transform stages', 'classification or consent', 'stores and consumers'],
prompt: 'Use Archify dataflow mode to map this data lineage. Name every data asset and transform, show consent or classification boundaries, distinguish streaming from batch paths, and identify stores plus downstream consumers. Do not use unlabeled flows.',
},
zh: {
title: '数据血缘', question: '数据从哪里来、如何变化、最终被谁消费?',
summary: '从来源经过同意、转换、敏感存储、数仓直到消费者的治理路径。',
useWhen: '适合分析架构、ETL/ELT 评审、PII 评估、数仓设计和特征血缘。',
avoidWhen: '如果受众需要请求时序或操作负责人,而不是数据资产,请换其他配方。',
include: ['数据来源与资产', '转换阶段', '分类或同意边界', '存储与消费者'],
prompt: '用 Archify 数据流模式梳理这段数据血缘。为每个数据资产和转换命名,展示用户同意或数据分类边界,区分流式与批处理路径,并标明存储和下游消费者;所有数据流都必须有标签。',
},
},
{
id: 'event-stream', type: 'dataflow', proof: 'event-stream',
presentation: { preset: 'signal-flow', motion: 'trace' },
start: {
en: { descriptionPrompt: 'Use Archify dataflow mode to map this data journey: [paste the sources, data assets, transforms, stores, boundaries, and consumers]. Label every flow, distinguish streaming from batch where relevant, and mark unknown classifications or ownership instead of inventing them.' },
zh: { descriptionPrompt: '用 Archify 数据流模式梳理下面的数据路径:[粘贴来源、数据资产、转换、存储、边界和消费者]。为每条数据流标注名称,在有意义时区分流式与批处理,未知的分类或归属要标明而不是编造。' },
},
signals: [['event stream', 15], ['kafka topology', 14], ['topic', 8], ['consumer group', 11], ['dead letter', 10], ['dlq', 10], ['事件流', 15], ['kafka 拓扑', 14], ['主题', 7], ['消费者组', 11], ['死信', 10], ['事件地铁图', 12]],
en: {
title: 'Event-stream topology', question: 'Which events move through which topics, processors, groups, and failure paths?',
summary: 'A stream map of producers, topics, ordered processors, consumer groups, state, replay, and DLQ.',
useWhen: 'Kafka/event-platform design, stream processing reviews, ownership, replay, and failure handling.',
avoidWhen: 'Topic names, consumer groups, and delivery semantics are not known—use a generic workflow instead.',
include: ['producers and event names', 'topics and ordering', 'processors and consumer groups', 'state, replay, and DLQ'],
prompt: 'Use Archify dataflow mode to draw this event-stream topology. Name producers, events, topics, ordered processors, consumer groups, state stores, replay paths, and the DLQ. Show ownership and delivery semantics only when supported by evidence.',
},
zh: {
title: '事件流拓扑', question: '哪些事件经过哪些 Topic、处理器、消费者组和失败路径?',
summary: '展示生产者、Topic、有序处理器、消费者组、状态、重放和 DLQ。',
useWhen: '适合 Kafka/事件平台设计、流处理评审、归属、重放和失败处理。',
avoidWhen: '如果 Topic、消费者组和投递语义都不清楚,请先用通用工作流,不要编造事件拓扑。',
include: ['生产者与事件名', 'Topic 与顺序', '处理器与消费者组', '状态、重放与 DLQ'],
prompt: '用 Archify 数据流模式绘制这段事件流拓扑。命名生产者、事件、Topic、有序处理器、消费者组、状态存储、重放路径和 DLQ;只有在证据充分时才标注归属和投递语义。',
},
},
{
id: 'object-lifecycle', type: 'lifecycle', proof: 'agent-run',
presentation: { preset: 'classic', motion: 'trace' },
start: {
en: { descriptionPrompt: 'Use Archify lifecycle mode to model this object: [paste its states, transition events, waits, retries, cancellation, and terminal outcomes]. Separate active, waiting, recoverable-failure, and terminal states, and never hide an ending. No repository is required.' },
zh: { descriptionPrompt: '用 Archify 生命周期模式建模这个对象:[粘贴它的状态、转换事件、等待、重试、取消和终态]。分开执行、等待、可恢复失败和终态,不要隐藏任何结束方式。不需要代码库。' },
},
signals: [['state machine', 15], ['object lifecycle', 14], ['status transition', 11], ['terminal state', 9], ['retry state', 8], ['状态机', 15], ['生命周期', 13], ['状态流转', 11], ['终态', 9], ['等待态', 8], ['重试状态', 8]],
en: {
title: 'Object lifecycle', question: 'Which states exist, what events move between them, and how does it end?',
summary: 'A state model with active work, waits, retries, cancellation, failure, and explicit terminal outcomes.',
useWhen: 'Tasks, orders, tickets, subscriptions, jobs, agent runs, or any durable object with status.',
avoidWhen: 'The object has no durable state and the real question is participant interaction over time.',
include: ['start and active states', 'event-labelled transitions', 'wait and retry states', 'all terminal outcomes'],
prompt: 'Use Archify lifecycle mode to model this object. Separate main progress, waiting or interruption states, and terminal outcomes. Label transitions with events, include retry, cancellation, timeout, success, and failure where real, and never hide an ending.',
},
zh: {
title: '对象生命周期', question: '有哪些状态、什么事件触发流转、最终如何结束?',
summary: '展示执行、等待、重试、取消、失败以及明确终态的状态模型。',
useWhen: '适合任务、订单、工单、订阅、作业、Agent Run 等带持久状态的对象。',
avoidWhen: '对象没有持久状态,真正问题是参与者随时间的交互时,请使用时序图。',
include: ['开始与执行态', '带事件的转换', '等待与重试态', '所有终态'],
prompt: '用 Archify 生命周期模式建模这个对象。分开主进度、等待或中断状态和终态;用事件标注转换,并在真实存在时展示重试、取消、超时、成功和失败,不能隐藏任何结束方式。',
},
},
{
id: 'deployment-lifecycle', type: 'lifecycle', proof: 'deployment-lifecycle',
presentation: { preset: 'signal-flow', motion: 'trace' },
signals: [['deployment lifecycle', 15], ['release state', 10], ['promotion state', 9], ['approval status', 8], ['rollback state', 10], ['部署生命周期', 15], ['发布状态', 10], ['晋级', 7], ['审批状态', 8], ['回滚状态', 10]],
en: {
title: 'Deployment lifecycle', question: 'What state is a release in, and what can happen next?',
summary: 'A deployment state model covering queued, building, verifying, approval, promotion, rollback, and terminal outcomes.',
useWhen: 'Release controllers, GitOps reconciliation, environment promotion, or deployment status APIs.',
avoidWhen: 'The question is the human/CI sequence of delivery actions rather than the deployment object state.',
include: ['queued and running states', 'verification and approval', 'promotion and rollback', 'success, failure, cancellation'],
prompt: 'Use Archify lifecycle mode to model the deployment object. Show queued, building, verifying, waiting for approval, promoting, rolling back, and every terminal outcome. Label the events and guards that permit each transition.',
},
zh: {
title: '部署生命周期', question: '一次发布当前处于什么状态,下一步可能发生什么?',
summary: '覆盖排队、构建、验证、审批、晋级、回滚和终态的部署状态模型。',
useWhen: '适合发布控制器、GitOps 对账、环境晋级和部署状态 API。',
avoidWhen: '如果重点是人员与 CI 的交付动作顺序,而不是部署对象状态,请用交付工作流。',
include: ['排队与执行态', '验证与审批', '晋级与回滚', '成功、失败与取消'],
prompt: '用 Archify 生命周期模式建模部署对象。展示排队、构建、验证、等待审批、晋级、回滚以及所有终态,并标注允许每次状态转换的事件和守卫条件。',
},
},
{
id: 'layout-repair', type: 'architecture', proof: 'web-app',
presentation: { preset: 'classic', motion: 'static' },
signals: [
['layout repair', 20], ['repair order', 20], ['fix order', 20],
['viewport overflow', 20], ['overflow', 16], ['scrollheight', 16], ['scrollwidth', 16],
['overlap', 16], ['label overlap', 20], ['edge through node', 20], ['crossing', 8],
['via', 7], ['waypoint', 14],
['布局修复', 20], ['修复顺序', 20], ['视口溢出', 20], ['溢出', 16], ['滚动高度', 16], ['滚动宽度', 16],
['重叠', 16], ['标签重叠', 20], ['连线穿过节点', 20], ['连线穿节点', 20], ['交叉', 8],
['途经点', 14], ['路径点', 14], ['拐点', 14],
],
en: {
title: 'Layout repair', question: 'Why does the existing diagram overflow, overlap, or route through nodes, and what should be fixed first?',
summary: 'Repair an existing diagram in its current mode using validation diagnostics, explicit waypoint semantics, and a measured desktop viewport budget.',
useWhen: 'An existing architecture, workflow, sequence, dataflow, or lifecycle diagram needs layout repair; keep its diagram type and presentation settings.',
avoidWhen: 'The task is choosing a new diagram type. Do not change topology, delete meaningful labels, or hide overflow just to pass checks.',
include: [
'repair order: schema → overlap → direction → crossings → labels',
'via contract: absolute [x, y] intermediate points; route = [start, ...via, end]',
'desktop viewport budget for the complete page, including header and necessary cards',
'validate after each edit, then inspect the final HTML in a browser',
],
prompt: 'Use Archify to repair this existing diagram while preserving its diagram type, topology, meaningful labels, and presentation settings. Follow references/authoring-contract.md in this order: (1) schema and missing/invalid meta.quality_profile; (2) node overlap or out-of-range placement; (3) edge-through-node and endpoint-direction errors; (4) crossings, ambiguous corridors, border runs, excessive detours, and route rhythm; (5) labels: label-to-node, label-to-label, then label-to-route clearance. Run validate after every edit and use diagnostics[] code, subject, evidence, and supportedFixes; apply one diagnosed geometry control at a time. Where the current schema supports via, it is an ordered array of absolute SVG [x, y] intermediate points: the route is [start, ...via, end], with start/end supplied by the node anchors. Explicit via points override automatic routing; they are not offsets or a request for automatic obstacle avoidance. For an orthogonal repair, align adjacent points on the same x or y and make the first/final segment respect fromSide/toSide; use only controls supported by the current diagram mode. Follow references/delivery-contract.md for the viewport budget: check 1440×900, 1600×1000, 1920×1080, and 2048×1320; require document.documentElement.scrollWidth <= window.innerWidth. Prefer document.documentElement.scrollHeight <= window.innerHeight, but preserve a Reader-declared readable vertical page scroll when browser-check explicitly accepts it after reaching the projected-text floor. Budget the entire page, including header, diagram, and necessary cards. Repair every other overflow by removing only redundant content or compacting spacing first; do not hide overflow, clip content, introduce an internal diagram scroller, stretch the SVG, or shrink typography to force a pass. Let finalize run the required browser evidence; run perceptual visual review only when requested or escalated by the delivery contract.',
},
zh: {
title: '布局修复', question: '现有图为什么仍然溢出、重叠或连线穿过节点,应该先修什么?',
summary: '保留现有图表模式,依据验证诊断、途经点语义和实测桌面视口预算修复布局。',
useWhen: '已有架构图、工作流、时序图、数据流或生命周期图需要修复布局;保留原来的图表类型和表现设置。',
avoidWhen: '任务是为新图选择类型时不要使用。不要为了通过检查改变拓扑、删除有意义的标签或隐藏溢出。',
include: [
'修复顺序:schema → 重叠 → 方向 → 交叉 → 标签',
'via 契约:绝对 [x, y] 中间点;路径 = [start, ...via, end]',
'包含标题和必要卡片的整页桌面视口预算',
'每次修改后 validate,最终在浏览器中检查 HTML',
],
prompt: '用 Archify 修复这张现有图,保留图表类型、拓扑、有意义的标签和表现设置。遵循 references/authoring-contract.md 的顺序:(1) schema 错误及缺失或无效的 meta.quality_profile;(2) 节点重叠或越界;(3) 连线穿过节点及端点方向错误;(4) 交叉、含混的共享通道、贴边走线、过度绕行和转弯节奏;(5) 标签与节点、标签与标签、标签与连线的间距。每次修改后运行 validate,依据 diagnostics[] 的 code、subject、evidence 和 supportedFixes,每次只应用一项有诊断依据的几何控制。当前 schema 支持 via 时,它是按顺序排列的绝对 SVG [x, y] 中间点数组:路径为 [start, ...via, end],起终点由节点锚点提供。显式 via 会覆盖自动路由,不是偏移量,也不会请求自动绕障。修复正交走线时,相邻点应共享 x 或 y,首尾线段应遵守 fromSide/toSide;只使用当前模式支持的控制字段。视口预算遵循 references/delivery-contract.md:检查 1440×900、1600×1000、1920×1080 和 2048×1320,要求 document.documentElement.scrollWidth <= window.innerWidth。优先满足 document.documentElement.scrollHeight <= window.innerHeight;如果 browser-check 明确确认 Reader 已达到投影文字下限并接受可读的页面纵向滚动,则保留该滚动。预算覆盖整页,包括标题、主图和必要卡片。其他溢出先通过移除冗余内容或压缩间距修复,不得靠隐藏溢出、裁切、内部滚动区、拉伸 SVG 或缩小字体强行通过。让 finalize 执行必需的浏览器证据;只在用户要求或 delivery contract 规定的升级条件下进行感知视觉审阅。',
},
},
];
export const SCENARIO_RECIPES = Object.freeze(RAW_RECIPES.map((recipe) => Object.freeze({
...recipe,
presentation: Object.freeze({ ...recipe.presentation }),
...(recipe.start ? { start: Object.freeze({
en: Object.freeze({ ...recipe.start.en }),
zh: Object.freeze({ ...recipe.start.zh }),
}) } : {}),
signals: Object.freeze(recipe.signals.map((signal) => Object.freeze(signal.slice()))),
en: Object.freeze({ ...recipe.en, include: Object.freeze(recipe.en.include.slice()) }),
zh: Object.freeze({ ...recipe.zh, include: Object.freeze(recipe.zh.include.slice()) }),
})));
export function detectGuideLanguage(value = '') {
return /[\u3400-\u9fff]/u.test(value) ? 'zh' : 'en';
}
export function startPromptsFor(recipe, lang = 'en') {
const language = lang === 'zh' ? 'zh' : 'en';
const copy = recipe[language];
const descriptionPrompt = recipe.start?.[language]?.descriptionPrompt;
if (!descriptionPrompt) {
throw new Error(`Scenario recipe ${JSON.stringify(recipe.id)} does not define a ${language} start prompt.`);
}
const repositoryPrompt = recipe.type === 'architecture'
? copy.prompt
: language === 'zh'
? `先检查这个仓库里的相关证据,然后${copy.prompt}不要编造代码无法支持的行为。`
: `Inspect this repository for evidence, then ${copy.prompt.charAt(0).toLowerCase()}${copy.prompt.slice(1)} Do not invent behavior that the code does not support.`;
return { descriptionPrompt, repositoryPrompt };
}
function normalized(value) {
return String(value || '').normalize('NFKC').toLowerCase().replace(/[\s_]+/g, ' ').trim();
}
function localized(recipe, lang) {
const copy = recipe[lang === 'zh' ? 'zh' : 'en'];
return {
id: recipe.id,
type: recipe.type,
proof: recipe.proof,
presentation: { ...recipe.presentation },
...copy,
include: copy.include.slice(),
};
}
export function listScenarioRecipes(lang = 'en') {
return SCENARIO_RECIPES.map((recipe) => localized(recipe, lang));
}
function scoreRecipe(recipe, query) {
const text = normalized(query);
if (!text) return { recipe, score: 0, matched: [] };
if (text === recipe.id || text === recipe.id.replace(/-/g, ' ')) {
return { recipe, score: 100, matched: [recipe.id] };
}
let score = 0;
const matched = [];
for (const [signal, weight] of recipe.signals) {
if (text.includes(normalized(signal))) {
score += weight;
matched.push(signal);
}
}
return { recipe, score, matched };
}
export function recommendScenario(query, options = {}) {
const lang = options.lang === 'zh' || options.lang === 'en' ? options.lang : detectGuideLanguage(query);
const ranked = SCENARIO_RECIPES.map((recipe) => scoreRecipe(recipe, query))
.sort((left, right) => right.score - left.score || SCENARIO_RECIPES.indexOf(left.recipe) - SCENARIO_RECIPES.indexOf(right.recipe));
const winner = ranked[0].score > 0 ? ranked[0] : { recipe: SCENARIO_RECIPES[0], score: 0, matched: [] };
const confidence = winner.score >= 14 ? 'high' : winner.score >= 7 ? 'medium' : 'low';
return {
ok: true,
mode: 'recommendation',
lang,
query: String(query || ''),
confidence,
matchedSignals: winner.matched.slice(),
recommendation: localized(winner.recipe, lang),
alternatives: ranked.filter((entry) => entry.recipe.id !== winner.recipe.id && entry.score > 0)
.slice(0, 2)
.map((entry) => ({ ...localized(entry.recipe, lang), score: entry.score })),
};
}
export function formatScenarioList(lang = 'en') {
const isZh = lang === 'zh';
const heading = isZh
? `Archify 场景配方(${SCENARIO_RECIPES.length})`
: `Archify scenario recipes (${SCENARIO_RECIPES.length})`;
const intro = isZh
? '先选择你要回答的问题,再选择图表类型。可运行:archify guide "你的场景"'
: 'Choose the question before the diagram type. Run: archify guide "your scenario"';
return [heading, '', intro, '', ...listScenarioRecipes(lang).flatMap((recipe) => [
`${recipe.id} [${recipe.type}] ${recipe.title}`,
` ${recipe.question}`,
])].join('\n');
}
export function formatScenarioRecommendation(result) {
const isZh = result.lang === 'zh';
const recipe = result.recommendation;
const labels = isZh ? {
heading: '推荐', question: '要回答的问题', use: '适合', avoid: '不要这样用', include: '必须包含', presentation: '表现建议', prompt: '可直接复制的提示词', alternatives: '其他可能', confidence: '置信度',
} : {
heading: 'Recommendation', question: 'Question answered', use: 'Use when', avoid: 'Avoid when', include: 'Must include', presentation: 'Presentation', prompt: 'Copy-ready prompt', alternatives: 'Other possible fits', confidence: 'Confidence',
};
const lines = [
`${labels.heading}: ${recipe.title} [${recipe.type}]`,
`${labels.confidence}: ${result.confidence}`,
`${labels.question}: ${recipe.question}`,
'',
`${labels.use}: ${recipe.useWhen}`,
`${labels.avoid}: ${recipe.avoidWhen}`,
`${labels.include}: ${recipe.include.join(isZh ? '、' : '; ')}`,
`${labels.presentation}: ${recipe.presentation.preset} · ${recipe.presentation.motion}`,
'',
`${labels.prompt}:`,
recipe.prompt,
];
if (result.alternatives.length) {
lines.push('', `${labels.alternatives}: ${result.alternatives.map((item) => `${item.title} [${item.type}]`).join(' · ')}`);
}
return lines.join('\n');
}
export function publicGuideData() {
return SCENARIO_RECIPES.map((recipe) => ({
...localized(recipe, 'en'),
en: recipe.en,
zh: recipe.zh,
signals: recipe.signals.map(([signal, weight]) => [signal, weight]),
}));
}
+18
View File
@@ -0,0 +1,18 @@
# Architecture layout repair
Use this after actual visual review finds several tangled routes. A successful machine receipt does not settle composition. Work on the existing candidate, retaining all required components, relationships, labels, evidence, boundaries, and node sizes.
## Choose the repair scope
If the main and secondary chains already read clearly, repair the isolated defect locally. If a main chain is blocked, several routes tangle, or a local fix moves the defect onto another route, reflow the connected scene in one edit. Preserve all semantics and user-fixed geometry; agent-generated positions and route controls may change. After moving nodes, remove stale generated route overrides so automatic routing can use the new placement.
The receipt's `directCorridorBlockers`, when present, names nodes between an edge's aligned endpoints. This is geometric evidence, not proof that the edge is the main path or a new validation failure. Trace the reader's actual main path first. When a listed blocker interrupts that path, reposition the connected group instead of adding another detour.
## One coherent repair
1. Trace the affected relationships and their endpoints in the JSON. Before choosing coordinates, write the reader’s main path as an ordered list of existing edges, then identify shared state and real feedback cycles. Every neighboring pair in that main path must have the relationship being explained; place other components on branches beside their actual owner. Use `validate architecture <candidate.json> --layout-json --repo-root <root>` once if the compact receipt and screenshot do not reveal the needed route or label geometry; omit `--repo-root` only for a design without repository evidence. Do not guess repeated waypoint coordinates.
2. Place those connected main-path neighbors in reading order. Put shared state between its readers/writers, on an adjacent row if necessary, so one writer does not need a line across the whole execution area. Arrange a feedback cycle in its actual edge order around an open rectangle, with other consumers beside their owner. For example, if the edges are `client → API → dispatcher → worker → collector → runner → dispatcher`, put the first four on the upper row, collector below worker, and runner below dispatcher: the return then uses the lower row and a short upward edge. This illustrates adjacency, not a graph or set of coordinates to copy; use only relationships present in the candidate. Preserve every edge and its actual direction.
3. Keep automatic endpoints for the new placement. Constrain a side only when a branch or return needs a specific corridor; check that corridor against every affected relationship, including storage branches. If that local constraint introduces another conflict, return to the connected placement instead of cycling through side combinations. Only use detailed `via` or label coordinates for a remaining measured defect. Keep external actors outside the resolved boundary rectangle, including its padding; not listing a node in `wraps` does not visually exclude it. Keep an internal relationship and its label inside the shared boundary unless crossing it conveys a real fact; do not imply an external hop merely to avoid another route.
4. When `composition/label-gap` reports a measured minimum, enlarge that clear gap or move the connected group onto another readable row. Keep the full label beside its own route; placing it in distant empty space does not repair the relationship. Compact unused gaps while preserving measured label space and the previous text size. Keep the main interaction and all required nodes readable in the default desktop view; do not trade crossings for a large blank canvas, tiny text, or a chain that doubles back without a semantic reason.
Run the complete `finalize` once after the edit, then `visual-check` on the successful artifact and inspect its desktop captures. When replacing an already reviewed artifact, use one fresh `--out-dir` for both commands as described in [the delivery contract](delivery-contract.md#a-new-candidate-at-an-existing-output-path). Trace the main path, each secondary chain, and every affected arrow and label. A bounded second repair may address a remaining specific defect. If it still fails visual acceptance, retain the candidate and report the concrete gap; do not count it as a successful repair or continue blind coordinate changes.
+419
View File
@@ -0,0 +1,419 @@
# Authoring contract
Read this reference only after the Fast authoring path calls for more detail. The schemas and examples remain authoritative.
## Composition repair
When correcting an authored overview's abstraction, map every affected role, relationship direction, protocol, boundary, condition, and source reference to its surviving node or relationship before regrouping. A startup citation does not prove a message protocol. Preserve each claim's inspected evidence. Keep user-supplied or agreed topology fixed; fewer routes alone do not justify merging. A boundary around one node requires an explicit isolation fact and must not merely repeat its label.
## Label repair
When a relationship label collides, move the label, adjust the route or spacing, then shorten the wording while preserving meaning. Omit wording only when both endpoints fully imply it and it conveys no protocol, action, direction, synchronous or asynchronous behavior, or cross-boundary mechanism. Spacing means clear gap rather than center distance; measured mask width takes precedence. The first-draft gap budget is in [Layout and routing](authoring-defaults.md#layout-and-routing).
For a disproportionate sublabel, keep its exact role or protocol concise and place the supplementary fact in a note or card. Preserve every required responsibility, protocol, and boundary fact. Use the first-draft node-width budget in [Layout and routing](authoring-defaults.md#layout-and-routing).
## Schema lookup
Read both the mode schema and `schemas/common.schema.json`. The mode schemas use `$ref`, so the common file is where shared enums live.
- `componentType`: `frontend`, `backend`, `database`, `cloud`, `security`, `messagebus`, `external`
- `variant`: `default`, `emphasis`, `security`, `dashed`
- Relationship IDs use the shared identifier pattern and must be unique in their collection.
Do not invent fields. Before writing any new field, enum, or constrained text, read its schema definition, including common `$ref` targets. In particular, check boundary kinds, repository identity, and source-reference shapes. An example demonstrates structure; it does not enumerate every valid value. Author fresh IDs, wording, facts, and layout.
## Workflow layout contracts
Use schema v2 for new workflows and keep schema v1 when an existing source must
retain fixed geometry. In both versions, `col` stays in `0..5` and semantic
edge labels are never deleted as a spacing repair. Do not change only
`schema_version` when absolute coordinates exist: follow the canonical
[migration and layout-receipt contract](../renderers/workflow/README.md#migration-and-layout-receipt).
The complete normative invariants live in the workflow renderer's
[layout contracts](../renderers/workflow/README.md#layout-contracts).
For sequential stages stacked in one container, use one v2 lane and group,
omit `meta.viewBox`, and center nodes around the lane content with symmetric
`yOffset` values such as `-90 / 0 / 90`. Keep semantic edge labels and act on
compiler diagnostics.
## Legend contract
Omit `meta.legend` for the truthful default: `auto` lists only semantic kinds
present in typed IR. Use `mode: "all"` for a renderer reference or
`mode: "hidden"` to remove the full legend. Under `entries`, only keys listed
by the selected mode schema are valid; each key accepts `label`, `visible`, or
both. `visible: true` may show an unused supported convention, while
`visible: false` hides it. `hidden` cannot be overridden.
A label override changes reader wording only. Never infer a kind from prose or
use the legend to compensate for missing nodes, states, messages, or flows.
Long labels are measured and wrap into deterministic rows. Architecture's
implicit automatic viewBox grows from that same measured footprint. For
backwards compatibility, a legacy document with no `meta.legend` may omit an
implicit auto legend that cannot fit its explicit viewBox; this never changes
its typed topology. Adding `meta.legend` makes the presentation intentional and
strict: if its resolved labels cannot fit the authored viewBox, shorten or hide
them, or widen the viewBox using the emitted diagnostic.
## Language consistency
Choose one primary authored language. An explicit user choice wins; otherwise
use the language of the request, or the conversation's dominant language when
the request itself is language-neutral. Separately choose the Viewer locale.
Always write the matching `meta.locale` as a well-formed language tag: `"en"`
for English, `"zh-CN"` for Simplified Chinese, `"es"` for Spanish, or any
other tag for another language. The renderer consumes the authored locale without inferring language
from diagram strings. Documents that omit it remain valid and default to
English.
`meta.locale` controls only renderer-owned reader surfaces: `<html lang>`, the
document-title suffix, default SVG description and focus labels, default legend
labels, and fixed Viewer controls, statuses, accessibility names, and errors.
It never translates authored content. Apply the primary language separately to
titles, subtitles, node and relationship copy, boundaries, lanes, groups,
legend label overrides, and cards. A bilingual diagram still
chooses one primary locale for the Viewer; follow an explicit primary-language
request, then prompt order or conversation dominance.
`en` and `zh-CN` are built-in Viewer catalogs and need nothing further. For
every other `meta.locale`, also set `meta.translations`: an object mapping the
renderer's canonical message keys (`catalogKeys()` in
`renderers/shared/i18n.mjs`) to translated strings whose `{placeholder}` tokens
match the English source exactly. Reuse suitable translations from `examples/locales/` or a previously reviewed
catalog; Spanish uses `examples/locales/es.json`. Translate missing keys or adapt terminology when the diagram needs it;
use the English source to check keys and placeholders. Example catalogs may
lag new Viewer keys; validation reports those gaps and uses English for them. A key that is missing, unrecognized, or has mismatched
placeholders falls back to its English string — `validate`/`render`/`deliver`
report the resulting coverage to stderr — rather than breaking the render or
silently shipping an untranslated string as if it were translated.
For older dev inputs using only `meta.locale: "es"`, copy the Spanish catalog
into `meta.translations` before rendering again. Existing standalone HTML
keeps its embedded translations.
For a requested language you cannot supply `meta.translations` for, do not
write a `meta.locale` with no built-in catalog and no translations. Keep every
reader-facing authored string in the requested language, omit `meta.locale` so
the renderer safely uses English, and explicitly tell the user that fixed
Viewer UI and `<html lang>` remain English and the artifact is not fully localized.
The fallback applies only to renderer-owned surfaces; it never
permits authored copy to fall back to English. Do not silently substitute
`zh-CN` for another language or Chinese locale, and do not machine-translate
`meta.translations` values without disclosing that they are unreviewed.
Keep exact product names, code identifiers, commands, protocols, API paths, and
environment names intact. Those terms may remain English inside localized copy,
but surrounding explanatory prose must still use the selected language.
Renderer-owned default legend labels follow `meta.locale`; author a
`meta.legend.entries.*.label` override only when the diagram needs different
domain wording, and keep that authored override in the primary language.
## Visual preset default
Omit `meta.visual_preset` by default. The renderer then opens the diagram in
`classic` for both light and dark color modes. Color mode and visual preset are
independent viewer state: switching Light / Dark must preserve the current
preset. Author `signal-flow`, `blueprint`, or `editorial` only when the user
explicitly requests that visual style.
## Engineering profile default
Omit `meta.engineering_profile` for an ordinary system architecture. Region,
cluster, and security boundary wording do not by themselves enable an
engineering profile. Enable `deployment-ownership` only when the user
explicitly asks for a production deployment topology, ownership handoff, or
fail-closed deployment review and the source facts are known. Once enabled,
do not remove the engineering profile merely to pass validation; repair the
authored facts or report the diagnostics truthfully.
## Title hierarchy
Use one concise title and let the diagram carry the explanation. Omit
`meta.subtitle` by default, and never use it to restate the title, nodes, edges,
or cards. Include one short supporting line only when the user explicitly asks
for a subtitle; an omitted or blank subtitle must not leave an empty visual row
in the generated viewer.
## Executable geometry rules
Generate one responsive artifact for laptops and external displays, preserving the authored SVG/viewBox, proportions, semantic geometry, and normal document flow. Use meaningful content rows and the Reader-declared readable page-scroll behavior when the complete diagram needs more height; viewport fitting does not authorize alternate topology or smaller typography.
- Node anchors start at side midpoints. `left`/`right` change the horizontal endpoint; `top`/`bottom` change the vertical endpoint. For an automatic Architecture relationship, unobstructed facing ports whose axis offset is under 16px may share one horizontal or vertical axis when both endpoints retain the 16px corner gutter. If exactly one endpoint belongs to a spread group, only its unshared counterpart moves; relationships spread at both endpoints keep their distinct ports and outside bridge unless a reciprocal facing pair can jointly use separate straight lanes while preserving endpoint spacing, labels, and all surrounding route and obstacle clearances.
- A side is a direction contract. The first and final route segment must be perpendicular and outward/inward in the named direction.
- In architecture, data-flow, and lifecycle diagrams, explicit `route: "straight"` requests one direct segment, which may be diagonal when endpoint sides are not pinned. The artifact checker preserves this intent; explicit sides, opaque-node clearance, and other quality gates still apply. `via` takes precedence and retains existing rules, including data-flow's requirement for orthogonal via segments.
- Automatic Port Spread is a default renderer behavior for architecture, workflow, data-flow, and lifecycle diagrams. Shared automatic endpoints spread deterministically and symmetrically with a 16px corner gutter. It does not apply to sequence messages, single relationships, or explicit `via`, `channelX`, `channelY`, `labelAt`, or non-`auto` routes.
- Showcase route rhythm: every nonzero segment must be at least 8px; every interior segment must be at least 16px. When spread ports are nearly parallel, the router uses a 24px endpoint stub and a 16px outside bridge instead of manufacturing a tiny dogleg.
- Showcase route compactness: an explicit Architecture route fails with `composition/excessive-route-detour` when its orthogonal length is at least 2.5 times an obstacle-aware legal route, adds at least 200px, and sends a control point at least 96px beyond the content envelope. The evidence records both lengths, ratio, excess, bounds, and excursion. Remove an unnecessary `via` or move the diagnosed corridor inward instead of enlarging the canvas. Related relationships that overlap on the same outer corridor by at least 32px are treated as an intentional bus and remain valid.
- Shared endpoint corridors are allowed only when they remain semantically unambiguous. Unrelated collinear overlap of 8px or more fails showcase.
- Container borders are intentional pass-through geometry, but a long edge running along a structural border is not.
- An edge crossing an unrelated opaque node is always a hard failure, independent of quality profile.
### Explicit `via` coordinates
Use the resolved departure anchor `S = [sx, sy]` and arrival anchor
`T = [tx, ty]`. Anchors start at side midpoints, but automatic routing and
Port Spread can move them as described above; do not assume an anchor copied
from an automatic route is the anchor of a newly authored explicit route.
Explicit `via` routes do not receive automatic Port Spread.
For the first waypoint `F = via[0]` and last waypoint `L = via[via.length - 1]`,
use these alignments and directions (SVG y increases downward):
| Side | Departure (`fromSide`): `S` → `F` | Arrival (`toSide`): `L` → `T` |
| --- | --- | --- |
| `top` | `F[0] === sx`, `F[1] < sy` | `L[0] === tx`, `L[1] < ty` |
| `bottom` | `F[0] === sx`, `F[1] > sy` | `L[0] === tx`, `L[1] > ty` |
| `left` | `F[1] === sy`, `F[0] < sx` | `L[1] === ty`, `L[0] < tx` |
| `right` | `F[1] === sy`, `F[0] > sx` | `L[1] === ty`, `L[0] > tx` |
For example, given a bottom departure anchor `S = [180, 160]` and a left
arrival anchor `T = [360, 260]`, this relationship fragment leaves downward
and enters the target rightward:
```json
{
"from": "source",
"to": "target",
"fromSide": "bottom",
"toSide": "left",
"via": [[180, 200], [300, 200], [300, 260]]
}
```
The full path is `[180, 160] → [180, 200] → [300, 200] → [300, 260] → [360, 260]`.
Changing only the first waypoint to `[200, 200]` makes the departure diagonal;
changing it to `[180, 120]` keeps its x aligned but leaves upward through the
source instead of outward from its bottom. Both violate `fromSide: "bottom"`
and produce `clean-flow/endpoint-side-direction`. The example establishes
endpoint direction only: keep the full route clear of unrelated nodes and
apply the other geometry rules above.
### Spacing and labels
In showcase Architecture, an unpinned connection label keeps its default position
when clear. If it collides, the renderer tries a bounded set of nearby positions
along the existing route, avoiding nodes, boundary titles, other labels and
other routes within the resolved canvas. Explicit `labelAt`, `labelDx`, `labelDy`
or `labelSegment` (including zero) disables this fallback. Routes and topology
stay unchanged; if no nearby position is clear, validation reports the original
collision. Inspect resolved labels with `--layout-json` before adding controls.
Standard placement retains its existing behavior.
Spacing recommendations mean clear gap between boxes, not center distance. A 200px center distance between 165px-wide nodes leaves only 35px of clear gap.
For a relationship label, require:
```text
clear gap > label mask width + 8px breathing room
label mask width ≈ 6.5px × ASCII units + 13px
CJK characters count as two units
```
Relationship labels are semantic data. If the gap is too small, move the label,
adjust the route or spacing, then shorten the wording while preserving meaning.
Omit only wording already fully implied by both endpoints and carrying no
protocol, action, direction, synchronous/asynchronous behavior, or
cross-boundary mechanism. Preserve every meaningful label.
Deleting it is not a spacing repair. If a relationship starts unlabeled because
its endpoints fully imply it, explain why the wording is redundant; this is a
semantic authoring choice, not a spacing repair. In workflow v2, let the compiler
allocate its measured mask before applying a diagnosed `labelAt`,
`labelDx`/`labelDy`, or `labelSegment`. Apply one diagnosed geometry control at
a time unless several edges share a constrained channel. In that case, plan the smallest coupled change from measured geometry and
validate it together. Architecture/workflow provide layout evidence through
`validate <type> <candidate.json> --layout-json`; for other types, use validation
diagnostics and the rendered SVG geometry.
Before adding manual routes, check whether unnecessary agent-added controls
disable automatic port spread; preserve user-required route intent. Use the
measured clearance rules above rather than guessing coordinates.
### Repair evidence
For architecture, `validate architecture <input.json> --layout-json` exposes the
resolved component boxes, boundary frames, connection points, and label positions.
A measurable rejected layout also returns these fields, with `ok: false`,
`contract: "archify-architecture-layout-v1"`, diagnostics, and exit 1. This is
repair evidence, not artifact acceptance; it writes no HTML. Malformed input or
an implementation failure retains the ordinary failure receipt without layout.
Use the measured failing side for `layout/boundary-out-of-bounds`. Left/top
negative coordinates need an inward move; increasing viewBox width/height only
addresses right/bottom overflow. Boundaries may wrap members across rows. Keep
real membership intact and recheck connected routes after moving members.
Automatic architecture canvases include route points as well as nodes, frames,
and labels. An authored viewBox remains authoritative. In showcase,
`layout/route-out-of-bounds` identifies clipped route points; negative coordinates
need an inward route, while right/bottom overflow can also use a larger authored
canvas. Recheck desktop readability after enlarging a canvas.
When several crossing/corridor diagnoses involve the same nodes, consider their
placement together before adding route controls. Apply one coherent repair and
validate it; independent label nudges cannot fix a shared layout bottleneck.
Compare diagnostics by code, subject, and stage instead of total count alone.
### Repair order
1. Fix missing/invalid `meta.quality_profile` and schema errors.
2. Fix node overlap or out-of-range placement.
3. Fix edge-through-node and endpoint-direction errors.
4. Fix crossings, ambiguous corridors, border runs, excessive detours, and route rhythm.
5. Fix label-to-node, label-to-label, then label-to-route clearance.
6. Fix labels that leave the canvas: move the label with `labelAt`/`labelDx`/`labelDy`/`labelSegment`, or widen `meta.viewBox`. Suggested `labelDx`/`labelDy` values replace the authored field; they are not added to it.
Run `validate` after every edit. Consume `diagnostics[]` by stable `code`, exact `subject`, measured `evidence`, and `supportedFixes`. If the diagnostic gives `labelAt`, use that point instead of estimating another offset.
## Mode placement
### Architecture
Choose overview or mechanism detail using [Composition and meaning](authoring-defaults.md#composition-and-meaning). Use one obvious primary reading path, which may step across meaningful rows when the requested topology needs room. Keep the overview readable at its chosen abstraction; expand implementation details when they answer the reader's question. Group only real ownership, trust, process, or deployment boundaries. Boundaries do not replace relationships.
Grid placement is preferred when the schema supports it. Free positions are appropriate for a bounded exception, not for prose-level coordinate planning. Keep external actors outside the system boundary when that is factually true.
### Workflow
Lanes express responsibility or phase. Columns `0..5` express logical
progression. Start new workflows on `readable-v2`; retain `fixed-v1` only for
legacy geometry compatibility. Keep the happy path monotonic, preserve semantic
edge labels, and route retries and exception returns outside the main lane
corridor.
#### Workflow viewport repair
When `viewer/viewport-overflow` includes `workflowLanes`, inspect the tallest
rendered frames and their node span before changing the source. Measurements
are CSS pixels; space above/below nodes includes lane titles and routing, so it
is not a removable-space budget. Frame IDs identify rendered lane indices.
Run `validate workflow <source.json> --layout-json` and match those frames to
source lanes and nodes. Check whether many steps share the last logical column
and use large `yOffset` values. Readable-v2 currently reserves symmetric space
around offsets and shares the base content height between lanes, so increasing
one offset can enlarge otherwise sparse lanes.
Where the source's ownership and explicit geometry permit, redistribute steps
across logical columns and meaningful lanes, keeping the main path monotonic.
Preserve every required node, relationship, label and semantic check. If ownership
or absolute pins prevent reflow, report that constraint instead of merging lanes
or moving pins automatically. Validate the changed JSON, deliver a fresh HTML,
then rerun browser checks and inspect the first screen; a static pass alone does
not settle viewport fit. These are repair directions, not guaranteed coordinates.
### Sequence
Participants are ordered by conversation role. Messages own their vertical order. Use return/async/security variants for meaning, not decoration; sequence does not use Automatic Port Spread.
### Dataflow
Stages express transformation or custody. Rows separate parallel streams. Label only data contracts, classifications, or cross-boundary movement that is not obvious.
### Lifecycle
Schema v2 (new diagrams): each populated lane is one row, `main` first,
`terminal` last, others in `lanes[]` order. `col` `0..4` is one shared x grid,
so a state placed in the column of the state it leaves gets a straight vertical
transition. Every transition, including the main path, is authored; there is no
implied rail. The renderer sizes the canvas, widens a column gap for a
same-row label, and routes automatic transitions orthogonally through row gaps.
Keep labels short: a gap carrying several parallel lines has little room.
Schema v1 (legacy): main phases use columns `0..4`; event and terminal bands
use columns `0..2`, and event/terminal column `N` aligns with main column
`N + 2`. Every lane other than `main` and `terminal` shares one middle band;
states in the same column there need distinct `yOffset` values.
In both versions a recoverable failure needs a real transition back to an
active state. A card saying “retry” is not topology.
## Repository evidence
When the diagram must reflect real code, inspect repository entrypoints,
runtime boundaries, storage, transports, and deployment configuration before
authoring. Record only evidence you actually verified. `--repo-root <path>` is
accepted by `render`, `validate`, `deliver`, and `preview` for every diagram
type, by architecture `compare`, and by workflow `migrate`; every mode verifies `meta.repository` and
node `sources` the same way. Migrating a source-backed workflow requires the same
`--repo-root` so its candidate is verified before replacing the destination.
Never infer runtime causality from file proximity
or naming alone.
Declare `meta.repository.url` and one full 40-character `revision`, then attach
`sources` to the mode's node collection (Architecture `components[]`, Workflow
and Data Flow `nodes[]`, Sequence `participants[]`, Lifecycle `states[]`) with
repository-relative `path`, optional `line`, `end_line`, and `label`.
Verification reads blobs at that commit, independently of working-tree edits.
Verification ignores local Git replacement refs, including those selected by
`GIT_REPLACE_REF_BASE`, and always reads the original objects at the pinned SHA.
It does not change repository configuration or delete replacement refs.
A matching local origin, available commit, bounded path,
blob, and valid line range are required in every link mode. Verification is
local and makes no remote requests; it establishes neither public availability
nor the current reader's access rights.
`link_mode` defaults to `web`. GitHub and Gitee HTTPS repository URLs generate
revision-pinned links; their public hosts select the provider automatically.
Optional `provider: "github"` or `"gitee"` must agree with the host. Existing
GitHub declarations and default delivery receipt fields remain compatible.
```json
{
"url": "https://gitee.com/team/service",
"revision": "0123456789abcdef0123456789abcdef01234567",
"provider": "gitee"
}
```
For an internal or unsupported forge, select `link_mode: "local-only"`. The
Viewer retains SRC markers, searchable file paths, line ranges, and revision
labels without repository or source hyperlinks. The evidence receipt adds
`linkMode: "local-only"`. `url` remains required as the expected origin identity;
local-only disables links, not identity verification. A repository without an
origin is not supported.
```json
{
"url": "http://git.internal:3000/Platform/Services/service",
"revision": "0123456789abcdef0123456789abcdef01234567",
"link_mode": "local-only"
}
```
Local-only accepts HTTP(S), `git@host:path`, and `ssh://git@host[:port]/path`
addresses, including nested namespaces. Declare a credential-free address;
HTTP(S) credentials on the checkout's origin are ignored for identity and
redacted from diagnostics. Hostnames compare case-insensitively; repository
paths retain case except for the existing GitHub behavior. A trailing slash
normalizes away. Only GitHub and Gitee normalize a terminal `.git` and match
standard HTTPS/443 with Git SSH/22. For other hosts, use the actual clone address:
transport, port, `.git` suffix, and remote-relative versus absolute paths must
match. For example, `git@host:Team/repo` differs from
`ssh://git@host/Team/repo`; `git@host:/Team/repo` matches the latter. SCP-style
paths preserve literal percent escapes, while URI paths decode them. SSH host
aliases and forge-specific browse/clone prefixes are not guessed.
GitLab/Gitea/Forgejo/Bitbucket web links are not implemented in this version;
use local-only until a tested link provider is available. Unknown web providers
fail with a diagnostic rather than emitting a guessed link.
## Hand-placed fallback
Use only when no renderer can run. Start from `assets/template.html`, keep semantic CSS classes, preserve the inline SVG/accessibility structure, and run the delivery visual checklist. Never introduce inline literal colors that break dark/light parity.
## Node icons
For domain-specific diagrams, set an optional `icon` on architecture components,
workflow/dataflow nodes, sequence participants, or lifecycle states. Choose
`calendar`, `clock`, `person`, `briefcase`, `flag`, or `moon` for everyday concepts;
the complete catalog (including existing technical and lifecycle symbols) is
`common.schema.json#/$defs/nodeIcon`. Use `icon: "none"` to hide the corner symbol.
Omitting `icon` keeps the type-based default. These inline SVG symbols are
renderer-owned and export with the diagram; URLs and raw SVG are not accepted.
Icon selection changes only the corner symbol. The node's type still determines
color and semantic grouping; brand marks remain independent. For a holiday
workflow, pair `type: "backend", icon: "calendar"` with
`meta.legend.entries.backend.label: "假期"`, and use `icon: "briefcase"` plus
an appropriate legend label for make-up work. Keep the node label meaningful:
icons are decorative and are hidden from assistive technology.
See [holiday planning](../examples/holiday-planning.workflow.json) for a complete workflow example.
+41
View File
@@ -0,0 +1,41 @@
# Authoring defaults
Read once before writing a fresh candidate. An existing frozen candidate going straight to `finalize` needs this only if repair changes its authorship.
## Composition and meaning
For Architecture, default to a system overview led by the main user journey unless the user asks for a narrower mechanism, module map, or deployment topology. Group cooperating roles in accurately named subsystems when that still explains the requested interaction. Separate roles when grouping would hide control ownership, a trust or persistence boundary, lifecycle behavior, or another distinction the reader asked about. Keep secondary and opt-in capabilities in concise sourced notes unless their path matters to the requested question. Name a deliberately narrower scope in the title.
Preserve every requested responsibility, relationship direction, protocol, and behavior-changing condition. Show approval, authorization, and state-transition gates on the affected node or relationship; a card alone cannot qualify an otherwise unconditional arrow. Keep source evidence with each asserted claim. Boundaries express real isolation, ownership, runtime, or persistence facts. Cards answer additional reader questions; they do not replace required topology. There is no node, edge, source, card, or boundary quota. If an authored overview needs regrouping after failure, use [Composition repair](authoring-contract.md#composition-repair); user-supplied or agreed topology remains fixed.
Relationship labels carry meaning. Give them clear space and preserve action, protocol, direction, async behavior, or cross-boundary meaning. A label may start absent only when both endpoints already fully imply it; a collision calls for spacing or routing repair. See [Label repair](authoring-contract.md#label-repair) when measured evidence reports a collision.
## Layout and routing
Place Architecture nodes by their actual connections before assigning coordinates; the router cannot rearrange boxes, so placement decides whether lines stay straight. Classify each relationship first, then place:
- **Main path**: the reader's main journey, neighbors adjacent in reading order. Let a medium path step through meaningful rows instead of making a shallow horizontal strip.
- **Branch or store**: directly above or below the node that owns, reads, or writes it, centered on that node so the edge is one straight segment. Keep all stores and branches of one row on the same side of it.
- **Return** (back to an earlier main-path node): put its source on the side of the main path with no branches or stores, so it runs through an empty corridor instead of crossing them.
- **Second entrance** into a node that already has an incoming edge: place the new source so it reaches that node from another side, usually directly below or above it.
- **Fan-out**: a side with k relationships needs at least `32 + 14 × (k − 1)`px (four need 74px). Spread a hub's counterparts over two or three sides, or enlarge the hub. Center a parent on its children and align a child with its only parent.
Before writing positions, trace each non-main relationship: its straight or one-bend corridor must not pass another node or cross another relationship. If it does, move the endpoint that is off the main path. Start the main actor and its first connected step together near the canvas origin; use content rows for vertical rhythm. Omit `meta.viewBox` for a fresh Architecture so the Reader measures intrinsic height. Keep supplied fixed geometry authoritative.
Start with automatic routes and endpoint sides. Pin a side only for a necessary branch, return, or supplied geometry. Reserve `via`, `channelX`, `channelY`, and label coordinates for measured defects. Before writing positions, budget each labeled main-path edge at `6.5px × ASCII units + 21px` of clear gap, counting CJK as two units; use its own label length, not a row-wide fixed gap. Size Architecture sublabels for their preferred 9px text at `5.4px × text units + 8px`, with CJK counting twice; keep supporting copy concise without dropping required facts. Do not trade readability or meaning for fewer crossings. The [Geometry reference](authoring-contract.md#executable-geometry-rules) has measured spacing, port, canvas, and route rules for a diagnosed layout problem.
## Evidence and schema
For a real repository, follow [Repository authoring](repository-authoring.md) while inspecting source. Freeze its credential-free origin and 40-character commit in `meta.repository`, attach inspected repository-relative `sources` to each key semantic node, and pass `--repo-root` to the first `finalize`. Each reference proves only the fact visible at that location. Follow material relationships and conditions to their actual source; do not reuse a startup citation as protocol or persistence evidence.
Examples show field shape, not legal values or source facts. Read the mode schema and shared definition before adding a field, enum, or constrained text. In particular, inspect Architecture boundary kinds. Keep longer evidence in a card while retaining the fact. See [Schema lookup](authoring-contract.md#schema-lookup) for details.
## Presentation and modes
Use one primary authored language from the user's choice or the request/conversation. Set `meta.locale` for built-in English (`en`) or Simplified Chinese (`zh-CN`); for other languages, including Spanish (`es`), supply `meta.translations` with reusable UI translations, or disclose the fixed Viewer UI and `<html lang>` English fallback. Keep exact product, code, protocol, command, API, and environment names while localizing surrounding explanation. See [Language consistency](authoring-contract.md#language-consistency) for bilingual cases.
Omit `meta.visual_preset` for classic, `meta.subtitle` for a title-only header, `meta.legend` for truthful auto, and `meta.engineering_profile` for an ordinary system overview. Explicit styles and a subtitle require a user request. Use legend or deployment ownership under the [legend](authoring-contract.md#legend-contract) or [engineering profile](authoring-contract.md#engineering-profile-default) contracts. Branding is optional and explicit when a node names a real product; [Brand marks](brand-marks.md) gives lookup and capture rules for that branch. Never let a badge replace semantic type, label, or relationship facts. Set required `meta.output` to a portable POSIX-relative `.html` path within the working directory; see [Output path contracts](delivery-contract.md#output-path-contracts) for native path exceptions.
For new Workflow use schema v2, preserving v1 for a fixed legacy source; use its [layout contracts](../renderers/workflow/README.md#layout-contracts) when lane or group geometry needs detail. For Sequence start with fixed columns; use `spread` when a wide viewBox leaves unused horizontal space or meaningful labels need width. For new Lifecycle use schema v2: every lane is its own row (`main` first, `terminal` last) and `col` `0..4` is the same x in every row, so place an interruption or exit in the column of the state it leaves; author the main path as transitions, omit `viewBox`, and keep transition labels short; a recoverable failure needs a real transition back. Read [Mode placement](authoring-contract.md#mode-placement) when a mode-specific placement or viewport problem needs more detail.
`finalize` performs the browser gate. Keep the complete drawing comfortably readable on desktop, with zero horizontal overflow. Use meaningful vertical rows and intrinsic-height page scroll when necessary. The 6px projected-text check is a failure floor; at 1440px, aim for ordinary context text around 7.5px or larger. See [Automated browser evidence](delivery-contract.md#automated-browser-evidence) when viewport evidence fails.
+82
View File
@@ -0,0 +1,82 @@
# Brand marks
Use a brand mark only when a real product, provider, model family, channel, or
service identity helps the reader. Semantic `type` still explains what the node
does; `brand` explains whose product it is.
## Agent decision path
1. Search the built-in catalogue when the request names a recognizable brand:
```bash
node bin/archify.mjs brands "Claude" --json
```
2. Put the returned canonical ID in the node, participant, or state:
```json
{
"id": "planner",
"type": "backend",
"label": "Claude",
"brand": "claude"
}
```
3. If there is no catalogue match and the user supplied the official website,
capture its icon explicitly:
```bash
node bin/archify.mjs brands capture "https://partner.example.com" --json
```
Put the command's digest-pinned `brand` value in the authored node:
```json
{
"id": "partner",
"type": "external",
"label": "Partner portal",
"brand": {
"url": "https://partner.example.com",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
}
```
4. If there is no match and no user-provided URL, omit `brand`. Do not invent a
URL or silently assign a visually similar company.
Known-brand URLs resolve to the bundled vector instead of using the network.
For discovered icon `href` attributes, capture decodes the basic named references
`amp`, `quot`, `apos`, `lt`, `gt` (and their defined uppercase aliases), plus
decimal and hexadecimal numeric references, once before URL resolution. Thus
`/icon.png?v=1&amp;size=32` requests `/icon.png?v=1&size=32`. URL percent escapes
remain intact; nested escapes are not decoded recursively. This bounded decoder
does not add a general HTML parser or support every named HTML entity.
HTML reads stop at an explicit head ending outside comments, raw-text elements
and quoted attributes, including when those tokens span network chunks. The
256 KiB head limit and capture deadline still apply; a larger body after the
head is not read for icon discovery.
Unknown URL capture accepts only bounded raster image formats, blocks
credentials, nonstandard public ports, and private or link-local destinations,
uses bounded concurrency and one total deadline, and returns the captured
content digest. Later render and validate operations require that exact digest;
blocked, unavailable, changed, oversized, or unsafe content fails closed instead
of silently changing the artifact.
Page, icon and redirect requests send `Accept-Encoding: identity`. Capture does
not decompress response bodies: a successful response declaring another content
coding is closed and rejected explicitly. This keeps the existing byte limits
and pinned digest tied to the unencoded representation. A later usable icon may
still succeed; otherwise an encoding error is retained instead of being hidden
by an unrelated favicon 404.
The final artifact never fetches a brand asset when opened. Preset vectors and
digest-verified captured site icons remain embedded in SVG, PNG, WebP, JPEG,
Share Card, and WebM exports.
Use `node bin/archify.mjs brands --json` to inspect all canonical IDs, aliases,
categories, domains, and provenance. Current categories cover AI, cloud,
engineering, data, collaboration, business systems, channels, languages, and
frameworks.
+588
View File
@@ -0,0 +1,588 @@
# Delivery contract
## Failed finalize and candidate repair
`finalize` stops at the first non-passing gate. Use compact stdout or `evidence.summaryReceipt`; read its full sidecar only when the summary lacks evidence needed for a coherent repair. A receipt with four artifact checks is basic validation, not showcase acceptance: require all nine checks, zero composition errors, and zero warnings. Fix `meta.quality_profile` and schema errors before geometry.
For a validation failure, edit the existing JSON in the connected neighborhood named by diagnostics before rerunning a command. Preserve requested semantics, meaningful labels, source evidence, and fixed or agreed topology. Several routes sharing nodes call for one placement repair; read [Architecture layout repair](architecture-layout-repair.md) for that case. Reflow a blocked main path rather than nudging unrelated labels. Keep unrelated geometry when its composition already reads clearly. Use `--layout-json` before editing only when compact evidence lacks needed measurements. Workflow v2 uses its stable compiler receipt, not solver internals, as authoring evidence.
After the edit, rerun the complete `finalize` command with `--quality showcase` and, for repository-backed work, `--repo-root <repo-root>`. If the output path already has browser evidence from another candidate, use a fresh `--out-dir <output-stem>.review-<revision>` for both the new `finalize` and any `visual-check`. Omit an earlier `--candidate-sha256` after editing because it binds the previous candidate. Compare diagnostics by code, subject, stage, and evidence, never by declining error count alone. If an issue survives two focused repairs, inspect measured geometry or the relevant contract; after one evidence-based retry, report the concrete gap.
Use standalone `validate` only for focused diagnosis, passing `--repo-root` for repository-backed work. Its passing receipt marks `candidateFrozen: true`; run `nextAction.arguments`, replacing only `<output.html>`, without editing, revalidating, or rereading the candidate. Retry later environmental or evidence failures against those frozen bytes. A measured reason to edit creates a new candidate and calls for the complete `finalize` without the old hash.
## Validate and deliver
`render` and direct renderer entry points print classified authoring failures
to stderr as readable diagnostics and exit 1. Input read/JSON parse failures
use `input/read` or `input/json-parse`; output filesystem failures use
`output/write` and identify the output path. Schema and layout failures keep
their existing rule codes. Use the advertised `validate --json` or
`deliver --json` interface for a machine receipt; `render` has no `--json` flag.
Unexpected implementation failures retain debugging information in human
mode and remain `internal/unclassified` in machine receipts.
Each delivered output has two artifact-specific metadata paths. When an output
stem is too long for those derived filenames, Archify shortens it and appends a
stable hash:
- `<output-stem>.delivery.json` records the latest completed attempt.
- `<output-stem>.delivery-pending.json` is the recovery journal for an attempt
in progress.
For a literal artifact stem that already matches Archify's reserved bounded-name
marker, a pre-namespace raw provenance sidecar remains a read fallback when the
encoded sidecar is absent. A pre-namespace raw pending journal is an independent
fail-closed barrier: it blocks checks and redelivery even when an encoded pending
journal also exists, and neither journal is silently replaced.
One directory-wide `.archify-delivery-lock.json` serializes every delivery that
resolves into the same physical output directory. This deliberately prevents
case, Unicode-normalization, Windows short-name, and symbolic-link aliases from
creating independent owners for one filesystem location. The tradeoff is that
deliveries to different artifact names in one directory also run serially;
provenance and pending journals remain artifact-specific.
For migration safety, Archify also detects and preserves a legacy
`<output-stem>.delivery-lock.json` beside the requested artifact. An existing
legacy entry is a fail-closed recovery barrier. While a new delivery owns the
directory mutex, it also holds temporary legacy-format fences for the requested
spelling and an existing artifact's physical target spelling. It acquires the
directory lock first, then all required compatibility fences before writing a
journal or artifact, and removes the directory lock before those fences during
release. This blocks an older Archify binary using
either known spelling from entering the delivery. A legacy fence whose raw
HEAD-era filename exceeds the host component limit is omitted because the old
binary could not create that lock or deliver that artifact on the host either.
`deliver` acquires the lock by exclusive `open(..., "wx")` before creating or
replacing the recovery journal. A successful exclusive create yields an
internal opaque ownership capability bound to that attempt. Journal creation,
failed-provenance recording, pair commit, rollback, journal finalization, and
lock release each verify the current capability inside the operation that
would mutate shared state. A rejected contender does not create a journal or
write failed provenance.
An existing directory or legacy lock is handled without automatic recovery:
| Observed lock state | Required `deliver` result |
| --- | --- |
| No directory entry | Attempt exclusive creation; only its success grants ownership. |
| Valid schema-v1 lock whose PID is running, or whose death cannot be established | Exit 1 with `delivery/concurrent-attempt`; preserve every shared path. |
| Valid schema-v1 lock whose PID is known to have exited | Exit 1 with `delivery/lock-stale`; preserve the lock, artifact, journal, and current provenance exactly. |
| Unreadable, malformed, symlink, dangling symlink, directory, or other non-regular lock entry | Exit 1 with `delivery/lock-invalid`; preserve the entry and every other shared path. |
| An acquired capability no longer matches the current lock or journal | Exit 1 with `delivery/ownership-lost`; stop all shared-path mutation. |
| The matching owner cannot remove its lock | Exit 1 with `delivery/lock-release`; preserve the lock. |
A `delivery/lock-stale` diagnostic identifies the absolute output and lock
paths plus the original PID and receipt ID. Recovery is deliberately explicit
and serial: stop all delivery attempts for that physical output directory,
confirm that no active delivery owns it and that the reported stale entry has
not been replaced, remove only the reported lock, then rerun `deliver`. Do not
remove an artifact, current provenance, or pending journal as part of
stale-lock recovery.
The lock protocol targets Node.js 18 or later on a local filesystem with
cooperating Archify processes. PID, receipt, and file-identity comparisons are
defensive checks, not an atomic compare-and-swap. Compatibility fences cover
the requested spelling and an existing physical-target spelling; they cannot
enumerate arbitrary hard-link names or previously unknown filesystem aliases,
so mixed-version delivery through such aliases remains out of scope. This
contract does not claim distributed-lock correctness on NFS, SMB, or other
network filesystems, and it cannot prevent an external process that ignores
the protocol from replacing shared paths.
Every no-clobber HTML publisher (`render`, `deliver`, `compare`, and `preview`)
captures the requested directory entry, canonical write slot, physical parent,
and existing target type, device/inode identity, and mode before staging, then
revalidates that snapshot immediately before replacement. An existing write
target must be a regular file with exactly one hard-link name. A target with
multiple hard-link names fails closed with `output/target-hardlinked`: replacing
the requested name cannot update unknown sibling names as one publication.
Hard links remain supported for read identity and input/alias collision checks;
they are unsupported only as write targets. A symbolic link to a single-link regular
file remains supported: publication preserves the symbolic-link entry and
applies the same protocol to its resolved target. Directory, FIFO, socket,
device, changing mode, new claimant, and indeterminate identity cases fail
before replacement.
Publication is no-clobber and recoverable, not crash-atomic replacement of an
existing target. To avoid overwriting a claimant that appears after the last
identity check, Archify first retains the bound old file in a private recovery
backup, removes the public name through identity-bound quarantine, and then
creates the new public name with an exclusive hard link. A caught failure rolls
back when the public slot and recovery binding still permit it. A process
interruption between those namespace operations can instead leave the public
path absent while the verified previous bytes remain in an adjacent private
recovery backup. Single-artifact publication records the original slot/alias
identity and backup inode, mode, SHA-256, and byte count in private
`.archify-remove-*/publication-recovery-v1.json`, beside `previous`. To make a
specific interrupted publication visible again, stop concurrent writers and run:
```bash
node bin/recover-output.mjs /absolute/path/to/.archify-remove-<id> --json
```
This is explicit recovery, not a directory scanner. Before linking, the helper
checks for a changed parent or alias, an altered/hardlinked record or backup,
digest or inode mismatch, and any existing public target. It restores only by
no-clobber hard link, so a new claimant is preserved rather than overwritten;
it never recursively removes unknown entries. A completed recovery is
idempotent. The record is evidence to be independently verified, not an
authority to restore arbitrary private bytes: the helper accepts it only from
the recorded generated child of the original physical target parent, with the
same directory identity. Name the exact directory reported by the interrupted
process and inspect an uncertain record manually. A non-cooperating process can
still swap pathnames after those checks and before Node.js `linkSync`; Node does
not expose a descriptor-bound link operation. Post-link identity verification
then fails closed and retains recovery evidence, rather than claiming recovery
or deleting an uncertain name. If recovery itself is interrupted after the
link, the old public bytes and private backup can both remain; a later recovery
run preserves the public target and needs explicit operator resolution. The
record is fsynced before the old public name is retired on platforms supporting
directory sync, and the tested guarantee is recovery after a killed process;
this is not a claim of power-loss, storage-controller, NFS, or SMB durability.
Paired flows retain their backup in private transaction staging. For `deliver`,
the pending journal and lock keep strict checkers fail-closed. The portable
Node.js filesystem API has no pathname
compare-and-swap that both replaces an existing name atomically and refuses to
overwrite a late claimant: `rename` would close the visibility gap only by
overwriting that claimant.
After ownership is established, `deliver` creates the journal before rendering
and keeps it through the recoverable HTML/sidecar pair commit. It removes the
journal only after that commit completes. A validation, render, or pair-commit
failure, or a process interruption, may therefore leave a journal. The journal
is a safety barrier: `check`, `browser-check`, and `visual-check` fail closed when any directory
entry exists at the journal or lock path, including an unreadable file,
symlink, or dangling symlink. Run deliveries targeting the same physical output
directory serially; one attempt must finish or be recovered before another
begins.
A successful sidecar has `schemaVersion: 1`, `status: "current"`,
`command: "deliver"`, a unique `receiptId`, the diagram `type`, an absolute
`input` path, an absolute `output` path matching the inspected
artifact, and specification/artifact SHA-256 and byte counts. Checkers treat a
missing, malformed, unsupported, or inconsistent field as invalid. They also
reject a sidecar symlink, including a dangling one. A checker binds provenance
to the artifact bytes it actually checks and verifies that binding again before
reporting success; a concurrent byte change fails. The provenance directory
entry itself must be a single-link regular file: `deliver` and strict check fail
closed with `delivery/provenance-hardlink-unsupported` when it has another hard
link, without scanning for or guessing the sibling name.
If a currently verified owner fails after an older HTML exists, Archify writes
a new `status: "failed"` sidecar and leaves the journal until recovery is
complete. An unreadable old HTML does not prevent that marker; its artifact hash
and byte count may be absent. If the sidecar is locked or otherwise unwritable,
Archify keeps the prior sidecar rather than deleting evidence, and the journal
prevents checkers from trusting it. A rejected concurrent, stale, or invalid
lock attempt does not write failed provenance. If ownership is lost, Archify
reports `delivery/ownership-lost`, does not overwrite or remove the successor's
artifact, provenance, journal, or lock, and does not claim recorded failed
provenance; a failure receipt may report `provenance: "unrecorded"`. If every
metadata path is unavailable, the same unrecorded status applies; no tool can
preserve that fact across processes. Restore metadata-path access and complete
a successful `deliver` before trusting the output.
Artifacts with no sidecar, journal, or lock remain supported for backward
compatibility and for the lower-level `render` command. Their checker receipts
report `provenance: "unknown"`; use `--require-provenance` to turn that state
into a non-zero failure when the workflow requires a successfully delivered
artifact:
```bash
node bin/archify.mjs check <output.html> --require-provenance
node bin/archify.mjs browser-check <output.html> --json --require-provenance
```
## Output path contracts
Archify intentionally separates durable authored paths from command-line paths:
- Required authored `meta.output` is a portable POSIX-relative path such as
`reports/diagram.html`. It uses `/`, ends in a non-empty `.html` basename,
and cannot contain an absolute or drive-relative prefix, URI, backslash,
empty or dot segment, control character, unpaired UTF-16 surrogate, Windows
alternate-data-stream separator or invalid filename character, trailing dot
or space, DOS device name, or a component over either the 255-byte UTF-8 or
255-code-unit UTF-16 limit. It resolves from the current working directory
and must remain physically inside that directory, with an `.html` target,
after symbolic links are followed. The durable output/archive profile also
conservatively rejects a Windows 8.3 short-name shape such as `PROGRA~1`;
descriptive repo/Git POSIX paths use a separate profile and are exempt.
- Explicit CLI output arguments use the active host's native syntax. They may
be relative or absolute, use native separators, and resolve outside the
current working directory. On Windows, ordinary drive-absolute, UNC, and
relative paths (including ordinary `.` and `..` navigation) are supported.
A system-resolved 8.3 spelling of an existing file or directory is accepted
when Archify can prove its physical identity; this native alias support does
not relax the durable output/archive profile's 8.3-shaped-name rejection.
Extended-length paths are limited to raw backslash-only `\\?\C:\...` and
`\\?\UNC\server\share\...` forms without dot segments; device namespaces,
malformed roots, drive-relative paths such as `C:file.html`, current-drive
roots such as `\file.html`, alternate data streams, reserved device names,
invalid or trailing filename characters, and overlong components fail
closed. POSIX CLI paths retain POSIX filename rules rather than inheriting
Windows spelling restrictions. Every host rejects NUL, unpaired surrogates,
and components that exceed its supported bound.
These contracts are not interchangeable: an explicit CLI output does not hide
an invalid durable `meta.output` (including a missing value), and `validate`
checks the authored output even when it does not publish to that path. A
workflow v1-to-v2 migration may explicitly receive a portable durable
replacement through `migrate workflow old.json new.json --to-schema 2 --output
reports/diagram.html`; that value is written only to its separate verified v2
destination. This migration-candidate exception does not repair the source or
bypass any non-output schema or compiler error. For every other repair, add a
portable POSIX-relative `.html` path to `meta.output`; no schema-version change
is otherwise required.
Run `finalize` directly on a complete first candidate and after every repair edit. Its embedded validation checks the candidate before delivery; use standalone `validate` only for focused diagnosis. After an edit, omit any earlier `--candidate-sha256`, which binds the previous candidate. CLI HTML output paths must end in
`.html`, including after symbolic-link resolution. Compare receipt paths must
end in `.json`. A type mismatch fails before writing with
`output/cli-extension` or `output/cli-resolved-extension`. These checks prevent
accidental file-type overwrites; they do not sandbox explicit CLI directories
or prevent replacement of an existing artifact of the expected type.
Use final verified delivery only after the candidate is frozen:
```bash
node bin/archify.mjs deliver <type> <candidate.json> <output.html> --quality showcase --json
```
Deliver reads the specification once, writes those exact bytes to a private same-directory candidate snapshot, renders that snapshot, runs the complete artifact checker, and only replaces the target after all artifact checks pass. The JSON receipt includes SHA-256 and byte counts for both `specification` and `artifact`.
For the ordinary agent handoff path, prefer the finalizer:
```bash
node bin/archify.mjs finalize <type> <candidate.json> <output.html> --quality showcase --json
```
`finalize` invokes verified `deliver` once, reuses its embedded showcase
validation result, then runs strict `check --require-provenance` and
`browser-check --require-provenance`. It stops at the first failed or skipped stage
and preserves that stage's full receipt. Its stdout is one compact JSON
object with gate statuses, bounded actionable diagnostics, artifact identity,
and evidence paths. The same compact object is written atomically to
`<output-stem>.finalize-summary.json`; use that file for normal failure repair.
Complete stage receipts and timings remain available for auditing in
`<output-stem>.finalize.json`. With `--out-dir`, both files are written there;
`--receipt <path.json>` overrides the full receipt path and derives a distinct
`<path>-summary.json`. Read the full receipt only when the compact summary is
truncated and its shown subjects and evidence cannot identify a coherent local
repair, or when complete audit evidence was explicitly requested. The compact
receipt reports `visualReview: "not-requested"`; the automated gate does not create images or require a perceptual reviewer. A compact `visualReviewRecommendation` retains positive crossover and route-detour metrics from the strict check so the author can apply the review escalation below without reading the full receipt. A recommendation does not change the machine exit code or claim that review happened. Its `affectedRoutes` identifies crossing pairs and detours (up to eight of each, with a truncation flag); the full strict-check `composition.routeReview` retains all affected relationships. Use these IDs to trace the routes in the captured default viewport. Detours may include `directCorridorBlockers`, identifying nodes between aligned endpoints. These are geometric review clues, not new validation failures or inferred main-path semantics. For a blocked main path or several tangled routes, follow [Architecture layout repair](architecture-layout-repair.md) and reflow the connected scene before tuning individual sides or labels. Preserve every semantic fact; retain unrelated positions only when their surrounding composition is already accepted.
For a measured automatic Architecture with a large unused leading area, the
compact receipt may include `layoutReviewRecommendation`. Its
`composition.leadingSpace` evidence accounts for nodes, boundary titles,
routes and labels. Check whether that space is intentional; if not, reposition
the connected scene while preserving meaning and user-fixed geometry, then
finalize again. This suggestion changes no gate or exit status and requires
no screenshot. A fixed canvas or uncertain measurement receives no suggestion.
A passing finalizer receipt is sufficient evidence for all four gates. Merely
naming the gates or requiring each one to pass does not require replaying their
standalone commands. Replay an individual command only when the request
explicitly requires separate executions or focused failure diagnosis needs it.
The individual commands remain authoritative and backward compatible. Use
them directly for focused diagnosis, recovery, or when only one gate is
required. A finalize failure does not relax any gate and does not turn a
preserved older artifact into a current successful delivery.
`finalize` overlaps private Chrome startup with delivery and strict checking.
It loads the artifact only after those gates pass and current provenance is
verified. The browser gate retains every viewport, theme, and stability check;
the browser closes at completion or an earlier failure. Its full stage receipt
records `execution: "in-process"` and the equivalent standalone `command` for
replay. Use total finalize duration to compare performance because Chrome
startup overlaps the earlier stages.
The pair commit is recoverable, not a claim that two filesystem paths change
atomically or are durable across power loss. Journal finalization is part of
that commit: a caught failure while verifying or removing the journal rolls
back the replaced files when possible and while ownership remains current. If
ownership is lost, the old attempt immediately stops renaming, rolling back,
finalizing the journal, recording failure provenance, or cleaning up shared
paths. Any private staging or recoverable backups remain available and are
identified by the failure diagnostic. If restoration fails for another reason,
the failure receipt likewise identifies retained backups for recovery. A
process interruption can leave the journal, backups, or private staging behind;
checkers then fail closed. Follow the reported recovery evidence before rerunning
`deliver` serially on the same output. A failed attempt exits non-zero and never invokes an opener; it never
authorizes visual evidence collection.
If exclusive creation succeeds but lock initialization fails, Archify may
record failed provenance and remove the incomplete lock only while its
capability still identifies that exact entry. A replacement is preserved.
Filesystem and cleanup errors are reported separately from an active concurrent
delivery. An active, stale, unrecognized, or otherwise preserved lock
independently prevents checkers from accepting the prior artifact. Fix the
reported filesystem error before retrying, and use another physical output
directory if the lock path contains unrelated data.
Lock release is part of delivery completion. If the artifact/provenance pair
has committed and the journal has finalized but the matching lock cannot be
removed, `deliver` exits 1 with `delivery/lock-release`, preserves the lock,
does not print a success receipt, and does not invoke an opener. The preserved
lock keeps strict checkers fail-closed. Only after pair commit, journal
finalization, and lock release all succeed may `deliver` exit zero, print its
success receipt, or run `--open`.
Run strict `check` after `deliver` exits zero. Run `browser-check` or optional
`visual-check` only after that strict check exits zero. A failed marker,
recovery journal, or delivery lock makes every checker fail before accepting
the preserved HTML; report the diagnostics and complete a successful recovery
delivery before collecting new browser evidence.
The delivery interface exposes four separate claims:
1. `deliver` proves deterministic artifact checks and byte identity.
2. `browser-check` collects required automated browser evidence from the exact artifact without capturing images.
3. `visual-check` optionally adds artifact-bound screenshots and a contact sheet.
4. Perceptual visual review records a human or image-capable reviewer's judgment.
Passing one claim never implies the others. Never claim that the deterministic receipt includes browser or perceptual review evidence.
## Recovering a failed comparison
`compare` commits an HTML artifact and its JSON receipt as a pair. If that commit
fails, it attempts to restore the previous files. A complete rollback removes
the temporary directory as usual.
If a previous file cannot be restored, compare exits non-zero with
`delta/commit-rollback-failed` and retains the recovery directory. In the JSON
failure receipt, `diagnostics[].evidence.recoveryDirectory` identifies that
directory and `recoveryFiles` lists `{ backup, target }` paths for the files whose
restoration failed. Human-readable diagnostics also print the recovery paths.
Resolve the filesystem error, inspect the current targets, and restore each
listed backup to its corresponding target before retrying. Keep the recovery
directory until both previous files have been recovered and verified; it can
also contain rejected candidate files, which must not be mistaken for backups.
Successful comparisons and failures before commit retain their normal cleanup.
## Automated browser evidence
`finalize` runs the required browser gate against the exact trusted HTML without
rerendering or modifying it. For focused diagnosis, the equivalent standalone
command is:
```bash
node bin/archify.mjs browser-check <output.html> --json --require-provenance
```
The zero-dependency command uses Chrome/Chromium through the DevTools pipe. It
measures light-theme containment at 1440×900, 1600×1000, 1920×1080, and
2048×1320, and verifies the requested light theme at all four viewports, the dark
theme at both endpoints, and READ/Still runtime states. A requested theme that
resolves to a different theme fails with measured evidence. It creates one
`<output-stem>.browser-check.json` receipt and no screenshots or contact sheet.
Pass `--out-dir <dir>` to place the receipt in a separate evidence directory.
The receipt binds the artifact SHA-256 and byte count, identifies
`evidenceKind: "automated-browser"`, and reports
`visualReview: "not-requested"`.
Horizontal overflow always fails. Normal document-level vertical scrolling is
accepted only with a renderer-declared contract and measured readable text.
Automatic canvases declare `data-reader-fit="intrinsic-height"`; their adaptive
Reader must reach its readable width and expose `data-reader-overflow="authored"`.
Architecture with an explicit `meta.viewBox` instead declares
`data-diagram-type="architecture"` and `data-reader-fit="authored-height"`:
its SVG coordinates, aspect ratio and existing Reader width behavior stay
unchanged. Its full SVG must remain inside the diagram panel without internal
scrolling or clipping, and the document must permit vertical scrolling.
The receipt records `verticalScrollAccepted: true` and
`overflowDisposition: "readable-vertical-scroll"`. Missing or unknown declarations,
explicit viewBoxes in other modes, unreadable text, horizontal overflow,
clipping and Viewer chrome collisions remain failures. Do not add an internal
diagram scroller or hide overflow.
`browser_evidence` in the handoff records only the outcome of this automated
command:
- `passed` maps from exit 0 and receipt `status: "pass"` after every required measurement completes and passes.
- `failed` maps from exit 1 and receipt `status: "fail"` when the inspection finds a defect, the command fails, or a runtime error leaves the evidence incomplete.
- `skipped` maps only from exit 2 and receipt `status: "skipped"` when Chrome/Chromium is unavailable and the inspection does not run.
Runtime failures leave incomplete evidence and must not be normalized to
`skipped`. They do not invalidate an already successful deterministic delivery.
Retry an environmental failure in a browser-capable execution context when
practical. Keep the packaged transport unchanged unless the failure reproduces
through that seam in a capable environment.
A provenance failure exits before browser inspection and persists a failed
browser-check receipt bound to the attempted artifact. If the failure receipt
cannot be written, the diagnostic names that incomplete evidence.
Both browser commands inspect the exact delivered HTML without modifying or rerendering it.
## Sequence width review
A passing `finalize` may report `layoutReviewRecommendation.action: "inspect-sequence-width"`. Its `evidence` measures the fixed participant columns' unused right-hand space after accounting for message labels, notes and segment titles. This advice adds no warning, failure, screenshot requirement or automatic geometry change.
For a newly authored candidate with omitted `meta.column_fit` and no user-fixed column geometry, save the candidate, set only `meta.column_fit` to `"spread"`, and rerun the complete `finalize` once with `--out-dir <folder>/width-review`. Keep participant order, messages and their y positions, labels, notes, sources and canvas dimensions. If that attempt fails, restore the candidate and finalize it with `--out-dir <folder>/width-restore`; report the remaining layout suggestion rather than iterating. Preserve an explicitly fixed layout or a supplied legacy candidate and disclose the suggestion without changing it. This review is about horizontal composition; a passing receipt still does not claim perceptual approval.
## Optional capture evidence
`visual-check` remains backward compatible for a requested or escalated
perceptual review:
```bash
node bin/archify.mjs visual-check <output.html> --summary --require-provenance
```
`--summary` returns compact JSON with all diagnostics and absolute paths to the complete receipt, contact sheet, and every screenshot. For a chosen visual review, inspect the relevant captures; capture success is not perceptual approval. `--json` retains the full receipt output for existing consumers. Both modes run the same checks and keep the same exit status. If cleanup fails after publication, the summary retains the final failure diagnostics and `publication` recovery details; the linked receipt records the earlier committed evidence.
It performs the same automated browser measurements, captures light/dark
screenshots at 1440×900 and 2048×1320, and writes four viewport PNG sidecars,
one relative-path HTML contact sheet, and one JSON receipt. `--out-dir <dir>` moves all of these sidecars together.
Open the HTML contact sheet in a browser or inspect the viewport PNGs with an image reader. Its receipt reports `visualReview: "pending"` because captures do not themselves
make a perceptual judgment. Capture and provenance failures follow the ownership rules below.
The receipt, contact sheet, and four PNGs form one owned evidence set. Before
capture, `visual-check` freezes every requested directory entry, its
canonical write slot and physical parent, and the target's absent/file state,
type, device/inode identity, and mode. Hard-linked evidence targets are not safe
write targets. All candidate files are created exclusively inside one random,
private staging directory beneath the physical evidence directory; the receipt
is published last. Each staged candidate must have exactly one hard-link name
before publication. The no-clobber publish link temporarily gives the staged
and final names a link count of two; unlinking the verified staged name must
leave the final entry with a link count of one. An unexpected external hard
link fails closed and its alias is never removed.
Chrome inspects one identity- and content-checked copy of the captured artifact
in a private local temporary directory, so browser file loading does not depend
on UNC or long-path support. The six publication candidates remain on the
evidence volume. Both temporary directories are cleaned without recursively
deleting unknown contents; retained entries include their recovery locations.
Immediately before committing anything, `visual-check` re-resolves and verifies
the complete six-path set. An absent-path claimant, existing-path replacement,
symbolic-link or dangling-link retarget, parent-topology change, hard link, or
indeterminate identity fails closed with `viewer/evidence-path-conflict`. The
claimant and every other final evidence path remain untouched. Cleanup removes
only this run's staged or published entries after rechecking their captured
identities; a changed or unknown entry is preserved.
An existing visual evidence set is replaceable only when a regular
`visual-check` receipt proves ownership of the same artifact and evidence
directory, and its exact sidecar manifest matches every existing contact-sheet
or PNG byte count and SHA-256 digest. A missing, malformed, unknown, mismatched,
or incomplete ownership record never authorizes deletion. Failed and skipped
runs retire prior screenshots/contact sheets only as part of the same verified
transaction when that ownership proof succeeds; otherwise they preserve all
unknown evidence and report `viewer/evidence-path-conflict`.
This rule also applies when Chrome is unavailable or provenance fails before
browser inspection: neither path may blindly delete stale-looking evidence. A
verified owned set may be recoverably retired before publishing a skipped or
failed receipt; unowned evidence remains intact. These outcomes do not invalidate an already
successful deterministic delivery and do not turn a perceptual visual review
into passed or failed. Retry an environmental failure through the supported
command in a browser-capable execution context when practical. Keep the
packaged transport unchanged unless the failure reproduces through that seam in
a capable environment.
`browser-check` applies the same private-snapshot, identity, ownership, and no-clobber rules to its single JSON receipt. Its namespace is separate from `visual-check`, so a browser-only rerun cannot remove capture evidence.
## A new candidate at an existing output path
Browser evidence belongs to exact artifact bytes. After editing a candidate whose previous HTML already has browser evidence, choose a fresh evidence directory before running the next `finalize`; this preserves the old receipts and captures without an avoidable ownership-conflict retry:
```bash
node bin/archify.mjs finalize architecture candidate.json diagram.html --quality showcase --repo-root <root> --out-dir diagram.review-2 --json
node bin/archify.mjs visual-check diagram.html --out-dir diagram.review-2 --summary --require-provenance
```
Keep the requested HTML path stable. Use a new revision directory for each changed candidate, and retain the same directory for retries of unchanged bytes. For a diagram without repository evidence, omit `--repo-root`. Let the commands create their output directory. A prior validation failure that produced no HTML or browser evidence needs no new directory. Never remove unknown evidence to make a retry pass.
## Optional opening
Add `--open` only when the user wants an immediate local preview. It runs after
the verified pair commit has completed, its recovery journal has been removed,
and the delivery lock has been released successfully. It uses one argument-array
OS opener with a five-second bound on macOS and Linux, and a fifteen-second bound
for PowerShell startup on Windows. The receipt records `open.status`; failed or
unavailable launch attempts also include normalized `open.failure` details.
Keep it off for CI, unattended agents, and non-interactive environments.
Failure or unsupported opening does not invalidate delivery; its
status proves only whether the local opener invocation succeeded.
## Last-Good Live Preview
For an active desktop authoring loop only:
```bash
node bin/archify.mjs preview <type> <input>.json <output>.html --quality showcase
```
Preview watches one explicit input on loopback, binds each stable digest to a private snapshot, and advances only after the existing verified delivery pipeline passes. Invalid, half-written, deleted, or superseded input leaves the previous verified revision on screen and on disk. Identical bytes do not rebuild or reload.
The preview runtime ships inside the zero-dependency Skill ZIP and must work without `node_modules`.
Never start it by default. Do not use it for CI, unattended agents, remote sharing, or mobile use. `--no-open` is only for a user who will open the printed local URL or for loop testing. Stop it with Ctrl-C before handoff. The first Ctrl-C drains the active delivery without publishing it; a second Ctrl-C forces shutdown of both delivery processes and HTTP connections, including incomplete requests. Shutdown preserves the last verified artifact and removes only staging files whose ownership can be verified. If delivery is interrupted before its receipt reaches Preview, unconfirmed files and recovery material may remain in the private staging directory; shutdown does not recursively delete unknown contents. Server state, port, source path, diagnostics, error text, and reload tokens must never enter the generated artifact or any export.
## Perceptual review
The automated path ends with the deterministic browser gate and reports
`visual_review: not_requested`. Ordinary generation does not require screenshots
or an image-reading step, including newly authored or repositioned Architecture.
Perceptual review is optional; use it for an explicit request or a concrete visual
investigation. Possible reasons include:
- the compact finalizer includes `visualReviewRecommendation` for crossings or detours (advisory, not a delivery gate);
- the user explicitly requests an aesthetic or visual review;
- a template, renderer, or Viewer change needs visual regression evidence;
- a novel layout or browser diagnostic leaves low confidence;
- the run is selected for sampled audit or dogfood.
For the default standalone desktop viewer, measure 1440×900, 1600×1000, 1920×1080, and 2048×1320. Require `document.documentElement.scrollWidth <= window.innerWidth` at every checked size. Prefer `scrollHeight <= window.innerHeight`; accept page-level vertical scrolling only through the Reader-declared readable exception defined above. At the largest checked viewport, inspect the rendered composition for a conspicuous empty lower band: the main panel and necessary conclusion cards should use the available height as a balanced whole, not collapse into a shallow strip. For unexpected overflow, repair the authored composition by removing only genuinely redundant content or compacting spacing before shrinking nodes, labels, or the main panel. Do not hide overflow, clip content, introduce an internal diagram scroller, or reduce node/label typography to make the measurement pass. Narrow/mobile containment may retain vertical page scrolling.
For an escalation, run `visual-check` on the current finalized artifact, inspect
its contact sheet with a capable image reader or human, and check both endpoint
themes, the default READ view, line crossings/corridors, label masks, node/card
fit, focus/search/passport closure, and export cleanliness. This review is
supplementary and never changes `browser_evidence`. An unconstrained browser
glance can support perceptual review only.
Report one truthful optional-review status:
- `visual_review: not_requested` — no review trigger applies; this is not a visual acceptance claim.
- `visual_review: passed` — only after inspecting the rendered artifact.
- `visual_review: skipped (image reader unavailable)` — a requested or triggered review could not run.
- `visual_review: failed` — with the concrete visible defect.
For an escalated review, use `correction_rounds: 0`, `correction_rounds: 1`, or
`correction_rounds: 2`; never exceed two focused correction rounds. When review
is not requested, use `correction_rounds: 0`. Never report
`visual_review: passed` without inspecting the artifact. If perceptual review
changes the candidate, rerun `finalize` because the previous specification and
artifact receipts are no longer current.
## Handoff receipt
Return:
```text
diagram_type: architecture|workflow|sequence|dataflow|lifecycle
output: /absolute/path/to/file.html
specification_sha256: <receipt value>
artifact_sha256: <receipt value>
validation: 9/9 showcase, 0 errors, 0 warnings
browser_evidence: passed|failed|skipped
visual_review: not_requested|passed|skipped (image reader unavailable)|failed
correction_rounds: 0|1|2
```
Derive `browser_evidence` only from the latest artifact-bound `browser-check`
receipt, normally the stage embedded by `finalize`. Record optional capture or
manual browser work separately with its artifact binding, viewport/theme scope,
and observations; never use it or `visual_review` to overwrite the automated
status.
Opening, preview status, Share Cards, and other viewer exports are not validation claims.
Finalize receipt publication uses the same identity-bound, no-clobber publisher and explicit recovery records described above. The full and summary receipts must be distinct from the candidate, artifact, delivery metadata, and browser receipt. Their targets must be absent or single-link regular files; symlink receipt entries and hardlinked targets fail closed. A later claimant or changed parent stops publication and remains untouched. Default receipt names share the physical artifact namespace and are bounded for the host filename limit. The two receipts are published individually, not as a crash-atomic pair; only a completed passing command is a successful handoff.
+105
View File
@@ -0,0 +1,105 @@
# Repository-backed architecture authoring
Use this reference when a diagram must explain a real repository. The source is
the authority for responsibilities, calls, boundaries, and persistence. The
diagram is complete when the requested meaning is covered and every asserted
fact has supporting source evidence.
## Explore on demand
1. **Freeze identity.** From the target repository, record `git rev-parse
HEAD`, `git remote get-url origin`, and `git status --short`. Remove HTTP(S)
userinfo (including usernames, passwords, and tokens) before recording the
origin or placing it in the candidate. Preserve its transport, port, path and
`.git` suffix; do not rewrite an internal SSH origin as HTTPS. Pin the credential-free URL and
forty-character revision in `meta.repository`. Use `link_mode: "local-only"`
for an SSH origin, unsupported forge, intentionally local-only source links,
or a local fixture whose HTTPS URL is only a repository identity; retain the
URL and revision. Web links require a supported GitHub or Gitee HTTPS origin. If the
worktree is dirty, record the changed paths. Repository evidence is verified
against committed bytes at the pinned revision, not working-tree edits:
inspect a clean checkout at that revision for any cited changed path. Do not
present uncommitted bytes as evidence for `HEAD`; `local-only` does not record
a verifiable snapshot of those bytes.
2. **Map the slice.** Use project instructions, manifests, entry points,
registrations, and deployment configuration to locate candidate runtime
units. Read the entry, configuration, and modules relevant to the request.
Follow imports and call sites
until the requested responsibility reaches its actual input, output, or
side effect. Read a small connected slice instead of scanning the repository
for a convenient label.
3. **Trace ownership.** Derive runtime and I/O relationships from the observed
actor, operation, and target at their call sites; deployment and trust
relationships use the corresponding configuration or enforcement evidence. Distinguish the controller requesting
an operation from the runtime that executes it and the store receiving bytes.
For a file or database edge, the source must identify its actual reader or
writer; a responsibility statement such as “maintains tasks” does not prove
direct I/O. Keep these facts with the source locations while reading, without
a separate planning artifact. Choose which distinctions need separate
nodes using [Composition and meaning](authoring-defaults.md#composition-and-meaning);
discovering an implementation role does not automatically add it to the overview.
A configured provider, an injected adapter, a local stub, and a durable
service are different claims; label the one the source supports.
4. **Record evidence while reading.** Keep exact repository-relative paths and
inclusive line ranges for each component and meaningful relationship. Follow
actual branches, retries, fallbacks, and error handling. A function that is
exported or configured but never called by the normal path is an optional
capability, not a required runtime edge. For a claim about authoritative
state change or control ownership, trace to the actual write or execution
site and the conditions that permit it; an upstream caller alone does not
establish those conditions.
5. **Name uncertainty.** Write unresolved questions beside the claim they
affect: for example, “`writeFile` is called here; durability is unknown.”
Resolve a question by reading the next relevant source range or preserve it
as an explicit unknown. Never turn a label, package description, or config
value into an unobserved service or behavior.
Stop exploring when every requested responsibility, relationship, and boundary
has supporting source entailment and the remaining unknowns cannot change that
coverage. There is no node, edge, citation, view, card, or boundary count to
hit. Do not add a summary step merely to signal completion.
Batch independent relevant files when known. Each additional read should answer
an unresolved question that can change the diagram. Reuse concise facts and
their source ranges already verified in this task; across revisions, recheck
the affected entry points, configuration, dependencies, and evidence.
## Choose an example by structure
Select the main example in the [Type router](../SKILL.md#type-router) before
loading its content, using the request and repository metadata already needed
for source inspection. Selection fits the existing read batch and needs no extra
message, command, or repository-wide scan. For mixed or unclear tasks, use the
requested responsibilities and entry points as they become known in normal
inspection; keep their actual roles. Read another example when a necessary
capability remains unexplained. Examples teach shape, not facts: a library need
not acquire filesystem nodes, and finished showcases still follow the
first-draft automatic-routing rule.
## Author from evidence
Use the mode's complete JSON shape, including repository identity,
components, and connections; every repository-backed component needs supporting source
references, while boundaries or cards are added only when they
answer a real reader question. Let automatic routes and automatic
viewBox sizing work first. Keep the primary path readable, put exception paths
beside their owner, and leave filesystem stores outside a control boundary when
the source shows a separate responsibility.
An existing example teaches field shape, not facts or arbitrary values. It does
not authorize a new boundary kind, a long note, a viewBox size, or a route
control. Consult the specific mode schema and `schemas/common.schema.json`
whether or not the selected example already contains the field; use the
schema's enum, length, identifier, and repository rules. Architecture
boundaries currently use `kind: "region"` or `kind: "security-group"`; source
references use `path`, `line`, and optional `end_line`.
Repository-backed components need concise, truthful `sources` references. Preserve
control ownership when summarizing filesystem I/O: the code that reads or writes
a file owns that action, while a pure in-memory transform receives and returns
values. This fact-check does not require a separate overview node for every helper. Use the existing examples for valid field shape, then replace all
identifiers, wording, source paths, and claims with inspected repository facts.
Loaded 100 of 155 files, more files were not shown because too many files have changed in this diff. Show more