@hadden-industries/markdown-quality 0.1.0-alpha.4 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,11 +4,9 @@ This package supplies one CLI and library for Markdown layout, sentence lines, G
4
4
  Consumers declare their authored-document scope in `.markdown-quality.json`.
5
5
  The package preserves the existing AGPL-3.0-only license.
6
6
 
7
- The current version is a candidate for public alpha distribution.
8
- Controlled native component clearance and Windows/Linux packed qualification passed for the retained binaries.
9
- The published alpha.2 tuple failed Linux registry execution because its Windows-packed native archive omitted executable permissions.
10
- Alpha.3 corrects the archive header and retains every native executable/source/notice byte.
11
- The corrected public tuple requires fresh registry qualification before use; both accepted pilots and recovery evidence still block stable v1.0.
7
+ Core and platform packages share one exact release version.
8
+ Install a release after its coordinated tuple passes registry and consumer qualification.
9
+ Registry publication and promotion to `latest` are separate outcomes.
12
10
 
13
11
  ```sh
14
12
  markdown-quality check --root /path/to/repository
package/SECURITY.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Security policy
2
2
 
3
- Before stable release, security support covers only the single prerelease designated by npm's `pilot` tag.
4
- After stable release, it covers only the latest stable release designated by `latest`, unless an explicit policy accepts another line.
3
+ Before stable promotion, security support covers only the single candidate designated by npm's `pilot` tag.
4
+ After stable promotion, it covers only the latest stable release designated by `latest`, unless an explicit policy accepts another line.
5
5
  Consumers should pin exact versions and qualify upgrades.
6
6
  Older releases remaining downloadable does not imply maintenance or backport support.
7
7
  The supported runtime is Node 24.21.0 on Windows x64 and Ubuntu 24.04 x64 with glibc 2.39, with the qualified core/native tuple and explicit preset.
package/docs/ci-trust.md CHANGED
@@ -15,8 +15,10 @@ A private shared Action is optional and does not solve public caller access.
15
15
  | Pilot fork PR | Reviewed base tooling and policy | Separate trusted acquisition | Candidate data only; no candidate scripts or graph | Architecture selected; qualification pending |
16
16
  | Tool or policy update PR | Existing accepted graph until review | New graph requires owner review | Never treat candidate graph or exclusions as trusted | Required review remains explicit |
17
17
 
18
- The package CI has no registry secret or OIDC permission.
19
- It installs with lifecycle scripts disabled and qualifies both OS targets.
18
+ Package tests and candidate/registry qualification have no registry secret or OIDC permission.
19
+ They install with lifecycle scripts disabled and qualify both OS targets.
20
+ The separately dispatched publisher grants OIDC issuance only to its main-restricted npm-publication job.
21
+ Its exact candidate/source/manifest gate precedes public pilot publication; registry qualification remains credential-free and latest promotion remains separately gated.
20
22
  Packed-consumer tests acquire public JavaScript dependencies before testing offline frozen-lock reinstallation and runtime execution.
21
23
  The source lock acquisition alone does not populate metadata for a fresh consumer install.
22
24
  It cannot serve as evidence that a pilot's private access works.
@@ -35,7 +37,7 @@ Before each pilot merge, the owner accepts the exact candidate head/tree and att
35
37
  Any changed candidate, base policy/workflow or tool identity invalidates that acceptance.
36
38
  A same-name candidate job or green PR summary cannot substitute for this record.
37
39
  This route provides manual owner enforcement; it does not claim automatic workflow-specific enforcement.
38
- No separate App, paid entitlement, write-capable status reporter, OIDC authority or new secret is created.
40
+ The pilot trusted-run boundary creates no separate App, paid entitlement, write-capable status reporter, OIDC authority or new secret.
39
41
  Negative hosted probes must still show malformed-link failure, candidate policy/ignore non-authority, candidate marker non-execution and credential absence.
40
42
  The single owner's own PR does not constitute independent human approval; any necessary integration route must be explicit and recorded.
41
43
 
@@ -4,12 +4,14 @@ Install an exact coordinated public release after registry availability is verif
4
4
  Acquisition needs no npm account or token; disable lifecycle scripts.
5
5
 
6
6
  ```sh
