1414 * uncommanded — a typed-only skill with no opencode command to type.
1515 * unmirrored — a Codex copy of a skill that has drifted from the Claude Code original.
1616 * duplicated — two agent files with one body.
17- *
18- * Contributor docs are checked separately for inline-code paths and npm packages that resolve to
19- * nothing in the checkout.
17+ * stale — a contributor document names a repository path or npm package that does not resolve.
2018 *
2119 * `unread` runs before all of them and reports this gate's own blind spot: an agent file the
2220 * classifier never opened, which every check below would otherwise read as a file that says nothing.
@@ -178,12 +176,41 @@ const INTENTIONALLY_MISSING_PATHS = [
178176 value : "webapp/.env" ,
179177 reason : "a developer-local environment file that must stay untracked" ,
180178 } ,
179+ {
180+ document : "docs/contributor/agent/workspace-abi.mdx" ,
181+ value : "inputs/manifest.json" ,
182+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
183+ } ,
184+ {
185+ document : "docs/contributor/agent/workspace-abi.mdx" ,
186+ value : "out/result.json" ,
187+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
188+ } ,
189+ {
190+ document : "docs/contributor/agent/workspace-abi.mdx" ,
191+ value : "out/watchdog-killed.json" ,
192+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
193+ } ,
194+ {
195+ document : "docs/contributor/artifact-source-contract.mdx" ,
196+ value : "inputs/manifest.json" ,
197+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
198+ } ,
199+ {
200+ document : "docs/contributor/practice-review-glossary.mdx" ,
201+ value : "inputs/history/delta.json" ,
202+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
203+ } ,
204+ {
205+ document : "docs/contributor/practice-review-glossary.mdx" ,
206+ value : "out/feedback.json" ,
207+ reason : "a path inside the staged agent workspace, not the repository checkout" ,
208+ } ,
181209] satisfies readonly ClaimException [ ] ;
182210
183211const PACKAGE_NAME = / ^ (?: @ [ a - z 0 - 9 ] [ a - z 0 - 9 . _ - ] * \/ [ a - z 0 - 9 ] [ a - z 0 - 9 . _ - ] * | [ a - z 0 - 9 ] [ a - z 0 - 9 . _ - ] * ) $ / ;
184212const PACKAGE_SHAPED = / - (?: c l i | c o n f i g | c o r e | j s | n o d e | p a c k a g e | p l u g i n | r e a c t | s d k | t e s t | t s ) $ / ;
185213const FILE_SHAPED = / \. (?: j a v a | j s | j s o n c ? | m d x ? | m j s | s h | t s | t s x | x m l | y a ? m l ) $ / ;
186- const RUNTIME_ROOTS = new Set ( [ "inputs" , "out" ] ) ;
187214const exists = ( repo : Repo , path : string ) : boolean =>
188215 repo . present . has ( path ) || repo . paths . some ( ( present ) => present . startsWith ( `${ path } /` ) ) ;
189216
@@ -225,9 +252,7 @@ function looksLikePath(value: string, roots: ReadonlySet<string>): boolean {
225252 roots . has ( first ) ||
226253 ( first . startsWith ( "." ) && value . includes ( "/" ) ) ||
227254 ( value . includes ( "/" ) && basename ( value ) . startsWith ( "." ) ) ||
228- ( FILE_SHAPED . test ( value ) &&
229- ! RUNTIME_ROOTS . has ( first ) &&
230- ( value . includes ( "/" ) || / \. m d x ? $ / . test ( value ) ) )
255+ ( FILE_SHAPED . test ( value ) && ( value . includes ( "/" ) || / \. m d x ? $ / . test ( value ) ) )
231256 ) ;
232257}
233258
@@ -256,7 +281,15 @@ function staleContributorClaims(repo: Repo): readonly string[] {
256281 const failures : string [ ] = [ ] ;
257282 for ( const file of repo . present . values ( ) ) {
258283 if ( file . kind !== "text" || ! isContributorDoc ( file . path ) ) continue ;
259- for ( const value of new Set ( codeSpans ( file . content , file . path . endsWith ( ".mdx" ) ) ) ) {
284+ let spans : readonly string [ ] ;
285+ try {
286+ spans = codeSpans ( file . content , file . path . endsWith ( ".mdx" ) ? "mdx" : "markdown" ) ;
287+ } catch ( error ) {
288+ throw new Error ( `${ file . path } : ${ error instanceof Error ? error . message : String ( error ) } ` , {
289+ cause : error ,
290+ } ) ;
291+ }
292+ for ( const value of new Set ( spans ) ) {
260293 const candidate = value . replace ( / [ . , : ; ] $ / , "" ) . replace ( / # .* $ / , "" ) ;
261294 if ( candidate . includes ( "…" ) || candidate . includes ( "..." ) || / [ * < > { } $ \s ] / . test ( candidate ) )
262295 continue ;
@@ -378,7 +411,7 @@ interface MarkdownNode {
378411 readonly children ?: readonly MarkdownNode [ ] ;
379412}
380413
381- export function codeSpans ( markdown : string , mdx = false ) : readonly string [ ] {
414+ export function codeSpans ( markdown : string , syntax : "markdown" | "mdx" ) : readonly string [ ] {
382415 const spans : string [ ] = [ ] ;
383416 const visit = ( node : MarkdownNode ) : void => {
384417 if ( node . type === "inlineCode" && typeof node . value === "string" ) {
@@ -389,7 +422,9 @@ export function codeSpans(markdown: string, mdx = false): readonly string[] {
389422 visit (
390423 fromMarkdown (
391424 markdown ,
392- mdx ? { extensions : [ mdxjs ( ) ] , mdastExtensions : [ mdxFromMarkdown ( ) ] } : undefined ,
425+ syntax === "mdx"
426+ ? { extensions : [ mdxjs ( ) ] , mdastExtensions : [ mdxFromMarkdown ( ) ] }
427+ : undefined ,
393428 ) ,
394429 ) ;
395430 return spans ;
@@ -766,6 +801,6 @@ if (process.argv[1] === import.meta.filename) {
766801 console . log (
767802 `check-agent-instructions: ${ repo . guides . length } AGENTS.md reach Claude Code and opencode; ` +
768803 `${ skills . length } skills, ${ mirrored } of them mirrored for Codex, ` +
769- `${ repo . commands . length } opencode commands; contributor-doc paths and packages resolve .` ,
804+ `${ repo . commands . length } opencode commands; contributor-doc references checked .` ,
770805 ) ;
771806}
0 commit comments