Documentation
¶
Index ¶
Constants ¶
const ( IndexPage = "index.md" LogPage = "log.md" ManifestFile = "manifest.json" // PracticesPage records what the repository declares about how it is built, tested, // gated, and owned — design §9.1. Reserved alongside the two above because its `type` // is checked in both directions: this filename must carry that type, and no other page // may. PracticesPage = "practices.md" )
Reserved page names, per OKF §9 and §8. Named constants because verify checks them and two spellings of the same filename would be a conformance bug that only appears on a case-sensitive filesystem.
const BundleDir = ".signpost"
BundleDir is the directory a bundle lives in, relative to the repository root.
Variables ¶
This section is empty.
Functions ¶
func RecordedCommit ¶ added in v0.2.0
RecordedCommit returns the commit sha the bundle at root records, or "" when there is no readable bundle, no manifest, or no resource in it.
Exported for one caller: `verify -as-of-bundle` has to know which commit to read history as of *before* it analyses anything, because seven churn attributes and the co-change edges are history-derived and land in page content. Everything else about the bundle is read after the analysis, which is why this is the only accessor of its kind.
No error is returned, and the absence is not distinguished from a failure, because the caller does the same thing either way: read history from HEAD and let the strict comparison report whatever is wrong. A bundle so broken that its manifest will not parse has a finding waiting for it in Verify, and a second one here would name the same defect twice.
Types ¶
type Finding ¶
type Finding struct {
// Kind groups the finding. Page is the bundle-relative path, empty for a finding about
// the bundle as a whole.
Kind FindingKind
Page string
// Detail says what is wrong in the terms the reader needs to fix it.
Detail string
}
Finding is one problem, on one file.
type FindingKind ¶
type FindingKind string
FindingKind names what kind of problem was found, so a caller can group findings without matching on message text.
const ( // FindingMissingBundle means there is nothing at .signpost to verify. FindingMissingBundle FindingKind = "missing-bundle" // FindingConformance is an OKF §11 violation: unparseable frontmatter, an empty // `type`, or a reserved filename carrying the wrong one. FindingConformance FindingKind = "conformance" // FindingBrokenLink is an `edges[].to`, a `sources[].resource`, or a prose link that // names a path the bundle does not contain. FindingBrokenLink FindingKind = "broken-link" // FindingStaleResource means a page or the manifest describes a different commit than // the one being verified. FindingStaleResource FindingKind = "stale-resource" // FindingOutOfDate means a rebuild would change the file's bytes. FindingOutOfDate FindingKind = "out-of-date" // FindingMissingPage means the bundle lacks a page this repository has a concept for. FindingMissingPage FindingKind = "missing-page" // FindingPageList means manifest.json's `pages` list disagrees with the pages a build // writes. Its own kind rather than a conformance finding, because the two need different // severities on a branch and severity must follow the kind rather than the message text: // a list that is short by exactly the pages this branch adds is the rebuild's job, where // unparseable frontmatter is not. FindingPageList FindingKind = "page-list" // FindingOrphanPage means a page describes a concept the repository no longer has. A // failure when a build would delete it and a warning when a build would keep it, per this // file's header — one kind either way, because the reader's question is the same and only // the remedy differs. FindingOrphanPage FindingKind = "orphan-page" // FindingStaleVerification means a page carries the `status:` mark §6.1 writes when a // human's review no longer matches the resource. A warning: the bundle is correct, and // what it needs is a reviewer rather than a rebuild. FindingStaleVerification FindingKind = "stale-verification" )
type Options ¶
type Options struct {
// Actor is stamped into every page's `generated.by`.
Actor Actor
// Resource is the base resource URI the pages describe, e.g.
// "git://example.com/repo@8f2a1c9". Each page appends its own path. Empty when the
// commit is unknown, in which case pages carry no resource — an absent provenance
// stamp being better than a wrong one.
Resource string
// Date is the `generated.at` value, YYYY-MM-DD. Taken from the commit rather than the
// clock, per vcs.Commit.Date, so a re-run at the same commit produces the same bytes.
Date string
// AsOfBundle makes Verify compare content while taking provenance from the bundle's own
// record instead of from this tree. Ignored everywhere else: a build always writes the
// commit it actually describes.
//
// Required by a consequence of §8.0 that is not optional. The bundle is built on the
// default branch only, so on a branch or a pull request its stamp names an older commit
// *by construction* — and the stamp is part of every page's bytes, so a strict verify
// reports every page as out of date on every pull request, including one that changed no
// code at all. It is also the only way the single-developer pattern can work: building
// locally and committing the bundle alongside the code stamps the parent commit, because
// the sha of the commit carrying the stamp does not exist until after it is written.
//
// This does not weaken the staleness check. Content is still compared byte for byte
// against a fresh render, so a change to the code still fails; only the two provenance
// fields are taken from the bundle. Nor does it rest on trusting the manifest: the
// manifest can only reach the tree through a commit, which makes a hand-edited stamp a
// reviewable diff in a machine-generated file, and forging one cannot hide stale content
// because the content comparison runs either way.
//
// The adoption is announced in Skipped rather than applied silently. This is the check
// whose quiet success would destroy the tool's value, so a run that relaxes it says which
// commit it judged against.
AsOfBundle bool
// Roles is the semantic pass's prose, keyed by node ID, already grounded and rendered
// by internal/semantic. Nil on every deterministic run, which is what keeps `build`
// byte-identical with no backend configured.
//
// A map of finished strings rather than anything the emitter composes. This package
// writes only what it can count (see this file's header), so the one honest way for it
// to carry a model's claim is to be handed the text and told which page it belongs on —
// keeping the emitter unable to invent prose even by accident, and keeping it free of
// any dependency on the model path.
Roles map[string]string
// Practices is the rendered body of the practices page — what the repository declares
// about how it is built, tested, gated, and owned, and what it does not (design §9.1).
// Empty means the page is not written at all.
//
// A finished string for the same reason as Roles, and it is worth being explicit about
// why the emitter does not compute this itself: the findings come from manifest facts
// and the file walk, neither of which is in the graph, so computing them here would
// give this package a dependency on the extraction packages and put a second kind of
// claim inside the emitter. internal/practice makes the claims and owns their wording;
// this package places the text and escapes it.
Practices string
}
Options configure a bundle emit.
type Page ¶
type Page struct {
// Frontmatter is the raw text between the `---` fences, excluding them, exactly as
// read. Kept raw because merging frontmatter is a different problem from merging
// body text and is handled by mergeFrontmatter, which needs the original to
// preserve keys it does not understand.
Frontmatter string
// Body is the page after the closing fence, as a region list in file order.
Body []Region
// HasFrontmatter distinguishes a page with empty frontmatter from one with none,
// which matters because the first is malformed and the second is a plain markdown
// file someone dropped in the directory.
HasFrontmatter bool
}
Page is a parsed bundle page: its frontmatter, and its body split into regions.
func ParsePage ¶
ParsePage splits a page into frontmatter and body regions.
It never fails. Every malformation — no fences, an unterminated region, a stray close marker — resolves to "treat the ambiguous text as human", because the cost of that choice is a managed region that stops being regenerated (visible: the page goes stale and `verify` says so) and the cost of the opposite is deleting someone's writing (invisible until they look).
func (*Page) HumanText ¶
HumanText returns everything outside managed regions, concatenated. Used by tests and by verify to assert nothing was lost; not used to rebuild a page, which needs the region order.
func (*Page) Managed ¶
Managed returns the text of the named region, and whether it was present.
An empty name is never present, even though every human region carries one. Without the guard, asking for a region a malformed marker failed to produce would return the first paragraph of someone's notes and report it as generated content — the exact confusion this file's parse rules exist to prevent, arriving through the lookup instead.
func (*Page) Merge ¶
Merge produces the page to write: generated content in the managed regions, everything else from the existing page.
`next` is the page signpost would write for a repository with no existing bundle. `p` is what is on disk. The result takes frontmatter per mergeFrontmatter, managed region text from next, and every human region from p — including human regions that sit between managed ones, in their original positions.
A managed region present in next but absent from p is appended. That is the growth path: a later signpost version emitting a new region must not require the page to be deleted first. It is appended rather than inserted at next's position, because next's position is relative to *its* regions and the human text around p's regions is the thing being preserved — inserting into that would mean deciding a human's paragraph belongs after rather than before the new content.
type Region ¶
type Region struct {
// Name is the managed region's name, or "" for human text.
Name string
// Text is the region's content. For a managed region this excludes the markers; for
// human text it is everything, verbatim.
Text string
}
Region is one span of a page body.
type Result ¶
type Result struct {
// Written is the bundle-relative path of every file written, sorted.
Written []string
// Created counts pages that did not exist before this run.
Created int
// Updated counts pages that existed and whose bytes changed.
Updated int
// Unchanged counts pages whose bytes were already correct. A large number here is the
// normal case on a re-run and is what byte-stability looks like from outside.
Unchanged int
// Preserved counts pages that had human text outside the managed regions, which this
// run carried across. Reported because it is the number that says the compounding
// mechanism is doing something.
Preserved int
// Downgraded lists pages whose human `verified:` no longer matches the resource being
// described, per §6.1. Surfaced rather than silent: a reviewer needs to know to look
// again.
Downgraded []string
// Stale lists pages with no corresponding node that were kept because somebody had
// written on them. Reported, not deleted: the decision is theirs.
Stale []string
// Removed lists pages with no corresponding node that held nothing but the skeleton a
// first emit wrote, so deleting them destroyed nothing. Named rather than counted, for
// the same reason Downgraded is: a file this run deleted is the one thing in a build a
// reader may want to recover from git.
Removed []string
}
Result reports what a write did, in enough detail for the CLI to say so.
type Verification ¶
type Verification struct {
// By is the actor string, e.g. "human:ecopelan".
By string
// At is the review date, YYYY-MM-DD.
At string
// Resource is the `resource:` value the page carried when the review was made, if the
// page recorded one. This is what makes a downgrade possible: without it, "verified"
// and "verified against something else" are indistinguishable.
Resource string
}
Verification is a `verified:` entry a human added.
type VerifyCounts ¶
type VerifyCounts struct {
Pages int
// Links is bundle-absolute prose links resolved. Edges and Sources are frontmatter
// `edges[].to` and `sources[].resource` values resolved.
Links int
Edges int
Sources int
}
VerifyCounts is what verify actually opened and resolved.
type VerifyResult ¶
type VerifyResult struct {
// Findings are failures. A non-empty Findings means the command exits non-zero, and
// that is the only rule a caller needs to know.
Findings []Finding
// Warnings are problems that do not make the bundle wrong. Reported because an
// unreported warning is indistinguishable from a clean result.
Warnings []Finding
// Pending are differences whose remedy is the rebuild that runs after this branch
// merges, so nobody can act on them here. Reported in full and counted separately;
// they do not make the command exit non-zero. See classifyPending for the whole
// argument, and note that this list is only ever populated under
// Options.AsOfBundle — a strict verify has no merge to defer to.
Pending []Finding
Checked VerifyCounts
// Skipped names each check that did not run, and why.
Skipped []string
}
VerifyResult is what one verification found.
func Verify ¶
Verify checks the bundle at root/.signpost against the repository g was built from.
g and opts must be the ones `build` would be invoked with, for the reason this file's header gives: the check is "would a build change anything", and a build with different options is a different question.
func (*VerifyResult) OK ¶
func (r *VerifyResult) OK() bool
OK reports whether anything failed, which is not the same question as whether the bundle matches. Under Options.AsOfBundle a result can be OK and carry a Pending list: the bundle genuinely differs from this tree and the branch is not the place that fixes it. A caller printing a verdict has to look at both, or it will claim a match that does not exist.