Archify Diagram Viewer 0.1.0
VS Code extension that previews Archify diagrams from their JSON sources (live, as you type) and opens rendered Archify HTML in a viewer tab. Bundles the Archify 3.0.1 renderer and runs it on VS Code's Node runtime. Adds validation diagnostics, JSON schema help, source-link navigation, export saving, render-to-file and open-in-browser commands. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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&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
@@ -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
@@ -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.
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# Update awareness
|
||||
|
||||
Read this file when a `finalize` or standalone `deliver` receipt has `update.noticeRequired: true`.
|
||||
|
||||
Keep one compact line in the final response, in the user's language, with `installedVersion`, `availableVersion`, and the official `releaseNotes` link. Say that the installed Skill has not changed and that the user can ask to snooze or ignore the reminder. If `source` is `cache`, say that a previous check at `checkedAt` found the update. A process message or tool output does not replace this final line.
|
||||
For `severity: "security"`, label it as a security update without making installation automatic or urgent by default.
|
||||
|
||||
You may translate the fixed local `noticeText`. Never quote, summarize, or translate the remote manifest's summary.
|
||||
|
||||
When the user explicitly asks to pause or stop this reminder, run `node scripts/check-update.mjs --snooze "<eventKey>"` (seven days) or `--ignore "<eventKey>"` (this exact release only) from the Skill directory with the receipt's `update.eventKey`, then report the returned status. Never run them on your own initiative; `--ack` is a no-op. These commands do not install an update, and a newer release notifies again.
|
||||
|
||||
The notice is information, not permission. Keep the installed version unchanged. This workflow never downloads, installs, or executes an update, and silence is never consent.
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
# Viewer Runtime reference
|
||||
|
||||
Read this only when the user asks for a reader-facing capability. Ordinary generation does not require implementing or re-documenting these features; they are already in the generated HTML.
|
||||
|
||||
## Exploration
|
||||
|
||||
- Diagram Guide lists current actions and shortcuts.
|
||||
- Reading Depth starts at READ at the default 100% scale, reveals FULL detail at 175%, and falls back to MAP only below 100%. Focus, route, and semantic interactions reveal their exact facts at any scale.
|
||||
- Semantic Lens summarizes selected node/relationship kinds without changing authored geometry.
|
||||
- Intent Trace previews a fine-pointer or keyboard target before committed focus.
|
||||
- Node Finder searches labels and stable IDs.
|
||||
- Semantic Passport opens on focus, shows authored upstream/downstream facts, supports a copyable deep link, has an explicit close action, closes on true outside activation and Escape, and never enters canonical export.
|
||||
- Semantic Radar mirrors the visible viewport and authored graph without becoming a second source of truth.
|
||||
- Direct Relationship Pin makes a unique compiled relationship operable while preserving the authored line and stable relationship identity. It must fail closed on conflicting source/target/label/ID metadata.
|
||||
- Route Probe resolves exactly two endpoints over authored directed relationships. It never infers a route from geometry.
|
||||
|
||||
## Motion and presentation
|
||||
|
||||
`meta.animation: "trace"` enables a finite reader-controlled Live/Still trace. Static is the default. Still, reduced motion, page hiding, print, and canonical export preserve complete static meaning. Presentation Stage changes viewer chrome and framing, never authored geometry. This is not a mobile product feature; narrow layouts get containment only.
|
||||
|
||||
## Canonical exports
|
||||
|
||||
The export menu can copy/download full-diagram PNG, download JPEG/WebP, download a dual-theme SVG, and record a trace-enabled WebM. Viewer state—Guide, Lens, finder, focus, route, camera, radar, presentation, motion ownership, and temporary overlays—must be removed from canonical export.
|
||||
|
||||
### Route Share Card
|
||||
|
||||
After a real directed Route Probe resolves, the reader may use **Export → Route Share Card**. It reuses the exact ordered route snapshot and the shared Share Card seam: `format=share-card`, `variant=route`. The isolated clone may use only static `data-share-route-*` decoration. It is download-only, fails closed for stale/unreachable/conflicting routes, and never becomes the canonical artifact.
|
||||
|
||||
### Reach Share Card
|
||||
|
||||
After a non-empty authored reachability query, the reader may use **Export → Reach Share Card**. It consumes the already resolved upstream/downstream node and edge set without rerunning traversal: `format=share-card`, `variant=reach`. The isolated clone may use only static `data-share-reach-*` decoration. It is download-only. Call it authored reachability—not impact, blast radius, breakage, or runtime causality.
|
||||
|
||||
## Truth boundary
|
||||
|
||||
Viewer exports are communication assets. They do not replace the checked HTML, the deterministic delivery receipt, or a real visual review. Do not add a hosted service, storage surface, dependency, schema branch, or mobile product surface for these viewer-only capabilities.
|
||||
Reference in New Issue
Block a user