Releases: cyberash-dev/project-map-cli
Release list
v3.0.0
Surfaces: project-map/cli 3.0.0 · project-map/map-document 3.1.0 ·
project-map/detection-facts 3.0.0 · project-map/package 1.0.0.
Fixed
- The two artifact fingerprints are two values.
analyzer_build_digestand
adapter_registry_digestwere one constant covering the detector tree and
the fact IR, so a machine whose tree-sitter grammar or YAML parser was pinned
at another version emitted other bytes under an identical fingerprint. The
registry value now covers the detector modules specialized to a language or a
framework; the analyzer value covers that set, the rest of the detector, the
shared tree-sitter and YAML bindings, and the name, version and registry
integrity of every pinned parser, read from the lockfile when the analyzer is
built. The registry value is what the
analysis unit folds in as its registry version, which is whatCTR-004calls
it. All three values change once, andbuild --checkreports exit 3 until
the artifact is rebuilt.
The provenance declares the pinned dependency set; it is not a hash of the
compiled grammars, which are built natively at install. - An artifact naming no fingerprint fails check mode with exit 3, rather
than being reported as ordinary drift. Bytes that are not a JSON object fail
the same way. Exit 1 keeps an absent artifact and a byte difference under
agreeing fingerprints. - A proof path that closes on itself is typed rather than fatal. The Python
declared-sink resolver followed a path argument back through local bindings
and formatting calls with no record of where it had been and no bound on how
far it went, sourl = url.format(x)exhausted the stack and raised out of
the build. It now carries a visited set and a three-edge budget, and returns
a normalized value rather than a syntax node, so it can name why it stopped:
unknown(recursive)for a cycle,unknown(depth_exceeded)for a fourth
edge, andunknown(dynamic)only where a hop reached nothing. Following a
name to its binding costs an edge; unwrapping a formatting call costs none. - An absolute code route is composed only from a declared entry point. A
router carrying no mount record was published at a bare path and graded
resolved, which is right for a composition root and silently wrong for a
sub-router mounted where the analysis cannot see — and the two are one
syntactic form.detect.inbound.serve_roots[]now names the declaration a
router reaches the outside through, its returned-value index, and the absolute
prefix it is exposed under. A registration that reaches no declared entry
point carriespath: unknown(unanchored_router)and its source anchor, and no
registration is published at a bare path under any condition. The partial
chain the analysis proved is evidence and is not emitted: publishing it would
push a suffix match onto the consumer, ambiguous wherever two mounts end in
the same segments.
The entry point's returned value is followed through up to three statically
resolved call edges, so a service whose root is built two calls below the
declared symbol still anchors. A symbol naming no declaration, several
declarations, or a value that is not a router raises the mandatory
serve_root_unresolvedand fails check mode on the code alone. net/http.ServeMuxis a router the detector reads. A repository
declaringnet/httpunderdetect.inbound.routers[]has its
Handle/HandleFuncregistrations claimed, with the pattern read under the
[METHOD ][HOST]/[PATH]grammar the multiplexer itself fixes.GET /xemits
both aGETand aHEADfact, because the multiplexer answers both. A
pattern naming no method leaves the method typed rather than expanding into a
verb set the source never wrote, and one naming a host types the path,
because the route it names is not the path alone. A multiplexer handed to a
pattern as its handler is a mount, which is how a sub-mux composes.- The specification cross-check is computed after the merge, per route.
openapi_route_not_in_codecounted inventory facts whose handler stayed
unknown, and every inventory fact carries an unknown handler, so it equalled
the served-route count of each contract whatever the code half proved. It now
names one route at a time and only fires where the merge left that route with
the inventory as its only provenance. The other direction is new:
router_route_not_in_openapinames a route the code proved and the inventory
does not declare. A registration whose path is unknown raises neither, since
it cannot merge andunanchored_routeralready says why.
Neither code is mandatory for check mode, and no configuration suppresses
either: a suppression flag that goes stale silently disables the cross-check. - A chi router now reaches its registrations across call edges. The
generated-server shape puts the registrations in a second method that
receives the router as a field of a struct parameter, and the idiomatic
layout puts the mount in a second file. Both lost the identity, so the route
was published without its mount prefix, gradedresolved, and never merged
with the served route it belonged to. Router identity is now resolved over
the whole unit and crosses up to three statically resolved call edges — the
bound every other resolver already spends — carried in a record seeded from
the composite literal at the call site. A struct field the literal never
assigned reads as the zero value Go fixes for its declared type, and a branch
guarded by comparing a proven router againstnilcontributes nothing. A
fourth edge is refused asunknown(depth_exceeded)rather than followed.
A mount prefix written as a package constant folds; it used to be a hole.
A body reached along two paths is entered at the shorter one: the walk keys
its de-duplication on the interprocedural distance as well as the seed, so a
helper called both directly and through a chain resolves whichever way the
two calls happen to be written, and its sites are reported once. - A mount whose sub-router did not resolve is diagnosed. It was abandoned
in silence, so every route below it published at a bare path with nothing in
the artifact saying a prefix was lost. The newrouter_mount_unresolvedis
not a mandatory check code: it names a shape of code, which the unclassified
baseline suppresses andbuild --strictratchets. - The Go sink-value fold says which of the two happened. Reaching no
further constant and refusing the next one both read asunknown(dynamic).
The next edge is now looked up before the budget is spent, so exhaustion is
unknown(depth_exceeded). The numeric bound is unchanged: three edges
resolve, a fourth is refused. - A python route is anchored at the call that serves it. The rule that an
absolute route is composed only from an anchor was landed for the router-value
form alone, so the declaration-DSL form kept composing a route for every
registration in the unit — a route table no application collects was published
as served and gradedresolved. Python needs no configuration for this: a
call resolving by import provenance toaiohttp.web.run_appis the anchor,
every application class each branch of the binding constructs is resolved, and
the route collections their class attributes hold are anchored at the root.
The fold follows tuples and lists, a splat of either,+concatenation, a
module constant in the same module or in an imported one, and an attribute of
another class, with the first binding in method-resolution order winning, so
an attribute a subclass rebinds no longer reaches the base value. A
registration nothing serves carriesunknown(unanchored_router), and a
serving call whose application resolves to no class raises the mandatory
serve_root_unresolved. - A declaration index is keyed by name, not by node identity. Every access
to a tree-sitter node returns a fresh wrapper, so an index keyed on the node
answered nothing and a directory fallback hid it: the anchor of a serve root
and the parent of a mount compared unequal, and repositories whose mounts sat
in another file of the package silently lost their prefix. Fixture coverage
could not see it; a run against a thirteen-hundred-file repository could.
Changed
- The endpoints section renders only a route the analysis proved. A
generated server whose options type is build-generated outside the analysis
unit folds the base-URL field tounknown(dynamic), which is correct rather
than a defect, and on a real service that is a third of the table naming a
route nobody can read, look up, or compare against the served specification.
HTTP endpointsnow renders a fact whose path is a literal; a fact carrying a
typed hole in its route stays in the facts artifact alone. Selection is on the
value in the Route column and not on the derived resolution, so a proven route
whose handler sits outside the unit keeps its row and pays only the method
cell. Where no fact has a proven route the section renders no heading and no
body.External dependenciesis unchanged, and the facts artifact does not
change by one byte.
v2.0.0
The document shrinks to what a rebuild does not change. Every field removed
below moved without any extracted fact moving, which made PROJECT_MAP.md
conflict on merge for reasons no reader cared about.
Surfaces: project-map/cli 2.0.0 · project-map/map-document 3.0.0 ·
project-map/detection-facts 2.0.0 · project-map/package 1.0.0.
Added
min_tool_version, a version floor with a ratchet. Abuildorfacts
run below the declared floor is refused with exit 7 before anything is read
beyond the configuration; abuildthat finishes at exit 0 raises the line to
its own<major>.<minor>.0, replacing the bytes of that value alone.
Adding the key to a repository is what stops installs that never receive this
release: the schema has rejected unknown top-level keys since v0.1.0, so every
published version refuses a configuration carrying it. See the README for the
deliberate costs.
The refusal names the remedy, not only the mismatch: it states that the tool
has to be updated, spellsnpm i -g project-map-cli@latestin full, and names
lowering the floor as the deliberate alternative.
Removed
- The generation-metadata section.
## Generation metadataand the
metadatasection id are gone. A configuration namingmetadataunder
sections, or passing it to--only, now exits 5. Drop the line. - The detection-coverage section.
## Detection coverageand the
detection_coveragesection id are gone the same way. The measures and the
aggregated diagnostics stay in the facts artifact undercoverageand
diagnostics, which is where a program already read them;--strictagainst
the unclassified baseline remains the way to fail a build on a new
unclassified site. - The generated header.
Generated by project-map v<version> on <timestamp> from revision <sha>andCoverage: <n> files scanned (<m> excluded).are
replaced by the constantGenerated by project-map. Do not edit by hand. - Line numbers in anchors. An entity, enum, table or worker names the file
that declares it, without:<line>. - Counts a collection is ranked on. The
Filescolumn of the bounded
contexts table and theReferenced from N module(s)bullet of an entity are
gone; the row order still reports the same magnitude. - The field count on the
Fields:label. The bullets under it are the
same list in full, soFields (8):reported nothing the page did not, and it
moved on every field added or removed.project-map.jsonstill carries the
array. - The count on the migrations heading.
### Migrations (last 5)becomes
### Recent migrations. That the list is a tail is information the rows do
not carry, so the heading keeps saying it; the number is the row count, which
the table already gives, and it moved whenever a repository crossed below its
ownstorage.last_n. - The
Resolutioncolumn of both detection tables.## HTTP endpoints
heads Method, Route, Provenance, Contracts;## External dependenciesheads
Owner, Method, Route, Destination. In the document the column restated the
cells beside it — a row the analyzer did not prove is the row whose method,
route or destination readsunknown(<reason>), and the reason names why.
Every fact in the artifact still carriesresolution.
Everything removed stays where a program reads it: project-map.json
(output.json) carries metadata, source.line, fileCount and
referencedFrom unchanged, and the facts artifact carries coverage,
diagnostics and resolution.
Changed
build --checkcompares byte for byte. With no non-reproducible field
left in the document, nothing is normalized away first. A document differing
only in whitespace is now out of date.- An extractor failure renders under
## Extraction errors, one bullet per
failure, immediately after the lead paragraph. The heading is not a section
id: no configuration turns it off, and it is absent when nothing failed. initwrites the section list withoutmetadata. No default list
ever nameddetection_coverage, so nothing else changes there.analysis_unit_digestcovers what the analysis unit declares. It folded
a hash of the whole configuration document; it now covers thedetect,
openapiandanalysis_unitsections alone, which is what the contract
always said. Every value of it changes once, sobuild --checkreports drift
onfacts.jsonuntil it is rebuilt and committed. No fact, diagnostic or
coverage measure changes value. Editing a key only the document reads,
entities.top_norsections, no longer dirties the artifact.initwritesmin_tool_versionwith the comment explaining it.- A value read out of the source renders as inline code everywhere.
Previously only the two detection sections did, and every other position
went through prose escaping: a project namedyandex_pay_plusopened the
document asyandex\_pay\_plus, and the same happened to a bounded-context
path, a table, a model, a revision, a migration summary and a worker topic.
The H1 is now# Project Map: `<name>`. A consumer matching
^# Project Map:still matches; one that unescaped\_stops needing to.
Migration
Drop - metadata and - detection_coverage from sections in
.project-map.yaml, then rebuild and commit PROJECT_MAP.md. A consumer that
parsed the tool version, the timestamp, <file>:<line>, a field count or a
resolution out of the markdown reads them from project-map.json and
<output.facts> instead, and one that matched a name against its escaped
spelling matches the declared spelling now.
v0.2.2
Replace Biome with the shared @cyberash-dev/dev-tooling ESLint + Prettier config as the single lint/format toolchain.
- Prettier (tabs) owns formatting; shared ESLint base enforces the comment policy and structural/type caps (no
any, no non-null!, no narrowingascasts, 80 lines/fn, 7 params, 10 members/class). - Type-aware lint via split tsconfig (
tsconfig.jsonlints src+tests,tsconfig.build.jsonbuilds src). - Whole tree reformatted to tabs; all surfaced violations fixed with no rule overrides.
- No behavior change: full test suite green and
build --checkdeterministic.
v0.2.1
Dogfood: add .project-map.yaml and generated PROJECT_MAP.md / project-map.json for this repo. Drop accidental self-dep on project-map-cli from package.json.