Skip to content

Releases

3.20.0 — The rules a client's feedback actually produced

Choose a tag to compare

@github-actions github-actions released this 02 Nov 00:58

The rules a client's feedback actually produced

core/rules/consistency.md — now 32 rules turning one project's client feedback into cross-role
law: the seven principles from their call, 18 review comments, and the regressions the work itself
caused. Six parts: where a decision lives, what the eye checks that a script cannot, colour that
survives a theme, state that tells the truth, chrome, and verifying without breaking what worked.

Every rule is named after the defect that produced it and carries the real measurement, not advice.
Three added this release, each one an audit that reported clean while the screen was wrong:

  • A fill that is pinned inside is not a fill. Width 704, sizing FILL, variant named
    width=fill — and it rendered 343 on 66 instances, because the master's inner frame was fixed to
    the mobile column. The literal lives where an override census structurally cannot see it.
  • A surface is separated by a difference, and the difference must survive both themes. Card and
    page both resolved to #0a0a0a in dark, so the card stopped existing. No contrast check fires: a
    card boundary is neither text nor a control.
  • After a layoutMode flip, every child's cross-axis sizing is stale. FILL means share what is
    left
    , not stretch. A flip collapsed grid rows to 66px and option rows to 1px.

The invariant-ground rule gained its surface-level case: chips on a mode-invariant banner bound to a
flipping token stayed readable in dark and became the same colour as the button beside them —
readable is not the test; saying the same thing in both themes is.

Wired into pica-ui-designer and pica-ux-designer, indexed in design-flow and the README.

3.19.1 — It works on a project's first day, and it works as installed

Choose a tag to compare

@github-actions github-actions released this 27 Sep 01:10

It works on a project's first day, and it works as installed

The repository's history was rewritten to remove three client identifiers from every past
commit and message. Every tag was re-pointed at its rewritten commit; the tree of every release is
unchanged apart from those words, and the current tree is byte-identical. A clone taken before today
has to be taken again.

pica-verify failed a brand-new project. Every check that drives a served demo or a build had no
needs, so on an empty state they all applied, all lacked their placeholders, and the run printed
"THIS RUNNER IS NOT REPORTING HONESTLY" and exited 1 before intake had started. They need a demo/,
html/ or src/ directory or a runners.json now: until one exists they abstain and say so, and
from then on a missing declaration is still UNSET.

pica-status was wrong in both layouts. Installed, after a single update, pica-core/ holds two
versions and qualified as the package root: the table said "READY core" five times and named nothing
else. In the repository it listed the worked example as a package and reported every
business-analyst file missing. It names both layouts now, the way pica-verify does, and takes the
highest version by number.

geometry-diff threw ENOENT when the capture or the dump did not exist yet. It abstains.

Two suites that run pica the way users have it. layout-check builds an installed cache from
this repository with a stale decoy version beside every package, and runs pica-status, pica-run,
pica-verify and a cross-package read from it, plus pica-verify and pica-status on an empty project.
Reverting pica-status makes it fail. thin-state-check runs every check on an empty state and on
the state left by intake, discovery and analysis, and fails on a crash. Both run in CI.

3.19.0 — Every check proven, the example built, and a specification generator that was one project's

Choose a tag to compare

@github-actions github-actions released this 26 Sep 19:02

Every check proven, the example built, and a specification generator that was one project's

The specification generator was written for one engagement and printed it into every project.
assemble-spec and model-diagram produced every heading in Vietnamese whatever the project's
language, and the legend named that engagement's upstream product and applications, with an example
reference and sentence lifted from its rules, on every project's page, including an English banking
example. Both now take their language from state.language (Vietnamese when the glossary carries
vi translations, English otherwise) and their names from state.applications and
state.integrationsNamed. They say what the state supports and no more: "read-only copies, never
written back" only when every named integration reads, an entity count that is counted, ownership
only where it is declared. On the project it was written for the output is unchanged apart from
those names. Client identifiers left in comments, rules and a plan are gone from the tree.

flow-walk-check follows declared destinations. A control that says where it goes (data-go,
data-tab, a ?scr= link) must land there, and back must return to the screen before, with the
stack rules pica's own vocabulary promises (go pushes, tab resets, a deep link lands with its owner
beneath it). wrong-destination is new, with a mutation.

