A usability-focused minor: makes adoption harder to get wrong at the
entry point, turns the invariant register into a core obligation, and
hardens the archived profile (exclusivity + a required non-empty mapped
system artifact + an untouched-template guard; semantic §6.6 content
enforcement remains tracked in #40).
- Profile classification is now an explicit first step of adoption.
docs/ADOPTION.md§4.0 ("Classify the profile first") makes profile
selection an evidence-based finding — a cheap §5 trigger scan with
file:lineevidence, a bias toward escalation, an adversarial
self-check of the cited evidence, and the classified set declared in
adoption.yaml's enforcedprofiles:field rather than only in the
handoff prose. The prior "start atcore/ lite is the default /
selectcoreonly at first" framing is flipped throughout (§0, §3.0,
§3.2, §4.0, §5);README.mdandREADME.ko.mdroute a bare "apply
this profile" prompt into classify-first; andPROFILE.md§5
clarifies that the smallest applicable set covers the system's actual
nature. Documentation only — no schema or validator change. - Existing tool output has a stated position in the core model.
docs/MAPPINGS.md§5 (new, non-normative) shows how to reference what a
repository's tools already produce — specification workflows, tests,
scanners and code-review tools, agent change records, build attestations —
from the registers instead of restating their results by hand. Each tool
class is placed against what it does not establish, because an assurance
argument fails when a strong answer to one question is filed as the answer
to another: a review tool reporting no findings is coverage, not proof, and
an agent change record is evidence of the action, not of correctness. §5.2
binds an evidence reference to a revision, release, artifact, or deployment
(PROFILE.md§11) and covers the restricted case; §5.5 states what no tool
supplies — intent, claim wording, defeater disposition, residual acceptance
— and that an agent-drafted record carries human authority only when the
owner's own approval act anchors it (PROFILE.md§7).README.mdand
README.ko.mdroute to it from the "complements your existing toolchain"
claim, which previously had nothing behind it. Documentation only — no
schema or validator change. - Profile composition is now explicit and aligned with validation.
Every non-archivedspecialized profile implicitly inherits §6.1
coreobligations. For an active adopter, the canonical smallest
declaration is[core]when no specialized trigger fires; otherwise it
lists every fired specialized profile and omitscore(an explicit
coreremains allowed and changes no obligation).archivedis
normatively exclusive, replaces §6.1–§6.5 active obligations while
retaining the pin and root agent instructions, and records its four §6.6
facts in the system artifact resolved frompaths.system(default
assurance/SYSTEM.md). This closes the gap where the validator enforced
exclusivity and artifact placement more strongly than the authoritative
profile text. The validator rejects an empty archived system artifact at
every stage and, atHUMAN_REVIEWEDorCONFORMANT, any of the four exact
shipped archived placeholders. It still does not determine whether replacement prose states
all four facts or is truthful; that structured semantic enforcement remains
#40, with the owner review guide as the explicit content backstop. - At least one invariant is now required at
core(previously an
obligation only fromservice).PROFILE.md§6.1, the lite envelope
schema (invariantsnow required), and the split-layout file-presence
and non-emptiness checks all enforce it — a repository with nothing
that must stay true has not found its invariants yet. - A minimal lite template (
templates/assurance.minimal.yaml) ships
the required-minimumcoreadoption — purpose, non-goals, system, one
invariant, one residual — with only the fields an adopter must fill, so
the fulltemplates/assurance.yamlis now the expanded alternative rather
than the default minimal starting point. The self-check validates both
templates, so neither can drift. - The adoption template no longer defaults to
core.
templates/adoption.yaml(and the copy example in
AGENTIC_ASSURANCE.md) ship aREPLACE_WITH_CLASSIFIED_PROFILE
sentinel inprofiles:— the central self-check substitutes it to a
valid enum, but an adopter who copies the template must replace it with
the classified set or validation fails on the unfilled placeholder, so
no adoption silently inherits acoredefault. (This is a completion
guard; detecting a wrong profile against the actual code remains the
under-classification backstop, tracked in #41.) - The normative stage and entry-artifact contracts are now explicit.
PROFILE.md§17 defines the cumulativeDRAFT,HUMAN_REVIEWED, and
CONFORMANTladder rather than leaving its substance only in the adoption
guide and schema descriptions. DRAFT includes applicable semantic checks;
HUMAN_REVIEWED requires a recorded classification (UNKNOWN allowed) for
every active critical invariant. Independently of review stage, every active
VERIFIEDcritical invariant needs at least one enforcement and one
verification reference; underservice, every critical invariant needs
both references regardless of conclusion status. A conformance claim requires an explicit
adoption_stage: CONFORMANT, where no critical residual remainsOPEN, no
claim or critical invariant isCONTRADICTED, everyVERIFIEDcritical
invariant has evidence, every critical invariant has an intent other than
UNKNOWNorACCIDENTAL, and public repositories carry noRESTRICTEDor
EMBARGOEDregister entry. At every stage, placeholder substitution remains
schema-only: it cannot fabricate an invariant mechanism or authority, a
residual acceptance/resolution record, or a closed-defeater resolution.
Closed defeater meanings and their mandatory non-blank disposition grounds
are now normative in §12. §17 also distinguishes the adopter's
human-approved full normative claim from the validator's structural and
mechanically decidable subset. §6.1 now expressly
requires rootAGENTIC_ASSURANCE.mdand rootAGENTS.mdreading-order
artifacts, and defines one system-description content minimum regardless of
lite, split, or mapped layout. The fuller §7 reconstruction remains the
normalSHOULD, not a mapping-only hard requirement. - Input and path handling now fail closed and remain resource-bounded.
Duplicate mapping keys in YAML or JSON are rejected instead of silently
using the last value. YAML merge keys are rejected before PyYAML can expand
a merge-alias DAG; ordinary anchors and aliases remain supported under the
logical-node bound, including for pre-v0.4 last-key-wins base comparison.
Policy YAML is normalized to the JSON-compatible data
model used by the schemas: binary, set/ordered-map, cyclic, and non-finite
values are rejected, while YAML dates are normalized to ISO strings.
Non-finite JSON constants and non-UTF-8 policy inputs are rejected; every
policy YAML input is limited to 5,242,880 bytes and
100,000 logical nodes after alias expansion, preventing compact alias-DAG
denial of service. Policy JSON is limited to 10,485,760 bytes, 256 nesting
levels, and 500,000 nodes; YAML and JSON numeric source tokens are limited to
4,096 characters on every supported Python version. Invalid implicit YAML
dates and other scalar-constructor failures now produce controlled diagnostics
rather than tracebacks. Adopter-owned prose and review files read directly by
the validator are each limited to 5,242,880 bytes: the two root assurance
guides, mapped system artifact, required service threat model, and declared
human-review record. The self-check matrix now includes Python 3.14 while
retaining 3.10 as the compatibility floor. Self-check meta-validates every JSON Schema and scans
tracked filenames losslessly even when a filename contains a newline;
schema references use an explicit offline registry, so an unresolved or
remote$reffails with a controlled diagnostic without network access;
malformed approval timestamps and HTTP(S) approval URLs are checked
explicitly at every stage rather than depending on optional JSON Schema
format packages. They produce ordinary diagnostics rather than a parser
traceback; approval URLs must name a host, contain no user information, use
a numeric in-range port when one is present, use a DNS/IPv4-style or
bracketed hexadecimal IPv6 host, and contain only valid percent escapes and
RFC 3986 path/query/fragment characters under the profile's deliberately
narrow ASCII HTTP(S) URL grammar (percent-encoded path/query/fragment data
must decode as UTF-8; internationalized hosts use an ASCII IDNA A-label
spelling; the complete RFC 3986 host surface is not claimed).
Public metadata pointerssecurity.restricted_recordand residual
private_detail_locationnow share a bounded non-actionable ASCII label
grammar (letters, digits, dot, underscore, and hyphen; at most 128
characters), so syntactically actionable URLs, slash paths, and@account
identifiers cannot pass merely because they are nonblank. The validator
cannot determine whether an otherwise valid opaque label is itself a secret;
never place a secret there. Replace an actionable locator with a public label
such asexternal_private_systemand keep the locator only in the restricted
system.
Non-empty policy strings made only of separators
or invisible format characters are rejected with their JSON path instead of
satisfying a required semantic value. Release identifiers use canonical
ASCII SemVer numbers (no leading zero; release candidates start atrc.1),
andVERSIONis one exact token line with no surrounding whitespace or
additional lines. Full-string schema tokens (repository slugs, commits, stable IDs,
and references) use true end-of-input assertions so a final newline cannot
bypass their grammar.
Filesystem artifact/root fields require repository-relative lexical paths
(an absolute spelling is rejected even when it points inside the project),
are limited to 4,096 characters, andpaths:is limited to 256 mappings,
and component routing globs must use the same canonical path domain:
absolute spellings and empty,.or..components now fail instead of
silently matching nothing. Duplicate component globs and IDs remain accepted
for v0.3 compatibility and count toward routing limits. Every explicitly
carried lite mapping plus a present DRAFT review-record
path receives the same containment/trust check as an active artifact. Every
trust-checked artifact/root field and every active routing glob in
components[].pathsrejects C0 controls and DEL;components[].testsis
recorded metadata, not a trust-checked or routing path field in this release.
An inferred profile checkout becomes a trust root and itsVERSIONis
compared with the adoption pin. A Git-backed profile checkout must also have
HEAD == upstream.commit, must execute that checkout's real
scripts/validate.py, and must have clean consumed validation resources at
that HEAD (VERSION,requirements-ci.txt,scripts/, andschemas/,
including untracked files under the resource directories).PROFILE.md§16
now states explicitly that a release or pre-release SHA is the matching
published tag's target and that a published tag cannot be deleted, moved, or
reused; this documents the already-enforced canonical tag rule and adds no
new adopter action or workflow behavior. An archive
without Git metadata emits an explicit warning that onlyVERSION, not
commit identity or resource cleanliness, was verified. Adopter artifacts must resolve
inside the project boundary without
resolving into Git metadata (.git), the pinned profile checkout, or the
schema tree, including case- or normalization-alias spellings on filesystems
that treat those names as identical. A rejected lite assurance path is not
reopened by reviewed-stage placeholder scanning. Portable relative symlinks
whose every hop remains inside the adopting project continue to work and now
emit a CODEOWNERS warning naming the lexical path, resolved target, and
target parent that need owner coverage. Absolute symlinks and relative
escape-then-reentry spellings fail even when they happen to resolve inside
HEAD, because a detached BASE worktree would give them a different identity. - Self-check workflow linting remains bounded. The digest-pinned
actionlintimage moves from 1.7.7 to 1.7.12, and Python-only workflow
bodies (including the multi-thousand-line materializer) run directly under
isolated Python custom shells instead of appearing to shellcheck as bash
heredocs. The greater-than-64-KiB materializer uses the equivalentpython3
shell spelling so actionlint 1.7.12 does not feed it into that release's
pre-start Pyflakes stdin path, which can deadlock at the runner pipe limit
(rhysd/actionlint#650andrhysd/actionlint#651); regression tests compile
and execute the body instead. Smaller Python steps remain Pyflakes-checked.
This is CI maintenance only; it adds no adopter action and does not change
workflow behavior. - Pull-request routing is bounded and safe at its inputs and CI sinks.
The drift job explicitly checks out the pull request's head SHA rather than
GitHub's default synthetic merge ref, keeping HEAD-side policy reads aligned
with the SHA used for changed-file and evidence diffs.
Component path globs now use a dynamic-programming matcher instead of a
backtracking regular expression. Count and length limits are supplemented by
20,000,000-cell aggregate glob-work and 20,000,000-unit impact-directive-scan
budgets; the changed-file list, PR body, and assurance diff are capped at
33,554,432, 1,048,576, and 20,971,520 bytes respectively, so many
individually valid inputs cannot recreate an unbounded CI workload. In PR
routing, changed-file records must be canonical repository-relative Git
paths (with one legacy leading./normalized); absolute paths, escapes,
repeated prefixes, and./..or empty components fail closed instead of
silently missing a component glob. In PR
prose, impact IDs, anonedirective and its mandatoryReason:, and an
optionalAssurance policy change:acknowledgment count only inside a
leading top-level directive block before ordinary visible content. The block
contains exactly one impact declaration when impact is asserted, starting
with eitherAssurance impact: INV-A, INV-B(comma-separated exact IDs) or
Assurance impact: none; duplicate, conflicting, or malformed impact lines
invalidate the declaration. AReason:or policy-change explanation must
contain visible plain-text content; zero-width characters, entities that
decode only to invisible characters, and empty inline HTML do not count.
HTML comments may precede it, but arbitrary
mentions, headings, blockquotes, links, code examples, and later directives
do not count. Raw-HTML child prose remains visible ordinary content but can
never become a top-level directive or reading-order line, including when an
HTML entity decodes to a newline. Only physical LF/CRLF records delimit
directives and diff lines; Unicode line separators remain content. A
policy-only acknowledgment may begin the block; combined
declarations must use the published impact → optional reason → optional
policy order, and an out-of-order line invalidates the block. Component
globs must contain non-whitespace text and use canonical repository-relative
path components. Untrusted component names and verdicts are encoded before
reaching ordinary terminal output, Actions log lines, annotations, or
Markdown summaries, so controls and unpaired surrogates cannot forge a
result line or crash output. The reusable
workflow resolves every head adoption path inside the adopter project before
reading it, rejects an absolute configuredadoption-file, excludes Git
metadata and the pinned profile checkout, and reserves each trusted checkout
destination only after anlstatnonexistence check so an adopter symlink
cannot redirect checkout writes. It runs all
trusted Python readers and validators in isolated mode so adopter-root Python
modules cannot shadow dependencies, validates
the complete release-version grammar before writing an output or constructing
a Git refspec, and safely encodes declaration paths in workflow commands. For
base comparison it recovers the literal prior input from the base version of
the actual caller identified bygithub.workflow_ref, so a file that merely
pre-existed at a new HEAD destination cannot replace the real baseline. A
fallback identity scan streams Git's NUL-delimited output and is fail-closed
at 100,000 tracked records, 64 MiB of raw listing data, 64 MiB of lexical file
surface, or 16 declaration candidates. Every tracked record is charged before
file-type, containment, or trusted-path filters, while an in-tree symlink and its tracked target
share one resolved declaration identity so aliases cannot create false
ambiguity. Base/head transition metadata also binds the lexical adoption path
to its contained resolved target; unchanged policy paths cannot be silently
retargeted. Each job requires the exact canonical reusable-workflow path
followed by its lowercase 40-hexjob.workflow_sha; a suffix match cannot
disguise a tag, branch, or different workflow. Changed-file production now invokes explicit rename and
retained-source copy detection, reports each source/destination once, and
caps raw name-status input before reading it. The CI assurance evidence diff
is synthesized from exact invariant-bearing lines in regular, deterministic
strict-text blobs reached by impact-eligible HEAD-active bindings. HEAD
symlinks, gitlinks, control-heavy or invalid-UTF-8 blobs, and PDF containers
cannot provide prose. Every exact line found anywhere in the pull request's
merge-base tree is subtracted through a deliberately broader cancellation
set (including binary and symlink blobs), so rename, retained copy,
remapping, file-type/text-eligibility transition, similarity heuristics, or
a later base-tip deletion cannot make unchanged policy look new. The
repository-wide subtraction is conservative and the synthetic diff exposes
only canonical IDs. Evidence enumeration deduplicates and ancestor-collapses
pathspecs, batches them below argv limits, and drains Git's output pipes
concurrently. All evidence Git operations, includingcat-fileblob reads,
share one 60-second deadline. Merge-base and HEAD enumeration together share
aggregate 64 MiB listing, 1 MiB diagnostic, and 100,000 tracked-tree-record
bounds. Base bindings and every explicit path remain
trust-checked for policy comparison. A workflow or public-assurance directory
binding whose final resolved target is the repository root — whether written
.or reached through a permitted in-project symlink — is also trust-checked
but cannot contribute positive evidence, so an ID placed in ordinary source
cannot satisfy its own routing gate. Within lite,
moving the effective system description between the inline field and a
mapped/default artifact is now a gated policy relocation even whenlayout
and the dormant path spelling stay unchanged. Standalone register-policy
comparison now also requires--base-registers-root,--project-root, and
--base-adoptiontogether; the two roots must exist, be directories, and
resolve to distinct non-overlapping trees, so one-sided, missing, aliased,
or ancestor/descendant-overlapping inspection fails closed. Standalone unified-diff evidence ignores added
symlink/gitlink payloads and cancels exact deleted lines against additions. - The pre-v0.4 starter-row policy-diff exception is exact and migration
scoped. It applies only to a policy comparison that actually moves the pin
from anunreleasedpilot commit or v0.1.x–v0.3.x to v0.4 or later, and
exempts only a register row equal to the complete entry object actually
shipped in those templates. Partial completion, a changed default, extra
project metadata, a genericREPLACE_WITH_string, or replacing completed
prose with a marker cannot hide that transition or a later deletion hop. A
comparison without that pre-v0.4 → v0.4+ pin transition, including one
whose base is already v0.4 or later, receives no legacy exemption. - Pre-v0.4 declarations remain comparable while they are repaired. For the
reusable workflow's BASE materialization only, a canonical pre-v0.4 or
unreleasedbase that strict parsing rejects may be reconstructed through
the bounded historical SafeLoader data surface: duplicate keys retain
last-key-wins behavior, merge keys are expanded only under the logical-node
bound, and scalar legacy extension keys are normalized to collision-checked
JSON strings. Direct standalone--base-adoptioncomparison supports only
the narrower duplicate-key last-wins migration case and otherwise fails
closed. This also
preserves historically valid blank project-owner data and mixed
active-plus-archivedprofile declarations long enough to compare their
policy. Recursive, non-finite, oversized, colliding, or unsupported values
still fail closed. The v0.4 head must use the strict JSON data model, remove
duplicate/merge/non-string-key syntax, complete project identity, and choose
either the exclusive[archived]profile or a valid active set;
compatibility never validates the historical shape on HEAD. - Stage and policy-diff inputs now fail closed. Explicit malformed stage
values on either side of a drift comparison are errors rather than being
omitted or coerced toDRAFT. Profile comparison uses the effective profile
set, including thecoreinherited by specialized active profiles, so
equivalent declarations do not report a false removal and a real inherited
obligation cannot disappear silently. The declaration diff also protects
committed project identity, approval provenance, specification workflow,
security policy, restricted-record label, public-assurance root, issue
controls, and effective
default-normalized artifact paths;archived→ active is a neutral explicit
profile-mode reclassification gate. A completedhuman_review.datemay
advance for a re-review but cannot be removed, invalidated, or backdated. - Policy comparison now preserves its own routing and approval semantics.
Moving the adoption declaration is a gated policy change because the new
path may escape existing CODEOWNERS coverage. When the configured HEAD path
is absent on the base, the reusable workflow strict-loads tracked regular
files regardless of extension or YAML spelling and accepts only a unique
canonical AAP declaration signature; ambiguity fails closed. Approval
provenance comparison now includes normalizedcoversscope (omission is
equivalent to[CONFORMANCE]), so a previously full approval cannot be
silently narrowed. Git changed paths remain NUL-separated through component
routing, preserving embedded newlines and edge spaces. Recorded residual or
defeater closure grounds are protected while a closed disposition remains;
reopening still remains a non-finding. The explicit acknowledgment remains
stage-proportional: it can downgrade a DRAFT finding to a warning, but a
HUMAN_REVIEWED or CONFORMANT finding remains an error even when acknowledged. - Previously documentary active-adoption fields now have structural
backstops. Active declarations require a material-change
specification_workflowwith a project-local root; project identity and the
required root instruction/adoption files must be non-empty; and from
HUMAN_REVIEWED,human_review.recordmust resolve to an existing,
non-empty project artifact. Review, approval, and residual-acceptance dates
may not be in the future (date-only values allow the civil date possible in
UTC+14), and at least one CONFORMANT approval must be on or after the review
date. Approval URLs and dates are also checked for HTTP(S) and ISO/RFC 3339
shape. These checks validate presence and syntax, not the truth or forge
state of a review or approval.
Adopter impact / upgrade actions
-
A previously conforming adoption now fails if it declares a
non-archived profile but has no invariant register, or an empty one.
The newly affected profiles arecore,data-curation,
trust-critical, andagent-runtime— before this release only
servicerequired an invariant, so any of these declared without
servicewas previously conforming. Add at least one invariant (the
properties that must remain true). Both pilot adoptions already carry
invariants, so no live adopter is affected. See the version
classification note below.upgrade.** Whether it is inline under lite or stored at
paths.system, the
description must identify the system being assured, its principal
responsibilities and material boundaries, and known material limitations or
unknowns. Fill any missing part and have the human owner review that prose.
The validator checks presence, non-emptiness, and reviewed-stage template
markers; it does not semantically parse those content elements, so a green
check cannot substitute for that review. -
layout: liteis nowcore-only;archived(and every other
non-coreprofile) uses the split layout. The lite envelope's
required fields (purpose, non-goals, invariants, residuals) are shaped
forcore, not archived's §6.6 obligations. This removes previously
validarchived+litesupport — a deliberate compatibility
change in this release, not the repair of a pre-existing bug. No live
adopter usesarchived. When moving from lite to an active specialized
profile, preserve the register arrays and IDs, move inline system prose to
paths.system, preservepurposeandnon_goalsthere or in another
owner-approved local intent artifact, and preserve or deliberately relocate
extensionsbefore droppinglayout. A move toarchivedinstead requires the owner-confirmed
four §6.6 facts in the mapped system artifact; retained active registers are
optional history and do not substitute for that archived record. -
Adopter files that relied on last-key-wins duplicate YAML/JSON, YAML merge
keys, or non-JSON-compatible YAML values (binary, set/ordered-map, cyclic, or
non-finite values); a non-finite JSON number; non-UTF-8 policy text; an
artifact symlink escaping the project (including into Git metadata (.git)
or the pinned profile checkout); an absolute artifact/root path or a path
containing C0 controls or DEL; a non-canonicalcomponents[].pathsglob
with an empty,.or..component; an approval URL with no host, user
information, a malformed port, or an invalid percent escape; a repository
slug, commit, stable ID, or reference whose scalar value ends in a newline;
or an explicitly malformed stage now fail closed. Remove duplicate and merge
keys by materializing an explicit mapping; represent YAML policy as ordinary
JSON-model scalars, arrays, and mappings; move artifacts into the adopting
project; encode policy text as UTF-8; make artifact/root locations and
component globs canonical repository-relative paths; repair URL ports and
percent escapes; rewrite an affected schema token as a single-line scalar
whose value has no terminal newline; and repair the stage declaration.
The reusable workflow can still compare a direct upgrade from a pre-v0.4 or
unreleasedbase by materializing the legacy declaration's bounded
historical SafeLoader result long enough to detect policy weakening,
including last-value duplicate keys, merge keys, and safely normalizable
scalar extension keys; direct standalone comparison provides only the
duplicate-key last-wins subset. Every file in the v0.4 head must be strict,
unambiguous JSON-model YAML. Likewise, replace a
legacy mixed active-plus-archivedprofile list with either[archived]
alone or the complete applicable active set; the compatibility reader does
not validate the old mixed form on the head.
Replace an absolute or escape-and-reentry policy symlink with a portable
relative in-repository link. When--profile-checkoutis omitted, the VERSION-bearing root
inferred from--schemasis now the same trust boundary and itsVERSION
must match the adopter pin. Use a real Git checkout atupstream.commitfor
local validation; a Git-backed checkout at another HEAD, running a different
validator, or carrying dirty validation resources now fails, while a source
archive warns that its commit identity and resource cleanliness cannot be
established. -
A policy YAML file over 5,242,880 bytes or whose aliases represent more than
100,000 logical nodes now fails. Policy JSON over 10,485,760 bytes, 256
nesting levels, or 500,000 nodes also fails with a controlled diagnostic.
Each directly read adopter-owned prose/review file is likewise capped at
5,242,880 bytes: rootAGENTIC_ASSURANCE.md, rootAGENTS.md, the mapped
system artifact, required service threat model, andhuman_review.record.
Component routing is limited to 256
components, 256 path globs and 256 invariant IDs per component, 20,000
changed paths, 1,024 characters per glob, and 4,096 characters per changed
path. Aggregate glob matching and impact-directive scanning each have a
20,000,000-work-unit budget. Split an oversized policy or change, and reduce
or narrow an oversized component map; routing input files are additionally
capped at 33,554,432 bytes (changed paths), 1,048,576 bytes (PR body), and
20,971,520 bytes (assurance diff). Aliases are not a way around the
logical-node limit. -
The
archivedprofile is now partially enforced (previously a
no-op).archivedmust be declared exclusively —[core, archived]
and any other active-plus-archived set is now an error, since a
repository retained solely for historical reference, not supported or
intended for current use, and without active operation, functional
maintenance, or feature development cannot also carry an active obligation.
Anarchivedadoption must carry a non-empty mappedsystemartifact at
every stage, includingDRAFT. AtHUMAN_REVIEWEDorCONFORMANT, each of
the four exact archived template markers is additionally rejected, so an
untouched template cannot pass. This is still a
completion guard, not full §6.6 enforcement: arbitrary replacement prose
is not parsed for all four facts and is not checked for truth. Structured,
field-level enforcement remains #40.docs/REVIEW-GUIDE.mdmakes the four
confirmations andpaths.systemresolution an owner checklist. Initial
adoption, factual correction, pin/stage/review upkeep, and agent-instruction
metadata do not by themselves count as functional maintenance; code,
dependency, or behavior work supporting current use requires reclassification. -
Stage tightening can require adopter edits. A
CONFORMANTactive
adopter whose critical invariant is classifiedACCIDENTALmust either
record the behavior as a gap/residual rather than an invariant, choose a
non-accidental intended classification with human authority, or lower the
claimed stage. At HUMAN_REVIEWED, every active critical invariant now needs
a recorded classification (UNKNOWNremains valid), and every affirmative
INTENDED/COMPATIBILITY/DEPRECATEDclassification needs human authority.
At every active stage, a severity-criticalinvariant recordedVERIFIED
needs at least one enforcement and one verification reference.
EveryACCEPTEDresidual now needsaccepted_by, a non-futureaccepted_at,
andacceptance_rationale; everyRESOLVEDresidual needs
resolution_note. The already-enforced rule that aMITIGATED,RESOLVED,
orWITHDRAWNdefeater needs non-blankresolutiongrounds is now encoded
in its schema and labeled as status-conditional in both starter templates.
Reviewed adopters must also add a real project-local
review record ifhuman_review.recordpreviously named a missing or empty
path; active adopters must declare their existing material-change workflow,
andspecification_workflow.rootmust resolve to a path that actually
exists and carries content — a readable, non-empty UTF-8 file, or a
directory holding at least one such file within the validator's bounded
scan. A stub or emptied workflow directory now fails.docs/ADOPTION.md
§3.6 states the exact bounds. -
Completed human acts cannot be future-dated. Repair a future
human_review.date, approvalat, or residualaccepted_at. Date-only
values allow the current civil date possible in UTC+14; timestamp values use
their stated offset. A retained residual acceptance remains subject to this
rule underarchived; archiving cannot make a future-dated completed act
valid. At CONFORMANT, ensure at least one attributable approval has a civil
date on or afterhuman_review.date.review_afterschedules now use the
same host-independent UTC+14 latest-civil-date boundary, so local and CI
runners in different time zones cannot disagree about whether one has passed. -
Scoped approvals no longer imply full conformance approval. An approval
with nocoverslist still attests the full claim. Ifcoversis present,
add the reservedCONFORMANCEtoken for that approval to satisfy the
CONFORMANT gate; a list containing only individual IDs is intentionally too
narrow. -
Complete the newly enforced declaration and root-file fields before the
pin upgrade.project.nameandproject.human_ownermust be non-blank,
andproject.repositorymust be anowner/nameslug. Root
AGENTIC_ASSURANCE.mdand rootAGENTS.mdmust be non-empty and contain the
exact assurance-guide and adoption-declaration references in one canonical
visible ordered block (hidden comments, code, and HTML do not satisfy it),
using the same customadoption-filepath in both when applicable.
Approval records need a non-blank approver, an absolute HTTP(S) review URL,
a valid non-future date/timestamp, and full conformance scope when used for
CONFORMANT; the qualifying approval's civil date must not precede the
human-review date.Every v0.3.x adoption fails this check until the block is present in both
files, because v0.3.2 required only that the two root files exist. In
practice the edit is one file: the v0.3.2AGENTS.mdtemplate already
carried the block, and itsAGENTIC_ASSURANCE.mdtemplate did not. Both
pilot adoptions were checked against this release —AGENTS.mdpasses in
both,AGENTIC_ASSURANCE.mdfails in both, so each needs the block added
there and nothing else. The block is, verbatim — substituting a custom
adoption-filepath for the second entry, identically in both files:Before any material change, read: 1. `AGENTIC_ASSURANCE.md`; 2. `.agentic-assurance/adoption.yaml`; -
issue_integrationvalues are now constrained, not merely declared.
public_security_issues_allowedmust befalseand
closing_requires_artifact_updatemust betrue, enforced by both the
schema (const) and the validator. v0.3.2 typed them as ordinary booleans
and described the first as required only for public repositories, so a
private adopter could legitimately declaretrue. It is now unconditional
and--repo-visibility privatedoes not relax it: repository visibility
bounds who can read a finding, not whether an exploitable one may be filed
as an ordinary Issue. An adopter that declaredpublic_security_issues_allowed: truemust set it tofalseand route potentially exploitable findings
through a private report or Security Advisory.PROFILE.md§14 now states
both values normatively, so the mechanical rule has an anchor. -
Fill in the root guides before
HUMAN_REVIEWED. Because this release
makes the two root guides required artifacts, they now carry the same
reviewed-stage obligation as the mapped system artifact: from
HUMAN_REVIEWEDonward neither may contain aREPLACE_WITH_marker,
including inside fenced sample blocks. Without this, an adoption could be
declaredCONFORMANTwhile the file agents are told to read first was still
the unfilled upstream template. Both pilot adoptions already satisfy it. -
Move pull-request directive lines into a leading block.
Assurance impact:,Reason:, andAssurance policy change:now count
only inside a leading top-level directive block, before any ordinary visible
content; previously they were recognized anywhere in the description.
Update the repository's pull-request template so the directive lines come
first, and re-edit any pull request left open across the pin upgrade — an
acknowledgment that sits below prose stops counting the moment the pin
moves. -
Required service threat models now have content guards. A service
adopter's mappedTHREAT_MODELmust be non-empty at every stage and, from
HUMAN_REVIEWED, must not retain a genericREPLACE_WITH_template marker. -
Default and custom declaration/artifact paths need coordinated owner
binding. Confirm that the effective owner-review boundary covers both root
instruction files, the CI caller, the effective adoption declaration, every
effective artifact location selected underpaths:(including standard
defaults),specification_workflow.root,human_review.record,
security.policy, andsecurity.public_assurance_root. Put a non-default
adoption-filepath in the reading order in both root files. When CODEOWNERS
is the mechanism, merge the shipped rules for the standard locations and add
explicit rules for every custom location; the shipped template now also
covers its adoption template's defaultSECURITY.md. A later declaration-path
move is now a stage-proportional policy finding, as is rewriting an existing
approval's normalizedcoversscope. For any covered location implemented
as a symlink, cover the lexical link itself (including
retargeting), the resolved target, and the target's parent or containing tree;
CODEOWNERS does not follow the validator's symlink resolution. The new
validator warning makes that requirement visible but cannot prove that code
owner review or branch protection is enabled. -
Date placeholders are caught at
HUMAN_REVIEWED. An unfilled
review_after: REPLACE_WITH_REVIEW_AFTER_DATEin a register now fails
fromHUMAN_REVIEWEDon, like every otherREPLACE_WITH_token. The
pre-v0.4review_after: YYYY-MM-DDsentinel remains a path-scoped
compatibility alias: DRAFT still tolerates it in residual and defeater
entries, whileHUMAN_REVIEWEDrejects it. A literalYYYY-MM-DD
elsewhere remains ordinary adopter data. The policy diff preserves both
sentinels as raw values, so a real re-review date cannot silently regress
back to an unfilled placeholder. The two official sentinel spellings are
treated as aliases during policy comparison: migration in either direction
is neutral, while replacing a real date with either sentinel remains a
weakening finding in split and lite layouts. -
All seven pre-v0.4 prose starter prompts are stage-compatible without
becoming false-green. The exact legacy strings with theReplace with
prefix in claimtext/scope, invarianttitle/statement/scope, defeater
statement, and residualsummaryremain tolerated atDRAFTbut
fail atHUMAN_REVIEWEDandCONFORMANT. Stage matching is scoped to those
original register fields regardless of the pinned base version, so identical
literal adopter data elsewhere is not reclassified as a placeholder.
Separately, the policy-diff exemption for deleting an otherwise unchanged
legacy starter row applies only to an actual direct upgrade from
unreleasedor v0.1.x–v0.3.x to v0.4.0 or later. -
Split-template self-check now enforces profile obligations as well as
entry semantics. Empty split invariant or residual starter registers
can no longer leave the central self-check green; they are checked with
the same non-emptycoreobligations as adopter validation and the lite
templates. -
The copied root-agent instruction block is now drift-guarded. Central
self-check extracts the normative §11 Markdown block from
templates/AGENTIC_ASSURANCE.mdand exact-compares it with the standalone
templates/AGENTS.mdcopy, failing closed when markers are missing or
duplicated. A prose-only edit can no longer invalidate the template's
“copied verbatim” promise while leaving self-check green. -
The expanded lite reference is complete for its documented standard
surface without claiming to enumerate local extensions. It now shows
the status-conditional defeaterresolutionfield and top-levelextensions
namespace. Template and adoption prose call it an expanded standard-field
reference rather than “every field.”
Fixed — placeholder scanning and diagnostics
- A completed artifact no longer fails for naming the marker convention.
The reviewed-stage prose scan matches a namedREPLACE_WITH_...token
rather than the bare prefix, and reports the token it found. The shipped
SYSTEM.mdandTHREAT_MODEL.mdinstructions were reworded so a copied
template does not carry a literal marker in its own guidance. - Lite prose is scanned by the same rule as split prose.
system,
purpose, andnon_goalsin the lite envelope are prose, and now follow
the prose rule instead of the structured-field substring rule. Identical
text previously passed in the split layout and failed in lite, and the
error quoted the adopter's entire paragraph instead of the token.
Structured register fields keep the stricter substring rule. - A malformed tagged scalar no longer escapes as a traceback.
!!bool
and!!timestampvalues that PyYAML cannot resolve are reported as
ordinary validation errors, preserving PyYAML's own diagnostic text, so
--jsonoutput stays well-formed. intent.authorityguidance ships with the templates. The invariant
templates now state inline that anINTENDED,COMPATIBILITY, or
DEPRECATEDclassification requires a named authority, matching the
guidance already given for the residual and defeater conditionals.
Version classification — a recorded owner decision, not a pre-existing rule
Under the §16 in force at v0.3.2 — "Major: removes, weakens, or
materially changes an obligation" — this release would have been a
major. It both adds obligations that break previously conforming
adoptions and removes one previously valid capability (archived +
lite).
§16's initial-development paragraph, under which adding or tightening an
obligation before v1.0.0 is a minor, was introduced in this
release and applied to it. That is a governing owner decision taken on
2026-07-20 and recorded here, rather than presented as a rule that
already existed; the classification of this release rests on that
decision, not on a policy that predates it.
Rationale: the profile is pre-v1.0.0 with two adopters, both under
this organization's control, and publishing v1.0.0 solely to carry
this change would assert an interface stability the profile does not yet
have. The paragraph states this project's 0.x operating policy; it
does not claim that semantic versioning universally assigns every such
change to a minor release. An adopter who prefers the stricter reading
should treat this release as breaking and plan the migration below
accordingly — the required work is identical either way.