7
- npm install --save-dev --save-exact --ignore-scripts @hadden-industries/markdown-quality@0.1.0-alpha.3
7
+ npm install --save-dev --save-exact --ignore-scripts @hadden-industries/markdown-quality@1.0.0
8
8
  npm ci --ignore-scripts
9
9
  ```
10
10
 
11
- Version `0.1.0-alpha.3` is published under the `pilot` tag and qualified for fresh anonymous root and isolated installations on Windows x64 and Ubuntu 24.04 x64.
12
- It remains a prerelease; pilot acceptance and stable promotion are pending.
11
+ The source candidate is `1.0.0`; confirm its registry availability and exact tuple qualification before using the command above.
12
+ Publication under `pilot` and promotion to `latest` are separate steps.
13
+ The previously qualified alpha.4 tuple remains immutable historical evidence.
14
+ Both consumer pilots must accept the exact stable tuple before promotion.
13
15
  Keep the consumer lockfile.
14
16
  An isolated `tooling/markdown` npm project supports consumers that do not use Node for their application.
15
17
  Its commands must pass the actual repository root.
@@ -75,6 +77,7 @@ Lint messages describe the violated rule without quoting document content.
75
77
  These are conservative operational limits; benchmark and platform acceptance remains part of candidate qualification.
76
78
 
77
79
  Formatting compares maintained parsed structure and literals before replacement and verifies convergence.
80
+ Fenced and indented code retain literal whitespace, including whitespace-only lines and trailing spaces; embedded code formatting remains disabled.
78
81
  The parsed comparison is an additional backstop, not a universal rendered-equivalence guarantee.
79
82
  It conservatively refuses unsupported semantic or literal changes.
80
83
  Ordinary file permission bits are preserved; ownership changes, alternate streams, ACL inheritance, arbitrary metadata, and adversarial filesystem races are not promised as preserved.
@@ -15,9 +15,16 @@ An opt-in compatible feature may use a minor version.
15
15
  Changed defaults require a new preset major, and a package major when existing invocations change.
16
16
  Dependency upgrades are classified from observed corpus and consumer deltas.
17
17
  Never overwrite a published version.
18
+ After npm accepts publication, allow up to five minutes for anonymous version metadata and both installer metadata representations to expose the exact approved integrity.
19
+ Only a processing 404 is retried; mismatched integrity and other failures stop delivery.
20
+ Publication is never repeated by this availability check.
21
+ The three bounded waits fit within the publisher's thirty-minute job limit.
18
22
 
19
- The current alpha is not stable v1.0.
23
+ The current source prepares version 1.0.0 under the pilot tag; stable promotion remains gated.
20
24
  Stable promotion requires the same qualified tuple on both platforms, public registry readback and unauthenticated installation, both pilot acceptances, and a timed restoration exercise.
25
+ The 1.0.1 corrective candidate restores checking and formatting of literal code lines containing trailing or whitespace-only spaces.
26
+ This repair changes shipped JavaScript and therefore requires a new coordinated patch version; completing the unchanged 1.0.0 archives remains a separate recovery operation.
27
+ Keep latest promotion blocked until the literal-preservation repair and exact candidate satisfy the same platform, registry and pilot gates.
21
28
  Changed/referrer mode and later fleet adoptions remain deferred.
22
29
 
23
30
  Prepare assets explicitly from their frozen manifest.
@@ -40,16 +47,29 @@ An upstream Cargo lock is a superset of features and development inputs and does
40
47
  The original upstream license and per-asset repack identity are retained.
41
48
 
42
49
  The owner selected public distribution after npm rejected the private publication with E402.
43
- All three packages declare public access and share the candidate version 0.1.0-alpha.4.
44
- The alpha.4 candidate avoids duplicate formatting of byte-identical documents while retaining independent prose checks and the changed-document preservation/convergence checks.
50
+ All three packages declare public access and share the source candidate version 1.0.0.
51
+ It retains alpha.4's qualified native executable/source/notice bytes and avoids duplicate formatting of byte-identical documents while retaining independent prose checks and preservation/convergence checks.
45
52
  The first alpha.3 hosted OWLAPI shadow checks passed correctness but exceeded the accepted 30-second budget on both platforms.
46
- Alpha.3 remains immutable historical evidence; alpha.4 requires fresh transported-archive, registry, and pilot qualification before promotion.
53
+ Alpha.3 and alpha.4 remain immutable historical evidence; every new tuple requires fresh transported-archive, registry, and exact-version pilot qualification before promotion.
47
54
  The public alpha.2 archives remain immutable historical delivery evidence and are unqualified for Linux execution.
48
55
  The packer normalizes the Linux npm member's executable mode with Python tarfile, verifies all member bytes are unchanged, and recomputes the final archive integrity.
49
56
  Qualify the exact transported release archives on both platforms, rather than each platform's independently generated package.
50
57
  The unpublished private alpha.1 candidate and all rejected attempts remain historical evidence.
51
58
  Direct first publication uses explicit public access; a staged placeholder is unnecessary.
52
- OIDC publication is preferred after actual package/workflow eligibility is configured.
59
+ The manual publisher uses npm 12.2.0 in hosted CI and requires all three npm trusted-publisher mappings for this repository, publish.yml and the npm-publication environment.
60
+ That environment must permit exactly the main branch.
61
+ Ordinary publication verifies a successful same-source candidate run, all three same-attempt jobs, the approved manifest digest and exact frozen archive bytes.
62
+ Recovery preserves the original candidate's source and archives separately from the current reviewed publishing-control source.
63
+ An admission job without OIDC binds a complete three-package origin map before effects; both platform jobs qualify previously attempted packages before the publisher may run.
64
+ Only a prior planned effect with an explicitly skipped publication step is eligible for first publication; an attempted or unknown outcome must be verified in the registry and cannot become eligible through a 404.
65
+ Each downstream job rehashes its downloaded archives against the admitted manifest.
66
+ The publisher rechecks current main, all three local archive hashes and version absence immediately before each single eligible publication, verifies both native archives before core, and uses only pilot.
67
+ Only the publication job has OIDC issuance permission; no long-lived npm token is supplied.
68
+ Both registry jobs use native npm signature/attestation verification and bind each installed package to its admitted source, workflow, exact publication run and attempt.
69
+ Each platform installs and cryptographically verifies its supported native package and core; opposite-platform native archives receive anonymous raw-byte verification, with the combined platform records covering the complete tuple.
70
+ Structural provenance fixtures do not establish cryptographic acceptance.
71
+ The workflow does not promote latest; both pilots and recovery must accept the exact tuple first.
72
+ GitHub artifacts expire after 30 days, so export the complete frozen source, manifest, archives and verification bundles to the retained evidence store before expiry, for the security policy's maintained-lifetime-plus-three-years floor.
53
73
  Public installation needs no registry token, subscription, or OIDC authority.
54
74
  Public npm provenance is eligible only through a qualified supported publisher; do not claim it for the local bootstrap.
55
75
  Retain attributable source/build/repack evidence and qualify attestations separately.
@@ -60,6 +80,17 @@ Qualify root and isolated consumers on both supported platforms from the registr
60
80
  Do not perform registry publication while rights, publisher authentication, independent assurance, or platform evidence remains missing.
61
81
  A partial publication stays unpromoted and receives a new corrective version if bytes change.
62
82
 
83
+ For unchanged-version recovery, manually dispatch `publish.yml` with the original `candidate-run` and `manifest-sha256`, plus the terminal prior `recovery-run` and exact `recovery-attempt`.
84
+ The original legacy publication can omit `recovery-origins-sha256` only when its source is the original artifact source and its retained manifest matches exactly.
85
+ For a later recovery, independently approve the prior admission map's SHA-256 and pass it as `recovery-origins-sha256`; never infer expected origins from the attestation being verified.
86
+ Retain the actual dispatch inputs, original candidate/manifest, admission artifact, per-package map, provider attempts/jobs, effect outcomes and both registry reports.
87
+ Use a new manual dispatch rather than blindly rerunning a partial publication.
88
+ After known acceptance, availability polling retries metadata 404 or an installer metadata document whose version map has not yet acquired the target version for at most five minutes with bounded requests; it never invokes publication again.
89
+ The version endpoint, abbreviated installer metadata and full installer metadata share that deadline; every visible target must match the approved name, version and integrity.
90
+ Anonymous qualification repeats this admission before installing; malformed metadata, mismatched bytes and access failures are terminal.
91
+ The publication job is bounded to 30 minutes to accommodate three independent visibility waits and native verification; failed or cancelled jobs require evidence reconciliation, not automatic publication retries.
92
+ An unavailable attempted package, mismatched bytes/origin, non-404 registry failure or unresolved live producer stops delivery until its outcome is established.
93
+
63
94
  For recovery, retain the exact consumer base revision, old manifest and lock, workflows, selected document preimages, and unrelated-file sentinels.
64
95
  Restore only migration-owned changes.
65
96
  Verify byte identities and required checks, then record elapsed restoration time.
@@ -75,7 +106,7 @@ Private reporting is enabled for this repository, as read back from GitHub on 20
75
106
  Review dependency/advisory health at each release preparation and monthly while maintained.
76
107
  Retain release source, build inputs, notices, recipient delivery/qualification evidence, and accepted recovery inputs for the maintained release lifetime plus three years after support ends, honoring any applicable license obligations requiring more.
77
108
  This is an operational retention floor, not a legal interpretation.
78
- Before stable, only the prerelease designated by `pilot` is maintained; after stable, only the latest stable release designated by `latest` is maintained unless another line is explicitly accepted.
109
+ Before stable promotion, only the candidate designated by `pilot` is maintained; after stable promotion, only the latest stable release designated by `latest` is maintained unless another line is explicitly accepted.
79
110
  Consumers remain exactly pinned and upgrades individually qualified; older downloadable releases have no implied support or backport promise.
80
111
 
81
112
  Pilot qualification uses six consecutive valid full checks per frozen corpus/platform, excluding installation, with observed nearest-rank sample p95 at most 30 seconds.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hadden-industries/markdown-quality",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "1.0.1",
4
4
  "description": "Shared, bounded Markdown formatting, GFM linting, and local-link checking",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
@@ -46,7 +46,7 @@
46
46
  "prettier": "3.9.9"
47
47
  },
48
48
  "optionalDependencies": {
49
- "@hadden-industries/markdown-quality-win32-x64": "0.1.0-alpha.4",
50
- "@hadden-industries/markdown-quality-linux-x64": "0.1.0-alpha.4"
49
+ "@hadden-industries/markdown-quality-win32-x64": "1.0.1",
50
+ "@hadden-industries/markdown-quality-linux-x64": "1.0.1"
51
51
  }
52
52
  }
package/src/formatting.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: AGPL-3.0-only
2
- import * as prettier from "prettier";
2
+ import { formatLayout } from "./literal-layout.js";
3
3
  import { parse } from "./analysis.js";
4
4
  import { runNative } from "./native-tool.js";
5
5
  import { checkProse } from "./prose-diagnostics.js";
@@ -30,8 +30,8 @@ export async function formatDocument(text, context, tool) {
30
30
  tabWidth: context.config.layout.tabWidth,
31
31
  plugins: [],
32
32
  };
33
- const layout = await prettier.format(text, options);
34
- const result = await prettier.format(runNative(tool, layout), options);
33
+ const layout = await formatLayout(text, options);
34
+ const result = await formatLayout(runNative(tool, layout), options);
35
35
  // Byte identity proves preservation and an observed fixed point of the entire
36
36
  // deterministic formatter pipeline. The independent prose check still runs.
37
37
  if (result === text)
@@ -41,7 +41,7 @@ export async function formatDocument(text, context, tool) {
41
41
  JSON.stringify(semantic(parse(result)))
42
42
  )
43
43
  fail("PRESERVATION", "Formatting changed parsed meaning or a literal.");
44
- const second = await prettier.format(runNative(tool, result), options);
44
+ const second = await formatLayout(runNative(tool, result), options);
45
45
  if (result !== second)
46
46
  fail("CONVERGENCE", "Formatter pipeline did not converge.");
47
47
  return { output: result, diagnostics: checkProse(tool, result) };
@@ -0,0 +1,79 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ import { createHash } from "node:crypto";
3
+ import * as prettier from "prettier";
4
+ import { parse } from "./analysis.js";
5
+ import { fail } from "./contracts.js";
6
+
7
+ // embeddedLanguageFormatting: off still allows Prettier to trim code-line
8
+ // whitespace. Keep those lines nonempty until layout finishes, then remove the
9
+ // absent marker before native analysis or the unchanged semantic guard runs.
10
+ export async function formatLayout(text, options) {
11
+ const rows = text.split(/(\r\n|\r|\n)/u);
12
+ const selected = new Set();
13
+ function visit(node, quoteDepth = 0) {
14
+ if (node.type === "blockquote") quoteDepth++;
15
+ if (node.type === "code") {
16
+ // mdast locates actual opening fences at their marker; indented code
17
+ // starts at its indentation, even if its literal text resembles a fence.
18
+ const opening = /^(`{3,}|~{3,})/u.exec(
19
+ text.slice(node.position.start.offset),
20
+ );
21
+ let closing = rows[(node.position.end.line - 1) * 2];
22
+ for (let quote = 0; quote < quoteDepth; quote++) {
23
+ const prefix = /^[ \t]*>[ \t]?/u.exec(closing);
24
+ if (!prefix) {
25
+ closing = "";
26
+ break;
27
+ }
28
+ closing = closing.slice(prefix[0].length);
29
+ }
30
+ closing = closing.replace(/^[ \t]+|[ \t]+$/gu, "");
31
+ const closed =
32
+ opening &&
33
+ closing.length >= opening[1].length &&
34
+ [...closing].every((character) => character === opening[1][0]) &&
35
+ // A fence-shaped line can itself be literal content. The maintained
36
+ // parser omits a real closer from its value; compare source/value line
37
+ // counts rather than inferring valid closing indentation ourselves.
38
+ (node.value === "" ||
39
+ node.position.end.line -
40
+ node.position.start.line -
41
+ (node.position.end.column === 1 ? 1 : 0) >
42
+ node.value.split(/\r\n|\r|\n/u).length);
43
+ // An EOF position at column one names the following empty source row,
44
+ // not another literal line. Marking it would add a newline to the value.
45
+ const endLine =
46
+ node.position.end.line -
47
+ (closed || node.position.end.column === 1 ? 1 : 0);
48
+ for (
49
+ let line = node.position.start.line - (opening ? 0 : 1);
50
+ line < endLine;
51
+ line++
52
+ )
53
+ if (
54
+ rows[line * 2] !== undefined &&
55
+ (rows[line * 2] === "" || /\s$/u.test(rows[line * 2]))
56
+ )
57
+ selected.add(line * 2);
58
+ }
59
+ for (const child of node.children ?? []) visit(child, quoteDepth);
60
+ }
61
+ visit(parse(text));
62
+ if (!selected.size) return prettier.format(text, options);
63
+ let marker;
64
+ for (let code = 0xe000; code < 0xe100; code++) {
65
+ const candidate = String.fromCharCode(code);
66
+ if (!text.includes(candidate)) {
67
+ marker = candidate;
68
+ break;
69
+ }
70
+ }
71
+ marker ??= `MQ_LITERAL_${createHash("sha256").update(text).digest("hex")}`;
72
+ if (text.includes(marker))
73
+ fail("PRESERVATION", "Cannot protect literal layout without a collision.");
74
+ for (const line of selected) rows[line] += marker;
75
+ const formatted = await prettier.format(rows.join(""), options);
76
+ if (formatted.split(marker).length - 1 !== selected.size)
77
+ fail("PRESERVATION", "Formatting changed literal layout protection.");
78
+ return formatted.replaceAll(marker, "");
79
+ }