The worked example is built, and nothing about the demo abstains any more. It has a build
(scripts/build.mjs into dist/), an empty state the demo actually renders (?state=empty), boards
that draw the tab bar the demo has, and Release buttons that open the payment rather than whole rows
that the design never made clickable, which is what build-diff reported the first time it could run.
proto.js deep-links from a bare #screen, the only link a published Artifact delivers, so the build
passes artifact-readiness-check; that check now accepts a query string when the page also routes on
the anchor. example-verify generates the specification, captures the boards and the served demo,
and runs everything: 51 checks pass, and build-check, artifact-readiness-check, build-diff,
document-check and figure-placement-check run for real.

The anchor rule nearly waved every React build through. Matching the word hashchange would
have accepted any bundle, because React DOM carries it in its event table; found by running the new
rule on a real React demo, whose query-only router it passed. It requires a read of location.hash
or a registered listener, and a mutation holds that line.

flow-walk-check crashed when the server stopped answering mid-walk, discarding every finding it
had made. It reports what it reached, or abstains if that was nothing. On a real demo it found a back
control labelled for one screen that returned there from a different one: the defect class it exists
for, in a project whose every other check was green.

axe-check and visual-baseline-check looked for their tools in the project only, while playwright
beside them was resolved from pica's own install; on CI, where the tools sit beside pica, both
abstained on the example while the mutation suite ran them fine. Both also look where the script is
installed now, and axe runs on the example in CI: five routes, no violations.

The runner ran the build and its reader side by side. build-check empties and rewrites the build
directory while artifact-readiness-check reads it; the build runs first, alone.

frame-inventory-check could not read the frame map /pica-port documents, and geometry-diff
collapsed two files that caption a frame the same way into one. Both read one format now,
{"<pkg>|<figma frame>": "<html screen>"} or "<file>|<html screen>", and refuse an unqualified
screen more than one file carries. geometry-diff abstains without figmaInScope, like its sibling,
and prints a row pica-verify can read.

No check is unproven. The mutation fixture synthesises a faithful Figma port from its capture, so
geometry-diff and frame-inventory-check are proven in both directions (five mutations). The two
checks that run inside Figma were already proven by mock-figma.mjs, which nothing ran: CI runs it,
and mutation-coverage-check counts them on that basis. 15 unproven at 3.16.1, 0 now. 180 caught,
0 missed. The README table lists 251 checks.

3.18.0 — A demo is walked, not only loaded

Choose a tag to compare

@github-actions github-actions released this 26 Sep 17:47

A demo is walked, not only loaded

Every browser check loaded a route fresh, measured it and left. That is how the worked example's
router shipped going blank on its first navigation (3.17.0 said "second click"; it was the first,
because the initial paint already put data-scr on <html>). flow-walk-check is new: from
every route it clicks every distinct control, then every control on the screen that produced, three
deep, replaying each path from a fresh load, and fails on a click that leaves nothing readable or
takes every frame off screen without navigating (blank-after-click), and on any script error
(script-error). Pointed at 3.16.1's proto.js it reports the blank at once. It runs under
pica-verify from the placeholders a demo already declares.

build-diff could never have run under pica-verify. Its manifest passed <state> where the
script reads the built capture, it needed .audit/demo-reference.json, and the capture always wrote
html-reference.json, so nothing could produce that file. The capture takes --as now, the manifest
passes the build, /pica-evaluate writes it there, and two mutations prove it.

Twelve checks spoke a dialect pica-verify cannot read. They printed FINDING [id] and a summary
line but no pass|FAIL <id> N finding(s) rows, so a clean run was counted as "0 assertion(s)",
--evidence had nothing to quote, and a failing run would have been reported as a runner fault
rather than as its findings. All twelve print rows now (the browser checks, traceability, acceptance,
document, figure placement, internal reference, screen states). The runner counts FINDING lines if a
check ever regresses, and the mutation suite scores a check that fires without a readable row as
MISSED
, so the contract is held on every push.

Three checks passed over nothing. document-check and figure-placement-check exited 0 with "does
not exist yet" when there was no spec, which the runner counted as a pass; they say it is an
abstention now, and their manifests need @docs/spec/index.html. internal-reference-check reported
"0 requirement identifiers" over a tree with no markup in it, which on the worked example was every
run; zero files read is an abstention, and the example points it at html/.

axe-check swallowed a route it could not scan. It printed the finding and set the exit code, and
then a run with no axe violations elsewhere exited 0. Unscannable routes count as findings.

Commands invoked scripts that did not exist. /pica-evaluate called a shell function no block
defines, naming a package deleted in 3.0.0; /pica did the same for schema-check; /pica-analyse
ran domain-check from business-analyst, which does not ship it. command-script-check promised to
fail on a form it could not read and passed over all of them. It fails on them now, and every
command resolves its scripts through node "$pica" <package> <script>.

The README listed 156 checks. There are 247. Twenty-two declared checks were missing from the
table the count is summed from. count-test now fails when a declared check is not in it.

173 caught, 0 missed. Four checks remain unproven: two run inside Figma, and two read a Figma dump.

3.17.0 — The example the README points at now passes the chain it is the example of

Choose a tag to compare

@github-actions github-actions released this 26 Sep 17:16

The example the README points at now passes the chain it is the example of

/pica-verify on examples/approvals reported four failures on 3.16.1, and CI was green, because
CI never ran it. Two of the four were the runner's own defects. Going through the rest turned up
eight more, and every one had the same shape: something reported as checked that could not have
been.

The runner handed checks the string "null". A check with an undeclared placeholder was run
anyway, with null in its place. build-check tried to execute a program called null and
reported a build failure; six browser checks loaded null?null and reported that nothing
answered. Arguments are now resolved before anything runs. A check with an undeclared placeholder
is not run, is shown as UNSET with what it lacks, and the run exits non-zero. A substitution
declared as null means this project has none: the option is left off the command line, the
check says what it did not measure, and the run prints the declaration. serve.url now fills
<servedDemo> and the build block fills <buildCmd>, <buildCwd> and <buildOut>, which is
how the scaffolding skill had always told projects to write them. --phase build is accepted: the
table printed a BUILD heading that the flag refused.

Two checks could never find the sector base. proposal-check looked for roles/analyst, a
directory renamed long ago, and in the cache sorted versions as strings, the defect 3.16.0 fixed in
the runner. palette-check looked relative to the working directory, which is the project, so on
every real project reserved-respected compared nothing. Both now resolve the sibling package from
where they are installed, highest version by number.

architecture-check's integrations could not fire. It read domainConstraints.integrations,
and domainConstraints is an array. It reads integrationsNamed now, the list the context diagram
already draws its external systems from, matched on system. The example declares one integration
with its contract, so the matching path runs on every CI build and not only the failing one.

Four checks passed over nothing or crashed instead of reporting.

  • visual-baseline-check reported "0 changed" on a run that had only just written every baseline.
    That run compared nothing, and now says so as an abstention.
  • screenreader-check and keyboard-check clicked the first match of --act / --open. In a
    prototype that keeps every screen in the page, that is often a hidden element: click() waited
    thirty seconds and threw. They click the first visible match, and name any route where nothing
    could be activated.
  • artifact-readiness-check said "no --dir does not exist".

The worked example itself. Its controls were 40px against a 44px floor. Its flow had no failure
branch, its navigation cited no evidence, its approved package had no pre-gate lens, and it carried
no directions and no architecture, so five checks either failed or abstained on it. Its prototype
router answered ?viewport=mobile and ?scr=typo with the home screen, which is the defect
url-param-guard exists for, and it changed screens without announcing anything. All of it is fixed
in the example, including proto.js, which projects copy. It now declares .pica/runners.json and
serves itself with a builtin-only scripts/serve.mjs.

proto.js went blank on its second click. It wrote the current screen to <html data-scr>,
and <html> then matched every [data-scr] query in the router: after the first move byId()
returned the document root and paint() hid it. It writes data-current now. The same click
showed the second defect: a task screen hides the tab bar, and the tab bar stayed, because
.tabbar{display:flex} beats the browser's [hidden] rule and only the first frame's bar was
touched. Every check passed over both, because each loads a route fresh and none clicks twice. They
were found by clicking the flow, which is rule 1, and no check yet does it for you.

Proof, both directions. 29 new mutations cover ten checks that mutation-coverage-check listed
as proven in neither direction: architecture, flow-paths, pre-gate-lens, palette, a11y, parity,
copy, flow, shell and code-tokens. 15 uncovered becomes 5, and those five are the two that run inside
Figma and three that need a Figma dump or a built demo's capture. 169 caught, 0 missed. CI also runs
scripts/example-verify.mjs: the example, captured and put through pica-verify, fails the build on
any failure or UNSET.

assets/flow.svg ran seven design packages in one row, under the stop card and off the right
edge, while flow-diagram --check called it current. Boxes wrap inside their lane now, sized to
their longer line.

The session banner advertised --to product and four stops. picaflow has had --to design|figma and
three stops since 2.0.0, and the banner says so now. state-schema.md documents flows with both
halves, navigation, directions, workPackages.preGateLens and architecture.

3.16.1 — A check that fails on every push is a check nobody reads

Choose a tag to compare

@github-actions github-actions released this 26 Sep 16:33

A check that fails on every push is a check nobody reads

CI on main had been red for twenty pushes and every release from 3.3.1 on had failed with it,
so the newest GitHub release was still 3.5.1 while the tags ran to v3.16.0. Nothing was wrong
with the code. Three things had drifted, and each one alone was enough:

  • The README version badge still read 3.0.2. count-test said so on every run, and it
    stops the job at the first failure, so it also hid the two below.
  • assets/flow.svg was stale. The check counts per specialist had grown (business-analyst
    4 → 8, ux-engineer 11 → 21) and the drawing still showed the old numbers.
  • The CHANGELOG stopped at 3.5.1. release-check refuses a tag with no section, so even
    a green suite could not have published 3.6.0 to 3.16.0. Those sections are written below from
    the commit that cut each version. The tags stay as they are: re-tagging a version in place is
    what release-check exists to prevent. 3.16.1 is the first release since 3.5.1 to carry all
    of them.
  • And behind all three, the mutation suite could never have passed. Seven browser-driven
    mutations were gated on @demo, which the generated fixture has never carried, so they
    skipped on every run and the CI gate treats any skip as a failure. They ship their own pages
    and ask nothing of the project. What they actually need is the toolchain the check imports, so
    they are gated on that now (module:playwright, module:playwright+axe-core, and so on). CI
    installs axe-core, pngjs and pixelmatch next to playwright, and the source-reading mutation
    uses the example's src when there is no demo/src. 140 caught, 0 missed, 0 skipped.

The habit this points at: run node scripts/count-test.mjs and node scripts/flow-diagram.mjs --check before tagging, not only the checks you just changed.

3.5.1

Choose a tag to compare

@vqdungwork vqdungwork released this 26 Sep 12:44

3.5.1

Reading it as the person who receives it

The published plain-language test is whether the intended reader can find what they need,
understand what they find, and use it. Reading a finished document in the recipient's
chair found seven defects in ten minutes that a week of building had not.

  • The title was a machine field. Đặc tả — professional services — the sector key, printed as
    a headline. The reader looks for their own product and meets an industry classification.
  • The opening paragraph was the analyst's evidence note, ending "interviews=0, xem
    docs/as-is.md"
    , then cut mid-word by a 300-character slice. The first paragraph is the one
    thing everybody reads.
  • Half the pictures were in English — work item, assignee, unconfirmed — drawn from a
    glossary that already held the Vietnamese and which no diagram had ever consulted. The original
    term stays in a quiet mono label beside the translation: both readers exist, and only one of
    them decides.
  • The permissions matrix read r and cru, twenty times, with no key anywhere on the page.
    Not a dense notation — an unanswered question printed twenty times.
  • Numbering implied an order that did not exist. Cards numbered 01–07 down a lane told the
    reader that three independent triggers were steps two, three and four of a process. The number
    is the stage now, so parallel things share one.
  • Emphasis by SHOUTING — how a register gets read back by the person who wrote it.
  • Text cut mid-word. Xem một dự án đang chạy t is not a shorter label, it is a broken one,
    and it was on every card with more than one next step.

The habit underneath all seven: a document assembled from a register inherits the register's
voice
, and someone has to read it as the recipient before it goes out — which cannot be the
register.

3.5.0

Choose a tag to compare

@vqdungwork vqdungwork released this 26 Sep 12:36

3.5.0

The reader who decides is the one who does not know the notation

A specification is signed by someone who knows the business and not the method. They are not
trying to find a fact; they are trying to see what a person can do, where they go, and what
the system holds. A register dump asks them to read every line and assemble the picture
themselves. They will not, and they are right not to.

BABOK names four models a specification owes its readers — process, use case, data, state. pica
held all four as validated data and drew none of them. The data was right and unread.

model-diagram now renders a use case diagram (actors and what each can do: the only picture
that answers "what can this person actually do", with no notation to learn) and an ERD with
relationships and cardinality drawn from the prose the model records them in. The assembled page
leads with them.

Three things that separate a diagram from a picture of one

Colour is the information, and one legend serves the whole document. Where a step happens and
where a screen's data comes from are the same question in two places: grey for the upstream
system, blue for what this product adds, olive for what a person does with no system at all. Learn
it once, read every grid at a glance — and see without reading a word how much of a working day
the product actually touches.

A grid beats a graph when the graph does not fit. Twenty-three steps as nodes and edges is
2860px wide and must be scrolled; the same steps as cards, grouped by who does them and ordered by
depth, fit any column. A diagram that must be scrolled sideways is a diagram nobody sees whole,
which is the only reason to draw one.

Unconfirmed points are marked, never filled in with a plausible figure — in their own colour,
where the work standing on them is, not filed in an assumptions register.

The assembled page went from 129 entries in eight registers over 20 325px, to a narrative in parts
— each opening with one sentence and then a grid — at 7 700px, with the registers in a closed
appendix for the one reader in ten who wants to look up a code.

And the data the pictures exposed

Drawing it found what reading it had not: every label in the TO-BE process was written in
unaccented Vietnamese, on the one model that was ever going to be shown as a picture.

3.4.0

Choose a tag to compare

@vqdungwork vqdungwork released this 26 Sep 12:29

3.4.0

Learning archify's surface and skipping its substance

3.3.0 generated SVG from typed data and called that "archify-style". It was the easy half.
Archify's point is that a diagram can be interrogated — focus a node, trace a route, step
through — and what shipped was a picture. A 23-step process read all at once is the same wall of
text in another medium.

Diagrams now carry data-node and data-edge, and the page that embeds them can focus a step:
everything not reachable from it dims, the route can be walked one hop forward or back, and the
count of steps ahead is stated. Three defects found by using it:

  • Edges paint over nodes and swallow the click. Several steps simply did not respond. A
    control that looks interactive and is not is worse than one that never offered.
  • The reachable-set walk drained its queue inside a filter predicate, so every route reported
    exactly one step ahead of itself — a wrong number, stated confidently.
  • Column was array position, not topological depth. A 23-step process became a 4570px
    horizontal ribbon in which nothing was parallel and no branch rejoined, about a graph whose
    whole point is that three roles act at once.

Drawing the model is a check the checks did not run

The renderer put all twenty-three steps in the first lane and left two lanes visibly empty. The
cause: lanes declared by display name, nodes referencing them by slug — two vocabularies for one
thing.

process-check catches exactly this, and has since it was written: point it at that state and
activity-laned reports thirteen findings. The check was correct, present, and never run,
because the chain stopped before the step that invokes it. A model that is only validated is
validated against the rules somebody remembered to write; a model that is drawn is validated
against everyone who sees it.

And the renderer must never fall back to lane zero: Math.max(0, indexOf(...)) turned an
unresolved lane into a silent default, so a data defect rendered as a design — an odd-looking
process rather than a broken one.

A specification is read by someone who will read as little as possible

The assembled page was rebuilt against artifact-design and the published guidance on scannable
technical documents. What was wrong: one system typeface, undeliberate greys, every one of 129
entries in an identical rounded card so hierarchy was flat, and prose running the full 1100px.

Now: three type roles (a serif for headings because this is a document somebody signs, a
utilitarian sans for density, a mono for ids because an id is data), warm neutrals, prose held to
68 characters, a sticky rail of registers with counts so a reader jumps instead of scrolling, and
registers rendered as tables — a register is one object with many rows, not many objects.
20 325px → 11 846px, and light-only by request.

3.3.2

Choose a tag to compare

@vqdungwork vqdungwork released this 26 Sep 12:15

3.3.2

A long list is not a visualisation

3.3.0 assembled thirty-five documents into one page and called it done. Asked whether the
visualisation was actually good, the honest answer was no: 129 items in a single linear column,
eighteen and a half screens of scrolling, diagrams at the very bottom where nobody reaches
them, every register rendered identically so a glossary entry looked exactly like a screen, and a
phone showing four labels and no content on its first screenful.

It was, precisely, the wall of text this project had spent a week diagnosing on somebody else's
report screen. Writing the rule does not exempt the next thing you build from it.

  • Lead with a map. The chain drawn as a chain — 3 segments → 9 pains → 13 use cases → 29
    requirements → 13 rules → 4 entities → 11 screens
    — each hop a proportional bar and a way in.
    Navigation answers "where does this one go"; a map answers "how much is there", which is asked
    first.
  • Pictures where they are read, not after eighteen screens of list.
  • Reference registers collapse. Six visible, a line saying how many are behind it. Open items
    never collapse.
  • A phone's first screen carries content, not four stacked labels.

Result: 20 325px → 15 361px, and the shape is legible in a second rather than assembled in the
reader's head over eighteen screens.

Two smaller lessons with wider reach. A wide diagram must scroll, not shrink — capped at the
column, a 23-step process became a grey smear, present and unreadable and worse than absent
because it looks answered. And the "scroll sideways" hint is decided by measuring at runtime,
never by a width threshold guessed in the generator: the guess captioned two diagrams that fitted
as needing a scroll they did not, and left the one that did unmarked on a phone.